mirror of
https://github.com/digital-asset/daml.git
synced 2024-09-20 09:17:43 +03:00
Port LedgerConfigurationServiceIT (#3188)
* Port LedgerConfigurationServiceIT * Remove unused imports * Fix compilation error * Port SemanticLedgerTestsIT * Expect failures for negative cases * Add meaningful parentheses * Do not require the ledger configuration test commands to succeed * Remove unused command tracking * Address https://github.com/digital-asset/daml/pull/3188#issuecomment-542616799 * Update unreleased.rst
This commit is contained in:
parent
2bc6b8d4f5
commit
83e305b667
@ -4,22 +4,18 @@
|
||||
package com.digitalasset.platform.tests.integration.ledger.api
|
||||
|
||||
import akka.stream.scaladsl.Sink
|
||||
import com.digitalasset.ledger.api.domain
|
||||
import com.digitalasset.ledger.api.testing.utils.{
|
||||
AkkaBeforeAndAfterAll,
|
||||
IsStatusException,
|
||||
SuiteResourceManagementAroundAll
|
||||
}
|
||||
import com.digitalasset.ledger.client.services.configuration.LedgerConfigurationClient
|
||||
import com.digitalasset.platform.api.grpc.GrpcApiUtil
|
||||
import com.digitalasset.platform.apitesting.{LedgerContext, MultiLedgerFixture}
|
||||
import com.digitalasset.platform.esf.TestExecutionSequencerFactory
|
||||
import io.grpc.Status
|
||||
import org.scalatest.concurrent.AsyncTimeLimitedTests
|
||||
import org.scalatest.time.Span
|
||||
import org.scalatest.time.SpanSugar._
|
||||
import org.scalatest.{AsyncWordSpec, Matchers, OptionValues}
|
||||
import scalaz.syntax.tag._
|
||||
|
||||
@SuppressWarnings(Array("org.wartremover.warts.Any"))
|
||||
class LedgerConfigurationServiceIT
|
||||
@ -49,17 +45,6 @@ class LedgerConfigurationServiceIT
|
||||
}
|
||||
}
|
||||
|
||||
"fail with the expected status on a ledger Id mismatch" in allFixtures { context =>
|
||||
new LedgerConfigurationClient(
|
||||
domain.LedgerId("not" + context.ledgerId.unwrap),
|
||||
context.ledgerConfigurationService).getLedgerConfiguration
|
||||
.runWith(Sink.head)(materializer)
|
||||
.failed map { ex =>
|
||||
IsStatusException(Status.NOT_FOUND.getCode)(ex)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,140 +0,0 @@
|
||||
// Copyright (c) 2019 The DAML Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package com.digitalasset.platform.tests.integration.ledger.api
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
|
||||
import akka.stream.scaladsl.Sink
|
||||
import com.digitalasset.ledger.api.testing.utils.MockMessages.ledgerEffectiveTime
|
||||
import com.digitalasset.ledger.api.testing.utils.{
|
||||
AkkaBeforeAndAfterAll,
|
||||
SuiteResourceManagementAroundAll
|
||||
}
|
||||
import com.digitalasset.ledger.api.v1.command_service.CommandServiceGrpc.CommandService
|
||||
import com.digitalasset.ledger.api.v1.command_service.SubmitAndWaitRequest
|
||||
import com.digitalasset.ledger.api.v1.command_submission_service.SubmitRequest
|
||||
import com.digitalasset.ledger.client.services.commands.{CommandClient, SynchronousCommandClient}
|
||||
import com.digitalasset.ledger.client.services.configuration.LedgerConfigurationClient
|
||||
import com.digitalasset.platform.apitesting.{LedgerContext, MultiLedgerFixture}
|
||||
import com.digitalasset.platform.esf.TestExecutionSequencerFactory
|
||||
import com.digitalasset.platform.tests.integration.ledger.api.commands.MultiLedgerCommandUtils
|
||||
import com.google.protobuf.empty.Empty
|
||||
import io.grpc.{Status, StatusRuntimeException}
|
||||
import org.scalatest.concurrent.AsyncTimeLimitedTests
|
||||
import org.scalatest.time.Span
|
||||
import org.scalatest.time.SpanSugar._
|
||||
import org.scalatest.{AsyncWordSpec, Matchers, OptionValues}
|
||||
|
||||
import scalaz.syntax.tag._
|
||||
import scalapb.lenses
|
||||
import scalapb.lenses.Lens
|
||||
|
||||
class SemanticLedgerConfigurationIT
|
||||
extends AsyncWordSpec
|
||||
with AkkaBeforeAndAfterAll
|
||||
with MultiLedgerFixture
|
||||
with MultiLedgerCommandUtils
|
||||
with SuiteResourceManagementAroundAll
|
||||
with TestExecutionSequencerFactory
|
||||
with AsyncTimeLimitedTests
|
||||
with Matchers
|
||||
with OptionValues {
|
||||
|
||||
override def timeLimit: Span = scaled(5.seconds)
|
||||
|
||||
private def configClient(ctx: LedgerContext): LedgerConfigurationClient =
|
||||
new LedgerConfigurationClient(ctx.ledgerId, ctx.ledgerConfigurationService)
|
||||
|
||||
private def newSyncClient(commandService: CommandService) =
|
||||
new SynchronousCommandClient(commandService)
|
||||
|
||||
private val newCommandId: () => String = {
|
||||
val atomicInteger = new AtomicInteger()
|
||||
() =>
|
||||
atomicInteger.incrementAndGet().toString
|
||||
}
|
||||
|
||||
private implicit class CommandClientOps(commandClient: CommandClient) {
|
||||
def send(request: SubmitRequest) =
|
||||
commandClient
|
||||
.withTimeProvider(None)
|
||||
.trackSingleCommand(request)
|
||||
|
||||
}
|
||||
|
||||
private def createRequest(
|
||||
ctx: LedgerContext,
|
||||
changes: Lens[SubmitAndWaitRequest, SubmitAndWaitRequest] => lenses.Mutation[
|
||||
SubmitAndWaitRequest]): SubmitAndWaitRequest = {
|
||||
submitAndWaitRequest.update(
|
||||
_.commands.ledgerId := ctx.ledgerId.unwrap,
|
||||
_.commands.commandId := newCommandId(),
|
||||
changes
|
||||
)
|
||||
}
|
||||
|
||||
"a ledger" when {
|
||||
"returning a minTTL in LedgerConfiguration" should {
|
||||
"accept an MRT just minTTL greater than LET" in allFixtures { context =>
|
||||
for {
|
||||
config <- configClient(context).getLedgerConfiguration.runWith(Sink.head)(materializer)
|
||||
syncClient = newSyncClient(context.commandService)
|
||||
request = createRequest(
|
||||
context,
|
||||
_.commands.maximumRecordTime.seconds := (ledgerEffectiveTime.seconds + config.minTtl.value.seconds))
|
||||
resp <- syncClient.submitAndWait(request)
|
||||
} yield (resp should equal(Empty()))
|
||||
}
|
||||
|
||||
"not accept an MRT less than minTTL greater than LET" in allFixtures { context =>
|
||||
val resF = for {
|
||||
config <- configClient(context).getLedgerConfiguration.runWith(Sink.head)(materializer)
|
||||
syncClient = newSyncClient(context.commandService)
|
||||
request = createRequest(
|
||||
context,
|
||||
_.commands.maximumRecordTime.seconds := ledgerEffectiveTime.seconds + config.minTtl.value.seconds - 1)
|
||||
|
||||
resp <- syncClient.submitAndWait(request)
|
||||
} yield (resp)
|
||||
|
||||
resF.failed.map { failure =>
|
||||
val exception = failure.asInstanceOf[StatusRuntimeException]
|
||||
exception.getStatus.getCode should equal(Status.Code.INVALID_ARGUMENT)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"returning a maxTTL in LedgerConfiguration" should {
|
||||
"accept an MRT exactly maxTTL greater than LET" in allFixtures { context =>
|
||||
for {
|
||||
config <- configClient(context).getLedgerConfiguration.runWith(Sink.head)(materializer)
|
||||
syncClient = newSyncClient(context.commandService)
|
||||
request = createRequest(
|
||||
context,
|
||||
_.commands.maximumRecordTime.seconds := ledgerEffectiveTime.seconds + config.maxTtl.value.seconds)
|
||||
resp <- syncClient.submitAndWait(request)
|
||||
} yield (resp should equal(Empty()))
|
||||
}
|
||||
|
||||
"not accept an MRT more than maxTTL greater than LET" in allFixtures { context =>
|
||||
val resF = for {
|
||||
config <- configClient(context).getLedgerConfiguration.runWith(Sink.head)(materializer)
|
||||
syncClient = newSyncClient(context.commandService)
|
||||
request = createRequest(
|
||||
context,
|
||||
_.commands.maximumRecordTime.seconds := ledgerEffectiveTime.seconds + config.maxTtl.value.seconds + 1)
|
||||
resp <- syncClient.submitAndWait(request)
|
||||
} yield (resp)
|
||||
|
||||
resF.failed.map { failure =>
|
||||
val exception = failure.asInstanceOf[StatusRuntimeException]
|
||||
exception.getStatus.getCode should equal(Status.Code.INVALID_ARGUMENT)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -11,6 +11,8 @@ import com.digitalasset.ledger.api.v1.admin.party_management_service.PartyManage
|
||||
import com.digitalasset.ledger.api.v1.admin.party_management_service.PartyManagementServiceGrpc.PartyManagementService
|
||||
import com.digitalasset.ledger.api.v1.command_service.CommandServiceGrpc
|
||||
import com.digitalasset.ledger.api.v1.command_service.CommandServiceGrpc.CommandService
|
||||
import com.digitalasset.ledger.api.v1.command_submission_service.CommandSubmissionServiceGrpc
|
||||
import com.digitalasset.ledger.api.v1.command_submission_service.CommandSubmissionServiceGrpc.CommandSubmissionService
|
||||
import com.digitalasset.ledger.api.v1.ledger_configuration_service.LedgerConfigurationServiceGrpc
|
||||
import com.digitalasset.ledger.api.v1.ledger_configuration_service.LedgerConfigurationServiceGrpc.LedgerConfigurationService
|
||||
import com.digitalasset.ledger.api.v1.ledger_identity_service.LedgerIdentityServiceGrpc
|
||||
@ -26,6 +28,7 @@ import io.grpc.Channel
|
||||
private[infrastructure] final class LedgerServices(channel: Channel) {
|
||||
val activeContracts: ActiveContractsService = ActiveContractsServiceGrpc.stub(channel)
|
||||
val command: CommandService = CommandServiceGrpc.stub(channel)
|
||||
val commandSubmission: CommandSubmissionService = CommandSubmissionServiceGrpc.stub(channel)
|
||||
val configuration: LedgerConfigurationService = LedgerConfigurationServiceGrpc.stub(channel)
|
||||
val identity: LedgerIdentityService = LedgerIdentityServiceGrpc.stub(channel)
|
||||
val partyManagement: PartyManagementService = PartyManagementServiceGrpc.stub(channel)
|
||||
|
@ -26,11 +26,13 @@ import com.digitalasset.ledger.api.v1.admin.party_management_service.{
|
||||
GetParticipantIdRequest
|
||||
}
|
||||
import com.digitalasset.ledger.api.v1.command_service.SubmitAndWaitRequest
|
||||
import com.digitalasset.ledger.api.v1.command_submission_service.SubmitRequest
|
||||
import com.digitalasset.ledger.api.v1.commands.{Command, Commands}
|
||||
import com.digitalasset.ledger.api.v1.event.Event.Event.Created
|
||||
import com.digitalasset.ledger.api.v1.event.{CreatedEvent, Event}
|
||||
import com.digitalasset.ledger.api.v1.ledger_configuration_service.{
|
||||
GetLedgerConfigurationRequest,
|
||||
GetLedgerConfigurationResponse,
|
||||
LedgerConfiguration
|
||||
}
|
||||
import com.digitalasset.ledger.api.v1.ledger_offset.LedgerOffset
|
||||
@ -437,6 +439,20 @@ private[testtool] final class ParticipantTestContext private[participant] (
|
||||
.flatMap(submitAndWaitForTransaction)
|
||||
.map(extractContracts)
|
||||
|
||||
def submitRequest(party: Party, commands: Command*): Future[SubmitRequest] =
|
||||
time().map(
|
||||
let =>
|
||||
new SubmitRequest(
|
||||
Some(new Commands(
|
||||
ledgerId = ledgerId,
|
||||
applicationId = applicationId,
|
||||
commandId = nextCommandId(),
|
||||
party = party.unwrap,
|
||||
ledgerEffectiveTime = timestamp(let),
|
||||
maximumRecordTime = timestamp(let.plusNanos(ttl.toNanos)),
|
||||
commands = commands
|
||||
))))
|
||||
|
||||
def submitAndWaitRequest(party: Party, commands: Command*): Future[SubmitAndWaitRequest] =
|
||||
time().map(
|
||||
let =>
|
||||
@ -451,6 +467,9 @@ private[testtool] final class ParticipantTestContext private[participant] (
|
||||
commands = commands
|
||||
))))
|
||||
|
||||
def submit(request: SubmitRequest): Future[Unit] =
|
||||
services.commandSubmission.submit(request).map(_ => ())
|
||||
|
||||
def submitAndWait(request: SubmitAndWaitRequest): Future[Unit] =
|
||||
services.command.submitAndWait(request).map(_ => ())
|
||||
|
||||
@ -463,26 +482,13 @@ private[testtool] final class ParticipantTestContext private[participant] (
|
||||
def submitAndWaitForTransactionTree(request: SubmitAndWaitRequest): Future[TransactionTree] =
|
||||
services.command.submitAndWaitForTransactionTree(request).map(_.getTransaction)
|
||||
|
||||
private def configurations[Res](
|
||||
request: GetLedgerConfigurationRequest,
|
||||
service: (GetLedgerConfigurationRequest, StreamObserver[Res]) => Unit): Future[Option[Res]] =
|
||||
SingleItemObserver.first[Res](service(request, _))
|
||||
def configuration(overrideLedgerId: Option[String] = None): Future[LedgerConfiguration] =
|
||||
SingleItemObserver
|
||||
.first[GetLedgerConfigurationResponse](
|
||||
services.configuration
|
||||
.getLedgerConfiguration(
|
||||
new GetLedgerConfigurationRequest(overrideLedgerId.getOrElse(ledgerId)),
|
||||
_))
|
||||
.map(_.fold(sys.error("No ledger configuration available."))(_.getLedgerConfiguration))
|
||||
|
||||
def latestConfiguration(): Future[LedgerConfiguration] =
|
||||
configurations(
|
||||
new GetLedgerConfigurationRequest(ledgerId),
|
||||
services.configuration.getLedgerConfiguration)
|
||||
.map(
|
||||
_.flatMap(_.ledgerConfiguration).getOrElse(sys.error("No ledger configuration available.")))
|
||||
|
||||
def latestMaxTtl(): Future[java.time.Duration] =
|
||||
latestConfiguration()
|
||||
.map(
|
||||
_.maxTtl
|
||||
.map(
|
||||
t =>
|
||||
java.time.Duration
|
||||
.ofSeconds(t.seconds)
|
||||
.plus(java.time.Duration.ofNanos(t.nanos.toLong)))
|
||||
.getOrElse(sys.error("Ledger configuration has no maxTtl duration.")))
|
||||
}
|
||||
|
@ -199,9 +199,9 @@ final class CommandService(session: LedgerSession) extends LedgerTestSuite(sessi
|
||||
for {
|
||||
ledger <- context.participant()
|
||||
alice <- ledger.allocateParty()
|
||||
request <- ledger.submitAndWaitRequest(alice, Dummy(alice).create.command)
|
||||
request <- ledger.submitRequest(alice, Dummy(alice).create.command)
|
||||
badLedgerId = request.update(_.commands.ledgerId := invalidLedgerId)
|
||||
failure <- ledger.submitAndWait(badLedgerId).failed
|
||||
failure <- ledger.submit(badLedgerId).failed
|
||||
} yield
|
||||
assertGrpcError(failure, Status.Code.NOT_FOUND, s"Ledger ID '$invalidLedgerId' not found.")
|
||||
}
|
||||
|
@ -1,109 +0,0 @@
|
||||
// Copyright (c) 2019 The DAML Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package com.daml.ledger.api.testtool.tests
|
||||
|
||||
import java.time.{Duration, Instant}
|
||||
|
||||
import com.daml.ledger.api.testtool.infrastructure.{LedgerSession, LedgerTest, LedgerTestSuite}
|
||||
import com.digitalasset.ledger.api.v1.command_service.SubmitAndWaitRequest
|
||||
import com.digitalasset.ledger.test_stable.Test.Dummy
|
||||
import com.google.protobuf.timestamp.Timestamp
|
||||
import io.grpc.{Status, StatusException, StatusRuntimeException}
|
||||
|
||||
final class CommandSubmissionLet(session: LedgerSession) extends LedgerTestSuite(session) {
|
||||
|
||||
private[this] def timestamp(i: Instant): Timestamp =
|
||||
new Timestamp(i.getEpochSecond, i.getNano)
|
||||
|
||||
private[this] def instant(t: Timestamp): Instant =
|
||||
Instant.ofEpochSecond(t.seconds, t.nanos.toLong)
|
||||
|
||||
/** Moves all time values in the request (the LET and MRT) by the specified amount.
|
||||
* This simulates a request from a client with a skewed clock. */
|
||||
private[this] def moveRequestTime(
|
||||
request: SubmitAndWaitRequest,
|
||||
offset: java.time.Duration): SubmitAndWaitRequest =
|
||||
request.copy(
|
||||
commands = request.commands.map(command =>
|
||||
command.copy(
|
||||
ledgerEffectiveTime = command.ledgerEffectiveTime.map(let =>
|
||||
timestamp(instant(let).plus(offset))),
|
||||
maximumRecordTime = command.maximumRecordTime.map(mrt =>
|
||||
timestamp(instant(mrt).plus(offset)))
|
||||
)))
|
||||
|
||||
private val submitAndWaitAbortIfLetHigh =
|
||||
LedgerTest("CSLAbortIfLetHigh", "SubmitAndWait returns ABORTED if LET is too high") { context =>
|
||||
for {
|
||||
ledger <- context.participant()
|
||||
alice <- ledger.allocateParty()
|
||||
maxTtl <- ledger.latestMaxTtl()
|
||||
request <- ledger
|
||||
.submitAndWaitRequest(alice, Dummy(alice).create.command)
|
||||
.map(moveRequestTime(_, maxTtl.plus(Duration.ofSeconds(1L))))
|
||||
submitFailure <- ledger.submitAndWait(request).failed
|
||||
} yield {
|
||||
assertGrpcError(submitFailure, Status.Code.ABORTED, "TRANSACTION_OUT_OF_TIME_WINDOW: ")
|
||||
}
|
||||
}
|
||||
|
||||
private val submitAndWaitSuccessIfLetRight =
|
||||
LedgerTest(
|
||||
"CSLSuccessIfLetRight",
|
||||
"SubmitAndWait returns OK if LET is within the accepted interval") { context =>
|
||||
// The maximum accepted clock skew depends on the ledger and is not exposed through the LedgerConfigurationService,
|
||||
// and there might be an actual clock skew between the devices running the test and the ledger.
|
||||
// This test therefore does not attempt to simulate any clock skew
|
||||
// but simply checks whether basic command submission with an unmodified LET works.
|
||||
val maxAcceptableSkew = Duration.ofMillis(0L)
|
||||
|
||||
for {
|
||||
ledger <- context.participant()
|
||||
alice <- ledger.allocateParty()
|
||||
request <- ledger
|
||||
.submitAndWaitRequest(alice, Dummy(alice).create.command)
|
||||
.map(moveRequestTime(_, maxAcceptableSkew))
|
||||
_ <- ledger.submitAndWait(request)
|
||||
} yield {
|
||||
// No assertions to make, since the command went through as expected
|
||||
()
|
||||
}
|
||||
}
|
||||
|
||||
private val submitAndWaitAbortIfLetLow =
|
||||
LedgerTest("CSLAbortIfLetLow", "SubmitAndWait returns ABORTED if LET is too low") { context =>
|
||||
for {
|
||||
ledger <- context.participant()
|
||||
alice <- ledger.allocateParty()
|
||||
maxTtl <- ledger.latestMaxTtl()
|
||||
request <- ledger
|
||||
.submitAndWaitRequest(alice, Dummy(alice).create.command)
|
||||
.map(
|
||||
moveRequestTime(
|
||||
_,
|
||||
Duration
|
||||
.ofSeconds(0)
|
||||
.minus(maxTtl)
|
||||
.minus(Duration.ofSeconds(1L))))
|
||||
submitFailure <- ledger.submitAndWait(request).failed
|
||||
} yield {
|
||||
// In this case, the ledger's response races with the client's timeout detection.
|
||||
// So we can't be sure what the error message will be.
|
||||
submitFailure match {
|
||||
case _: StatusRuntimeException => ()
|
||||
case _: StatusException => ()
|
||||
case _ =>
|
||||
assert(
|
||||
false,
|
||||
"submitAndWait did not fail with a StatusException or StatusRuntimeException")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override val tests: Vector[LedgerTest] = Vector(
|
||||
submitAndWaitAbortIfLetHigh,
|
||||
submitAndWaitSuccessIfLetRight,
|
||||
submitAndWaitAbortIfLetLow
|
||||
)
|
||||
}
|
@ -0,0 +1,183 @@
|
||||
// Copyright (c) 2019 The DAML Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package com.daml.ledger.api.testtool.tests
|
||||
|
||||
import com.daml.ledger.api.testtool.infrastructure.{LedgerSession, LedgerTest, LedgerTestSuite}
|
||||
import com.digitalasset.ledger.api.v1.ledger_configuration_service.LedgerConfiguration
|
||||
import com.digitalasset.ledger.test_stable.Test.Dummy
|
||||
import com.google.protobuf.duration.Duration
|
||||
import com.google.protobuf.timestamp.Timestamp
|
||||
import io.grpc.{Status, StatusException, StatusRuntimeException}
|
||||
|
||||
class LedgerConfigurationService(session: LedgerSession) extends LedgerTestSuite(session) {
|
||||
|
||||
private[this] val configSucceeds =
|
||||
LedgerTest("ConfigSucceeds", "Return a valid configuration for a valid request") { context =>
|
||||
for {
|
||||
ledger <- context.participant()
|
||||
config <- ledger.configuration()
|
||||
} yield {
|
||||
assert(config.minTtl.isDefined, "The minTTL field of the configuration is empty")
|
||||
assert(config.maxTtl.isDefined, "The maxTTL field of the configuration is empty")
|
||||
}
|
||||
}
|
||||
|
||||
private[this] val configLedgerId =
|
||||
LedgerTest("ConfigLedgerId", "Return NOT_FOUND to invalid ledger identifier") { context =>
|
||||
val invalidLedgerId = "THIS_IS_AN_INVALID_LEDGER_ID"
|
||||
for {
|
||||
ledger <- context.participant()
|
||||
failure <- ledger.configuration(overrideLedgerId = Some(invalidLedgerId)).failed
|
||||
} yield {
|
||||
assertGrpcError(failure, Status.Code.NOT_FOUND, s"Ledger ID '$invalidLedgerId' not found.")
|
||||
}
|
||||
}
|
||||
|
||||
private def sum(t: Timestamp, d: Duration): Timestamp =
|
||||
t.withSeconds(t.seconds + d.seconds + ((t.nanos + d.nanos) / 1E9).toLong)
|
||||
.withNanos(((t.nanos + d.nanos) % 1E9).toInt)
|
||||
|
||||
private def offset(t: Timestamp, s: Long): Timestamp =
|
||||
Timestamp(t.seconds + s, t.nanos)
|
||||
|
||||
private[this] val configJustMinTtl =
|
||||
LedgerTest("ConfigJustMinTtl", "LET+minTTL should be an acceptable MRT") { context =>
|
||||
for {
|
||||
ledger <- context.participant()
|
||||
party <- ledger.allocateParty()
|
||||
LedgerConfiguration(Some(minTtl), _) <- ledger.configuration()
|
||||
request <- ledger.submitRequest(party, Dummy(party).create.command)
|
||||
let = request.getCommands.getLedgerEffectiveTime
|
||||
adjustedRequest = request.update(_.commands.maximumRecordTime := sum(let, minTtl))
|
||||
_ <- ledger.submit(adjustedRequest)
|
||||
} yield {
|
||||
// Nothing to do, success is enough
|
||||
}
|
||||
}
|
||||
|
||||
private[this] val configUnderflowMinTtl =
|
||||
LedgerTest("ConfigUnderflowMinTtl", "LET+minTTL-1 should NOT be an acceptable MRT") { context =>
|
||||
for {
|
||||
ledger <- context.participant()
|
||||
party <- ledger.allocateParty()
|
||||
LedgerConfiguration(Some(minTtl), _) <- ledger.configuration()
|
||||
request <- ledger.submitRequest(party, Dummy(party).create.command)
|
||||
let = request.getCommands.getLedgerEffectiveTime
|
||||
adjustedRequest = request.update(
|
||||
_.commands.maximumRecordTime := offset(sum(let, minTtl), -1))
|
||||
failure <- ledger.submit(adjustedRequest).failed
|
||||
} yield {
|
||||
assertGrpcError(failure, Status.Code.INVALID_ARGUMENT, "out of bounds")
|
||||
}
|
||||
}
|
||||
|
||||
private[this] val configJustMaxTtl =
|
||||
LedgerTest("ConfigJustMaxTtl", "LET+maxTTL should be an acceptable MRT") { context =>
|
||||
for {
|
||||
ledger <- context.participant()
|
||||
party <- ledger.allocateParty()
|
||||
LedgerConfiguration(_, Some(maxTtl)) <- ledger.configuration()
|
||||
request <- ledger.submitRequest(party, Dummy(party).create.command)
|
||||
let = request.getCommands.getLedgerEffectiveTime
|
||||
adjustedRequest = request.update(_.commands.maximumRecordTime := sum(let, maxTtl))
|
||||
_ <- ledger.submit(adjustedRequest)
|
||||
} yield {
|
||||
// Nothing to do, success is enough
|
||||
}
|
||||
}
|
||||
|
||||
private[this] val configOverflowMaxTtl =
|
||||
LedgerTest("ConfigOverflowMaxTtl", "LET+maxTTL+1 should NOT be an acceptable MRT") { context =>
|
||||
for {
|
||||
ledger <- context.participant()
|
||||
party <- ledger.allocateParty()
|
||||
LedgerConfiguration(_, Some(maxTtl)) <- ledger.configuration()
|
||||
request <- ledger.submitRequest(party, Dummy(party).create.command)
|
||||
let = request.getCommands.getLedgerEffectiveTime
|
||||
adjustedRequest = request.update(
|
||||
_.commands.maximumRecordTime := offset(sum(let, maxTtl), 1))
|
||||
failure <- ledger.submit(adjustedRequest).failed
|
||||
} yield {
|
||||
assertGrpcError(failure, Status.Code.INVALID_ARGUMENT, "out of bounds")
|
||||
}
|
||||
}
|
||||
|
||||
// Adds (maxTTL+1) seconds to a Timestamp
|
||||
private def overflow(maxTtl: Duration)(t: Timestamp): Timestamp =
|
||||
t.withSeconds(t.seconds + maxTtl.seconds + ((t.nanos + maxTtl.nanos) / 1E9).toLong + 1L)
|
||||
.withNanos(((t.nanos + maxTtl.nanos) % 1E9).toInt)
|
||||
|
||||
// Subtracts (maxTTL+1) seconds from a Timestamp
|
||||
private def underflow(maxTtl: Duration)(t: Timestamp): Timestamp =
|
||||
t.withSeconds(t.seconds - maxTtl.seconds + ((t.nanos + maxTtl.nanos) / 1E9).toLong - 1L)
|
||||
.withNanos(((t.nanos - maxTtl.nanos) % 1E9).toInt)
|
||||
|
||||
private[this] val submitSuccessIfLetRight =
|
||||
LedgerTest(
|
||||
"CSLSuccessIfLetRight",
|
||||
"Submission returns OK if LET is within the accepted interval") { context =>
|
||||
// The maximum accepted clock skew depends on the ledger and is not exposed through the LedgerConfigurationService,
|
||||
// and there might be an actual clock skew between the devices running the test and the ledger.
|
||||
// This test therefore does not attempt to simulate any clock skew
|
||||
// but simply checks whether basic command submission with an unmodified LET works.
|
||||
|
||||
for {
|
||||
ledger <- context.participant()
|
||||
alice <- ledger.allocateParty()
|
||||
request <- ledger.submitRequest(alice, Dummy(alice).create.command)
|
||||
_ <- ledger.submit(request)
|
||||
} yield {
|
||||
// No assertions to make, since the command went through as expected
|
||||
}
|
||||
}
|
||||
|
||||
private[this] val submitAbortIfLetHigh =
|
||||
LedgerTest("CSLAbortIfLetHigh", "Submission returns ABORTED if LET is too high") { context =>
|
||||
for {
|
||||
ledger <- context.participant()
|
||||
alice <- ledger.allocateParty()
|
||||
LedgerConfiguration(_, Some(maxTtl)) <- ledger.configuration()
|
||||
request <- ledger.submitRequest(alice, Dummy(alice).create.command)
|
||||
invalidRequest = request
|
||||
.update(_.commands.ledgerEffectiveTime.modify(overflow(maxTtl)))
|
||||
.update(_.commands.maximumRecordTime.modify(overflow(maxTtl)))
|
||||
failure <- ledger.submit(invalidRequest).failed
|
||||
} yield {
|
||||
assertGrpcError(failure, Status.Code.ABORTED, "TRANSACTION_OUT_OF_TIME_WINDOW: ")
|
||||
}
|
||||
}
|
||||
|
||||
private[this] val submitAbortIfLetLow =
|
||||
LedgerTest("CSLAbortIfLetLow", "Submission returns ABORTED if LET is too low") { context =>
|
||||
for {
|
||||
ledger <- context.participant()
|
||||
alice <- ledger.allocateParty()
|
||||
LedgerConfiguration(_, Some(maxTtl)) <- ledger.configuration()
|
||||
request <- ledger.submitRequest(alice, Dummy(alice).create.command)
|
||||
invalidRequest = request
|
||||
.update(_.commands.ledgerEffectiveTime.modify(underflow(maxTtl)))
|
||||
.update(_.commands.maximumRecordTime.modify(underflow(maxTtl)))
|
||||
failure <- ledger.submit(invalidRequest).failed
|
||||
} yield {
|
||||
// In this case, the ledger's response races with the client's timeout detection.
|
||||
// So we can't be sure what the error message will be.
|
||||
failure match {
|
||||
case _: StatusRuntimeException | _: StatusException => ()
|
||||
case _ => fail("Submission should have failed with gRPC exception")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override val tests: Vector[LedgerTest] = Vector(
|
||||
configSucceeds,
|
||||
configLedgerId,
|
||||
configJustMinTtl,
|
||||
configUnderflowMinTtl,
|
||||
configJustMaxTtl,
|
||||
configOverflowMaxTtl,
|
||||
submitSuccessIfLetRight,
|
||||
submitAbortIfLetHigh,
|
||||
submitAbortIfLetLow
|
||||
)
|
||||
}
|
@ -33,7 +33,7 @@ package object tests {
|
||||
"TransactionServiceIT" -> (new TransactionService(_)),
|
||||
"WitnessesIT" -> (new Witnesses(_)),
|
||||
"WronglyTypedContractIdIT" -> (new WronglyTypedContractId(_)),
|
||||
"CommandSubmissionLetIT" -> (new CommandSubmissionLet(_))
|
||||
"LedgerConfigurationServiceIT" -> (new LedgerConfigurationService(_))
|
||||
)
|
||||
|
||||
val all = default ++ optional
|
||||
|
@ -8,3 +8,4 @@ This page contains release notes for the SDK.
|
||||
|
||||
HEAD — ongoing
|
||||
--------------
|
||||
- [DAML Ledger Integration Kit] Skew/LET/MRT/Config tests consolidated in a single suite.
|
||||
|
Loading…
Reference in New Issue
Block a user