diff --git a/data/beaconrestapi/src/integration-test/java/tech/pegasys/teku/beaconrestapi/v2/beacon/PostAttestationsV2ElectraIntegrationTest.java b/data/beaconrestapi/src/integration-test/java/tech/pegasys/teku/beaconrestapi/v2/beacon/PostAttestationsV2ElectraIntegrationTest.java new file mode 100644 index 00000000000..23c761613c5 --- /dev/null +++ b/data/beaconrestapi/src/integration-test/java/tech/pegasys/teku/beaconrestapi/v2/beacon/PostAttestationsV2ElectraIntegrationTest.java @@ -0,0 +1,78 @@ +/* + * Copyright Consensys Software Inc., 2024 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package tech.pegasys.teku.beaconrestapi.v2.beacon; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import okhttp3.Response; +import org.junit.jupiter.api.BeforeEach; +import tech.pegasys.teku.beaconrestapi.handlers.v2.beacon.PostAttestationsV2; +import tech.pegasys.teku.infrastructure.json.JsonUtil; +import tech.pegasys.teku.infrastructure.json.types.SerializableTypeDefinition; +import tech.pegasys.teku.spec.SpecMilestone; +import tech.pegasys.teku.spec.TestSpecFactory; +import tech.pegasys.teku.spec.datastructures.operations.Attestation; +import tech.pegasys.teku.spec.datastructures.operations.SingleAttestation; +import tech.pegasys.teku.spec.schemas.SchemaDefinitionsElectra; +import tech.pegasys.teku.spec.util.DataStructureUtil; + +public class PostAttestationsV2ElectraIntegrationTest extends PostAttestationsV2IntegrationTest { + protected SerializableTypeDefinition> attestationsListTypeDef; + + @BeforeEach + void setup() { + spec = TestSpecFactory.createMinimalElectra(); + specMilestone = SpecMilestone.ELECTRA; + startRestAPIAtGenesis(specMilestone); + dataStructureUtil = new DataStructureUtil(spec); + attestationsListTypeDef = + SerializableTypeDefinition.listOf( + SchemaDefinitionsElectra.required(spec.getGenesisSchemaDefinitions()) + .getSingleAttestationSchema() + .getJsonTypeDefinition()); + } + + @Override + protected List getAttestationList(final int listSize) { + final List attestations = new ArrayList<>(listSize); + for (int i = 0; i < listSize; i++) { + attestations.add(dataStructureUtil.randomSingleAttestation()); + } + return attestations; + } + + @Override + @SuppressWarnings("unchecked") + protected Response postAttestations(final List attestations, final String milestone) + throws IOException { + final SerializableTypeDefinition> attestationsListTypeDef = + SerializableTypeDefinition.listOf( + SchemaDefinitionsElectra.required(spec.getGenesisSchemaDefinitions()) + .getSingleAttestationSchema() + .getJsonTypeDefinition()); + if (milestone == null) { + return post( + PostAttestationsV2.ROUTE, + JsonUtil.serialize((List) attestations, attestationsListTypeDef)); + } + return post( + PostAttestationsV2.ROUTE, + JsonUtil.serialize((List) attestations, attestationsListTypeDef), + Collections.emptyMap(), + Optional.of(milestone)); + } +} diff --git a/data/beaconrestapi/src/integration-test/java/tech/pegasys/teku/beaconrestapi/v2/beacon/PostAttestationsV2IntegrationTest.java b/data/beaconrestapi/src/integration-test/java/tech/pegasys/teku/beaconrestapi/v2/beacon/PostAttestationsV2IntegrationTest.java index a5ceee89376..8a4d9b35036 100644 --- a/data/beaconrestapi/src/integration-test/java/tech/pegasys/teku/beaconrestapi/v2/beacon/PostAttestationsV2IntegrationTest.java +++ b/data/beaconrestapi/src/integration-test/java/tech/pegasys/teku/beaconrestapi/v2/beacon/PostAttestationsV2IntegrationTest.java @@ -20,13 +20,14 @@ import static tech.pegasys.teku.infrastructure.http.RestApiConstants.HEADER_CONSENSUS_VERSION; import com.fasterxml.jackson.databind.JsonNode; +import java.io.IOException; +import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.Locale; import java.util.Optional; import okhttp3.Response; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.TestTemplate; +import org.junit.jupiter.api.Test; import tech.pegasys.teku.beaconrestapi.AbstractDataBackedRestAPIIntegrationTest; import tech.pegasys.teku.beaconrestapi.handlers.v2.beacon.PostAttestationsV2; import tech.pegasys.teku.infrastructure.async.SafeFuture; @@ -35,84 +36,51 @@ import tech.pegasys.teku.infrastructure.json.types.SerializableTypeDefinition; import tech.pegasys.teku.infrastructure.unsigned.UInt64; import tech.pegasys.teku.spec.SpecMilestone; -import tech.pegasys.teku.spec.TestSpecContext; -import tech.pegasys.teku.spec.TestSpecInvocationContextProvider; +import tech.pegasys.teku.spec.TestSpecFactory; import tech.pegasys.teku.spec.datastructures.operations.Attestation; import tech.pegasys.teku.spec.util.DataStructureUtil; import tech.pegasys.teku.validator.api.SubmitDataError; -@TestSpecContext(milestone = {SpecMilestone.PHASE0, SpecMilestone.ELECTRA}) public class PostAttestationsV2IntegrationTest extends AbstractDataBackedRestAPIIntegrationTest { - private DataStructureUtil dataStructureUtil; - private SpecMilestone specMilestone; - private SerializableTypeDefinition> attestationsListTypeDef; + protected DataStructureUtil dataStructureUtil; + protected SpecMilestone specMilestone; @BeforeEach - void setup(final TestSpecInvocationContextProvider.SpecContext specContext) { - spec = specContext.getSpec(); - specMilestone = specContext.getSpecMilestone(); + void setup() { + spec = TestSpecFactory.createMinimalPhase0(); + specMilestone = SpecMilestone.PHASE0; startRestAPIAtGenesis(specMilestone); - dataStructureUtil = specContext.getDataStructureUtil(); - attestationsListTypeDef = - SerializableTypeDefinition.listOf( - spec.getGenesisSchemaDefinitions() - .getAttestationSchema() - .castTypeToAttestationSchema() - .getJsonTypeDefinition()); + dataStructureUtil = new DataStructureUtil(spec); } - @TestTemplate + @Test void shouldPostAttestations_NoErrors() throws Exception { - final List attestations = - List.of(dataStructureUtil.randomAttestation(), dataStructureUtil.randomAttestation()); + final List attestations = getAttestationList(2); when(validatorApiChannel.sendSignedAttestations(attestations)) .thenReturn(SafeFuture.completedFuture(Collections.emptyList())); - final Response response = - post( - PostAttestationsV2.ROUTE, - JsonUtil.serialize(attestations, attestationsListTypeDef), - Collections.emptyMap(), - Optional.of(specMilestone.name().toLowerCase(Locale.ROOT))); + final Response response = postAttestations(attestations, specMilestone.name()); assertThat(response.code()).isEqualTo(SC_OK); assertThat(response.body().string()).isEmpty(); } - @TestTemplate + @Test void shouldPartiallyPostAttestations_ReturnsErrors() throws Exception { final SubmitDataError firstSubmitDataError = new SubmitDataError(UInt64.ZERO, "Bad attestation"); final SubmitDataError secondSubmitDataError = new SubmitDataError(UInt64.ONE, "Very bad attestation"); - final List attestations; - if (specMilestone.isGreaterThanOrEqualTo(SpecMilestone.ELECTRA)) { - attestations = - List.of( - dataStructureUtil.randomAttestation(), - dataStructureUtil.randomAttestation(), - dataStructureUtil.randomAttestation()); - } else { - attestations = - List.of( - dataStructureUtil.randomAttestation(), - dataStructureUtil.randomAttestation(), - dataStructureUtil.randomAttestation()); - } + final List attestations = getAttestationList(3); when(validatorApiChannel.sendSignedAttestations(attestations)) .thenReturn( SafeFuture.completedFuture(List.of(firstSubmitDataError, secondSubmitDataError))); - final Response response = - post( - PostAttestationsV2.ROUTE, - JsonUtil.serialize(attestations, attestationsListTypeDef), - Collections.emptyMap(), - Optional.of(specMilestone.name().toLowerCase(Locale.ROOT))); + final Response response = postAttestations(attestations, specMilestone.name()); assertThat(response.code()).isEqualTo(SC_BAD_REQUEST); final JsonNode resultAsJsonNode = JsonTestUtil.parseAsJsonNode(response.body().string()); @@ -130,16 +98,14 @@ void shouldPartiallyPostAttestations_ReturnsErrors() throws Exception { .isEqualTo(secondSubmitDataError.message()); } - @TestTemplate + @Test void shouldFailWhenMissingConsensusHeader() throws Exception { - final List attestations = - List.of(dataStructureUtil.randomAttestation(), dataStructureUtil.randomAttestation()); + final List attestations = getAttestationList(2); when(validatorApiChannel.sendSignedAttestations(attestations)) .thenReturn(SafeFuture.completedFuture(Collections.emptyList())); - final Response response = - post(PostAttestationsV2.ROUTE, JsonUtil.serialize(attestations, attestationsListTypeDef)); + final Response response = postAttestations(attestations, null); assertThat(response.code()).isEqualTo(SC_BAD_REQUEST); @@ -148,20 +114,14 @@ void shouldFailWhenMissingConsensusHeader() throws Exception { .isEqualTo("Missing required header value for (%s)", HEADER_CONSENSUS_VERSION); } - @TestTemplate + @Test void shouldFailWhenBadConsensusHeaderValue() throws Exception { - final List attestations = - List.of(dataStructureUtil.randomAttestation(), dataStructureUtil.randomAttestation()); + final List attestations = getAttestationList(2); when(validatorApiChannel.sendSignedAttestations(attestations)) .thenReturn(SafeFuture.completedFuture(Collections.emptyList())); final String badConsensusHeaderValue = "NonExistingMileStone"; - final Response response = - post( - PostAttestationsV2.ROUTE, - JsonUtil.serialize(attestations, attestationsListTypeDef), - Collections.emptyMap(), - Optional.of(badConsensusHeaderValue)); + final Response response = postAttestations(attestations, badConsensusHeaderValue); assertThat(response.code()).isEqualTo(SC_BAD_REQUEST); @@ -172,4 +132,33 @@ void shouldFailWhenBadConsensusHeaderValue() throws Exception { "Invalid value for (%s) header: %s", HEADER_CONSENSUS_VERSION, badConsensusHeaderValue)); } + + protected List getAttestationList(final int listSize) { + final List attestations = new ArrayList<>(listSize); + for (int i = 0; i < listSize; i++) { + attestations.add(dataStructureUtil.randomAttestation()); + } + return attestations; + } + + @SuppressWarnings("unchecked") + protected Response postAttestations(final List attestations, final String milestone) + throws IOException { + final SerializableTypeDefinition> attestationsListTypeDef = + SerializableTypeDefinition.listOf( + spec.getGenesisSchemaDefinitions() + .getAttestationSchema() + .castTypeToAttestationSchema() + .getJsonTypeDefinition()); + if (milestone == null) { + return post( + PostAttestationsV2.ROUTE, + JsonUtil.serialize((List) attestations, attestationsListTypeDef)); + } + return post( + PostAttestationsV2.ROUTE, + JsonUtil.serialize((List) attestations, attestationsListTypeDef), + Collections.emptyMap(), + Optional.of(milestone)); + } }