Add ledger API tests about command deduplication information in completions [KVL-1057] (#10748)

* Add Ledger API Test Tool tests about command deduplication information present in completions

CHANGELOG_BEGIN
CHANGELOG_END

* Address review comments

* Fix offset reference and move new tests to a separate, optional suite

* Cover rejections as well

* Test both the command and command submission services

* Run new test suite (only) on sandbox-classic append-only and daml-on-sql

* Use the append-only schema with daml-on-sql for CommandDeduplicationInfoIT

* Fix tests except offset, successful completions only.

* Remove completion offset test as it's not supported by most ledgers

* Remove support for multiple submissions as rejection completions are not being tested for the moment

* Consolidate test cases for faster run

* Avoid forbidden characters in short identifiers

* Clarify assert

* Remove wrong test about deduplication time being preserved in completion

* Adhere to the Scala style guide

* Eliminate some code duplication

* fmt

* Make 3e404be compile

* Never assume a specific deduplication period format in completions

* Code cleanup

* Pass party to completionStreamRequest to avoid ILLEGAL_ARG failures

* Enable in append-only mode for all ledgers that support it

* Clarify that the new tests are append-only-only

* Update ledger/ledger-on-sql/BUILD.bazel

Fix suite name

* Update ledger/sandbox-classic/BUILD.bazel

Fix suite name

* fmt
This commit is contained in:
fabiotudone-da 2021-09-09 09:56:14 +02:00 committed by GitHub
parent 61a07b1986
commit a03f52a15e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 327 additions and 9 deletions

View File

@ -166,6 +166,22 @@ conformance_test(
],
)
conformance_test(
name = "conformance-test-append-only-command-completion-dedup-info",
server = ":daml-on-sql-ephemeral-postgresql",
server_args = [
"--ledgerid=conformance-test",
"--port=6865",
"--enable-append-only-schema",
],
test_tool_args = [
"--verbose",
"--include=" +
"AppendOnlyCompletionDeduplicationInfoITCommandService" +
",AppendOnlyCompletionDeduplicationInfoITCommandSubmissionService",
],
)
genrule(
name = "docs",
srcs = [

View File

@ -87,6 +87,7 @@ da_scala_binary(
"//ledger/test-common:test-common-%s" % lf_version,
"//ledger/test-common:model-tests-%s.scala" % lf_version,
"//ledger/test-common:dar-files-%s-lib" % lf_version,
"//ledger-api/grpc-definitions:ledger_api_proto_scala",
"//libs-scala/build-info",
"//libs-scala/grpc-utils",
"//libs-scala/resources",
@ -139,6 +140,7 @@ da_scala_binary(
deps = [
"//daml-lf/language",
"//ledger/test-common:dar-files-%s-lib" % lf_version,
"//ledger-api/grpc-definitions:ledger_api_proto_scala",
":ledger-api-test-tool-%s-lib" % lf_version,
":ledger-api-test-tool-%s-test-suites" % lf_version,
],

View File

@ -10,8 +10,6 @@ import scala.collection.mutable.ListBuffer
import scala.concurrent.{ExecutionContext, Future}
private[testtool] abstract class LedgerTestSuite {
val name: String = getClass.getSimpleName
private val testCaseBuffer: ListBuffer[LedgerTestCase] = ListBuffer()
final lazy val tests: Vector[LedgerTestCase] = testCaseBuffer.toVector
@ -38,4 +36,6 @@ private[testtool] abstract class LedgerTestSuite {
)
)
}
private[testtool] def name: String = getClass.getSimpleName
}

View File

@ -0,0 +1,192 @@
// Copyright (c) 2021 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.daml.ledger.api.testtool.suites
import com.daml.ledger.api.SubmissionIdGenerator
import com.daml.ledger.api.testtool.infrastructure.Allocation._
import com.daml.ledger.api.testtool.infrastructure.LedgerTestSuite
import com.daml.ledger.api.testtool.infrastructure.participant.ParticipantTestContext
import com.daml.ledger.api.testtool.suites.AppendOnlyCompletionDeduplicationInfoIT._
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.Command
import com.daml.ledger.api.v1.completion.Completion
import com.daml.ledger.api.v1.ledger_offset.LedgerOffset
import com.daml.ledger.client.binding
import com.daml.ledger.client.binding.Primitive
import com.daml.ledger.test.model.Test.Dummy
import com.daml.lf.data.Ref
import com.daml.lf.data.Ref.SubmissionId
import com.daml.platform.testing.WithTimeout
import io.grpc.Status
import scala.concurrent.duration.DurationInt
import scala.concurrent.{ExecutionContext, Future}
final class AppendOnlyCompletionDeduplicationInfoIT[ServiceRequest](
service: Service[ServiceRequest]
) extends LedgerTestSuite {
private val serviceName: String = service.productPrefix
override private[testtool] def name = super.name + serviceName
test(
shortIdentifier = s"CCDIIncludeDedupInfo$serviceName",
description = s"Deduplication information is preserved in completions ($serviceName)",
allocate(SingleParty),
)(implicit ec => { case Participants(Participant(ledger, party)) =>
val requestWithoutSubmissionId = service.buildRequest(ledger, party)
val requestWithSubmissionId = service.buildRequest(ledger, party, Some(RandomSubmissionId))
for {
optNoDeduplicationSubmittedCompletion <- service.submitRequest(
ledger,
party,
requestWithoutSubmissionId,
)
optSubmissionIdSubmittedCompletion <- service
.submitRequest(ledger, party, requestWithSubmissionId)
} yield {
assertApplicationIdIsPreserved(ledger.applicationId, optNoDeduplicationSubmittedCompletion)
assertSubmissionIdIsGenerated(optNoDeduplicationSubmittedCompletion)
assertDeduplicationPeriodIsReported(optNoDeduplicationSubmittedCompletion)
assertSubmissionIdIsPreserved(optSubmissionIdSubmittedCompletion, RandomSubmissionId)
}
})
}
private[testtool] object AppendOnlyCompletionDeduplicationInfoIT {
private[testtool] sealed trait Service[ProtoRequestType] extends Serializable with Product {
def buildRequest(
ledger: ParticipantTestContext,
party: Primitive.Party,
optSubmissionId: Option[Ref.SubmissionId] = None,
): ProtoRequestType
def submitRequest(
ledger: ParticipantTestContext,
party: Primitive.Party,
request: ProtoRequestType,
)(implicit ec: ExecutionContext): Future[Option[Completion]]
}
case object CommandService extends Service[SubmitAndWaitRequest] {
override def buildRequest(
ledger: ParticipantTestContext,
party: binding.Primitive.Party,
optSubmissionId: Option[SubmissionId],
): SubmitAndWaitRequest = {
val request = ledger.submitAndWaitRequest(party, simpleCreate(party))
optSubmissionId
.map { submissionId =>
request.update(_.commands.submissionId := submissionId)
}
.getOrElse(request)
}
override def submitRequest(
ledger: ParticipantTestContext,
party: binding.Primitive.Party,
request: SubmitAndWaitRequest,
)(implicit ec: ExecutionContext): Future[Option[Completion]] =
for {
offset <- ledger.currentEnd()
_ <- ledger.submitAndWait(request)
completion <- singleCompletionAfterOffset(ledger, party, offset)
} yield completion
}
case object CommandSubmissionService extends Service[SubmitRequest] {
override def buildRequest(
ledger: ParticipantTestContext,
party: binding.Primitive.Party,
optSubmissionId: Option[SubmissionId],
): SubmitRequest = {
val request = ledger.submitRequest(party, simpleCreate(party))
optSubmissionId
.map { submissionId =>
request.update(_.commands.submissionId := submissionId)
}
.getOrElse(request)
}
override def submitRequest(
ledger: ParticipantTestContext,
party: binding.Primitive.Party,
request: SubmitRequest,
)(implicit ec: ExecutionContext): Future[Option[Completion]] =
for {
offset <- ledger.currentEnd()
_ <- ledger.submit(request)
completion <- singleCompletionAfterOffset(ledger, party, offset)
} yield completion
}
private def singleCompletionAfterOffset(
ledger: ParticipantTestContext,
party: binding.Primitive.Party,
offset: LedgerOffset,
): Future[Option[Completion]] =
WithTimeout(5.seconds)(
ledger.findCompletion(ledger.completionStreamRequest(offset)(party))(_ => true)
)
private def assertSubmissionIdIsPreserved(
optCompletion: Option[Completion],
requestedSubmissionId: Ref.SubmissionId,
): Unit = {
val submissionIdCompletion = assertDefined(optCompletion)
val actualSubmissionId = submissionIdCompletion.submissionId
assert(submissionIdCompletion.status.forall(_.code == Status.Code.OK.value()))
assert(
actualSubmissionId == requestedSubmissionId,
"Wrong submission ID in completion, " +
s"expected: $requestedSubmissionId, actual: $actualSubmissionId",
)
}
private def assertDeduplicationPeriodIsReported(
optCompletion: Option[Completion]
): Unit = {
val completion = assertDefined(optCompletion)
assert(completion.status.forall(_.code == Status.Code.OK.value()))
assert(completion.deduplicationPeriod.isDefined, "The deduplication period was not reported")
}
private def assertSubmissionIdIsGenerated(optCompletion: Option[Completion]): Unit = {
val completion = assertDefined(optCompletion)
assert(completion.status.forall(_.code == Status.Code.OK.value()))
assert(
Ref.SubmissionId.fromString(completion.submissionId).isRight,
"Missing or invalid submission ID in completion",
)
}
private def assertApplicationIdIsPreserved(
requestedApplicationId: String,
optCompletion: Option[Completion],
): Unit = {
val expectedApplicationId = requestedApplicationId
assertDefined(optCompletion)
val applicationIdCompletion = optCompletion.get
assert(applicationIdCompletion.status.forall(_.code == Status.Code.OK.value()))
val actualApplicationId = applicationIdCompletion.applicationId
assert(
Ref.ApplicationId.fromString(actualApplicationId).contains(expectedApplicationId),
"Wrong application ID in completion, " +
s"expected: $expectedApplicationId, actual: $actualApplicationId",
)
}
private def assertDefined(optCompletion: Option[Completion]): Completion = {
assert(optCompletion.isDefined, "No completion has been produced")
optCompletion.get
}
private def simpleCreate(party: Primitive.Party): Command = Dummy(party).create.command
private val RandomSubmissionId =
Ref.SubmissionId.assertFromString(SubmissionIdGenerator.Random.generate())
}

View File

@ -3,8 +3,6 @@
package com.daml.ledger.api.testtool.suites
import java.util.regex.Pattern
import com.daml.ledger.api.testtool.infrastructure.Allocation._
import com.daml.ledger.api.testtool.infrastructure.Assertions._
import com.daml.ledger.api.testtool.infrastructure.LedgerTestSuite
@ -13,6 +11,7 @@ import com.daml.ledger.test.model.Test.Dummy._
import com.daml.platform.testing.{TimeoutException, WithTimeout}
import io.grpc.Status
import java.util.regex.Pattern
import scala.concurrent.duration.DurationInt
final class CommandSubmissionCompletionIT extends LedgerTestSuite {
@ -163,5 +162,4 @@ final class CommandSubmissionCompletionIT extends LedgerTestSuite {
// Nothing to do, if the two completions are found the test is passed
}
})
}

View File

@ -3,13 +3,16 @@
package com.daml.ledger.api.testtool.tests
import java.nio.file.Path
import com.daml.ledger.api.testtool.infrastructure.{BenchmarkReporter, Envelope, LedgerTestSuite}
import com.daml.ledger.api.testtool.suites.AppendOnlyCompletionDeduplicationInfoIT.{
CommandService,
CommandSubmissionService,
}
import com.daml.ledger.api.testtool.suites._
import com.daml.lf.language.LanguageVersion
import com.daml.ledger.test.TestDar
import com.daml.lf.language.LanguageVersion
import java.nio.file.Path
import scala.collection.SortedSet
import scala.concurrent.duration.FiniteDuration
@ -64,6 +67,8 @@ object Tests {
): Vector[LedgerTestSuite] =
Vector(
new CommandDeduplicationOffsetIT,
new AppendOnlyCompletionDeduplicationInfoIT(CommandService),
new AppendOnlyCompletionDeduplicationInfoIT(CommandSubmissionService),
new KVCommandDeduplicationIT(timeoutScaleFactor, ledgerClockGranularity),
new ContractIdIT,
new MultiPartySubmissionIT,
@ -93,5 +98,4 @@ object Tests {
private[testtool] val PerformanceTestsKeys: SortedSet[String] =
SortedSet(Envelope.All.map(_.name): _*)
}

View File

@ -150,6 +150,24 @@ conformance_test(
],
)
conformance_test(
name = "conformance-test-append-only-command-completion-dedup-info",
ports = [6865],
server = ":app",
server_args = [
"--contract-id-seeding=testing-weak",
"--index-append-only-schema",
"--mutable-contract-state-cache",
"--participant=participant-id=example,port=6865",
],
test_tool_args = [
"--verbose",
"--include=" +
"AppendOnlyCompletionDeduplicationInfoITCommandService" +
",AppendOnlyCompletionDeduplicationInfoITCommandSubmissionService",
],
)
conformance_test(
name = "conformance-test-min-1.14",
lf_versions = ["1.14"],

View File

@ -396,6 +396,64 @@ conformance_test(
],
)
conformance_test(
name = "conformance-test-append-only-postgres-command-completion-dedup-info",
ports = [6865],
server = ":conformance-test-postgresql-bin",
server_args = [
"--contract-id-seeding=testing-weak",
"--participant participant-id=conformance-test,port=6865,contract-state-cache-max-size=1,contract-key-state-cache-max-size=1",
"--index-append-only-schema",
"--mutable-contract-state-cache",
],
tags = [],
test_tool_args = [
"--verbose",
"--include=" +
"AppendOnlyCompletionDeduplicationInfoITCommandService" +
",AppendOnlyCompletionDeduplicationInfoITCommandSubmissionService",
],
)
conformance_test(
name = "conformance-test-append-only-h2-command-completion-dedup-info",
ports = [6865],
server = ":conformance-test-h2-memory-bin",
server_args = [
"--contract-id-seeding=testing-weak",
"--participant participant-id=conformance-test,port=6865,contract-state-cache-max-size=1,contract-key-state-cache-max-size=1",
"--index-append-only-schema",
"--mutable-contract-state-cache",
"--jdbc-url=jdbc:h2:mem:daml-on-sql-conformance-test",
],
tags = [],
test_tool_args = [
"--verbose",
"--include=" +
"AppendOnlyCompletionDeduplicationInfoITCommandService" +
",AppendOnlyCompletionDeduplicationInfoITCommandSubmissionService",
],
)
conformance_test(
name = "conformance-test-append-only-oracle-command-completion-dedup-info",
ports = [6865],
server = ":conformance-test-oracle-bin",
server_args = [
"--contract-id-seeding=testing-weak",
"--participant participant-id=conformance-test,port=6865,contract-state-cache-max-size=1,contract-key-state-cache-max-size=1",
"--index-append-only-schema",
"--mutable-contract-state-cache",
],
tags = [] if oracle_testing else ["manual"],
test_tool_args = [
"--verbose",
"--include=" +
"AppendOnlyCompletionDeduplicationInfoITCommandService" +
",AppendOnlyCompletionDeduplicationInfoITCommandSubmissionService",
],
)
conformance_test(
name = "conformance-test-oracle",
ports = [6865],

View File

@ -437,3 +437,19 @@ server_conformance_test(
"--include=ContractIdIT:Accept",
],
)
server_conformance_test(
name = "conformance-test-append-only-command-completion-dedup-info",
server_args = [
"--wall-clock-time",
"--contract-id-seeding=testing-weak",
"--enable-append-only-schema",
],
servers = ONLY_POSTGRES_SERVER,
test_tool_args = [
"--open-world",
"--include=" +
"AppendOnlyCompletionDeduplicationInfoITCommandService" +
",AppendOnlyCompletionDeduplicationInfoITCommandSubmissionService",
],
)

View File

@ -339,6 +339,20 @@ server_conformance_test(
],
)
server_conformance_test(
name = "next-conformance-test-append-only-command-completion-dedup-info",
server_args = [
"--enable-append-only-schema",
],
servers = {"postgresql": NEXT_SERVERS["postgresql"]},
test_tool_args = [
"--open-world",
"--include=" +
"AppendOnlyCompletionDeduplicationInfoITCommandService" +
",AppendOnlyCompletionDeduplicationInfoITCommandSubmissionService",
],
)
server_conformance_test(
name = "next-conformance-test-command-deduplication",
servers = NEXT_SERVERS,