mirror of
https://github.com/digital-asset/daml.git
synced 2024-11-10 10:46:11 +03:00
Migrate LedgerClient test from to Canton (#16795)
This commit is contained in:
parent
99cbff4eff
commit
90c023e98e
@ -44,6 +44,7 @@ da_scala_library(
|
|||||||
"//libs-scala/resources",
|
"//libs-scala/resources",
|
||||||
"//libs-scala/scala-utils",
|
"//libs-scala/scala-utils",
|
||||||
"//libs-scala/timer-utils",
|
"//libs-scala/timer-utils",
|
||||||
|
"//test-common",
|
||||||
"@maven//:org_scalatest_scalatest_compatible",
|
"@maven//:org_scalatest_scalatest_compatible",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
@ -22,7 +22,8 @@ import org.scalatest.Suite
|
|||||||
import scala.concurrent.duration.DurationInt
|
import scala.concurrent.duration.DurationInt
|
||||||
import scala.concurrent.{ExecutionContext, Future}
|
import scala.concurrent.{ExecutionContext, Future}
|
||||||
|
|
||||||
import java.nio.file.{Files, Path, Paths}
|
import java.nio.file.{Path, Paths, Files}
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
@scala.annotation.nowarn("msg=match may not be exhaustive")
|
@scala.annotation.nowarn("msg=match may not be exhaustive")
|
||||||
object CantonFixture {
|
object CantonFixture {
|
||||||
@ -46,16 +47,14 @@ object CantonFixture {
|
|||||||
Paths.get(rlocation("test-common/test-certificates/" + src))
|
Paths.get(rlocation("test-common/test-certificates/" + src))
|
||||||
}
|
}
|
||||||
|
|
||||||
private val counter = new java.util.concurrent.atomic.AtomicLong()
|
|
||||||
|
|
||||||
def freshLong() = counter.getAndIncrement()
|
|
||||||
|
|
||||||
def freshName(prefix: String): String = {
|
def freshName(prefix: String): String = {
|
||||||
assert(!prefix.contains('_'))
|
assert(!prefix.contains('_'))
|
||||||
prefix + "__" + freshLong().toString
|
prefix + "__" + UUID.randomUUID()
|
||||||
}
|
}
|
||||||
|
|
||||||
def freshUserId() = Ref.UserId.assertFromString(freshName("user"))
|
def freshUserId(): Ref.UserId = Ref.UserId.assertFromString(freshName("user"))
|
||||||
|
|
||||||
|
def freshParty(): Ref.Party = Ref.Party.assertFromString(freshName("Party"))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,192 @@
|
|||||||
|
// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package com.daml.lf
|
||||||
|
package integrationtest
|
||||||
|
|
||||||
|
import java.nio.file.Path
|
||||||
|
import java.util
|
||||||
|
|
||||||
|
import com.daml.ledger.api.domain
|
||||||
|
import com.daml.ledger.api.testing.utils.{MockMessages => M}
|
||||||
|
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.Command.{Create, Exercise}
|
||||||
|
import com.daml.ledger.api.v1.commands.{Command, Commands, CreateCommand, ExerciseCommand}
|
||||||
|
import com.daml.ledger.api.v1.value.Value.Sum
|
||||||
|
import com.daml.ledger.api.v1.value.Value.Sum.{Bool, Party, Text, Timestamp}
|
||||||
|
import com.daml.ledger.api.v1.value.{Identifier, Record, RecordField, Value, Variant}
|
||||||
|
import com.daml.lf.archive.DarReader
|
||||||
|
import com.daml.lf.data.Ref
|
||||||
|
import com.daml.platform.participant.util.ValueConversions._
|
||||||
|
import com.daml.platform.testing.TestTemplateIdentifiers
|
||||||
|
import scalaz.syntax.tag._
|
||||||
|
|
||||||
|
trait TestCommands {
|
||||||
|
|
||||||
|
import TestCommands.SubmitRequestEnhancer
|
||||||
|
|
||||||
|
protected def darFile: Path
|
||||||
|
|
||||||
|
protected lazy val packageId: Ref.PackageId =
|
||||||
|
DarReader.assertReadArchiveFromFile(darFile.toFile).main.pkgId
|
||||||
|
|
||||||
|
protected lazy val templateIds = new TestTemplateIdentifiers(packageId)
|
||||||
|
|
||||||
|
protected def buildRequest(
|
||||||
|
ledgerId: domain.LedgerId,
|
||||||
|
commandId: String,
|
||||||
|
commands: Seq[Command],
|
||||||
|
applicationId: String,
|
||||||
|
party: String,
|
||||||
|
): SubmitRequest =
|
||||||
|
M.submitRequest.update(
|
||||||
|
_.commands.commandId := commandId,
|
||||||
|
_.commands.ledgerId := ledgerId.unwrap,
|
||||||
|
_.commands.applicationId := applicationId,
|
||||||
|
_.commands.party := party,
|
||||||
|
_.commands.commands := commands,
|
||||||
|
)
|
||||||
|
|
||||||
|
protected def dummyCommands(
|
||||||
|
ledgerId: domain.LedgerId,
|
||||||
|
commandId: String,
|
||||||
|
applicationId: String,
|
||||||
|
party: String,
|
||||||
|
): SubmitRequest =
|
||||||
|
buildRequest(
|
||||||
|
ledgerId = ledgerId,
|
||||||
|
commandId = commandId,
|
||||||
|
commands = List(
|
||||||
|
createWithOperator(templateIds.dummy, party),
|
||||||
|
createWithOperator(templateIds.dummyWithParam, party),
|
||||||
|
createWithOperator(templateIds.dummyFactory, party),
|
||||||
|
),
|
||||||
|
applicationId = applicationId,
|
||||||
|
party = party,
|
||||||
|
)
|
||||||
|
|
||||||
|
protected def dummyMultiPartyCommands(
|
||||||
|
ledgerId: domain.LedgerId,
|
||||||
|
commandId: String,
|
||||||
|
applicationId: String,
|
||||||
|
party: String,
|
||||||
|
actAs: Seq[String],
|
||||||
|
readAs: Seq[String],
|
||||||
|
): SubmitRequest = {
|
||||||
|
// This method returns a multi-party submission, however the Daml contract uses a single party.
|
||||||
|
// Pick a random party for the Daml contract (it needs to be one of the submitters).
|
||||||
|
val operator = actAs.headOption.getOrElse(party)
|
||||||
|
dummyCommands(ledgerId, commandId, applicationId, operator)
|
||||||
|
.update(
|
||||||
|
_.commands.party := party,
|
||||||
|
_.commands.actAs := actAs,
|
||||||
|
_.commands.readAs := readAs,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected def createWithOperator(templateId: Identifier, party: String): Command =
|
||||||
|
Command(
|
||||||
|
Create(
|
||||||
|
CreateCommand(
|
||||||
|
Some(templateId),
|
||||||
|
Some(Record(Some(templateId), List(RecordField("operator", Some(Value(Party(party))))))),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
private def oneKilobyteString: String = {
|
||||||
|
val numChars = 500 // each char takes 2 bytes for now in Java 8
|
||||||
|
val array = new Array[Char](numChars)
|
||||||
|
util.Arrays.fill(array, 'a')
|
||||||
|
new String(array)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected def oneKbCommand(templateId: Identifier): Command =
|
||||||
|
Command(
|
||||||
|
Create(
|
||||||
|
CreateCommand(
|
||||||
|
Some(templateId),
|
||||||
|
Some(
|
||||||
|
Record(
|
||||||
|
Some(templateId),
|
||||||
|
List(
|
||||||
|
RecordField("operator", Some(Value(Party("party")))),
|
||||||
|
RecordField("text", Some(Value(Text(oneKilobyteString)))),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
protected def paramShowcaseArgs: Record = {
|
||||||
|
val variant = Value(Value.Sum.Variant(Variant(None, "SomeInteger", 1.asInt64)))
|
||||||
|
val nestedVariant = Vector("value" -> variant).asRecordValue
|
||||||
|
val integerList = Vector(1, 2).map(_.toLong.asInt64).asList
|
||||||
|
Record(
|
||||||
|
Some(templateIds.parameterShowcase),
|
||||||
|
Vector(
|
||||||
|
RecordField("operator", "Alice".asParty),
|
||||||
|
RecordField("integer", 1.asInt64),
|
||||||
|
RecordField("decimal", "1.1".asNumeric),
|
||||||
|
RecordField("text", Value(Text("text"))),
|
||||||
|
RecordField("bool", Value(Bool(true))),
|
||||||
|
RecordField("time", Value(Timestamp(0))),
|
||||||
|
RecordField(
|
||||||
|
"relTime",
|
||||||
|
42.asInt64,
|
||||||
|
), // RelTime gets now compiled to Integer with the new primitive types
|
||||||
|
RecordField("nestedOptionalInteger", nestedVariant),
|
||||||
|
RecordField("integerList", integerList),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected def paramShowcase: Commands = Commands(
|
||||||
|
"ledgerId",
|
||||||
|
"workflowId",
|
||||||
|
"appId",
|
||||||
|
"cmd",
|
||||||
|
"Alice",
|
||||||
|
Seq(
|
||||||
|
Command(
|
||||||
|
Command.Command.Create(
|
||||||
|
CreateCommand(Some(templateIds.parameterShowcase), Option(paramShowcaseArgs))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
protected def oneKbCommandRequest(
|
||||||
|
ledgerId: domain.LedgerId,
|
||||||
|
commandId: String,
|
||||||
|
applicationId: String,
|
||||||
|
party: String,
|
||||||
|
): SubmitRequest =
|
||||||
|
buildRequest(
|
||||||
|
ledgerId = ledgerId,
|
||||||
|
commandId = commandId,
|
||||||
|
commands = List(oneKbCommand(templateIds.textContainer)),
|
||||||
|
applicationId = applicationId,
|
||||||
|
party = party,
|
||||||
|
)
|
||||||
|
|
||||||
|
protected def exerciseWithUnit(
|
||||||
|
templateId: Identifier,
|
||||||
|
contractId: String,
|
||||||
|
choice: String,
|
||||||
|
args: Option[Value] = Some(Value(Sum.Record(Record.defaultInstance))),
|
||||||
|
): Command =
|
||||||
|
Command(Exercise(ExerciseCommand(Some(templateId), contractId, choice, args)))
|
||||||
|
|
||||||
|
import scala.language.implicitConversions
|
||||||
|
implicit def SubmitRequestEnhancer(request: SubmitRequest): SubmitRequestEnhancer =
|
||||||
|
new SubmitRequestEnhancer(request)
|
||||||
|
}
|
||||||
|
|
||||||
|
object TestCommands {
|
||||||
|
implicit final class SubmitRequestEnhancer(private val request: SubmitRequest) extends AnyVal {
|
||||||
|
def toSync: SubmitAndWaitRequest = SubmitAndWaitRequest(request.commands)
|
||||||
|
}
|
||||||
|
}
|
@ -86,7 +86,7 @@ da_scala_test_suite(
|
|||||||
name = "ledger-api-client-integration-tests",
|
name = "ledger-api-client-integration-tests",
|
||||||
srcs = glob(["src/it/**/*.scala"]),
|
srcs = glob(["src/it/**/*.scala"]),
|
||||||
data = [
|
data = [
|
||||||
"//test-common:dar-files",
|
"//test-common:dar-files-default",
|
||||||
],
|
],
|
||||||
resources = [
|
resources = [
|
||||||
"src/it/resources/logback-test.xml",
|
"src/it/resources/logback-test.xml",
|
||||||
@ -95,9 +95,12 @@ da_scala_test_suite(
|
|||||||
"@maven//:com_typesafe_akka_akka_actor",
|
"@maven//:com_typesafe_akka_akka_actor",
|
||||||
"@maven//:com_typesafe_akka_akka_stream",
|
"@maven//:com_typesafe_akka_akka_stream",
|
||||||
],
|
],
|
||||||
|
tags = ["cpu:4"],
|
||||||
deps = [
|
deps = [
|
||||||
":ledger-api-client",
|
":ledger-api-client",
|
||||||
|
"//bazel_tools/runfiles:scala_runfiles",
|
||||||
"//daml-lf/engine",
|
"//daml-lf/engine",
|
||||||
|
"//daml-lf/integration-test-lib",
|
||||||
"//daml-lf/transaction",
|
"//daml-lf/transaction",
|
||||||
"//language-support/scala/bindings",
|
"//language-support/scala/bindings",
|
||||||
"//ledger-api/rs-grpc-bridge",
|
"//ledger-api/rs-grpc-bridge",
|
||||||
@ -117,6 +120,7 @@ da_scala_test_suite(
|
|||||||
"//libs-scala/concurrent",
|
"//libs-scala/concurrent",
|
||||||
"//libs-scala/contextualized-logging",
|
"//libs-scala/contextualized-logging",
|
||||||
"//libs-scala/grpc-utils",
|
"//libs-scala/grpc-utils",
|
||||||
|
"//libs-scala/jwt",
|
||||||
"//libs-scala/ledger-resources",
|
"//libs-scala/ledger-resources",
|
||||||
"//libs-scala/logging-entries",
|
"//libs-scala/logging-entries",
|
||||||
"//libs-scala/ports",
|
"//libs-scala/ports",
|
||||||
@ -124,6 +128,7 @@ da_scala_test_suite(
|
|||||||
"//observability/metrics",
|
"//observability/metrics",
|
||||||
"//observability/tracing",
|
"//observability/tracing",
|
||||||
"//test-common",
|
"//test-common",
|
||||||
|
"//test-common:dar-files-default-lib",
|
||||||
"@maven//:ch_qos_logback_logback_classic",
|
"@maven//:ch_qos_logback_logback_classic",
|
||||||
"@maven//:io_netty_netty_handler",
|
"@maven//:io_netty_netty_handler",
|
||||||
],
|
],
|
||||||
|
@ -8,12 +8,9 @@ import java.util.concurrent.TimeUnit
|
|||||||
import akka.NotUsed
|
import akka.NotUsed
|
||||||
import akka.stream.scaladsl.{Sink, Source}
|
import akka.stream.scaladsl.{Sink, Source}
|
||||||
import com.daml.api.util.TimeProvider
|
import com.daml.api.util.TimeProvider
|
||||||
|
import com.daml.bazeltools.BazelRunfiles
|
||||||
import com.daml.ledger.api.domain
|
import com.daml.ledger.api.domain
|
||||||
import com.daml.ledger.api.testing.utils.{
|
import com.daml.ledger.api.testing.utils.{IsStatusException, SuiteResourceManagementAroundAll}
|
||||||
IsStatusException,
|
|
||||||
MockMessages,
|
|
||||||
SuiteResourceManagementAroundAll,
|
|
||||||
}
|
|
||||||
import com.daml.ledger.api.v1.command_completion_service.CommandCompletionServiceGrpc
|
import com.daml.ledger.api.v1.command_completion_service.CommandCompletionServiceGrpc
|
||||||
import com.daml.ledger.api.v1.command_submission_service.{
|
import com.daml.ledger.api.v1.command_submission_service.{
|
||||||
CommandSubmissionServiceGrpc,
|
CommandSubmissionServiceGrpc,
|
||||||
@ -37,12 +34,12 @@ import com.daml.ledger.client.services.commands.{
|
|||||||
CompletionStreamElement,
|
CompletionStreamElement,
|
||||||
}
|
}
|
||||||
import com.daml.ledger.client.services.testing.time.StaticTime
|
import com.daml.ledger.client.services.testing.time.StaticTime
|
||||||
import com.daml.ledger.runner.common.Config
|
import com.daml.ledger.test.ModelTestDar
|
||||||
import com.daml.lf.crypto.Hash
|
import com.daml.lf.crypto
|
||||||
|
import com.daml.lf.data.{Bytes, Ref}
|
||||||
|
import com.daml.lf.integrationtest.{CantonFixture, TestCommands}
|
||||||
import com.daml.lf.value.Value.ContractId
|
import com.daml.lf.value.Value.ContractId
|
||||||
import com.daml.platform.participant.util.ValueConversions._
|
import com.daml.platform.participant.util.ValueConversions._
|
||||||
import com.daml.platform.sandbox.fixture.SandboxFixture
|
|
||||||
import com.daml.platform.sandbox.services.TestCommands
|
|
||||||
import com.daml.util.Ctx
|
import com.daml.util.Ctx
|
||||||
import com.google.rpc.code.Code
|
import com.google.rpc.code.Code
|
||||||
import io.grpc.{Status, StatusRuntimeException}
|
import io.grpc.{Status, StatusRuntimeException}
|
||||||
@ -58,15 +55,21 @@ import scala.concurrent.duration.FiniteDuration
|
|||||||
import scala.util.Success
|
import scala.util.Success
|
||||||
import scala.util.control.NonFatal
|
import scala.util.control.NonFatal
|
||||||
|
|
||||||
|
import java.nio.file.Paths
|
||||||
|
|
||||||
final class CommandClientIT
|
final class CommandClientIT
|
||||||
extends AsyncWordSpec
|
extends AsyncWordSpec
|
||||||
with TestCommands
|
with TestCommands
|
||||||
with SandboxFixture
|
with CantonFixture
|
||||||
with Matchers
|
with Matchers
|
||||||
with SuiteResourceManagementAroundAll
|
with SuiteResourceManagementAroundAll
|
||||||
with TryValues
|
with TryValues
|
||||||
with Inside {
|
with Inside {
|
||||||
|
|
||||||
|
protected def darFile = Paths.get(BazelRunfiles.rlocation(ModelTestDar.path))
|
||||||
|
|
||||||
|
override protected lazy val darFiles: List[java.nio.file.Path] = List(darFile)
|
||||||
|
|
||||||
private val defaultCommandClientConfiguration =
|
private val defaultCommandClientConfiguration =
|
||||||
CommandClientConfiguration(
|
CommandClientConfiguration(
|
||||||
maxCommandsInFlight = 1,
|
maxCommandsInFlight = 1,
|
||||||
@ -74,19 +77,28 @@ final class CommandClientIT
|
|||||||
defaultDeduplicationTime = Duration.ofSeconds(30),
|
defaultDeduplicationTime = Duration.ofSeconds(30),
|
||||||
)
|
)
|
||||||
|
|
||||||
private val testLedgerId = domain.LedgerId("ledgerId")
|
private val testLedgerId =
|
||||||
private val testNotLedgerId = domain.LedgerId("hotdog")
|
domain.LedgerId(config.ledgerIds.head)
|
||||||
|
private val testNotLedgerId =
|
||||||
|
domain.LedgerId(CantonFixture.freshName("hotdog"))
|
||||||
|
|
||||||
|
private lazy val channel = config.channel(suiteResource.value.head)
|
||||||
|
private lazy val defaultClient = defaultLedgerClient()
|
||||||
|
private def freshParty() = for {
|
||||||
|
client <- defaultClient
|
||||||
|
details <- client.partyManagementClient.allocateParty(Some(CantonFixture.freshParty()), None)
|
||||||
|
} yield details.party
|
||||||
|
|
||||||
private def commandClientWithoutTime(
|
private def commandClientWithoutTime(
|
||||||
ledgerId: domain.LedgerId,
|
ledgerId: domain.LedgerId,
|
||||||
applicationId: String = MockMessages.applicationId,
|
appId: String = applicationId.unwrap,
|
||||||
configuration: CommandClientConfiguration = defaultCommandClientConfiguration,
|
configuration: CommandClientConfiguration = defaultCommandClientConfiguration,
|
||||||
): CommandClient =
|
): CommandClient =
|
||||||
new CommandClient(
|
new CommandClient(
|
||||||
CommandSubmissionServiceGrpc.stub(channel),
|
CommandSubmissionServiceGrpc.stub(channel),
|
||||||
CommandCompletionServiceGrpc.stub(channel),
|
CommandCompletionServiceGrpc.stub(channel),
|
||||||
ledgerId,
|
ledgerId,
|
||||||
applicationId,
|
appId,
|
||||||
configuration,
|
configuration,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -100,21 +112,30 @@ final class CommandClientIT
|
|||||||
|
|
||||||
private def commandClient(
|
private def commandClient(
|
||||||
ledgerId: domain.LedgerId = testLedgerId,
|
ledgerId: domain.LedgerId = testLedgerId,
|
||||||
applicationId: String = MockMessages.applicationId,
|
appId: String = applicationId.unwrap,
|
||||||
configuration: CommandClientConfiguration = defaultCommandClientConfiguration,
|
configuration: CommandClientConfiguration = defaultCommandClientConfiguration,
|
||||||
): Future[CommandClient] =
|
): Future[CommandClient] =
|
||||||
timeProvider(ledgerId)
|
timeProvider(ledgerId)
|
||||||
.map(_ => commandClientWithoutTime(ledgerId, applicationId, configuration))
|
.map(_ =>
|
||||||
|
commandClientWithoutTime(appId = appId, ledgerId = ledgerId, configuration = configuration)
|
||||||
|
)
|
||||||
|
|
||||||
override protected def config: Config = super.config.copy(ledgerId = testLedgerId.unwrap)
|
|
||||||
|
|
||||||
private val submittingPartyList = List(MockMessages.party)
|
|
||||||
private val LedgerBegin = LedgerOffset(Boundary(LEDGER_BEGIN))
|
private val LedgerBegin = LedgerOffset(Boundary(LEDGER_BEGIN))
|
||||||
|
|
||||||
private def submitRequest(commandId: String, individualCommands: Seq[Command]): SubmitRequest =
|
private def submitRequest(
|
||||||
buildRequest(testLedgerId, commandId, individualCommands)
|
commandId: String,
|
||||||
|
individualCommands: Seq[Command],
|
||||||
|
party: Ref.Party,
|
||||||
|
): SubmitRequest =
|
||||||
|
buildRequest(
|
||||||
|
ledgerId = testLedgerId,
|
||||||
|
commandId = commandId,
|
||||||
|
commands = individualCommands,
|
||||||
|
applicationId = applicationId.unwrap,
|
||||||
|
party = party,
|
||||||
|
)
|
||||||
|
|
||||||
private def submitRequestWithId(commandId: String): SubmitRequest =
|
private def submitRequestWithId(commandId: String, party: Ref.Party) =
|
||||||
submitRequest(
|
submitRequest(
|
||||||
commandId,
|
commandId,
|
||||||
List(
|
List(
|
||||||
@ -123,15 +144,16 @@ final class CommandClientIT
|
|||||||
Some(
|
Some(
|
||||||
Record(
|
Record(
|
||||||
Some(templateIds.dummy),
|
Some(templateIds.dummy),
|
||||||
Seq(RecordField("operator", Option(MockMessages.party.asParty))),
|
Seq(RecordField("operator", Some(party.asParty))),
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
).wrap
|
).wrap
|
||||||
),
|
),
|
||||||
|
party,
|
||||||
)
|
)
|
||||||
|
|
||||||
private def commandSubmissionWithId(commandId: String): CommandSubmission =
|
private def commandSubmissionWithId(commandId: String, party: Ref.Party): CommandSubmission =
|
||||||
CommandSubmission(submitRequestWithId(commandId).getCommands)
|
CommandSubmission(submitRequestWithId(commandId, party).getCommands)
|
||||||
|
|
||||||
// Commands and completions can be read out of order. Since we use GRPC monocalls to send,
|
// Commands and completions can be read out of order. Since we use GRPC monocalls to send,
|
||||||
// they can even be sent out of order.
|
// they can even be sent out of order.
|
||||||
@ -177,12 +199,13 @@ final class CommandClientIT
|
|||||||
*/
|
*/
|
||||||
private def readExpectedCommandIds(
|
private def readExpectedCommandIds(
|
||||||
client: CommandClient,
|
client: CommandClient,
|
||||||
|
party: Ref.Party,
|
||||||
checkpoint: LedgerOffset,
|
checkpoint: LedgerOffset,
|
||||||
expected: Set[String],
|
expected: Set[String],
|
||||||
timeLimit: Span = 6.seconds,
|
timeLimit: Span = 6.seconds,
|
||||||
): Future[(Set[String], Set[String])] =
|
): Future[(Set[String], Set[String])] =
|
||||||
readExpectedElements(
|
readExpectedElements(
|
||||||
client.completionSource(submittingPartyList, checkpoint).collect {
|
client.completionSource(List(party), checkpoint).collect {
|
||||||
case CompletionStreamElement.CompletionElement(c, _) => c.commandId
|
case CompletionStreamElement.CompletionElement(c, _) => c.commandId
|
||||||
},
|
},
|
||||||
expected,
|
expected,
|
||||||
@ -221,7 +244,7 @@ final class CommandClientIT
|
|||||||
}
|
}
|
||||||
|
|
||||||
"fail with the expected status on a ledger Id mismatch" in {
|
"fail with the expected status on a ledger Id mismatch" in {
|
||||||
commandClientWithoutTime(testNotLedgerId)
|
commandClientWithoutTime(ledgerId = testNotLedgerId)
|
||||||
.getCompletionEnd()
|
.getCompletionEnd()
|
||||||
.failed map IsStatusException(Status.NOT_FOUND)
|
.failed map IsStatusException(Status.NOT_FOUND)
|
||||||
}
|
}
|
||||||
@ -233,8 +256,9 @@ final class CommandClientIT
|
|||||||
val contexts = 1 to 10
|
val contexts = 1 to 10
|
||||||
|
|
||||||
for {
|
for {
|
||||||
|
party <- freshParty()
|
||||||
client <- commandClient()
|
client <- commandClient()
|
||||||
result <- Source(contexts.map(i => Ctx(i, commandSubmissionWithId(i.toString))))
|
result <- Source(contexts.map(i => Ctx(i, commandSubmissionWithId(i.toString, party))))
|
||||||
.via(client.submissionFlow())
|
.via(client.submissionFlow())
|
||||||
.map(_.map(_.isSuccess))
|
.map(_.map(_.isSuccess))
|
||||||
.runWith(Sink.seq)
|
.runWith(Sink.seq)
|
||||||
@ -244,21 +268,26 @@ final class CommandClientIT
|
|||||||
}
|
}
|
||||||
|
|
||||||
"fail with the expected status on a ledger Id mismatch" in {
|
"fail with the expected status on a ledger Id mismatch" in {
|
||||||
val aSubmission = commandSubmissionWithId("1")
|
|
||||||
val submission = aSubmission.copy(
|
for {
|
||||||
|
party <- freshParty()
|
||||||
|
aSubmission = commandSubmissionWithId("1", party)
|
||||||
|
submission = aSubmission.copy(
|
||||||
commands = aSubmission.commands.update(_.ledgerId := testNotLedgerId.unwrap)
|
commands = aSubmission.commands.update(_.ledgerId := testNotLedgerId.unwrap)
|
||||||
)
|
)
|
||||||
Source
|
err <- Source
|
||||||
.single(Ctx(1, submission))
|
.single(Ctx(1, submission))
|
||||||
.via(commandClientWithoutTime(testNotLedgerId).submissionFlow())
|
.via(commandClientWithoutTime(ledgerId = testNotLedgerId).submissionFlow())
|
||||||
.runWith(Sink.head)
|
.runWith(Sink.head)
|
||||||
.map(err => IsStatusException(Status.NOT_FOUND)(err.value.failure.exception))
|
} yield IsStatusException(Status.NOT_FOUND)(err.value.failure.exception)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
"fail with INVALID REQUEST for empty application ids" in {
|
"fail with INVALID REQUEST for empty application ids" in {
|
||||||
val request = submitRequestWithId("7000").update(_.commands.applicationId := "")
|
|
||||||
val resF = for {
|
val resF = for {
|
||||||
client <- commandClient(applicationId = "")
|
party <- freshParty()
|
||||||
|
request = submitRequestWithId("7000", party).update(_.commands.applicationId := "")
|
||||||
|
client <- commandClient(appId = "")
|
||||||
res <- client.submitSingleCommand(request)
|
res <- client.submitSingleCommand(request)
|
||||||
} yield res
|
} yield res
|
||||||
|
|
||||||
@ -275,8 +304,9 @@ final class CommandClientIT
|
|||||||
|
|
||||||
"fail with INVALID REQUEST for empty application ids" in {
|
"fail with INVALID REQUEST for empty application ids" in {
|
||||||
val completionsF = for {
|
val completionsF = for {
|
||||||
client <- commandClient(applicationId = "")
|
party <- freshParty()
|
||||||
completionsSource = client.completionSource(submittingPartyList, LedgerBegin)
|
client <- commandClient(appId = "")
|
||||||
|
completionsSource = client.completionSource(List(party), LedgerBegin)
|
||||||
completions <- completionsSource.takeWithin(5.seconds).runWith(Sink.seq)
|
completions <- completionsSource.takeWithin(5.seconds).runWith(Sink.seq)
|
||||||
} yield completions
|
} yield completions
|
||||||
|
|
||||||
@ -289,10 +319,13 @@ final class CommandClientIT
|
|||||||
}
|
}
|
||||||
|
|
||||||
"fail with the expected status on a ledger Id mismatch" in {
|
"fail with the expected status on a ledger Id mismatch" in {
|
||||||
commandClientWithoutTime(testNotLedgerId)
|
for {
|
||||||
.completionSource(submittingPartyList, LedgerBegin)
|
party <- freshParty()
|
||||||
|
err <- commandClientWithoutTime(ledgerId = testNotLedgerId)
|
||||||
|
.completionSource(List(party), LedgerBegin)
|
||||||
.runWith(Sink.head)
|
.runWith(Sink.head)
|
||||||
.failed map IsStatusException(Status.NOT_FOUND)
|
.failed
|
||||||
|
} yield IsStatusException(Status.NOT_FOUND)(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
"return completions of commands submitted before subscription if they are after the offset" in {
|
"return completions of commands submitted before subscription if they are after the offset" in {
|
||||||
@ -304,10 +337,11 @@ final class CommandClientIT
|
|||||||
|
|
||||||
// val for type inference
|
// val for type inference
|
||||||
val resultF = for {
|
val resultF = for {
|
||||||
|
party <- freshParty()
|
||||||
client <- commandClient()
|
client <- commandClient()
|
||||||
checkpoint <- client.getCompletionEnd()
|
checkpoint <- client.getCompletionEnd()
|
||||||
submissionResults <- Source(
|
submissionResults <- Source(
|
||||||
commandIds.map(i => Ctx(i, commandSubmissionWithId(i.toString)))
|
commandIds.map(i => Ctx(i, commandSubmissionWithId(i.toString, party)))
|
||||||
)
|
)
|
||||||
.flatMapMerge(10, randomDelay)
|
.flatMapMerge(10, randomDelay)
|
||||||
.via(client.submissionFlow())
|
.via(client.submissionFlow())
|
||||||
@ -315,10 +349,8 @@ final class CommandClientIT
|
|||||||
.runWith(Sink.seq)
|
.runWith(Sink.seq)
|
||||||
_ = submissionResults.foreach(v => v shouldBe a[Success[_]])
|
_ = submissionResults.foreach(v => v shouldBe a[Success[_]])
|
||||||
|
|
||||||
result <- readExpectedCommandIds(client, checkpoint.getOffset, commandIdStrings)
|
result <- readExpectedCommandIds(client, party, checkpoint.getOffset, commandIdStrings)
|
||||||
} yield {
|
} yield result
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
resultF map { case (seenCommandIds, remainingCommandIds) =>
|
resultF map { case (seenCommandIds, remainingCommandIds) =>
|
||||||
// N.B.: completions may include already-seen elements, and may be out of order
|
// N.B.: completions may include already-seen elements, and may be out of order
|
||||||
@ -336,15 +368,17 @@ final class CommandClientIT
|
|||||||
val commandIdStrings = Set(commandIds.map(_.toString): _*)
|
val commandIdStrings = Set(commandIds.map(_.toString): _*)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
|
party <- freshParty()
|
||||||
client <- commandClient()
|
client <- commandClient()
|
||||||
checkpoint <- client.getCompletionEnd()
|
checkpoint <- client.getCompletionEnd()
|
||||||
_ <- Source(commandIds.map(i => Ctx(i, commandSubmissionWithId(i.toString))))
|
_ <- Source(commandIds.map(i => Ctx(i, commandSubmissionWithId(i.toString, party))))
|
||||||
.flatMapMerge(10, randomDelay)
|
.flatMapMerge(10, randomDelay)
|
||||||
.via(client.submissionFlow())
|
.via(client.submissionFlow())
|
||||||
.map(_.context)
|
.map(_.context)
|
||||||
.runWith(Sink.ignore)
|
.runWith(Sink.ignore)
|
||||||
(seenCommandIds, remainingCommandIds) <- readExpectedCommandIds(
|
(seenCommandIds, remainingCommandIds) <- readExpectedCommandIds(
|
||||||
client,
|
client,
|
||||||
|
party,
|
||||||
checkpoint.getOffset,
|
checkpoint.getOffset,
|
||||||
commandIdStrings,
|
commandIdStrings,
|
||||||
)
|
)
|
||||||
@ -359,49 +393,49 @@ final class CommandClientIT
|
|||||||
|
|
||||||
"return the contexts for commands as they are completed" in {
|
"return the contexts for commands as they are completed" in {
|
||||||
val contexts = 6001.to(6010)
|
val contexts = 6001.to(6010)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
|
party <- freshParty()
|
||||||
client <- commandClient()
|
client <- commandClient()
|
||||||
tracker <- client.trackCommands[Int](submittingPartyList)
|
tracker <- client.trackCommands[Int](List(party))
|
||||||
result <- Source(contexts.map(i => Ctx(i, commandSubmissionWithId(i.toString))))
|
result <- Source(contexts.map(i => Ctx(i, commandSubmissionWithId(i.toString, party))))
|
||||||
.via(tracker)
|
.via(tracker)
|
||||||
.map(_.context)
|
.map(_.context)
|
||||||
.runWith(Sink.seq)
|
.runWith(Sink.seq)
|
||||||
} yield {
|
} yield result should contain theSameElementsAs contexts
|
||||||
result should contain theSameElementsAs contexts
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
"complete the stream when there's nothing to track" in {
|
"complete the stream when there's nothing to track" in {
|
||||||
for {
|
for {
|
||||||
|
party <- freshParty()
|
||||||
client <- commandClient()
|
client <- commandClient()
|
||||||
tracker <- client.trackCommands[Int](submittingPartyList)
|
tracker <- client.trackCommands[Int](List(party))
|
||||||
_ <- Source.empty[Ctx[Int, CommandSubmission]].via(tracker).runWith(Sink.ignore)
|
_ <- Source.empty[Ctx[Int, CommandSubmission]].via(tracker).runWith(Sink.ignore)
|
||||||
} yield {
|
} yield succeed
|
||||||
succeed
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
"not accept commands with missing args, return INVALID_ARGUMENT" in {
|
"not accept commands with missing args, return INVALID_ARGUMENT" in {
|
||||||
val expectedMessageSubstring =
|
val expectedMessageSubstring = "Expecting 1 field for record"
|
||||||
"Expecting 1 field for record"
|
for {
|
||||||
val commandWithInvalidArgs =
|
party <- freshParty()
|
||||||
|
commandWithInvalidArgs =
|
||||||
submitRequest(
|
submitRequest(
|
||||||
"Creating_contracts_for_invalid_arg_test",
|
"Creating_contracts_for_invalid_arg_test",
|
||||||
List(CreateCommand(Some(templateIds.dummy), Some(Record())).wrap),
|
List(CreateCommand(Some(templateIds.dummy), Some(Record())).wrap),
|
||||||
|
party,
|
||||||
)
|
)
|
||||||
|
a <- assertCommandFailsWithCode(
|
||||||
assertCommandFailsWithCode(
|
|
||||||
commandWithInvalidArgs,
|
commandWithInvalidArgs,
|
||||||
Code.INVALID_ARGUMENT,
|
Code.INVALID_ARGUMENT,
|
||||||
expectedMessageSubstring,
|
expectedMessageSubstring,
|
||||||
)
|
)
|
||||||
|
} yield a
|
||||||
}
|
}
|
||||||
|
|
||||||
"not accept commands with args of the wrong type, return INVALID_ARGUMENT" in {
|
"not accept commands with args of the wrong type, return INVALID_ARGUMENT" in {
|
||||||
val expectedMessageSubstring =
|
val expectedMessageSubstring = "mismatching type"
|
||||||
"mismatching type"
|
for {
|
||||||
val command =
|
party <- freshParty()
|
||||||
|
command =
|
||||||
submitRequest(
|
submitRequest(
|
||||||
"Boolean_param_with_wrong_type",
|
"Boolean_param_with_wrong_type",
|
||||||
List(
|
List(
|
||||||
@ -413,19 +447,21 @@ final class CommandClientIT
|
|||||||
),
|
),
|
||||||
).wrap
|
).wrap
|
||||||
),
|
),
|
||||||
|
party,
|
||||||
)
|
)
|
||||||
|
a <- assertCommandFailsWithCode(
|
||||||
assertCommandFailsWithCode(
|
|
||||||
command,
|
command,
|
||||||
Code.INVALID_ARGUMENT,
|
Code.INVALID_ARGUMENT,
|
||||||
expectedMessageSubstring,
|
expectedMessageSubstring,
|
||||||
)
|
)
|
||||||
|
} yield a
|
||||||
}
|
}
|
||||||
|
|
||||||
"not accept commands with unknown args, return INVALID_ARGUMENT" in {
|
"not accept commands with unknown args, return INVALID_ARGUMENT" in {
|
||||||
val expectedMessageSubstring =
|
val expectedMessageSubstring = "Missing record field"
|
||||||
"Missing record field"
|
for {
|
||||||
val command =
|
party <- freshParty()
|
||||||
|
command =
|
||||||
submitRequest(
|
submitRequest(
|
||||||
"Param_with_wrong_name",
|
"Param_with_wrong_name",
|
||||||
List(
|
List(
|
||||||
@ -437,58 +473,69 @@ final class CommandClientIT
|
|||||||
),
|
),
|
||||||
).wrap
|
).wrap
|
||||||
),
|
),
|
||||||
|
party,
|
||||||
)
|
)
|
||||||
|
a <- assertCommandFailsWithCode(
|
||||||
assertCommandFailsWithCode(
|
|
||||||
command,
|
command,
|
||||||
Code.INVALID_ARGUMENT,
|
Code.INVALID_ARGUMENT,
|
||||||
expectedMessageSubstring,
|
expectedMessageSubstring,
|
||||||
)
|
)
|
||||||
|
} yield a
|
||||||
}
|
}
|
||||||
|
|
||||||
"not accept commands with malformed decimals, return INVALID_ARGUMENT" in {
|
"not accept commands with malformed decimals, return INVALID_ARGUMENT" in {
|
||||||
val commandId = "Malformed_decimal"
|
import com.daml.ledger.api.v1.value._
|
||||||
val expectedMessageSubString =
|
|
||||||
"""Could not read Numeric string "1E-19""""
|
|
||||||
|
|
||||||
val command = submitRequest(
|
val commandId = "Malformed_decimal"
|
||||||
|
val expectedMessageSubString = """Could not read Numeric string "1E-19""""
|
||||||
|
|
||||||
|
for {
|
||||||
|
party <- freshParty()
|
||||||
|
command = submitRequest(
|
||||||
commandId,
|
commandId,
|
||||||
List(
|
Seq(
|
||||||
CreateCommand(
|
CreateCommand(
|
||||||
Some(templateIds.parameterShowcase),
|
Some(templateIds.parameterShowcase),
|
||||||
Some(recordWithArgument(paramShowcaseArgs, RecordField("decimal", "1E-19".asNumeric))),
|
Some(
|
||||||
|
recordWithArgument(paramShowcaseArgs, RecordField("decimal", "1E-19".asNumeric))
|
||||||
|
),
|
||||||
).wrap
|
).wrap
|
||||||
),
|
),
|
||||||
|
party,
|
||||||
)
|
)
|
||||||
|
a <- assertCommandFailsWithCode(command, Code.INVALID_ARGUMENT, expectedMessageSubString)
|
||||||
assertCommandFailsWithCode(command, Code.INVALID_ARGUMENT, expectedMessageSubString)
|
} yield a
|
||||||
}
|
}
|
||||||
|
|
||||||
"not accept commands with bad obligables, return INVALID_ARGUMENT" in {
|
"not accept commands with bad obligables, return INVALID_ARGUMENT" in {
|
||||||
val command =
|
for {
|
||||||
|
party <- freshParty()
|
||||||
|
command =
|
||||||
submitRequest(
|
submitRequest(
|
||||||
"Obligable_error",
|
"Obligable_error",
|
||||||
List(
|
List(
|
||||||
CreateCommand(
|
CreateCommand(
|
||||||
Some(templateIds.dummy),
|
Some(templateIds.dummy),
|
||||||
Some(
|
Some(
|
||||||
List("operator" -> ("not" + MockMessages.party).asParty)
|
List("operator" -> ("not" + party).asParty)
|
||||||
.asRecordOf(templateIds.dummy)
|
.asRecordOf(templateIds.dummy)
|
||||||
),
|
),
|
||||||
).wrap
|
).wrap
|
||||||
),
|
),
|
||||||
|
party,
|
||||||
)
|
)
|
||||||
|
a <- assertCommandFailsWithCode(command, Code.INVALID_ARGUMENT, "requires authorizers")
|
||||||
|
} yield a
|
||||||
|
|
||||||
assertCommandFailsWithCode(command, Code.INVALID_ARGUMENT, "requires authorizers")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
"not accept exercises with bad contract IDs, return ABORTED" in {
|
"not accept exercises with bad contract IDs, return ABORTED" in {
|
||||||
val contractId = ContractId.V1(
|
val dummySuffix: Bytes = Bytes.assertFromString("00")
|
||||||
Hash.hashPrivateKey(
|
val contractId =
|
||||||
"#deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef-123"
|
ContractId.V1.assertBuild(crypto.Hash.hashPrivateKey("secret"), dummySuffix)
|
||||||
)
|
for {
|
||||||
)
|
party <- freshParty()
|
||||||
val command =
|
command =
|
||||||
submitRequest(
|
submitRequest(
|
||||||
"Exercise_contract_not_found",
|
"Exercise_contract_not_found",
|
||||||
List(
|
List(
|
||||||
@ -499,9 +546,11 @@ final class CommandClientIT
|
|||||||
Some(unit),
|
Some(unit),
|
||||||
).wrap
|
).wrap
|
||||||
),
|
),
|
||||||
|
party,
|
||||||
)
|
)
|
||||||
|
a <- assertCommandFailsWithCode(command, Code.NOT_FOUND, "CONTRACT_NOT_FOUND")
|
||||||
|
} yield a
|
||||||
|
|
||||||
assertCommandFailsWithCode(command, Code.NOT_FOUND, "CONTRACT_NOT_FOUND")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,32 +4,25 @@
|
|||||||
package com.daml.ledger.client
|
package com.daml.ledger.client
|
||||||
|
|
||||||
import com.daml.grpc.GrpcException
|
import com.daml.grpc.GrpcException
|
||||||
import com.daml.ledger.api.domain
|
import com.daml.jwt.JwtSigner
|
||||||
import com.daml.ledger.api.testing.utils.{AkkaBeforeAndAfterAll, SuiteResourceManagementAroundEach}
|
import com.daml.jwt.domain.DecodedJwt
|
||||||
|
import com.daml.ledger.api.auth.AuthServiceJWTCodec
|
||||||
|
import com.daml.ledger.api.auth.CustomDamlJWTPayload
|
||||||
import com.daml.ledger.client.configuration.{
|
import com.daml.ledger.client.configuration.{
|
||||||
CommandClientConfiguration,
|
|
||||||
LedgerClientConfiguration,
|
LedgerClientConfiguration,
|
||||||
LedgerIdRequirement,
|
LedgerIdRequirement,
|
||||||
|
CommandClientConfiguration,
|
||||||
}
|
}
|
||||||
import com.daml.ledger.runner.common.Config
|
import com.daml.lf.integrationtest.CantonFixture
|
||||||
import com.daml.platform.sandbox.SandboxRequiringAuthorization
|
|
||||||
import com.daml.platform.sandbox.fixture.SandboxFixture
|
|
||||||
import org.scalatest.Inside
|
import org.scalatest.Inside
|
||||||
import org.scalatest.matchers.should.Matchers
|
import org.scalatest.matchers.should.Matchers
|
||||||
import org.scalatest.wordspec.AsyncWordSpec
|
import org.scalatest.wordspec.AsyncWordSpec
|
||||||
import scalaz.syntax.tag._
|
|
||||||
|
|
||||||
final class LedgerClientAuthIT
|
final class LedgerClientAuthIT extends AsyncWordSpec with Matchers with Inside with CantonFixture {
|
||||||
extends AsyncWordSpec
|
|
||||||
with Matchers
|
|
||||||
with Inside
|
|
||||||
with AkkaBeforeAndAfterAll
|
|
||||||
with SuiteResourceManagementAroundEach
|
|
||||||
with SandboxFixture
|
|
||||||
with SandboxRequiringAuthorization {
|
|
||||||
|
|
||||||
private val LedgerId =
|
protected val jwtSecret: String = java.util.UUID.randomUUID.toString
|
||||||
domain.LedgerId(s"${classOf[LedgerClientAuthIT].getSimpleName.toLowerCase}-ledger-id")
|
|
||||||
|
override protected lazy val authSecret = Some(jwtSecret)
|
||||||
|
|
||||||
private val ClientConfigurationWithoutToken = LedgerClientConfiguration(
|
private val ClientConfigurationWithoutToken = LedgerClientConfiguration(
|
||||||
applicationId = classOf[LedgerClientAuthIT].getSimpleName,
|
applicationId = classOf[LedgerClientAuthIT].getSimpleName,
|
||||||
@ -38,11 +31,32 @@ final class LedgerClientAuthIT
|
|||||||
token = None,
|
token = None,
|
||||||
)
|
)
|
||||||
|
|
||||||
private val ClientConfiguration = ClientConfigurationWithoutToken.copy(
|
private val emptyToken = CustomDamlJWTPayload(
|
||||||
token = Some(toHeader(readOnlyToken("Read-only party")))
|
ledgerId = None,
|
||||||
|
participantId = None,
|
||||||
|
applicationId = None,
|
||||||
|
exp = None,
|
||||||
|
admin = false,
|
||||||
|
actAs = Nil,
|
||||||
|
readAs = Nil,
|
||||||
)
|
)
|
||||||
|
|
||||||
override protected def config: Config = super.config.copy(ledgerId = LedgerId.unwrap)
|
private val ClientConfiguration = ClientConfigurationWithoutToken.copy(
|
||||||
|
token = Some(
|
||||||
|
JwtSigner.HMAC256
|
||||||
|
.sign(
|
||||||
|
DecodedJwt(
|
||||||
|
"""{"alg": "HS256", "typ": "JWT"}""",
|
||||||
|
AuthServiceJWTCodec.compactPrint(emptyToken.copy(readAs = List("Alice")), false),
|
||||||
|
),
|
||||||
|
jwtSecret,
|
||||||
|
)
|
||||||
|
.getOrElse(sys.error("Failed to generate token"))
|
||||||
|
.value
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
lazy val channel = config.channel(suiteResource.value.head)
|
||||||
|
|
||||||
"the ledger client" when {
|
"the ledger client" when {
|
||||||
"it has a read-only token" should {
|
"it has a read-only token" should {
|
||||||
@ -50,7 +64,7 @@ final class LedgerClientAuthIT
|
|||||||
for {
|
for {
|
||||||
client <- LedgerClient(channel, ClientConfiguration)
|
client <- LedgerClient(channel, ClientConfiguration)
|
||||||
} yield {
|
} yield {
|
||||||
client.ledgerId should be(LedgerId)
|
client.ledgerId should be(config.ledgerIds.head)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,7 +89,7 @@ final class LedgerClientAuthIT
|
|||||||
.allocateParty(
|
.allocateParty(
|
||||||
hint = Some(partyName),
|
hint = Some(partyName),
|
||||||
displayName = Some(partyName),
|
displayName = Some(partyName),
|
||||||
token = Some(toHeader(adminToken)),
|
token = config.adminToken,
|
||||||
)
|
)
|
||||||
} yield {
|
} yield {
|
||||||
allocatedParty.displayName should be(Some(partyName))
|
allocatedParty.displayName should be(Some(partyName))
|
||||||
|
@ -4,16 +4,14 @@
|
|||||||
package com.daml.ledger.client
|
package com.daml.ledger.client
|
||||||
|
|
||||||
import com.daml.ledger.api.domain
|
import com.daml.ledger.api.domain
|
||||||
import com.daml.ledger.api.domain.{IdentityProviderConfig, IdentityProviderId, JwksUrl}
|
import com.daml.ledger.api.domain.{IdentityProviderId}
|
||||||
import com.daml.ledger.api.testing.utils.{AkkaBeforeAndAfterAll, SuiteResourceManagementAroundEach}
|
|
||||||
import com.daml.ledger.client.configuration.{
|
import com.daml.ledger.client.configuration.{
|
||||||
CommandClientConfiguration,
|
CommandClientConfiguration,
|
||||||
LedgerClientConfiguration,
|
LedgerClientConfiguration,
|
||||||
LedgerIdRequirement,
|
LedgerIdRequirement,
|
||||||
}
|
}
|
||||||
import com.daml.ledger.runner.common.Config
|
import com.daml.lf.integrationtest.CantonFixture
|
||||||
import com.daml.lf.data.Ref
|
import com.daml.lf.data.Ref
|
||||||
import com.daml.platform.sandbox.fixture.SandboxFixture
|
|
||||||
import com.google.protobuf.field_mask.FieldMask
|
import com.google.protobuf.field_mask.FieldMask
|
||||||
import io.grpc.ManagedChannel
|
import io.grpc.ManagedChannel
|
||||||
import org.scalatest.Inside
|
import org.scalatest.Inside
|
||||||
@ -22,26 +20,19 @@ import org.scalatest.wordspec.AsyncWordSpec
|
|||||||
import scalaz.OneAnd
|
import scalaz.OneAnd
|
||||||
import scalaz.syntax.tag._
|
import scalaz.syntax.tag._
|
||||||
|
|
||||||
final class LedgerClientIT
|
final class LedgerClientIT extends AsyncWordSpec with Matchers with Inside with CantonFixture {
|
||||||
extends AsyncWordSpec
|
|
||||||
with Matchers
|
|
||||||
with Inside
|
|
||||||
with AkkaBeforeAndAfterAll
|
|
||||||
with SuiteResourceManagementAroundEach
|
|
||||||
with SandboxFixture {
|
|
||||||
|
|
||||||
private val LedgerId =
|
private val LedgerId = domain.LedgerId(config.ledgerIds.head)
|
||||||
domain.LedgerId(s"${classOf[LedgerClientIT].getSimpleName.toLowerCase}-ledger-id")
|
|
||||||
|
lazy val channel = config.channel(suiteResource.value.head)
|
||||||
|
|
||||||
private val ClientConfiguration = LedgerClientConfiguration(
|
private val ClientConfiguration = LedgerClientConfiguration(
|
||||||
applicationId = classOf[LedgerClientIT].getSimpleName,
|
applicationId = applicationId.unwrap,
|
||||||
ledgerIdRequirement = LedgerIdRequirement.none,
|
ledgerIdRequirement = LedgerIdRequirement.none,
|
||||||
commandClient = CommandClientConfiguration.default,
|
commandClient = CommandClientConfiguration.default,
|
||||||
token = None,
|
token = None,
|
||||||
)
|
)
|
||||||
|
|
||||||
override protected def config: Config = super.config.copy(ledgerId = LedgerId.unwrap)
|
|
||||||
|
|
||||||
"the ledger client" should {
|
"the ledger client" should {
|
||||||
"retrieve the ledger ID" in {
|
"retrieve the ledger ID" in {
|
||||||
for {
|
for {
|
||||||
@ -52,14 +43,14 @@ final class LedgerClientIT
|
|||||||
}
|
}
|
||||||
|
|
||||||
"make some requests" in {
|
"make some requests" in {
|
||||||
val partyName = "Alice"
|
val partyName = CantonFixture.freshName("Alice")
|
||||||
for {
|
for {
|
||||||
client <- LedgerClient(channel, ClientConfiguration)
|
client <- LedgerClient(channel, ClientConfiguration)
|
||||||
// The request type is irrelevant here; the point is that we can make some.
|
// The request type is irrelevant here; the point is that we can make some.
|
||||||
allocatedParty <- client.partyManagementClient
|
allocatedParty <- client.partyManagementClient
|
||||||
.allocateParty(hint = Some(partyName), displayName = None)
|
.allocateParty(hint = Some(partyName), displayName = None)
|
||||||
retrievedParties <- client.partyManagementClient
|
retrievedParties <- client.partyManagementClient
|
||||||
.getParties(OneAnd(Ref.Party.assertFromString(partyName), Set.empty))
|
.getParties(OneAnd(Ref.Party.assertFromString(allocatedParty.party), Set.empty))
|
||||||
} yield {
|
} yield {
|
||||||
retrievedParties should be(List(allocatedParty))
|
retrievedParties should be(List(allocatedParty))
|
||||||
}
|
}
|
||||||
@ -78,22 +69,25 @@ final class LedgerClientIT
|
|||||||
}
|
}
|
||||||
|
|
||||||
"identity provider config" should {
|
"identity provider config" should {
|
||||||
val config = IdentityProviderConfig(
|
def freshConfig(): domain.IdentityProviderConfig = domain.IdentityProviderConfig(
|
||||||
IdentityProviderId.Id(Ref.LedgerString.assertFromString("abcd")),
|
domain.IdentityProviderId.Id(
|
||||||
|
Ref.LedgerString.assertFromString(CantonFixture.freshName("abcd"))
|
||||||
|
),
|
||||||
isDeactivated = false,
|
isDeactivated = false,
|
||||||
JwksUrl.assertFromString("http://jwks.some.domain:9999/jwks"),
|
domain.JwksUrl.assertFromString("http://jwks.some.domain:9999/jwks"),
|
||||||
"SomeUser",
|
CantonFixture.freshName("SomeUser"),
|
||||||
Some("SomeAudience"),
|
Some(CantonFixture.freshName("SomeAudience")),
|
||||||
)
|
)
|
||||||
|
|
||||||
val updatedConfig = config.copy(
|
def updateConfig(config: domain.IdentityProviderConfig) = config.copy(
|
||||||
isDeactivated = true,
|
isDeactivated = true,
|
||||||
jwksUrl = JwksUrl("http://someotherurl"),
|
jwksUrl = domain.JwksUrl("http://someotherurl"),
|
||||||
issuer = "ANewIssuer",
|
issuer = CantonFixture.freshName("ANewIssuer"),
|
||||||
audience = Some("ChangedAudience"),
|
audience = Some(CantonFixture.freshName("ChangedAudience")),
|
||||||
)
|
)
|
||||||
|
|
||||||
"create an identity provider" in {
|
"create an identity provider" in {
|
||||||
|
val config = freshConfig()
|
||||||
for {
|
for {
|
||||||
client <- LedgerClient(channel, ClientConfiguration)
|
client <- LedgerClient(channel, ClientConfiguration)
|
||||||
createdConfig <- client.identityProviderConfigClient.createIdentityProviderConfig(
|
createdConfig <- client.identityProviderConfigClient.createIdentityProviderConfig(
|
||||||
@ -105,6 +99,7 @@ final class LedgerClientIT
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
"get an identity provider" in {
|
"get an identity provider" in {
|
||||||
|
val config = freshConfig()
|
||||||
for {
|
for {
|
||||||
client <- LedgerClient(channel, ClientConfiguration)
|
client <- LedgerClient(channel, ClientConfiguration)
|
||||||
_ <- client.identityProviderConfigClient.createIdentityProviderConfig(config, None)
|
_ <- client.identityProviderConfigClient.createIdentityProviderConfig(config, None)
|
||||||
@ -117,6 +112,8 @@ final class LedgerClientIT
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
"update an identity provider" in {
|
"update an identity provider" in {
|
||||||
|
val config = freshConfig()
|
||||||
|
val updatedConfig = updateConfig(config)
|
||||||
for {
|
for {
|
||||||
client <- LedgerClient(channel, ClientConfiguration)
|
client <- LedgerClient(channel, ClientConfiguration)
|
||||||
_ <- client.identityProviderConfigClient.createIdentityProviderConfig(config, None)
|
_ <- client.identityProviderConfigClient.createIdentityProviderConfig(config, None)
|
||||||
@ -136,8 +133,11 @@ final class LedgerClientIT
|
|||||||
}
|
}
|
||||||
|
|
||||||
"list identity providers" in {
|
"list identity providers" in {
|
||||||
|
val config = freshConfig()
|
||||||
|
val updatedConfig = updateConfig(config)
|
||||||
for {
|
for {
|
||||||
client <- LedgerClient(channel, ClientConfiguration)
|
client <- LedgerClient(channel, ClientConfiguration)
|
||||||
|
before <- client.identityProviderConfigClient.listIdentityProviderConfigs(None)
|
||||||
config1 <- client.identityProviderConfigClient.createIdentityProviderConfig(config, None)
|
config1 <- client.identityProviderConfigClient.createIdentityProviderConfig(config, None)
|
||||||
config2 <- client.identityProviderConfigClient.createIdentityProviderConfig(
|
config2 <- client.identityProviderConfigClient.createIdentityProviderConfig(
|
||||||
updatedConfig.copy(identityProviderId =
|
updatedConfig.copy(identityProviderId =
|
||||||
@ -145,23 +145,25 @@ final class LedgerClientIT
|
|||||||
),
|
),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
respConfig <- client.identityProviderConfigClient.listIdentityProviderConfigs(None)
|
after <- client.identityProviderConfigClient.listIdentityProviderConfigs(None)
|
||||||
} yield {
|
} yield {
|
||||||
respConfig.toSet should contain theSameElementsAs (Set(config2, config1))
|
(after.toSet -- before) should contain theSameElementsAs Set(config2, config1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"delete identity provider" in {
|
"delete identity provider" in {
|
||||||
|
val config = freshConfig()
|
||||||
for {
|
for {
|
||||||
client <- LedgerClient(channel, ClientConfiguration)
|
client <- LedgerClient(channel, ClientConfiguration)
|
||||||
|
before <- client.identityProviderConfigClient.listIdentityProviderConfigs(None)
|
||||||
config1 <- client.identityProviderConfigClient.createIdentityProviderConfig(config, None)
|
config1 <- client.identityProviderConfigClient.createIdentityProviderConfig(config, None)
|
||||||
_ <- client.identityProviderConfigClient.deleteIdentityProviderConfig(
|
_ <- client.identityProviderConfigClient.deleteIdentityProviderConfig(
|
||||||
config1.identityProviderId,
|
config1.identityProviderId,
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
respConfig <- client.identityProviderConfigClient.listIdentityProviderConfigs(None)
|
after <- client.identityProviderConfigClient.listIdentityProviderConfigs(None)
|
||||||
} yield {
|
} yield {
|
||||||
respConfig.toSet should be(Set.empty)
|
before.toSet should be(after.toSet)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user