From 517e866df539eae71c5c7745ed9a3bf953b3e5e9 Mon Sep 17 00:00:00 2001 From: nicu-da Date: Thu, 30 Sep 2021 04:20:46 -0700 Subject: [PATCH] Extract common code for command dedup conformance tests [KVL-1090] (#11092) * Extract common code for command dedup conformance tests CHANGELOG_BEGIN CHANGELOG_END --- .../CommandDeduplicationBase.scala | 201 +++++++----------- .../KVCommandDeduplicationBase.scala | 9 +- 2 files changed, 79 insertions(+), 131 deletions(-) diff --git a/ledger/ledger-api-test-tool/src/main/scala/com/daml/ledger/api/testtool/infrastructure/deduplication/CommandDeduplicationBase.scala b/ledger/ledger-api-test-tool/src/main/scala/com/daml/ledger/api/testtool/infrastructure/deduplication/CommandDeduplicationBase.scala index 24c4974f771a..bc6d8377e2ea 100644 --- a/ledger/ledger-api-test-tool/src/main/scala/com/daml/ledger/api/testtool/infrastructure/deduplication/CommandDeduplicationBase.scala +++ b/ledger/ledger-api-test-tool/src/main/scala/com/daml/ledger/api/testtool/infrastructure/deduplication/CommandDeduplicationBase.scala @@ -11,6 +11,7 @@ import com.daml.ledger.api.testtool.infrastructure.LedgerTestSuite import com.daml.ledger.api.testtool.infrastructure.ProtobufConverters._ import com.daml.ledger.api.testtool.infrastructure.deduplication.CommandDeduplicationBase.DeduplicationFeatures import com.daml.ledger.api.testtool.infrastructure.participant.ParticipantTestContext +import com.daml.ledger.api.v1.command_service.SubmitAndWaitRequest import com.daml.ledger.api.v1.command_submission_service.SubmitRequest import com.daml.ledger.api.v1.commands.Commands.DeduplicationPeriod import com.daml.ledger.api.v1.completion.Completion @@ -19,7 +20,6 @@ import com.daml.ledger.client.binding.Primitive.Party import com.daml.ledger.test.model.DA.Types.Tuple2 import com.daml.ledger.test.model.Test.{Dummy, DummyWithAnnotation, TextKey, TextKeyOperations} import com.daml.timer.Delayed -import io.grpc.Status import io.grpc.Status.Code import scala.annotation.nowarn @@ -55,55 +55,44 @@ private[testtool] abstract class CommandDeduplicationBase( runConcurrently = false, )(implicit ec => configuredParticipants => { case Participants(Participant(ledger, party)) => - lazy val requestA1 = ledger - .submitRequest(party, DummyWithAnnotation(party, "First submission").create.command) + val request = ledger + .submitRequest(party, DummyWithAnnotation(party, "Duplicate command").create.command) .update( _.commands.deduplicationPeriod := DeduplicationPeriod.DeduplicationTime( deduplicationDuration.asProtobuf ) ) - lazy val requestA2 = ledger - .submitRequest(party, DummyWithAnnotation(party, "Second submission").create.command) - .update( - _.commands.deduplicationPeriod := DeduplicationPeriod - .DeduplicationDuration( - deduplicationDuration.asProtobuf - ), //same semantics as `DeduplicationTime` - _.commands.commandId := requestA1.commands.get.commandId, - ) runGivenDeduplicationWait(configuredParticipants) { deduplicationWait => for { - // Submit command A (first deduplication window) + // Submit command (first deduplication window) // Note: the second submit() in this block is deduplicated and thus rejected by the ledger API server, // only one submission is therefore sent to the ledger. - completion1 <- submitRequestAndAssertCompletionAccepted(ledger)(requestA1, party) - _ <- submitRequestAndAssertDeduplication(ledger)(requestA1, party) + completion1 <- submitRequestAndAssertCompletionAccepted(ledger)(request, party) + _ <- submitRequestAndAssertDeduplication(ledger)(request, party) // Wait until the end of first deduplication window _ <- Delayed.by(deduplicationWait)(()) - // Submit command A (second deduplication window) + // Submit command (second deduplication window) // Note: the deduplication window is guaranteed to have passed on both // the ledger API server and the ledger itself, since the test waited more than // `deduplicationSeconds` after receiving the first command *completion*. // The first submit() in this block should therefore lead to an accepted transaction. - completion2 <- submitRequestAndAssertCompletionAccepted(ledger)(requestA2, party) - _ <- submitRequestAndAssertDeduplication(ledger)(requestA2, party) + completion2 <- submitRequestAndAssertCompletionAccepted(ledger)(request, party) + _ <- submitRequestAndAssertDeduplication(ledger)(request, party) // Inspect created contracts - activeContracts <- ledger.activeContracts(party) + _ <- assertPartyHasActiveContracts(ledger)( + party = party, + noOfActiveContracts = 2, + ) } yield { assert( - completion1.commandId == requestA1.commands.get.commandId, + completion1.commandId == request.commands.get.commandId, "The command ID of the first completion does not match the command ID of the submission", ) assert( - completion2.commandId == requestA2.commands.get.commandId, + completion2.commandId == request.commands.get.commandId, "The command ID of the second completion does not match the command ID of the submission", ) - - assert( - activeContracts.size == 2, - s"There should be 2 active contracts, but received $activeContracts", - ) } } } @@ -175,51 +164,30 @@ private[testtool] abstract class CommandDeduplicationBase( runConcurrently = false, )(implicit ec => configuredParticipants => { case Participants(Participant(ledger, party)) => - val requestA = ledger + val request = ledger .submitAndWaitRequest(party, Dummy(party).create.command) .update( _.commands.deduplicationTime := deduplicationDuration.asProtobuf ) runGivenDeduplicationWait(configuredParticipants) { deduplicationWait => for { - // Submit command A (first deduplication window) - _ <- ledger.submitAndWait(requestA) - failure1 <- ledger - .submitAndWait(requestA) - .mustFail("submitting a request for the second time, in the first deduplication window") + // Submit command (first deduplication window) + _ <- ledger.submitAndWait(request) + _ <- submitAndWaitRequestAndAssertDeduplication(ledger)(request) // Wait until the end of first deduplication window _ <- Delayed.by(deduplicationWait)(()) - // Submit command A (second deduplication window) - _ <- ledger.submitAndWait(requestA) - failure2 <- ledger - .submitAndWait(requestA) - .mustFail( - "submitting a request for the second time, in the second deduplication window" - ) + // Submit command (second deduplication window) + _ <- ledger.submitAndWait(request) + _ <- submitAndWaitRequestAndAssertDeduplication(ledger)(request) // Inspect created contracts - activeContracts <- ledger.activeContracts(party) - } yield { - assertGrpcError( - failure1, - Status.Code.ALREADY_EXISTS, - exceptionMessageSubstring = None, - checkDefiniteAnswerMetadata = true, + _ <- assertPartyHasActiveContracts(ledger)( + party = party, + noOfActiveContracts = 2, ) - assertGrpcError( - failure2, - Status.Code.ALREADY_EXISTS, - exceptionMessageSubstring = None, - checkDefiniteAnswerMetadata = true, - ) - - assert( - activeContracts.size == 2, - s"There should be 2 active contracts, but received $activeContracts", - ) - } + } yield {} } } ) @@ -236,46 +204,21 @@ private[testtool] abstract class CommandDeduplicationBase( for { // Submit a command as alice - _ <- ledger.submit(aliceRequest) - failure1 <- ledger - .submit(aliceRequest) - .mustFail("submitting a request as Alice for the second time") + _ <- submitRequestAndAssertCompletionAccepted(ledger)(aliceRequest, alice) + _ <- submitRequestAndAssertDeduplication(ledger)(aliceRequest) // Submit another command that uses same commandId, but is submitted by Bob - _ <- ledger.submit(bobRequest) - failure2 <- ledger - .submit(bobRequest) - .mustFail("submitting the same request as Bob, for the second time") - - // Wait for command completions and inspect the ledger state - _ <- ledger.firstCompletions(alice) - _ <- ledger.firstCompletions(bob) - aliceContracts <- ledger.activeContracts(alice) - bobContracts <- ledger.activeContracts(bob) - } yield { - assertGrpcError( - failure1, - Status.Code.ALREADY_EXISTS, - exceptionMessageSubstring = None, - checkDefiniteAnswerMetadata = true, + _ <- submitRequestAndAssertCompletionAccepted(ledger)(bobRequest, bob) + _ <- submitRequestAndAssertDeduplication(ledger)(bobRequest) + _ <- assertPartyHasActiveContracts(ledger)( + party = alice, + noOfActiveContracts = 1, ) - assertGrpcError( - failure2, - Status.Code.ALREADY_EXISTS, - exceptionMessageSubstring = None, - checkDefiniteAnswerMetadata = true, + _ <- assertPartyHasActiveContracts(ledger)( + party = bob, + noOfActiveContracts = 1, ) - - assert( - aliceContracts.length == 1, - s"Only one contract was expected to be seen by $alice but ${aliceContracts.length} appeared", - ) - - assert( - bobContracts.length == 1, - s"Only one contract was expected to be seen by $bob but ${bobContracts.length} appeared", - ) - } + } yield {} }) test( @@ -291,46 +234,36 @@ private[testtool] abstract class CommandDeduplicationBase( for { // Submit a command as alice _ <- ledger.submitAndWait(aliceRequest) - failure1 <- ledger - .submitAndWait(aliceRequest) - .mustFail("submitting a request as Alice for the second time") + _ <- submitAndWaitRequestAndAssertDeduplication(ledger)(aliceRequest) // Submit another command that uses same commandId, but is submitted by Bob _ <- ledger.submitAndWait(bobRequest) - failure2 <- ledger - .submitAndWait(bobRequest) - .mustFail("submitting the same request as Bob, for the second time") - + _ <- submitAndWaitRequestAndAssertDeduplication(ledger)(bobRequest) // Inspect the ledger state - aliceContracts <- ledger.activeContracts(alice) - bobContracts <- ledger.activeContracts(bob) - } yield { - assertGrpcError( - failure1, - Status.Code.ALREADY_EXISTS, - exceptionMessageSubstring = None, - checkDefiniteAnswerMetadata = true, + _ <- assertPartyHasActiveContracts(ledger)( + party = alice, + noOfActiveContracts = 1, ) - assertGrpcError( - failure2, - Status.Code.ALREADY_EXISTS, - exceptionMessageSubstring = None, - checkDefiniteAnswerMetadata = true, - ) - - assert( - aliceContracts.length == 1, - s"Only one contract was expected to be seen by $alice but ${aliceContracts.length} appeared", - ) - - assert( - bobContracts.length == 1, - s"Only one contract was expected to be seen by $bob but ${bobContracts.length} appeared", + _ <- assertPartyHasActiveContracts(ledger)( + party = bob, + noOfActiveContracts = 1, ) - } + } yield {} }) - def submitRequestAndAssertCompletionAccepted( + protected def assertPartyHasActiveContracts( + ledger: ParticipantTestContext + )(party: Party, noOfActiveContracts: Int)(implicit ec: ExecutionContext): Future[Unit] = { + ledger + .activeContracts(party) + .map(contracts => + assert( + contracts.length == noOfActiveContracts, + s"Expected $noOfActiveContracts active contracts for $party but found ${contracts.length} active contracts", + ) + ) + } + protected def submitRequestAndAssertCompletionAccepted( ledger: ParticipantTestContext )(request: SubmitRequest, parties: Party*)(implicit ec: ExecutionContext): Future[Completion] = { submitRequestAndAssertCompletionStatus(ledger)(request, Code.OK, parties: _*) @@ -369,6 +302,22 @@ private[testtool] abstract class CommandDeduplicationBase( ) ) + protected def submitAndWaitRequestAndAssertDeduplication(ledger: ParticipantTestContext)( + request: SubmitAndWaitRequest + )(implicit ec: ExecutionContext): Future[Unit] = { + ledger + .submitAndWait(request) + .mustFail("Request was accepted but we were expecting it to fail with a duplicate error") + .map( + assertGrpcError( + _, + expectedCode = Code.ALREADY_EXISTS, + exceptionMessageSubstring = None, + checkDefiniteAnswerMetadata = true, + ) + ) + } + protected def submitRequestAndAssertAsyncDeduplication(ledger: ParticipantTestContext)( request: SubmitRequest, parties: Party* diff --git a/ledger/ledger-api-test-tool/src/main/scala/com/daml/ledger/api/testtool/infrastructure/deduplication/KVCommandDeduplicationBase.scala b/ledger/ledger-api-test-tool/src/main/scala/com/daml/ledger/api/testtool/infrastructure/deduplication/KVCommandDeduplicationBase.scala index d9a16d8f3d7f..4b71a9951b4e 100644 --- a/ledger/ledger-api-test-tool/src/main/scala/com/daml/ledger/api/testtool/infrastructure/deduplication/KVCommandDeduplicationBase.scala +++ b/ledger/ledger-api-test-tool/src/main/scala/com/daml/ledger/api/testtool/infrastructure/deduplication/KVCommandDeduplicationBase.scala @@ -62,7 +62,10 @@ abstract class KVCommandDeduplicationBase( // Deduplication has finished completion2 <- submitRequestAndAssertCompletionAccepted(ledger)(request, party) // Inspect created contracts - activeContracts <- ledger.activeContracts(party) + _ <- assertPartyHasActiveContracts(ledger)( + party = party, + noOfActiveContracts = 2, + ) } yield { assert( completion1.commandId == request.commands.get.commandId, @@ -95,10 +98,6 @@ abstract class KVCommandDeduplicationBase( s"Second completion deduplication period [${completion1.deduplicationPeriod}] is not the max deduplication", ) } - assert( - activeContracts.size == 2, - s"There should be 2 active contracts, but received $activeContracts", - ) } } }