mirror of
https://github.com/digital-asset/daml.git
synced 2024-09-20 01:07:18 +03:00
Simplify new test tool (#2573)
* Simplify new test tool * Shorten exercise to leverage ContractId * Remove dead code * Address https://github.com/digital-asset/daml/pull/2573#discussion_r314702191 * Address https://github.com/digital-asset/daml/pull/2573#discussion_r314700739 * Simplify transaction requests
This commit is contained in:
parent
6a0ebc9f5e
commit
eb965866cc
@ -1,256 +0,0 @@
|
||||
// Copyright (c) 2019 The DAML Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package com.daml.ledger.api.testtool.infrastructure
|
||||
|
||||
import java.time.{Clock, Instant}
|
||||
|
||||
import com.digitalasset.ledger.api.v1.active_contracts_service.{
|
||||
GetActiveContractsRequest,
|
||||
GetActiveContractsResponse
|
||||
}
|
||||
import com.digitalasset.ledger.api.v1.admin.party_management_service.AllocatePartyRequest
|
||||
import com.digitalasset.ledger.api.v1.command_service.SubmitAndWaitRequest
|
||||
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_identity_service.GetLedgerIdentityRequest
|
||||
import com.digitalasset.ledger.api.v1.ledger_offset.LedgerOffset
|
||||
import com.digitalasset.ledger.api.v1.testing.time_service.{
|
||||
GetTimeRequest,
|
||||
GetTimeResponse,
|
||||
SetTimeRequest
|
||||
}
|
||||
import com.digitalasset.ledger.api.v1.transaction.{Transaction, TransactionTree}
|
||||
import com.digitalasset.ledger.api.v1.transaction_filter.{
|
||||
Filters,
|
||||
InclusiveFilters,
|
||||
TransactionFilter
|
||||
}
|
||||
import com.digitalasset.ledger.api.v1.transaction_service.{
|
||||
GetLedgerEndRequest,
|
||||
GetTransactionByIdRequest,
|
||||
GetTransactionsRequest
|
||||
}
|
||||
import com.digitalasset.ledger.api.v1.value.{Identifier, Value}
|
||||
import com.digitalasset.ledger.client.binding.Primitive.Party
|
||||
import com.digitalasset.ledger.client.binding.{Contract, Primitive, Template, ValueDecoder}
|
||||
import com.digitalasset.platform.testing.{FiniteStreamObserver, SingleItemObserver}
|
||||
import com.google.protobuf.timestamp.Timestamp
|
||||
import io.grpc.stub.StreamObserver
|
||||
import scalaz.Tag
|
||||
import scalaz.syntax.tag._
|
||||
|
||||
import scala.concurrent.duration.Duration
|
||||
import scala.concurrent.{ExecutionContext, Future}
|
||||
import scala.util.control.NonFatal
|
||||
|
||||
object LedgerBindings {
|
||||
|
||||
private def filter(templateIds: Seq[Identifier]): Filters =
|
||||
new Filters(if (templateIds.isEmpty) None else Some(new InclusiveFilters(templateIds)))
|
||||
|
||||
private def transactionFilter(
|
||||
parties: Seq[String],
|
||||
templateIds: Seq[Identifier]): TransactionFilter = {
|
||||
val templateIdFilter = filter(templateIds)
|
||||
new TransactionFilter(Map(parties.map(_ -> templateIdFilter): _*))
|
||||
}
|
||||
|
||||
private val end = LedgerOffset(
|
||||
LedgerOffset.Value.Boundary(LedgerOffset.LedgerBoundary.LEDGER_END))
|
||||
|
||||
}
|
||||
|
||||
private[infrastructure] final class LedgerBindings(
|
||||
services: LedgerServices,
|
||||
commandTtlFactor: Double)(implicit ec: ExecutionContext) {
|
||||
|
||||
val ledgerId: Future[String] =
|
||||
for {
|
||||
response <- services.identity.getLedgerIdentity(new GetLedgerIdentityRequest)
|
||||
} yield response.ledgerId
|
||||
|
||||
private def timestampToInstant(t: Timestamp): Instant =
|
||||
Instant.EPOCH.plusSeconds(t.seconds).plusNanos(t.nanos.toLong)
|
||||
|
||||
private def instantToTimestamp(t: Instant): Timestamp =
|
||||
new Timestamp(t.getEpochSecond, t.getNano)
|
||||
|
||||
def time: Future[Instant] =
|
||||
for {
|
||||
id <- ledgerId
|
||||
t <- SingleItemObserver
|
||||
.first[GetTimeResponse](services.time.getTime(new GetTimeRequest(id), _))
|
||||
.map(_.map(r => timestampToInstant(r.currentTime.get)))
|
||||
.recover {
|
||||
case NonFatal(_) => Some(Clock.systemUTC().instant())
|
||||
}
|
||||
} yield t.get
|
||||
|
||||
def passTime(t: Duration): Future[Unit] =
|
||||
for {
|
||||
id <- ledgerId
|
||||
currentInstant <- time
|
||||
currentTime = Some(instantToTimestamp(currentInstant))
|
||||
newTime = Some(instantToTimestamp(currentInstant.plusNanos(t.toNanos)))
|
||||
result <- services.time.setTime(new SetTimeRequest(id, currentTime, newTime)).map(_ => ())
|
||||
} yield result
|
||||
|
||||
def allocateParty(partyIdHint: String): Future[Party] =
|
||||
services.partyManagement
|
||||
.allocateParty(new AllocatePartyRequest(partyIdHint = partyIdHint))
|
||||
.map(r => Party(r.partyDetails.get.party))
|
||||
|
||||
def ledgerEnd: Future[LedgerOffset] =
|
||||
for {
|
||||
id <- ledgerId
|
||||
response <- services.transaction.getLedgerEnd(new GetLedgerEndRequest(id))
|
||||
} yield response.offset.get
|
||||
|
||||
def activeContracts(
|
||||
parties: Seq[Party],
|
||||
templateIds: Seq[Identifier]): Future[Vector[CreatedEvent]] =
|
||||
for {
|
||||
id <- ledgerId
|
||||
contracts <- FiniteStreamObserver[GetActiveContractsResponse](
|
||||
services.activeContracts.getActiveContracts(
|
||||
new GetActiveContractsRequest(
|
||||
ledgerId = id,
|
||||
filter = Some(LedgerBindings.transactionFilter(Tag.unsubst(parties), templateIds)),
|
||||
verbose = true
|
||||
),
|
||||
_
|
||||
)
|
||||
)
|
||||
} yield contracts.flatMap(_.activeContracts)
|
||||
|
||||
private def transactions[Res, Tx](service: (GetTransactionsRequest, StreamObserver[Res]) => Unit)(
|
||||
extract: Res => Seq[Tx])(
|
||||
begin: LedgerOffset,
|
||||
parties: Seq[Party],
|
||||
templateIds: Seq[Identifier]): Future[Vector[Tx]] =
|
||||
for {
|
||||
id <- ledgerId
|
||||
txs <- FiniteStreamObserver[Res](
|
||||
service(
|
||||
new GetTransactionsRequest(
|
||||
ledgerId = id,
|
||||
begin = Some(begin),
|
||||
end = Some(LedgerBindings.end),
|
||||
filter = Some(LedgerBindings.transactionFilter(Tag.unsubst(parties), templateIds)),
|
||||
verbose = true
|
||||
),
|
||||
_
|
||||
))
|
||||
} yield txs.flatMap(extract)
|
||||
|
||||
def flatTransactions(
|
||||
begin: LedgerOffset,
|
||||
parties: Seq[Party],
|
||||
templateIds: Seq[Identifier]): Future[Vector[Transaction]] =
|
||||
transactions(services.transaction.getTransactions)(_.transactions)(begin, parties, templateIds)
|
||||
|
||||
def transactionTrees(
|
||||
begin: LedgerOffset,
|
||||
parties: Seq[Party],
|
||||
templateIds: Seq[Identifier]): Future[Vector[TransactionTree]] =
|
||||
transactions(services.transaction.getTransactionTrees)(_.transactions)(
|
||||
begin,
|
||||
parties,
|
||||
templateIds)
|
||||
|
||||
def getTransactionById(transactionId: String, parties: Seq[Party]): Future[TransactionTree] =
|
||||
for {
|
||||
id <- ledgerId
|
||||
transaction <- services.transaction.getTransactionById(
|
||||
new GetTransactionByIdRequest(id, transactionId, Tag.unsubst(parties)))
|
||||
} yield transaction.transaction.get
|
||||
|
||||
private def decodeCreated[T <: Template[T]](event: CreatedEvent)(
|
||||
implicit decoder: ValueDecoder[T]): Option[Contract[T]] =
|
||||
for {
|
||||
record <- event.createArguments
|
||||
a <- decoder.read(Value.Sum.Record(record))
|
||||
} yield
|
||||
Contract(
|
||||
Primitive.ContractId(event.contractId),
|
||||
a,
|
||||
event.agreementText,
|
||||
event.signatories,
|
||||
event.observers,
|
||||
event.contractKey)
|
||||
|
||||
def create[T <: Template[T]: ValueDecoder](
|
||||
party: Party,
|
||||
applicationId: String,
|
||||
commandId: String,
|
||||
template: Template[T]
|
||||
): Future[Contract[T]] =
|
||||
for {
|
||||
request <- prepareSubmission(party, applicationId, commandId, Seq(template.create.command))
|
||||
response <- submitAndWaitForTransaction(request).map(_.events.collect {
|
||||
case Event(Created(e)) => decodeCreated(e).get
|
||||
}.head)
|
||||
} yield response
|
||||
|
||||
def createAndGetTransactionId[T <: Template[T]: ValueDecoder](
|
||||
party: Party,
|
||||
applicationId: String,
|
||||
commandId: String,
|
||||
template: Template[T]
|
||||
): Future[(String, Contract[T])] =
|
||||
for {
|
||||
request <- prepareSubmission(party, applicationId, commandId, Seq(template.create.command))
|
||||
response <- submitAndWaitForTransaction(request).map(tx =>
|
||||
tx.transactionId -> tx.events.collect {
|
||||
case Event(Created(e)) => decodeCreated(e).get
|
||||
}.head)
|
||||
} yield response
|
||||
|
||||
def exercise[T](
|
||||
party: Party,
|
||||
applicationId: String,
|
||||
commandId: String,
|
||||
exercise: Party => Primitive.Update[T]
|
||||
): Future[TransactionTree] =
|
||||
for {
|
||||
request <- prepareSubmission(party, applicationId, commandId, Seq(exercise(party).command))
|
||||
response <- submitAndWaitForTransactionTree(request)
|
||||
} yield response
|
||||
|
||||
def prepareSubmission(
|
||||
party: Party,
|
||||
applicationId: String,
|
||||
commandId: String,
|
||||
commands: Seq[Command]): Future[SubmitAndWaitRequest] =
|
||||
for {
|
||||
id <- ledgerId
|
||||
let <- time
|
||||
mrt = let.plusSeconds(math.floor(30 * commandTtlFactor).toLong)
|
||||
} yield
|
||||
new SubmitAndWaitRequest(
|
||||
Some(
|
||||
new Commands(
|
||||
ledgerId = id,
|
||||
applicationId = applicationId,
|
||||
commandId = commandId,
|
||||
party = party.unwrap,
|
||||
ledgerEffectiveTime = Some(new Timestamp(let.getEpochSecond, let.getNano)),
|
||||
maximumRecordTime = Some(new Timestamp(mrt.getEpochSecond, mrt.getNano)),
|
||||
commands = commands
|
||||
)))
|
||||
|
||||
def submitAndWait(request: SubmitAndWaitRequest): Future[Unit] =
|
||||
services.command.submitAndWait(request).map(_ => ())
|
||||
|
||||
def submitAndWaitForTransactionId(request: SubmitAndWaitRequest): Future[String] =
|
||||
services.command.submitAndWaitForTransactionId(request).map(_.transactionId)
|
||||
|
||||
def submitAndWaitForTransaction(request: SubmitAndWaitRequest): Future[Transaction] =
|
||||
services.command.submitAndWaitForTransaction(request).map(_.transaction.get)
|
||||
|
||||
def submitAndWaitForTransactionTree(request: SubmitAndWaitRequest): Future[TransactionTree] =
|
||||
services.command.submitAndWaitForTransactionTree(request).map(_.transaction.get)
|
||||
|
||||
}
|
@ -5,6 +5,8 @@ package com.daml.ledger.api.testtool.infrastructure
|
||||
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
import com.digitalasset.ledger.api.v1.ledger_identity_service.GetLedgerIdentityRequest
|
||||
import com.digitalasset.ledger.api.v1.transaction_service.GetLedgerEndRequest
|
||||
import io.grpc.ManagedChannel
|
||||
import io.grpc.netty.{NegotiationType, NettyChannelBuilder}
|
||||
import io.netty.channel.nio.NioEventLoopGroup
|
||||
@ -19,16 +21,17 @@ import scala.util.Try
|
||||
private[testtool] final class LedgerSession private (
|
||||
val config: LedgerSessionConfiguration,
|
||||
channel: ManagedChannel,
|
||||
eventLoopGroup: NioEventLoopGroup)(implicit ec: ExecutionContext) {
|
||||
eventLoopGroup: NioEventLoopGroup)(implicit val executionContext: ExecutionContext) {
|
||||
|
||||
private[this] val logger = LoggerFactory.getLogger(classOf[LedgerSession])
|
||||
|
||||
private[this] val services: LedgerServices = new LedgerServices(channel)
|
||||
|
||||
private[this] val bindings: LedgerBindings = new LedgerBindings(services, config.commandTtlFactor)
|
||||
|
||||
private[testtool] def createTestContext(applicationId: String): Future[LedgerTestContext] =
|
||||
bindings.ledgerEnd.map(new LedgerTestContext(applicationId, _, bindings))
|
||||
for {
|
||||
id <- services.identity.getLedgerIdentity(new GetLedgerIdentityRequest).map(_.ledgerId)
|
||||
end <- services.transaction.getLedgerEnd(new GetLedgerEndRequest(id)).map(_.getOffset)
|
||||
} yield new LedgerTestContext(id, applicationId, end, services, config.commandTtlFactor)
|
||||
|
||||
private[testtool] def close(): Unit = {
|
||||
logger.info(s"Disconnecting from ledger at ${config.host}:${config.port}...")
|
||||
|
@ -11,11 +11,7 @@ object LedgerTest {
|
||||
|
||||
def apply(shortIdentifier: String, description: String, timeout: Long = 30000L)(
|
||||
test: LedgerTestContext => Future[Unit]): LedgerTest =
|
||||
new LedgerTest(
|
||||
Ref.LedgerString.fromString(shortIdentifier).fold(m => throw sys.error(m), identity),
|
||||
description,
|
||||
timeout,
|
||||
test)
|
||||
new LedgerTest(Ref.LedgerString.assertFromString(shortIdentifier), description, timeout, test)
|
||||
|
||||
}
|
||||
|
||||
|
@ -3,30 +3,83 @@
|
||||
|
||||
package com.daml.ledger.api.testtool.infrastructure
|
||||
|
||||
import java.time.Instant
|
||||
import java.time.{Clock, Instant}
|
||||
|
||||
import com.digitalasset.daml.lf.data.Ref
|
||||
import com.digitalasset.daml.lf.language.Ast
|
||||
import com.digitalasset.ledger.api.v1.active_contracts_service.{
|
||||
GetActiveContractsRequest,
|
||||
GetActiveContractsResponse
|
||||
}
|
||||
import com.digitalasset.ledger.api.v1.admin.party_management_service.AllocatePartyRequest
|
||||
import com.digitalasset.ledger.api.v1.command_service.SubmitAndWaitRequest
|
||||
import com.digitalasset.ledger.api.v1.commands.Command
|
||||
import com.digitalasset.ledger.api.v1.event.CreatedEvent
|
||||
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_offset.LedgerOffset
|
||||
import com.digitalasset.ledger.api.v1.testing.time_service.{
|
||||
GetTimeRequest,
|
||||
GetTimeResponse,
|
||||
SetTimeRequest
|
||||
}
|
||||
import com.digitalasset.ledger.api.v1.transaction.{Transaction, TransactionTree}
|
||||
import com.digitalasset.ledger.api.v1.transaction_filter.{
|
||||
Filters,
|
||||
InclusiveFilters,
|
||||
TransactionFilter
|
||||
}
|
||||
import com.digitalasset.ledger.api.v1.transaction_service.{
|
||||
GetTransactionByIdRequest,
|
||||
GetTransactionsRequest
|
||||
}
|
||||
import com.digitalasset.ledger.api.v1.value.Identifier
|
||||
import com.digitalasset.ledger.client.binding.Primitive.Party
|
||||
import com.digitalasset.ledger.client.binding.{Contract, Primitive, Template, ValueDecoder}
|
||||
import com.digitalasset.ledger.client.binding.{Primitive, Template}
|
||||
import com.digitalasset.platform.testing.{FiniteStreamObserver, SingleItemObserver}
|
||||
import com.google.protobuf.timestamp.Timestamp
|
||||
import io.grpc.stub.StreamObserver
|
||||
import scalaz.Tag
|
||||
import scalaz.syntax.tag._
|
||||
|
||||
import scala.concurrent.duration.Duration
|
||||
import scala.concurrent.{ExecutionContext, Future}
|
||||
import scala.util.control.NonFatal
|
||||
|
||||
final class LedgerTestContext(
|
||||
object LedgerTestContext {
|
||||
|
||||
private[this] def filter(templateIds: Seq[Identifier]): Filters =
|
||||
new Filters(if (templateIds.isEmpty) None else Some(new InclusiveFilters(templateIds)))
|
||||
|
||||
private def transactionFilter(
|
||||
parties: Seq[String],
|
||||
templateIds: Seq[Identifier]): Some[TransactionFilter] =
|
||||
Some(new TransactionFilter(Map(parties.map(_ -> filter(templateIds)): _*)))
|
||||
|
||||
private def timestamp(i: Instant): Some[Timestamp] =
|
||||
Some(new Timestamp(i.getEpochSecond, i.getNano))
|
||||
|
||||
private def timestampToInstant(t: Timestamp): Instant =
|
||||
Instant.EPOCH.plusSeconds(t.seconds).plusNanos(t.nanos.toLong)
|
||||
|
||||
private def instantToTimestamp(t: Instant): Timestamp =
|
||||
new Timestamp(t.getEpochSecond, t.getNano)
|
||||
|
||||
private val end = LedgerOffset(
|
||||
LedgerOffset.Value.Boundary(LedgerOffset.LedgerBoundary.LEDGER_END))
|
||||
|
||||
private val defaultTtlSeconds = 30
|
||||
|
||||
}
|
||||
|
||||
private[infrastructure] final class LedgerTestContext(
|
||||
val ledgerId: String,
|
||||
val applicationId: String,
|
||||
val offsetAtStart: LedgerOffset,
|
||||
bindings: LedgerBindings)(implicit val ec: ExecutionContext)
|
||||
extends ExecutionContext {
|
||||
referenceOffset: LedgerOffset,
|
||||
services: LedgerServices,
|
||||
commandTtlFactor: Double)(implicit ec: ExecutionContext) {
|
||||
|
||||
override def execute(runnable: Runnable): Unit = ec.execute(runnable)
|
||||
override def reportFailure(cause: Throwable): Unit = ec.reportFailure(cause)
|
||||
import LedgerTestContext._
|
||||
|
||||
private[this] def timestampWithTtl(i: Instant): Some[Timestamp] =
|
||||
timestamp(i.plusSeconds(math.floor(defaultTtlSeconds * commandTtlFactor).toLong))
|
||||
|
||||
private[this] val nextPartyHintId: () => String = {
|
||||
val it = Iterator.from(0).map(n => s"$applicationId-party-$n")
|
||||
@ -39,64 +92,123 @@ final class LedgerTestContext(
|
||||
it.synchronized(it.next())
|
||||
}
|
||||
|
||||
val ledgerId: Future[String] = bindings.ledgerId
|
||||
def time(): Future[Instant] =
|
||||
SingleItemObserver
|
||||
.first[GetTimeResponse](services.time.getTime(new GetTimeRequest(ledgerId), _))
|
||||
.map(_.map(r => timestampToInstant(r.getCurrentTime)).get)
|
||||
.recover {
|
||||
case NonFatal(_) => Clock.systemUTC().instant()
|
||||
}
|
||||
|
||||
def passTime(t: Duration): Future[Unit] =
|
||||
for {
|
||||
currentInstant <- time()
|
||||
currentTime = Some(instantToTimestamp(currentInstant))
|
||||
newTime = Some(instantToTimestamp(currentInstant.plusNanos(t.toNanos)))
|
||||
result <- services.time
|
||||
.setTime(new SetTimeRequest(ledgerId, currentTime, newTime))
|
||||
.map(_ => ())
|
||||
} yield result
|
||||
|
||||
def allocateParty(): Future[Party] =
|
||||
bindings.allocateParty(nextPartyHintId())
|
||||
services.partyManagement
|
||||
.allocateParty(new AllocatePartyRequest(partyIdHint = nextPartyHintId()))
|
||||
.map(r => Party(r.partyDetails.get.party))
|
||||
|
||||
def time: Future[Instant] = bindings.time
|
||||
def allocateParties(n: Int): Future[Vector[Party]] =
|
||||
Future.sequence(Vector.fill(n)(allocateParty()))
|
||||
|
||||
def passTime(t: Duration): Future[Unit] = bindings.passTime(t)
|
||||
def activeContracts(parties: Party*): Future[Vector[CreatedEvent]] =
|
||||
for {
|
||||
contracts <- FiniteStreamObserver[GetActiveContractsResponse](
|
||||
services.activeContracts.getActiveContracts(
|
||||
new GetActiveContractsRequest(
|
||||
ledgerId = ledgerId,
|
||||
filter = transactionFilter(Tag.unsubst(parties), Seq.empty),
|
||||
verbose = true
|
||||
),
|
||||
_
|
||||
)
|
||||
)
|
||||
} yield contracts.flatMap(_.activeContracts)
|
||||
|
||||
def activeContracts(
|
||||
private def getTransactionsRequest(parties: Seq[Party]): GetTransactionsRequest =
|
||||
new GetTransactionsRequest(
|
||||
ledgerId = ledgerId,
|
||||
begin = Some(referenceOffset),
|
||||
end = Some(end),
|
||||
filter = transactionFilter(Tag.unsubst(parties), Seq.empty),
|
||||
verbose = true
|
||||
)
|
||||
|
||||
private def transactions[Res](
|
||||
parties: Seq[Party],
|
||||
templateIds: Seq[Identifier]): Future[Vector[CreatedEvent]] =
|
||||
bindings.activeContracts(parties, templateIds)
|
||||
service: (GetTransactionsRequest, StreamObserver[Res]) => Unit): Future[Vector[Res]] =
|
||||
FiniteStreamObserver[Res](service(getTransactionsRequest(parties), _))
|
||||
|
||||
def create[T <: Template[T]: ValueDecoder](
|
||||
party: Party,
|
||||
template: Template[T]): Future[Contract[T]] =
|
||||
bindings.create(party, applicationId, nextCommandId(), template)
|
||||
def flatTransactions(parties: Party*): Future[Vector[Transaction]] =
|
||||
transactions(parties, services.transaction.getTransactions).map(_.flatMap(_.transactions))
|
||||
|
||||
def createAndGetTransactionId[T <: Template[T]: ValueDecoder](
|
||||
def transactionTrees(parties: Party*): Future[Vector[TransactionTree]] =
|
||||
transactions(parties, services.transaction.getTransactionTrees).map(_.flatMap(_.transactions))
|
||||
|
||||
def transactionTreeById(transactionId: String, parties: Party*): Future[TransactionTree] =
|
||||
services.transaction
|
||||
.getTransactionById(
|
||||
new GetTransactionByIdRequest(ledgerId, transactionId, Tag.unsubst(parties)))
|
||||
.map(_.getTransaction)
|
||||
|
||||
def create[T](
|
||||
party: Party,
|
||||
template: Template[T]): Future[(String, Contract[T])] =
|
||||
bindings.createAndGetTransactionId(party, applicationId, nextCommandId(), template)
|
||||
template: Template[T]
|
||||
): Future[Primitive.ContractId[T]] =
|
||||
submitAndWaitRequest(party, template.create.command)
|
||||
.flatMap(submitAndWaitForTransaction)
|
||||
.map(_.events.collect {
|
||||
case Event(Created(e)) => Primitive.ContractId(e.contractId)
|
||||
}.head)
|
||||
|
||||
def createAndGetTransactionId[T](
|
||||
party: Party,
|
||||
template: Template[T]
|
||||
): Future[(String, Primitive.ContractId[T])] =
|
||||
submitAndWaitRequest(party, template.create.command)
|
||||
.flatMap(submitAndWaitForTransaction)
|
||||
.map(tx =>
|
||||
tx.transactionId -> tx.events.collect {
|
||||
case Event(Created(e)) => Primitive.ContractId(e.contractId)
|
||||
}.head)
|
||||
|
||||
def exercise[T](
|
||||
party: Party,
|
||||
exercise: Party => Primitive.Update[T]
|
||||
): Future[TransactionTree] = bindings.exercise(party, applicationId, nextCommandId(), exercise)
|
||||
): Future[TransactionTree] =
|
||||
submitAndWaitRequest(party, exercise(party).command).flatMap(submitAndWaitForTransactionTree)
|
||||
|
||||
def flatTransactions(
|
||||
parties: Seq[Party],
|
||||
templateIds: Seq[Identifier]): Future[Vector[Transaction]] =
|
||||
bindings.flatTransactions(offsetAtStart, parties, templateIds)
|
||||
|
||||
def transactionTrees(
|
||||
parties: Seq[Party],
|
||||
templateIds: Seq[Identifier]): Future[Vector[TransactionTree]] =
|
||||
bindings.transactionTrees(offsetAtStart, parties, templateIds)
|
||||
|
||||
def transactionTreeById(transactionId: String, parties: Seq[Party]): Future[TransactionTree] =
|
||||
bindings.getTransactionById(transactionId, parties)
|
||||
|
||||
def prepareSubmission(party: Party, command: Command): Future[SubmitAndWaitRequest] =
|
||||
bindings.prepareSubmission(party, applicationId, nextCommandId(), Seq(command))
|
||||
def submitAndWaitRequest(party: Party, commands: Command*): Future[SubmitAndWaitRequest] =
|
||||
time().map(
|
||||
let =>
|
||||
new SubmitAndWaitRequest(
|
||||
Some(new Commands(
|
||||
ledgerId = ledgerId,
|
||||
applicationId = applicationId,
|
||||
commandId = nextCommandId(),
|
||||
party = party.unwrap,
|
||||
ledgerEffectiveTime = timestamp(let),
|
||||
maximumRecordTime = timestampWithTtl(let),
|
||||
commands = commands
|
||||
))))
|
||||
|
||||
def submitAndWait(request: SubmitAndWaitRequest): Future[Unit] =
|
||||
bindings.submitAndWait(request)
|
||||
services.command.submitAndWait(request).map(_ => ())
|
||||
|
||||
def submitAndWaitForTransactionId(request: SubmitAndWaitRequest): Future[String] =
|
||||
bindings.submitAndWaitForTransactionId(request)
|
||||
services.command.submitAndWaitForTransactionId(request).map(_.transactionId)
|
||||
|
||||
def submitAndWaitForTransaction(request: SubmitAndWaitRequest): Future[Transaction] =
|
||||
bindings.submitAndWaitForTransaction(request)
|
||||
services.command.submitAndWaitForTransaction(request).map(_.getTransaction)
|
||||
|
||||
def submitAndWaitForTransactionTree(request: SubmitAndWaitRequest): Future[TransactionTree] =
|
||||
bindings.submitAndWaitForTransactionTree(request)
|
||||
|
||||
def semanticTesterLedger(parties: Set[Ref.Party], packages: Map[Ref.PackageId, Ast.Package]) =
|
||||
new SemanticTesterLedger(bindings)(parties, packages)(this)
|
||||
services.command.submitAndWaitForTransactionTree(request).map(_.getTransaction)
|
||||
|
||||
}
|
||||
|
@ -3,20 +3,9 @@
|
||||
|
||||
package com.daml.ledger.api.testtool.infrastructure
|
||||
|
||||
import java.time.Instant
|
||||
|
||||
import com.daml.ledger.api.testtool.infrastructure.LedgerTestSuite.SkipTestException
|
||||
import com.digitalasset.ledger.api.v1.command_service.SubmitAndWaitRequest
|
||||
import com.digitalasset.ledger.api.v1.commands.Command
|
||||
import com.digitalasset.ledger.api.v1.event.CreatedEvent
|
||||
import com.digitalasset.ledger.api.v1.transaction.{Transaction, TransactionTree}
|
||||
import com.digitalasset.ledger.api.v1.value.Identifier
|
||||
import com.digitalasset.ledger.client.binding.Primitive.Party
|
||||
import com.digitalasset.ledger.client.binding.{Contract, Primitive, Template, ValueDecoder}
|
||||
import io.grpc.{Status, StatusException, StatusRuntimeException}
|
||||
import scalapb.lenses.{Lens, Mutation}
|
||||
|
||||
import scala.concurrent.duration.Duration
|
||||
import scala.concurrent.{ExecutionContext, Future}
|
||||
|
||||
private[testtool] object LedgerTestSuite {
|
||||
@ -31,114 +20,13 @@ private[testtool] abstract class LedgerTestSuite(val session: LedgerSession) {
|
||||
|
||||
val tests: Vector[LedgerTest] = Vector.empty
|
||||
|
||||
protected final implicit val ec: ExecutionContext = session.executionContext
|
||||
|
||||
final def skip(reason: String): Future[Unit] = Future.failed(SkipTestException(reason))
|
||||
|
||||
final def skipIf(reason: String)(p: => Boolean): Future[Unit] =
|
||||
if (p) skip(reason) else Future.successful(())
|
||||
|
||||
final def time()(implicit context: LedgerTestContext): Future[Instant] =
|
||||
context.time
|
||||
|
||||
final def passTime(t: Duration)(implicit context: LedgerTestContext): Future[Unit] =
|
||||
context.passTime(t)
|
||||
|
||||
final def activeContracts(party: Party, parties: Party*)(
|
||||
implicit context: LedgerTestContext): Future[Vector[CreatedEvent]] =
|
||||
context.activeContracts(party +: parties, Seq.empty)
|
||||
|
||||
final def activeContractsByTemplateId(party: Party, parties: Party*)(templateIds: Identifier*)(
|
||||
implicit context: LedgerTestContext): Future[Vector[CreatedEvent]] =
|
||||
context.activeContracts(party +: parties, templateIds)
|
||||
|
||||
final def ledgerId()(implicit context: LedgerTestContext): Future[String] =
|
||||
context.ledgerId
|
||||
|
||||
final def allocateParty()(implicit context: LedgerTestContext): Future[Party] =
|
||||
context.allocateParty()
|
||||
|
||||
final def allocateParties(n: Int)(implicit context: LedgerTestContext): Future[Vector[Party]] =
|
||||
Future.sequence(Vector.fill(n)(allocateParty()))
|
||||
|
||||
final def create[T <: Template[T]: ValueDecoder](template: Template[T])(party: Party)(
|
||||
implicit context: LedgerTestContext): Future[Contract[T]] =
|
||||
context.create(party, template)
|
||||
|
||||
final def createAndGetTransactionId[T <: Template[T]: ValueDecoder](template: Template[T])(
|
||||
party: Party)(implicit context: LedgerTestContext): Future[(String, Contract[T])] =
|
||||
context.createAndGetTransactionId(party, template)
|
||||
|
||||
final def exercise[T](exercise: Party => Primitive.Update[T])(party: Party)(
|
||||
implicit context: LedgerTestContext): Future[TransactionTree] =
|
||||
context.exercise(party, exercise)
|
||||
|
||||
final def flatTransactions(party: Party, parties: Party*)(
|
||||
implicit context: LedgerTestContext): Future[Vector[Transaction]] =
|
||||
context.flatTransactions(party +: parties, Seq.empty)
|
||||
|
||||
final def flatTransactionsByTemplateId(party: Party, parties: Party*)(templateIds: Identifier*)(
|
||||
implicit context: LedgerTestContext): Future[Vector[Transaction]] =
|
||||
context.flatTransactions(party +: parties, templateIds)
|
||||
|
||||
final def transactionTrees(party: Party, parties: Party*)(
|
||||
implicit context: LedgerTestContext): Future[Vector[TransactionTree]] =
|
||||
context.transactionTrees(party +: parties, Seq.empty)
|
||||
|
||||
final def transactionTreeById(transactionId: String, party: Party, parties: Party*)(
|
||||
implicit context: LedgerTestContext): Future[TransactionTree] =
|
||||
context.transactionTreeById(transactionId, party +: parties)
|
||||
|
||||
final def transactionTreesByTemplateId(party: Party, parties: Party*)(templateIds: Identifier*)(
|
||||
implicit context: LedgerTestContext): Future[Vector[TransactionTree]] =
|
||||
context.transactionTrees(party +: parties, templateIds)
|
||||
|
||||
def submitAndWait(
|
||||
party: Party,
|
||||
command: Command,
|
||||
pollutants: (
|
||||
Lens[SubmitAndWaitRequest, SubmitAndWaitRequest] => Mutation[SubmitAndWaitRequest])*)(
|
||||
implicit context: LedgerTestContext): Future[Unit] =
|
||||
for {
|
||||
request <- context.prepareSubmission(party, command)
|
||||
polluted = request.update(pollutants: _*)
|
||||
response <- context.submitAndWait(polluted)
|
||||
} yield response
|
||||
|
||||
def submitAndWaitForTransactionId(
|
||||
party: Party,
|
||||
command: Command,
|
||||
pollutants: (
|
||||
Lens[SubmitAndWaitRequest, SubmitAndWaitRequest] => Mutation[SubmitAndWaitRequest])*)(
|
||||
implicit context: LedgerTestContext): Future[String] =
|
||||
for {
|
||||
request <- context.prepareSubmission(party, command)
|
||||
polluted = request.update(pollutants: _*)
|
||||
response <- context.submitAndWaitForTransactionId(polluted)
|
||||
} yield response
|
||||
|
||||
def submitAndWaitForTransaction(
|
||||
party: Party,
|
||||
command: Command,
|
||||
pollutants: (
|
||||
Lens[SubmitAndWaitRequest, SubmitAndWaitRequest] => Mutation[SubmitAndWaitRequest])*)(
|
||||
implicit context: LedgerTestContext): Future[Transaction] =
|
||||
for {
|
||||
request <- context.prepareSubmission(party, command)
|
||||
polluted = request.update(pollutants: _*)
|
||||
response <- context.submitAndWaitForTransaction(polluted)
|
||||
} yield response
|
||||
|
||||
def submitAndWaitForTransactionTree(
|
||||
party: Party,
|
||||
command: Command,
|
||||
pollutants: (
|
||||
Lens[SubmitAndWaitRequest, SubmitAndWaitRequest] => Mutation[SubmitAndWaitRequest])*)(
|
||||
implicit context: LedgerTestContext): Future[TransactionTree] =
|
||||
for {
|
||||
request <- context.prepareSubmission(party, command)
|
||||
polluted = request.update(pollutants: _*)
|
||||
response <- context.submitAndWaitForTransactionTree(polluted)
|
||||
} yield response
|
||||
|
||||
final def assertGrpcError[A](t: Throwable, expectedCode: Status.Code, pattern: String)(
|
||||
implicit ec: ExecutionContext): Unit = {
|
||||
|
||||
@ -154,4 +42,5 @@ private[testtool] abstract class LedgerTestSuite(val session: LedgerSession) {
|
||||
message.contains(pattern),
|
||||
s"Error message did not contain [$pattern], but was [$message].")
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -3,8 +3,6 @@
|
||||
|
||||
package com.daml.ledger.api.testtool.infrastructure
|
||||
|
||||
import java.util.UUID
|
||||
|
||||
import com.digitalasset.daml.lf.command.Commands
|
||||
import com.digitalasset.daml.lf.data.Ref.Party
|
||||
import com.digitalasset.daml.lf.data.{Ref, Time}
|
||||
@ -17,37 +15,31 @@ import com.digitalasset.ledger.client.binding.Primitive
|
||||
import com.digitalasset.platform.common.PlatformTypes.Events
|
||||
import com.digitalasset.platform.participant.util.LfEngineToApi
|
||||
|
||||
import scala.concurrent.Future
|
||||
import scala.concurrent.{ExecutionContext, Future}
|
||||
import scala.concurrent.duration.DurationLong
|
||||
|
||||
private[infrastructure] final class SemanticTesterLedger(bindings: LedgerBindings)(
|
||||
private[testtool] final class SemanticTesterLedger(
|
||||
ledger: LedgerTestContext,
|
||||
parties: Set[Ref.Party],
|
||||
packages: Map[Ref.PackageId, Ast.Package])(implicit context: LedgerTestContext)
|
||||
packages: Map[Ref.PackageId, Ast.Package])(implicit ec: ExecutionContext)
|
||||
extends SemanticTester.GenericLedger {
|
||||
|
||||
private def lfCommandToApiCommand(party: String, commands: Commands) =
|
||||
for (ledgerId <- context.ledgerId)
|
||||
yield
|
||||
LfEngineToApi.lfCommandToApiCommand(
|
||||
party,
|
||||
ledgerId,
|
||||
commands.commandsReference,
|
||||
context.applicationId,
|
||||
Some(LfEngineToApi.toTimestamp(commands.ledgerEffectiveTime.toInstant)),
|
||||
Some(LfEngineToApi.toTimestamp(commands.ledgerEffectiveTime.toInstant.plusSeconds(30L))),
|
||||
commands
|
||||
)
|
||||
LfEngineToApi.lfCommandToApiCommand(
|
||||
party,
|
||||
ledger.ledgerId,
|
||||
commands.commandsReference,
|
||||
ledger.applicationId,
|
||||
Some(LfEngineToApi.toTimestamp(commands.ledgerEffectiveTime.toInstant)),
|
||||
Some(LfEngineToApi.toTimestamp(commands.ledgerEffectiveTime.toInstant.plusSeconds(30L))),
|
||||
commands
|
||||
)
|
||||
|
||||
private val apiScenarioTransform = for (ledgerId <- context.ledgerId)
|
||||
yield new ApiScenarioTransform(ledgerId, packages)
|
||||
private val apiScenarioTransform = new ApiScenarioTransform(ledger.ledgerId, packages)
|
||||
|
||||
private def apiTransactionToLfEvents(
|
||||
tree: TransactionTree): Future[Events[String, Value.AbsoluteContractId]] =
|
||||
for {
|
||||
transform <- apiScenarioTransform
|
||||
result = transform.eventsFromApiTransaction(tree)
|
||||
future <- result.fold(Future.failed, Future.successful)
|
||||
} yield future
|
||||
apiScenarioTransform.eventsFromApiTransaction(tree).fold(Future.failed, Future.successful)
|
||||
|
||||
override type EventNodeId = String
|
||||
|
||||
@ -59,22 +51,21 @@ private[infrastructure] final class SemanticTesterLedger(bindings: LedgerBinding
|
||||
Value.AbsoluteContractId,
|
||||
Value.VersionedValue[Value.AbsoluteContractId]]] =
|
||||
for {
|
||||
apiCommands <- lfCommandToApiCommand(party, lfCommands)
|
||||
request <- bindings.prepareSubmission(
|
||||
request <- ledger.submitAndWaitRequest(
|
||||
Primitive.Party(party),
|
||||
context.applicationId,
|
||||
s"${context.applicationId}-${UUID.randomUUID}",
|
||||
apiCommands.commands)
|
||||
id <- bindings.submitAndWaitForTransactionId(request)
|
||||
tree <- bindings.getTransactionById(id, parties.toSeq.map(Primitive.Party(_: String)))
|
||||
lfCommandToApiCommand(party, lfCommands).commands: _*)
|
||||
id <- ledger.submitAndWaitForTransactionId(request)
|
||||
tree <- ledger.transactionTreeById(id, parties.toSeq.map(Primitive.Party(_: String)): _*)
|
||||
events <- apiTransactionToLfEvents(tree)
|
||||
} yield events
|
||||
|
||||
override def passTime(dtMicros: Long): Future[Unit] =
|
||||
bindings.passTime(dtMicros.micros)
|
||||
ledger.passTime(dtMicros.micros)
|
||||
|
||||
override def currentTime: Future[Time.Timestamp] =
|
||||
bindings.time.map(
|
||||
Time.Timestamp.fromInstant(_).fold(reason => throw new RuntimeException(reason), identity))
|
||||
ledger
|
||||
.time()
|
||||
.map(
|
||||
Time.Timestamp.fromInstant(_).fold(reason => throw new RuntimeException(reason), identity))
|
||||
|
||||
}
|
||||
|
@ -11,11 +11,12 @@ import scalaz.syntax.tag._
|
||||
final class CommandService(session: LedgerSession) extends LedgerTestSuite(session) {
|
||||
private val submitAndWaitTest =
|
||||
LedgerTest("CSsubmitAndWait", "SubmitAndWait creates a contract of the expected template") {
|
||||
implicit context =>
|
||||
ledger =>
|
||||
for {
|
||||
alice <- allocateParty()
|
||||
_ <- submitAndWait(alice, Dummy(alice).create.command)
|
||||
active <- activeContracts(alice)
|
||||
alice <- ledger.allocateParty()
|
||||
request <- ledger.submitAndWaitRequest(alice, Dummy(alice).create.command)
|
||||
_ <- ledger.submitAndWait(request)
|
||||
active <- ledger.activeContracts(alice)
|
||||
} yield {
|
||||
assert(active.size == 1)
|
||||
val dummyTemplateId = active.flatMap(_.templateId.toList).head
|
||||
@ -25,12 +26,13 @@ final class CommandService(session: LedgerSession) extends LedgerTestSuite(sessi
|
||||
|
||||
private val submitAndWaitForTransactionIdTest = LedgerTest(
|
||||
"CSsubmitAndWaitForTransactionId",
|
||||
"SubmitAndWaitForTransactionId returns a valid transaction identifier") { implicit context =>
|
||||
"SubmitAndWaitForTransactionId returns a valid transaction identifier") { ledger =>
|
||||
for {
|
||||
alice <- allocateParty()
|
||||
transactionId <- submitAndWaitForTransactionId(alice, Dummy(alice).create.command)
|
||||
retrievedTransaction <- transactionTreeById(transactionId, alice)
|
||||
transactions <- flatTransactions(alice)
|
||||
alice <- ledger.allocateParty()
|
||||
request <- ledger.submitAndWaitRequest(alice, Dummy(alice).create.command)
|
||||
transactionId <- ledger.submitAndWaitForTransactionId(request)
|
||||
retrievedTransaction <- ledger.transactionTreeById(transactionId, alice)
|
||||
transactions <- ledger.flatTransactions(alice)
|
||||
} yield {
|
||||
|
||||
assert(transactionId.nonEmpty, "The transaction identifier was empty but shouldn't.")
|
||||
@ -57,7 +59,7 @@ final class CommandService(session: LedgerSession) extends LedgerTestSuite(sessi
|
||||
|
||||
assert(
|
||||
retrievedEvent.kind.isCreated,
|
||||
s"The only event seen should be a created but instead it's ${retrievedEvent}")
|
||||
s"The only event seen should be a created but instead it's $retrievedEvent")
|
||||
assert(
|
||||
retrievedEvent.getCreated == created,
|
||||
s"The retrieved created event does not match the one in the flat transactions: event=$created retrieved=$retrievedEvent"
|
||||
@ -68,10 +70,11 @@ final class CommandService(session: LedgerSession) extends LedgerTestSuite(sessi
|
||||
|
||||
private val submitAndWaitForTransactionTest = LedgerTest(
|
||||
"CSsubmitAndWaitForTransaction",
|
||||
"SubmitAndWaitForTransaction returns a transaction") { implicit context =>
|
||||
"SubmitAndWaitForTransaction returns a transaction") { ledger =>
|
||||
for {
|
||||
alice <- allocateParty()
|
||||
transaction <- submitAndWaitForTransaction(alice, Dummy(alice).create.command)
|
||||
alice <- ledger.allocateParty()
|
||||
request <- ledger.submitAndWaitRequest(alice, Dummy(alice).create.command)
|
||||
transaction <- ledger.submitAndWaitForTransaction(request)
|
||||
} yield {
|
||||
assert(
|
||||
transaction.transactionId.nonEmpty,
|
||||
@ -93,10 +96,11 @@ final class CommandService(session: LedgerSession) extends LedgerTestSuite(sessi
|
||||
|
||||
private val submitAndWaitForTransactionTreeTest = LedgerTest(
|
||||
"CSsubmitAndWaitForTransactionTree",
|
||||
"SubmitAndWaitForTransactionTree returns a transaction tree") { implicit context =>
|
||||
"SubmitAndWaitForTransactionTree returns a transaction tree") { ledger =>
|
||||
for {
|
||||
alice <- allocateParty()
|
||||
transactionTree <- submitAndWaitForTransactionTree(alice, Dummy(alice).create.command)
|
||||
alice <- ledger.allocateParty()
|
||||
request <- ledger.submitAndWaitRequest(alice, Dummy(alice).create.command)
|
||||
transactionTree <- ledger.submitAndWaitForTransactionTree(request)
|
||||
} yield {
|
||||
assert(
|
||||
transactionTree.transactionId.nonEmpty,
|
||||
@ -117,43 +121,30 @@ final class CommandService(session: LedgerSession) extends LedgerTestSuite(sessi
|
||||
|
||||
private val resendingSubmitAndWait = LedgerTest(
|
||||
"CSduplicateSubmitAndWait",
|
||||
"SubmitAndWait should be idempotent when reusing the same command identifier") {
|
||||
implicit context =>
|
||||
val duplicateCommandId = "CSduplicateSubmitAndWait"
|
||||
for {
|
||||
alice <- allocateParty()
|
||||
_ <- submitAndWait(
|
||||
alice,
|
||||
Dummy(alice).create.command,
|
||||
_.commands.commandId := duplicateCommandId)
|
||||
_ <- submitAndWait(
|
||||
alice,
|
||||
Dummy(alice).create.command,
|
||||
_.commands.commandId := duplicateCommandId)
|
||||
transactions <- flatTransactions(alice)
|
||||
} yield {
|
||||
assert(
|
||||
transactions.size == 1,
|
||||
s"Expected only 1 transaction, but received ${transactions.size}")
|
||||
"SubmitAndWait should be idempotent when reusing the same command identifier") { ledger =>
|
||||
for {
|
||||
alice <- ledger.allocateParty()
|
||||
request <- ledger.submitAndWaitRequest(alice, Dummy(alice).create.command)
|
||||
_ <- ledger.submitAndWait(request)
|
||||
_ <- ledger.submitAndWait(request)
|
||||
transactions <- ledger.flatTransactions(alice)
|
||||
} yield {
|
||||
assert(
|
||||
transactions.size == 1,
|
||||
s"Expected only 1 transaction, but received ${transactions.size}")
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val resendingSubmitAndWaitForTransactionId = LedgerTest(
|
||||
"CSduplicateSubmitAndWaitForTransactionId",
|
||||
"SubmitAndWaitForTransactionId should be idempotent when reusing the same command identifier") {
|
||||
implicit context =>
|
||||
val duplicateCommandId = "CSduplicateSubmitAndWaitForTransactionId"
|
||||
ledger =>
|
||||
for {
|
||||
alice <- allocateParty()
|
||||
transactionId1 <- submitAndWaitForTransactionId(
|
||||
alice,
|
||||
Dummy(alice).create.command,
|
||||
_.commands.commandId := duplicateCommandId)
|
||||
transactionId2 <- submitAndWaitForTransactionId(
|
||||
alice,
|
||||
Dummy(alice).create.command,
|
||||
_.commands.commandId := duplicateCommandId)
|
||||
alice <- ledger.allocateParty()
|
||||
request <- ledger.submitAndWaitRequest(alice, Dummy(alice).create.command)
|
||||
transactionId1 <- ledger.submitAndWaitForTransactionId(request)
|
||||
transactionId2 <- ledger.submitAndWaitForTransactionId(request)
|
||||
} yield {
|
||||
assert(
|
||||
transactionId1 == transactionId2,
|
||||
@ -164,18 +155,12 @@ final class CommandService(session: LedgerSession) extends LedgerTestSuite(sessi
|
||||
private val resendingSubmitAndWaitForTransaction = LedgerTest(
|
||||
"CSduplicateSubmitAndWaitForTransaction",
|
||||
"SubmitAndWaitForTransaction should be idempotent when reusing the same command identifier") {
|
||||
implicit context =>
|
||||
val duplicateCommandId = "CSduplicateSubmitAndWaitForTransaction"
|
||||
ledger =>
|
||||
for {
|
||||
alice <- allocateParty()
|
||||
transaction1 <- submitAndWaitForTransaction(
|
||||
alice,
|
||||
Dummy(alice).create.command,
|
||||
_.commands.commandId := duplicateCommandId)
|
||||
transaction2 <- submitAndWaitForTransaction(
|
||||
alice,
|
||||
Dummy(alice).create.command,
|
||||
_.commands.commandId := duplicateCommandId)
|
||||
alice <- ledger.allocateParty()
|
||||
request <- ledger.submitAndWaitRequest(alice, Dummy(alice).create.command)
|
||||
transaction1 <- ledger.submitAndWaitForTransaction(request)
|
||||
transaction2 <- ledger.submitAndWaitForTransaction(request)
|
||||
} yield {
|
||||
assert(
|
||||
transaction1 == transaction2,
|
||||
@ -186,18 +171,12 @@ final class CommandService(session: LedgerSession) extends LedgerTestSuite(sessi
|
||||
private val resendingSubmitAndWaitForTransactionTree = LedgerTest(
|
||||
"CSduplicateSubmitAndWaitForTransactionTree",
|
||||
"SubmitAndWaitForTransactionTree should be idempotent when reusing the same command identifier") {
|
||||
implicit context =>
|
||||
val duplicateCommandId = "CSduplicateSubmitAndWaitForTransactionTree"
|
||||
ledger =>
|
||||
for {
|
||||
alice <- allocateParty()
|
||||
transactionTree1 <- submitAndWaitForTransactionTree(
|
||||
alice,
|
||||
Dummy(alice).create.command,
|
||||
_.commands.commandId := duplicateCommandId)
|
||||
transactionTree2 <- submitAndWaitForTransactionTree(
|
||||
alice,
|
||||
Dummy(alice).create.command,
|
||||
_.commands.commandId := duplicateCommandId)
|
||||
alice <- ledger.allocateParty()
|
||||
request <- ledger.submitAndWaitRequest(alice, Dummy(alice).create.command)
|
||||
transactionTree1 <- ledger.submitAndWaitForTransactionTree(request)
|
||||
transactionTree2 <- ledger.submitAndWaitForTransactionTree(request)
|
||||
} yield {
|
||||
assert(
|
||||
transactionTree1 == transactionTree2,
|
||||
@ -207,56 +186,52 @@ final class CommandService(session: LedgerSession) extends LedgerTestSuite(sessi
|
||||
|
||||
private val submitAndWaitWithInvalidLedgerIdTest = LedgerTest(
|
||||
"CSsubmitAndWaitInvalidLedgerId",
|
||||
"SubmitAndWait should fail for invalid ledger ids") { implicit context =>
|
||||
"SubmitAndWait should fail for invalid ledger ids") { ledger =>
|
||||
val invalidLedgerId = "CSsubmitAndWaitInvalidLedgerId"
|
||||
for {
|
||||
alice <- allocateParty()
|
||||
failure <- submitAndWait(
|
||||
alice,
|
||||
Dummy(alice).create.command,
|
||||
_.commands.ledgerId := invalidLedgerId).failed
|
||||
alice <- ledger.allocateParty()
|
||||
request <- ledger.submitAndWaitRequest(alice, Dummy(alice).create.command)
|
||||
badLedgerId = request.update(_.commands.ledgerId := invalidLedgerId)
|
||||
failure <- ledger.submitAndWait(badLedgerId).failed
|
||||
} yield
|
||||
assertGrpcError(failure, Status.Code.NOT_FOUND, s"Ledger ID '$invalidLedgerId' not found.")
|
||||
}
|
||||
|
||||
private val submitAndWaitForTransactionIdWithInvalidLedgerIdTest = LedgerTest(
|
||||
"CSsubmitAndWaitForTransactionIdInvalidLedgerId",
|
||||
"SubmitAndWaitForTransactionId should fail for invalid ledger ids") { implicit context =>
|
||||
"SubmitAndWaitForTransactionId should fail for invalid ledger ids") { ledger =>
|
||||
val invalidLedgerId = "CSsubmitAndWaitForTransactionIdInvalidLedgerId"
|
||||
for {
|
||||
alice <- allocateParty()
|
||||
failure <- submitAndWaitForTransactionId(
|
||||
alice,
|
||||
Dummy(alice).create.command,
|
||||
_.commands.ledgerId := invalidLedgerId).failed
|
||||
alice <- ledger.allocateParty()
|
||||
request <- ledger.submitAndWaitRequest(alice, Dummy(alice).create.command)
|
||||
badLedgerId = request.update(_.commands.ledgerId := invalidLedgerId)
|
||||
failure <- ledger.submitAndWaitForTransactionId(badLedgerId).failed
|
||||
} yield
|
||||
assertGrpcError(failure, Status.Code.NOT_FOUND, s"Ledger ID '$invalidLedgerId' not found.")
|
||||
}
|
||||
|
||||
private val submitAndWaitForTransactionWithInvalidLedgerIdTest = LedgerTest(
|
||||
"CSsubmitAndWaitForTransactionInvalidLedgerId",
|
||||
"SubmitAndWaitForTransaction should fail for invalid ledger ids") { implicit context =>
|
||||
"SubmitAndWaitForTransaction should fail for invalid ledger ids") { ledger =>
|
||||
val invalidLedgerId = "CSsubmitAndWaitForTransactionInvalidLedgerId"
|
||||
for {
|
||||
alice <- allocateParty()
|
||||
failure <- submitAndWaitForTransaction(
|
||||
alice,
|
||||
Dummy(alice).create.command,
|
||||
_.commands.ledgerId := invalidLedgerId).failed
|
||||
alice <- ledger.allocateParty()
|
||||
request <- ledger.submitAndWaitRequest(alice, Dummy(alice).create.command)
|
||||
badLedgerId = request.update(_.commands.ledgerId := invalidLedgerId)
|
||||
failure <- ledger.submitAndWaitForTransaction(badLedgerId).failed
|
||||
} yield
|
||||
assertGrpcError(failure, Status.Code.NOT_FOUND, s"Ledger ID '$invalidLedgerId' not found.")
|
||||
}
|
||||
|
||||
private val submitAndWaitForTransactionTreeWithInvalidLedgerIdTest = LedgerTest(
|
||||
"CSsubmitAndWaitForTransactionTreeInvalidLedgerId",
|
||||
"SubmitAndWaitForTransactionTree should fail for invalid ledger ids") { implicit context =>
|
||||
"SubmitAndWaitForTransactionTree should fail for invalid ledger ids") { ledger =>
|
||||
val invalidLedgerId = "CSsubmitAndWaitForTransactionTreeInvalidLedgerId"
|
||||
for {
|
||||
alice <- allocateParty()
|
||||
failure <- submitAndWaitForTransactionTree(
|
||||
alice,
|
||||
Dummy(alice).create.command,
|
||||
_.commands.ledgerId := invalidLedgerId).failed
|
||||
alice <- ledger.allocateParty()
|
||||
request <- ledger.submitAndWaitRequest(alice, Dummy(alice).create.command)
|
||||
badLedgerId = request.update(_.commands.ledgerId := invalidLedgerId)
|
||||
failure <- ledger.submitAndWaitForTransactionTree(badLedgerId).failed
|
||||
} yield
|
||||
assertGrpcError(failure, Status.Code.NOT_FOUND, s"Ledger ID '$invalidLedgerId' not found.")
|
||||
}
|
||||
|
@ -18,32 +18,31 @@ final class ContractKeys(session: LedgerSession) extends LedgerTestSuite(session
|
||||
|
||||
val fetchDivulgedContract =
|
||||
LedgerTest("CKFetchOrLookup", "Divulged contracts can be fetched or looked up by key") {
|
||||
implicit context =>
|
||||
ledger =>
|
||||
val key = s"${UUID.randomUUID.toString}-key"
|
||||
for {
|
||||
Vector(owner, delegate) <- allocateParties(2)
|
||||
Vector(owner, delegate) <- ledger.allocateParties(2)
|
||||
|
||||
// create contracts to work with
|
||||
delegated <- create(Delegated(owner, key))(owner)
|
||||
delegation <- create(Delegation(owner, delegate))(owner)
|
||||
showDelegated <- create(ShowDelegated(owner, delegate))(owner)
|
||||
delegated <- ledger.create(owner, Delegated(owner, key))
|
||||
delegation <- ledger.create(owner, Delegation(owner, delegate))
|
||||
showDelegated <- ledger.create(owner, ShowDelegated(owner, delegate))
|
||||
|
||||
// divulge the contract
|
||||
_ <- exercise(showDelegated.contractId.exerciseShowIt(_, delegated.contractId))(owner)
|
||||
_ <- ledger.exercise(owner, showDelegated.exerciseShowIt(_, delegated))
|
||||
|
||||
// fetch delegated
|
||||
_ <- exercise(delegation.contractId.exerciseFetchDelegated(_, delegated.contractId))(
|
||||
delegate)
|
||||
_ <- ledger.exercise(delegate, delegation.exerciseFetchDelegated(_, delegated))
|
||||
|
||||
// fetch by key delegation is not allowed
|
||||
_ <- exercise(
|
||||
delegation.contractId
|
||||
.exerciseFetchByKeyDelegated(_, owner, key, Some(delegated.contractId)))(delegate)
|
||||
_ <- ledger.exercise(
|
||||
delegate,
|
||||
delegation.exerciseFetchByKeyDelegated(_, owner, key, Some(delegated)))
|
||||
|
||||
// lookup by key delegation is not allowed
|
||||
_ <- exercise(
|
||||
delegation.contractId
|
||||
.exerciseLookupByKeyDelegated(_, owner, key, Some(delegated.contractId)))(delegate)
|
||||
_ <- ledger.exercise(
|
||||
delegate,
|
||||
delegation.exerciseLookupByKeyDelegated(_, owner, key, Some(delegated)))
|
||||
} yield {
|
||||
// No assertions to make, since all exercises went through as expected
|
||||
()
|
||||
@ -53,31 +52,29 @@ final class ContractKeys(session: LedgerSession) extends LedgerTestSuite(session
|
||||
val rejectFetchingUndisclosedContract =
|
||||
LedgerTest(
|
||||
"CKNoFetchUndisclosed",
|
||||
"Contract Keys should reject fetching an undisclosed contract") { implicit context =>
|
||||
"Contract Keys should reject fetching an undisclosed contract") { ledger =>
|
||||
val key = s"${UUID.randomUUID.toString}-key"
|
||||
for {
|
||||
Vector(owner, delegate) <- allocateParties(2)
|
||||
Vector(owner, delegate) <- ledger.allocateParties(2)
|
||||
|
||||
// create contracts to work with
|
||||
delegated <- create(Delegated(owner, key))(owner)
|
||||
delegation <- create(Delegation(owner, delegate))(owner)
|
||||
delegated <- ledger.create(owner, Delegated(owner, key))
|
||||
delegation <- ledger.create(owner, Delegation(owner, delegate))
|
||||
|
||||
// fetch should fail
|
||||
fetchFailure <- exercise(
|
||||
delegation.contractId
|
||||
.exerciseFetchDelegated(_, delegated.contractId))(delegate).failed
|
||||
fetchFailure <- ledger
|
||||
.exercise(delegate, delegation.exerciseFetchDelegated(_, delegated))
|
||||
.failed
|
||||
|
||||
// this fetch still fails even if we do not check that the submitter
|
||||
// is in the lookup maintainer, since we have the visibility check
|
||||
// implement as part of #753.
|
||||
fetchByKeyFailure <- exercise(
|
||||
delegation.contractId
|
||||
.exerciseFetchByKeyDelegated(_, owner, key, None))(delegate).failed
|
||||
fetchByKeyFailure <- ledger
|
||||
.exercise(delegate, delegation.exerciseFetchByKeyDelegated(_, owner, key, None))
|
||||
.failed
|
||||
|
||||
// lookup by key should work
|
||||
_ <- exercise(
|
||||
delegation.contractId
|
||||
.exerciseLookupByKeyDelegated(_, owner, key, None))(delegate)
|
||||
_ <- ledger.exercise(delegate, delegation.exerciseLookupByKeyDelegated(_, owner, key, None))
|
||||
} yield {
|
||||
assertGrpcError(
|
||||
fetchFailure,
|
||||
@ -88,80 +85,78 @@ final class ContractKeys(session: LedgerSession) extends LedgerTestSuite(session
|
||||
}
|
||||
|
||||
val processContractKeys =
|
||||
LedgerTest("CKMaintainerScoped", "Contract keys should be scoped by maintainer") {
|
||||
implicit context =>
|
||||
val keyPrefix = UUID.randomUUID.toString
|
||||
val key1 = s"$keyPrefix-some-key"
|
||||
val key2 = s"$keyPrefix-some-other-key"
|
||||
val unknownKey = s"$keyPrefix-unknown-key"
|
||||
LedgerTest("CKMaintainerScoped", "Contract keys should be scoped by maintainer") { ledger =>
|
||||
val keyPrefix = UUID.randomUUID.toString
|
||||
val key1 = s"$keyPrefix-some-key"
|
||||
val key2 = s"$keyPrefix-some-other-key"
|
||||
val unknownKey = s"$keyPrefix-unknown-key"
|
||||
|
||||
for {
|
||||
Vector(alice, bob) <- allocateParties(2)
|
||||
for {
|
||||
Vector(alice, bob) <- ledger.allocateParties(2)
|
||||
|
||||
//create contracts to work with
|
||||
tk1 <- create(TextKey(alice, key1, List(bob)))(alice)
|
||||
tk2 <- create(TextKey(alice, key2, List(bob)))(alice)
|
||||
aliceTKO <- create(TextKeyOperations(alice))(alice)
|
||||
bobTKO <- create(TextKeyOperations(bob))(bob)
|
||||
//create contracts to work with
|
||||
tk1 <- ledger.create(alice, TextKey(alice, key1, List(bob)))
|
||||
tk2 <- ledger.create(alice, TextKey(alice, key2, List(bob)))
|
||||
aliceTKO <- ledger.create(alice, TextKeyOperations(alice))
|
||||
bobTKO <- ledger.create(bob, TextKeyOperations(bob))
|
||||
|
||||
// creating a contract with a duplicate key should fail
|
||||
duplicateKeyFailure <- create(TextKey(alice, key1, List(bob)))(alice).failed
|
||||
// creating a contract with a duplicate key should fail
|
||||
duplicateKeyFailure <- ledger.create(alice, TextKey(alice, key1, List(bob))).failed
|
||||
|
||||
// trying to lookup an unauthorized key should fail
|
||||
bobLooksUpTextKeyFailure <- exercise(
|
||||
bobTKO.contractId
|
||||
.exerciseTKOLookup(_, Tuple2(alice, key1), Some(tk1.contractId)))(bob).failed
|
||||
// trying to lookup an unauthorized key should fail
|
||||
bobLooksUpTextKeyFailure <- ledger
|
||||
.exercise(bob, bobTKO.exerciseTKOLookup(_, Tuple2(alice, key1), Some(tk1)))
|
||||
.failed
|
||||
|
||||
// trying to lookup an unauthorized non-existing key should fail
|
||||
bobLooksUpBogusTextKeyFailure <- exercise(
|
||||
bobTKO.contractId.exerciseTKOLookup(_, Tuple2(alice, unknownKey), None))(bob).failed
|
||||
// trying to lookup an unauthorized non-existing key should fail
|
||||
bobLooksUpBogusTextKeyFailure <- ledger
|
||||
.exercise(bob, bobTKO.exerciseTKOLookup(_, Tuple2(alice, unknownKey), None))
|
||||
.failed
|
||||
|
||||
// successful, authorized lookup
|
||||
_ <- exercise(
|
||||
aliceTKO.contractId
|
||||
.exerciseTKOLookup(_, Tuple2(alice, key1), Some(tk1.contractId)))(alice)
|
||||
// successful, authorized lookup
|
||||
_ <- ledger.exercise(alice, aliceTKO.exerciseTKOLookup(_, Tuple2(alice, key1), Some(tk1)))
|
||||
|
||||
// successful fetch
|
||||
_ <- exercise(
|
||||
aliceTKO.contractId.exerciseTKOFetch(_, Tuple2(alice, key1), tk1.contractId))(alice)
|
||||
// successful fetch
|
||||
_ <- ledger.exercise(alice, aliceTKO.exerciseTKOFetch(_, Tuple2(alice, key1), tk1))
|
||||
|
||||
// successful, authorized lookup of non-existing key
|
||||
_ <- exercise(aliceTKO.contractId.exerciseTKOLookup(_, Tuple2(alice, unknownKey), None))(
|
||||
alice)
|
||||
// successful, authorized lookup of non-existing key
|
||||
_ <- ledger.exercise(alice, aliceTKO.exerciseTKOLookup(_, Tuple2(alice, unknownKey), None))
|
||||
|
||||
// failing fetch
|
||||
aliceFailedFetch <- exercise(
|
||||
aliceTKO.contractId.exerciseTKOFetch(_, Tuple2(alice, unknownKey), tk1.contractId))(
|
||||
alice).failed
|
||||
// failing fetch
|
||||
aliceFailedFetch <- ledger
|
||||
.exercise(alice, aliceTKO.exerciseTKOFetch(_, Tuple2(alice, unknownKey), tk1))
|
||||
.failed
|
||||
|
||||
// now we exercise the contract, thus archiving it, and then verify
|
||||
// that we cannot look it up anymore
|
||||
_ <- exercise(tk1.contractId.exerciseTextKeyChoice)(alice)
|
||||
_ <- exercise(aliceTKO.contractId.exerciseTKOLookup(_, Tuple2(alice, key1), None))(alice)
|
||||
// now we exercise the contract, thus archiving it, and then verify
|
||||
// that we cannot look it up anymore
|
||||
_ <- ledger.exercise(alice, tk1.exerciseTextKeyChoice)
|
||||
_ <- ledger.exercise(alice, aliceTKO.exerciseTKOLookup(_, Tuple2(alice, key1), None))
|
||||
|
||||
// lookup the key, consume it, then verify we cannot look it up anymore
|
||||
_ <- exercise(
|
||||
aliceTKO.contractId
|
||||
.exerciseTKOConsumeAndLookup(_, tk2.contractId, Tuple2(alice, key2)))(alice)
|
||||
// lookup the key, consume it, then verify we cannot look it up anymore
|
||||
_ <- ledger.exercise(
|
||||
alice,
|
||||
aliceTKO.exerciseTKOConsumeAndLookup(_, tk2, Tuple2(alice, key2)))
|
||||
|
||||
// failing create when a maintainer is not a signatory
|
||||
maintainerNotSignatoryFailed <- create(MaintainerNotSignatory(alice, bob))(alice).failed
|
||||
} yield {
|
||||
assertGrpcError(duplicateKeyFailure, Status.Code.INVALID_ARGUMENT, "DuplicateKey")
|
||||
assertGrpcError(
|
||||
bobLooksUpTextKeyFailure,
|
||||
Status.Code.INVALID_ARGUMENT,
|
||||
"requires authorizers")
|
||||
assertGrpcError(
|
||||
bobLooksUpBogusTextKeyFailure,
|
||||
Status.Code.INVALID_ARGUMENT,
|
||||
"requires authorizers")
|
||||
assertGrpcError(aliceFailedFetch, Status.Code.INVALID_ARGUMENT, "couldn't find key")
|
||||
assertGrpcError(
|
||||
maintainerNotSignatoryFailed,
|
||||
Status.Code.INVALID_ARGUMENT,
|
||||
"are not a subset of the signatories")
|
||||
}
|
||||
// failing create when a maintainer is not a signatory
|
||||
maintainerNotSignatoryFailed <- ledger
|
||||
.create(alice, MaintainerNotSignatory(alice, bob))
|
||||
.failed
|
||||
} yield {
|
||||
assertGrpcError(duplicateKeyFailure, Status.Code.INVALID_ARGUMENT, "DuplicateKey")
|
||||
assertGrpcError(
|
||||
bobLooksUpTextKeyFailure,
|
||||
Status.Code.INVALID_ARGUMENT,
|
||||
"requires authorizers")
|
||||
assertGrpcError(
|
||||
bobLooksUpBogusTextKeyFailure,
|
||||
Status.Code.INVALID_ARGUMENT,
|
||||
"requires authorizers")
|
||||
assertGrpcError(aliceFailedFetch, Status.Code.INVALID_ARGUMENT, "couldn't find key")
|
||||
assertGrpcError(
|
||||
maintainerNotSignatoryFailed,
|
||||
Status.Code.INVALID_ARGUMENT,
|
||||
"are not a subset of the signatories")
|
||||
}
|
||||
}
|
||||
|
||||
override val tests: Vector[LedgerTest] = Vector(
|
||||
|
@ -19,32 +19,36 @@ final class ContractKeysSubmitterIsMaintainer(session: LedgerSession)
|
||||
|
||||
val fetchDivulgedContract =
|
||||
LedgerTest("CKNoFetchOrLookup", "Divulged contracts cannot be fetched or looked up by key") {
|
||||
implicit context =>
|
||||
ledger =>
|
||||
val key = s"${UUID.randomUUID.toString}-key"
|
||||
for {
|
||||
Vector(owner, delegate) <- allocateParties(2)
|
||||
Vector(owner, delegate) <- ledger.allocateParties(2)
|
||||
|
||||
// create contracts to work with
|
||||
delegated <- create(Delegated(owner, key))(owner)
|
||||
delegation <- create(Delegation(owner, delegate))(owner)
|
||||
showDelegated <- create(ShowDelegated(owner, delegate))(owner)
|
||||
delegated <- ledger.create(owner, Delegated(owner, key))
|
||||
delegation <- ledger.create(owner, Delegation(owner, delegate))
|
||||
showDelegated <- ledger.create(owner, ShowDelegated(owner, delegate))
|
||||
|
||||
// divulge the contract
|
||||
_ <- exercise(showDelegated.contractId.exerciseShowIt(_, delegated.contractId))(owner)
|
||||
|
||||
_ <- ledger.exercise(owner, showDelegated.exerciseShowIt(_, delegated))
|
||||
// fetch delegated
|
||||
_ <- exercise(delegation.contractId.exerciseFetchDelegated(_, delegated.contractId))(
|
||||
delegate)
|
||||
_ <- ledger.exercise(delegate, delegation.exerciseFetchDelegated(_, delegated))
|
||||
|
||||
// fetch by key delegation is not allowed
|
||||
fetchByKeyFailure <- exercise(
|
||||
delegation.contractId
|
||||
.exerciseFetchByKeyDelegated(_, owner, key, Some(delegated.contractId)))(delegate).failed
|
||||
fetchByKeyFailure <- ledger
|
||||
.exercise(
|
||||
delegate,
|
||||
delegation
|
||||
.exerciseFetchByKeyDelegated(_, owner, key, Some(delegated)))
|
||||
.failed
|
||||
|
||||
// lookup by key delegation is not allowed
|
||||
lookupByKeyFailure <- exercise(
|
||||
delegation.contractId
|
||||
.exerciseLookupByKeyDelegated(_, owner, key, Some(delegated.contractId)))(delegate).failed
|
||||
lookupByKeyFailure <- ledger
|
||||
.exercise(
|
||||
delegate,
|
||||
delegation
|
||||
.exerciseLookupByKeyDelegated(_, owner, key, Some(delegated)))
|
||||
.failed
|
||||
} yield {
|
||||
assertGrpcError(
|
||||
fetchByKeyFailure,
|
||||
@ -60,29 +64,38 @@ final class ContractKeysSubmitterIsMaintainer(session: LedgerSession)
|
||||
val rejectFetchingUndisclosedContract =
|
||||
LedgerTest(
|
||||
"CKSubmitterIsMaintainerNoFetchUndisclosed",
|
||||
"Contract Keys should reject fetching an undisclosed contract") { implicit context =>
|
||||
"Contract Keys should reject fetching an undisclosed contract") { ledger =>
|
||||
val key = s"${UUID.randomUUID.toString}-key"
|
||||
for {
|
||||
Vector(owner, delegate) <- allocateParties(2)
|
||||
Vector(owner, delegate) <- ledger.allocateParties(2)
|
||||
|
||||
// create contracts to work with
|
||||
delegated <- create(Delegated(owner, key))(owner)
|
||||
delegation <- create(Delegation(owner, delegate))(owner)
|
||||
delegated <- ledger.create(owner, Delegated(owner, key))
|
||||
delegation <- ledger.create(owner, Delegation(owner, delegate))
|
||||
|
||||
// fetch should fail
|
||||
fetchFailure <- exercise(
|
||||
delegation.contractId
|
||||
.exerciseFetchDelegated(_, delegated.contractId))(delegate).failed
|
||||
fetchFailure <- ledger
|
||||
.exercise(
|
||||
delegate,
|
||||
delegation
|
||||
.exerciseFetchDelegated(_, delegated))
|
||||
.failed
|
||||
|
||||
// fetch by key should fail
|
||||
fetchByKeyFailure <- exercise(
|
||||
delegation.contractId
|
||||
.exerciseFetchByKeyDelegated(_, owner, key, None))(delegate).failed
|
||||
fetchByKeyFailure <- ledger
|
||||
.exercise(
|
||||
delegate,
|
||||
delegation
|
||||
.exerciseFetchByKeyDelegated(_, owner, key, None))
|
||||
.failed
|
||||
|
||||
// lookup by key should fail
|
||||
lookupByKeyFailure <- exercise(
|
||||
delegation.contractId
|
||||
.exerciseLookupByKeyDelegated(_, owner, key, None))(delegate).failed
|
||||
lookupByKeyFailure <- ledger
|
||||
.exercise(
|
||||
delegate,
|
||||
delegation
|
||||
.exerciseLookupByKeyDelegated(_, owner, key, None))
|
||||
.failed
|
||||
} yield {
|
||||
assertGrpcError(
|
||||
fetchFailure,
|
||||
@ -102,64 +115,73 @@ final class ContractKeysSubmitterIsMaintainer(session: LedgerSession)
|
||||
val processContractKeys =
|
||||
LedgerTest(
|
||||
"CKSubmitterIsMaintainerMaintainerScoped",
|
||||
"Contract keys should be scoped by maintainer") { implicit context =>
|
||||
"Contract keys should be scoped by maintainer") { ledger =>
|
||||
val keyPrefix = UUID.randomUUID.toString
|
||||
val key1 = s"$keyPrefix-some-key"
|
||||
val key2 = s"$keyPrefix-some-other-key"
|
||||
val unknownKey = s"$keyPrefix-unknown-key"
|
||||
|
||||
for {
|
||||
Vector(alice, bob) <- allocateParties(2)
|
||||
Vector(alice, bob) <- ledger.allocateParties(2)
|
||||
|
||||
//create contracts to work with
|
||||
tk1 <- create(TextKey(alice, key1, List(bob)))(alice)
|
||||
tk2 <- create(TextKey(alice, key2, List(bob)))(alice)
|
||||
aliceTKO <- create(TextKeyOperations(alice))(alice)
|
||||
bobTKO <- create(TextKeyOperations(bob))(bob)
|
||||
tk1 <- ledger.create(alice, TextKey(alice, key1, List(bob)))
|
||||
tk2 <- ledger.create(alice, TextKey(alice, key2, List(bob)))
|
||||
aliceTKO <- ledger.create(alice, TextKeyOperations(alice))
|
||||
bobTKO <- ledger.create(bob, TextKeyOperations(bob))
|
||||
|
||||
// creating a contract with a duplicate key should fail
|
||||
duplicateKeyFailure <- create(TextKey(alice, key1, List(bob)))(alice).failed
|
||||
duplicateKeyFailure <- ledger.create(alice, TextKey(alice, key1, List(bob))).failed
|
||||
|
||||
// trying to lookup an unauthorized key should fail
|
||||
bobLooksUpTextKeyFailure <- exercise(
|
||||
bobTKO.contractId
|
||||
.exerciseTKOLookup(_, DamlTuple2(alice, key1), Some(tk1.contractId)))(bob).failed
|
||||
bobLooksUpTextKeyFailure <- ledger
|
||||
.exercise(
|
||||
bob,
|
||||
bobTKO
|
||||
.exerciseTKOLookup(_, DamlTuple2(alice, key1), Some(tk1)))
|
||||
.failed
|
||||
|
||||
// trying to lookup an unauthorized non-existing key should fail
|
||||
bobLooksUpBogusTextKeyFailure <- exercise(
|
||||
bobTKO.contractId.exerciseTKOLookup(_, DamlTuple2(alice, unknownKey), None))(bob).failed
|
||||
bobLooksUpBogusTextKeyFailure <- ledger
|
||||
.exercise(bob, bobTKO.exerciseTKOLookup(_, DamlTuple2(alice, unknownKey), None))
|
||||
.failed
|
||||
|
||||
// successful, authorized lookup
|
||||
_ <- exercise(
|
||||
aliceTKO.contractId
|
||||
.exerciseTKOLookup(_, DamlTuple2(alice, key1), Some(tk1.contractId)))(alice)
|
||||
_ <- ledger.exercise(
|
||||
alice,
|
||||
aliceTKO
|
||||
.exerciseTKOLookup(_, DamlTuple2(alice, key1), Some(tk1)))
|
||||
|
||||
// successful fetch
|
||||
_ <- exercise(
|
||||
aliceTKO.contractId.exerciseTKOFetch(_, DamlTuple2(alice, key1), tk1.contractId))(alice)
|
||||
_ <- ledger.exercise(alice, aliceTKO.exerciseTKOFetch(_, DamlTuple2(alice, key1), tk1))
|
||||
|
||||
// successful, authorized lookup of non-existing key
|
||||
_ <- exercise(
|
||||
aliceTKO.contractId.exerciseTKOLookup(_, DamlTuple2(alice, unknownKey), None))(alice)
|
||||
_ <- ledger.exercise(
|
||||
alice,
|
||||
aliceTKO.exerciseTKOLookup(_, DamlTuple2(alice, unknownKey), None))
|
||||
|
||||
// failing fetch
|
||||
aliceFailedFetch <- exercise(
|
||||
aliceTKO.contractId
|
||||
.exerciseTKOFetch(_, DamlTuple2(alice, unknownKey), tk1.contractId))(alice).failed
|
||||
aliceFailedFetch <- ledger
|
||||
.exercise(
|
||||
alice,
|
||||
aliceTKO
|
||||
.exerciseTKOFetch(_, DamlTuple2(alice, unknownKey), tk1))
|
||||
.failed
|
||||
|
||||
// now we exercise the contract, thus archiving it, and then verify
|
||||
// that we cannot look it up anymore
|
||||
_ <- exercise(tk1.contractId.exerciseTextKeyChoice)(alice)
|
||||
_ <- exercise(aliceTKO.contractId.exerciseTKOLookup(_, DamlTuple2(alice, key1), None))(
|
||||
alice)
|
||||
_ <- ledger.exercise(alice, tk1.exerciseTextKeyChoice)
|
||||
_ <- ledger.exercise(alice, aliceTKO.exerciseTKOLookup(_, DamlTuple2(alice, key1), None))
|
||||
|
||||
// lookup the key, consume it, then verify we cannot look it up anymore
|
||||
_ <- exercise(
|
||||
aliceTKO.contractId
|
||||
.exerciseTKOConsumeAndLookup(_, tk2.contractId, DamlTuple2(alice, key2)))(alice)
|
||||
_ <- ledger.exercise(
|
||||
alice,
|
||||
aliceTKO.exerciseTKOConsumeAndLookup(_, tk2, DamlTuple2(alice, key2)))
|
||||
|
||||
// failing create when a maintainer is not a signatory
|
||||
maintainerNotSignatoryFailed <- create(MaintainerNotSignatory(alice, bob))(alice).failed
|
||||
maintainerNotSignatoryFailed <- ledger
|
||||
.create(alice, MaintainerNotSignatory(alice, bob))
|
||||
.failed
|
||||
} yield {
|
||||
assertGrpcError(duplicateKeyFailure, Status.Code.INVALID_ARGUMENT, "DuplicateKey")
|
||||
assertGrpcError(
|
||||
|
@ -13,16 +13,15 @@ final class Divulgence(session: LedgerSession) extends LedgerTestSuite(session)
|
||||
private val transactionServiceDivulgence =
|
||||
LedgerTest(
|
||||
"DivulgenceTx",
|
||||
"Divulged contracts should not be exposed by the transaction service") { implicit context =>
|
||||
"Divulged contracts should not be exposed by the transaction service") { ledger =>
|
||||
for {
|
||||
Vector(alice, bob) <- allocateParties(2)
|
||||
divulgence1 <- create(Divulgence1(alice))(alice)
|
||||
divulgence2 <- create(Divulgence2(bob, alice))(bob)
|
||||
_ <- exercise(divulgence2.contractId.exerciseDivulgence2Archive(_, divulgence1.contractId))(
|
||||
alice)
|
||||
bobTransactions <- flatTransactions(bob)
|
||||
bobTrees <- transactionTrees(bob)
|
||||
transactionsForBoth <- flatTransactions(alice, bob)
|
||||
Vector(alice, bob) <- ledger.allocateParties(2)
|
||||
divulgence1 <- ledger.create(alice, Divulgence1(alice))
|
||||
divulgence2 <- ledger.create(bob, Divulgence2(bob, alice))
|
||||
_ <- ledger.exercise(alice, divulgence2.exerciseDivulgence2Archive(_, divulgence1))
|
||||
bobTransactions <- ledger.flatTransactions(bob)
|
||||
bobTrees <- ledger.transactionTrees(bob)
|
||||
transactionsForBoth <- ledger.flatTransactions(alice, bob)
|
||||
} yield {
|
||||
|
||||
// Inspecting the flat transaction stream as seen by Bob
|
||||
@ -47,8 +46,8 @@ final class Divulgence(session: LedgerSession) extends LedgerTestSuite(session)
|
||||
|
||||
val contractId = event.created.get.contractId
|
||||
assert(
|
||||
contractId == divulgence2.contractId,
|
||||
s"The only visible event should be the creation of the second contract (expected ${divulgence2.contractId}, got $contractId instead)"
|
||||
contractId == divulgence2,
|
||||
s"The only visible event should be the creation of the second contract (expected $divulgence2, got $contractId instead)"
|
||||
)
|
||||
|
||||
// Inspecting the transaction trees as seen by Bob
|
||||
@ -78,8 +77,8 @@ final class Divulgence(session: LedgerSession) extends LedgerTestSuite(session)
|
||||
|
||||
val createDivulgence2ContractId = createDivulgence2.getCreated.contractId
|
||||
assert(
|
||||
createDivulgence2ContractId == divulgence2.contractId,
|
||||
s"The event where Divulgence2 is created should have the same contract identifier as the created contract (expected ${divulgence2.contractId}, got $createDivulgence2ContractId instead)"
|
||||
createDivulgence2ContractId == divulgence2,
|
||||
s"The event where Divulgence2 is created should have the same contract identifier as the created contract (expected $divulgence2, got $createDivulgence2ContractId instead)"
|
||||
)
|
||||
|
||||
val exerciseOnDivulgence2Transaction = bobTrees(1)
|
||||
@ -95,7 +94,7 @@ final class Divulgence(session: LedgerSession) extends LedgerTestSuite(session)
|
||||
s"Expected event to be an exercise"
|
||||
)
|
||||
|
||||
assert(exerciseOnDivulgence2.getExercised.contractId == divulgence2.contractId)
|
||||
assert(exerciseOnDivulgence2.getExercised.contractId == divulgence2)
|
||||
|
||||
assert(exerciseOnDivulgence2.getExercised.childEventIds.size == 1)
|
||||
|
||||
@ -105,7 +104,7 @@ final class Divulgence(session: LedgerSession) extends LedgerTestSuite(session)
|
||||
|
||||
assert(exerciseOnDivulgence1.kind.isExercised)
|
||||
|
||||
assert(exerciseOnDivulgence1.getExercised.contractId == divulgence1.contractId)
|
||||
assert(exerciseOnDivulgence1.getExercised.contractId == divulgence1)
|
||||
|
||||
assert(exerciseOnDivulgence1.getExercised.childEventIds.isEmpty)
|
||||
|
||||
@ -133,8 +132,8 @@ final class Divulgence(session: LedgerSession) extends LedgerTestSuite(session)
|
||||
|
||||
val firstCreationForBoth = firstEventForBoth.created.get
|
||||
assert(
|
||||
firstCreationForBoth.contractId == divulgence1.contractId,
|
||||
s"The creation seen by filtering for both $alice and $bob was expected to be ${divulgence1.contractId} but is ${firstCreationForBoth.contractId} instead"
|
||||
firstCreationForBoth.contractId == divulgence1,
|
||||
s"The creation seen by filtering for both $alice and $bob was expected to be $divulgence1 but is ${firstCreationForBoth.contractId} instead"
|
||||
)
|
||||
|
||||
assert(
|
||||
@ -147,58 +146,56 @@ final class Divulgence(session: LedgerSession) extends LedgerTestSuite(session)
|
||||
private val activeContractServiceDivulgence = {
|
||||
LedgerTest(
|
||||
"DivulgenceAcs",
|
||||
"Divulged contracts should not be exposed by the active contract service") {
|
||||
implicit context =>
|
||||
for {
|
||||
Vector(alice, bob) <- allocateParties(2)
|
||||
divulgence1 <- create(Divulgence1(alice))(alice)
|
||||
divulgence2 <- create(Divulgence2(bob, alice))(bob)
|
||||
_ <- exercise(divulgence2.contractId.exerciseDivulgence2Fetch(_, divulgence1.contractId))(
|
||||
alice)
|
||||
activeForBobOnly <- activeContracts(bob)
|
||||
activeForBoth <- activeContracts(alice, bob)
|
||||
} yield {
|
||||
"Divulged contracts should not be exposed by the active contract service") { ledger =>
|
||||
for {
|
||||
Vector(alice, bob) <- ledger.allocateParties(2)
|
||||
divulgence1 <- ledger.create(alice, Divulgence1(alice))
|
||||
divulgence2 <- ledger.create(bob, Divulgence2(bob, alice))
|
||||
_ <- ledger.exercise(alice, divulgence2.exerciseDivulgence2Fetch(_, divulgence1))
|
||||
activeForBobOnly <- ledger.activeContracts(bob)
|
||||
activeForBoth <- ledger.activeContracts(alice, bob)
|
||||
} yield {
|
||||
|
||||
// Bob only sees Divulgence2
|
||||
assert(
|
||||
activeForBobOnly.size == 1,
|
||||
s"$bob should see only one active contract but sees ${activeForBobOnly.size} instead")
|
||||
assert(
|
||||
activeForBobOnly.head.contractId == divulgence2.contractId,
|
||||
s"$bob should see ${divulgence2.contractId} but sees ${activeForBobOnly.head.contractId} instead"
|
||||
)
|
||||
// Bob only sees Divulgence2
|
||||
assert(
|
||||
activeForBobOnly.size == 1,
|
||||
s"$bob should see only one active contract but sees ${activeForBobOnly.size} instead")
|
||||
assert(
|
||||
activeForBobOnly.head.contractId == divulgence2,
|
||||
s"$bob should see $divulgence2 but sees ${activeForBobOnly.head.contractId} instead"
|
||||
)
|
||||
|
||||
// Since we're filtering for Bob only Bob will be the only reported witness even if Alice sees the contract
|
||||
assert(
|
||||
activeForBobOnly.head.witnessParties == Seq(bob),
|
||||
s"The witness parties as seen by $bob should only include him but it is instead ${activeForBobOnly.head.witnessParties}"
|
||||
)
|
||||
// Since we're filtering for Bob only Bob will be the only reported witness even if Alice sees the contract
|
||||
assert(
|
||||
activeForBobOnly.head.witnessParties == Seq(bob),
|
||||
s"The witness parties as seen by $bob should only include him but it is instead ${activeForBobOnly.head.witnessParties}"
|
||||
)
|
||||
|
||||
// Alice sees both
|
||||
assert(
|
||||
activeForBoth.size == 2,
|
||||
s"The active contracts as seen by $alice and $bob should be two but are ${activeForBoth.size} instead")
|
||||
val divulgence1ContractId = Tag.unwrap(divulgence1.contractId)
|
||||
val divulgence2ContractId = Tag.unwrap(divulgence2.contractId)
|
||||
val activeForBothContractIds = activeForBoth.map(_.contractId).sorted
|
||||
val expectedContractIds = Seq(divulgence1ContractId, divulgence2ContractId).sorted
|
||||
assert(
|
||||
activeForBothContractIds == expectedContractIds,
|
||||
s"${divulgence1.contractId} and ${divulgence2.contractId} are expected to be seen when filtering for $alice and $bob but instead the following contract identifiers are seen: $activeForBothContractIds"
|
||||
)
|
||||
val divulgence1Witnesses =
|
||||
activeForBoth.find(_.contractId == divulgence1ContractId).get.witnessParties.sorted
|
||||
val divulgence2Witnesses =
|
||||
activeForBoth.find(_.contractId == divulgence2ContractId).get.witnessParties.sorted
|
||||
assert(
|
||||
divulgence1Witnesses == Seq(alice),
|
||||
s"The witness parties of the first contract should only include $alice but it is instead $divulgence1Witnesses ($bob)"
|
||||
)
|
||||
assert(
|
||||
divulgence2Witnesses == Tag.unsubst(Seq(alice, bob)).sorted,
|
||||
s"The witness parties of the second contract should include $alice and $bob but it is instead $divulgence2Witnesses"
|
||||
)
|
||||
}
|
||||
// Alice sees both
|
||||
assert(
|
||||
activeForBoth.size == 2,
|
||||
s"The active contracts as seen by $alice and $bob should be two but are ${activeForBoth.size} instead")
|
||||
val divulgence1ContractId = Tag.unwrap(divulgence1)
|
||||
val divulgence2ContractId = Tag.unwrap(divulgence2)
|
||||
val activeForBothContractIds = activeForBoth.map(_.contractId).sorted
|
||||
val expectedContractIds = Seq(divulgence1ContractId, divulgence2ContractId).sorted
|
||||
assert(
|
||||
activeForBothContractIds == expectedContractIds,
|
||||
s"$divulgence1 and $divulgence2 are expected to be seen when filtering for $alice and $bob but instead the following contract identifiers are seen: $activeForBothContractIds"
|
||||
)
|
||||
val divulgence1Witnesses =
|
||||
activeForBoth.find(_.contractId == divulgence1ContractId).get.witnessParties.sorted
|
||||
val divulgence2Witnesses =
|
||||
activeForBoth.find(_.contractId == divulgence2ContractId).get.witnessParties.sorted
|
||||
assert(
|
||||
divulgence1Witnesses == Seq(alice),
|
||||
s"The witness parties of the first contract should only include $alice but it is instead $divulgence1Witnesses ($bob)"
|
||||
)
|
||||
assert(
|
||||
divulgence2Witnesses == Tag.unsubst(Seq(alice, bob)).sorted,
|
||||
s"The witness parties of the second contract should include $alice and $bob but it is instead $divulgence2Witnesses"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,12 +5,15 @@ package com.daml.ledger.api.testtool.tests
|
||||
|
||||
import com.daml.ledger.api.testtool.infrastructure.{LedgerSession, LedgerTest, LedgerTestSuite}
|
||||
|
||||
import scala.concurrent.Future
|
||||
|
||||
final class Identity(session: LedgerSession) extends LedgerTestSuite(session) {
|
||||
|
||||
private[this] val identity =
|
||||
LedgerTest("IdNotEmpty", "A ledger should return a non-empty string as its identity") {
|
||||
implicit context =>
|
||||
for (id <- ledgerId) yield assert(id.nonEmpty, "The returned ledger identifier was empty")
|
||||
ledger =>
|
||||
Future.successful(
|
||||
assert(ledger.ledgerId.nonEmpty, "The returned ledger identifier was empty"))
|
||||
}
|
||||
|
||||
override val tests: Vector[LedgerTest] = Vector(identity)
|
||||
|
@ -3,7 +3,12 @@
|
||||
|
||||
package com.daml.ledger.api.testtool.tests
|
||||
|
||||
import com.daml.ledger.api.testtool.infrastructure.{LedgerSession, LedgerTest, LedgerTestSuite}
|
||||
import com.daml.ledger.api.testtool.infrastructure.{
|
||||
LedgerSession,
|
||||
LedgerTest,
|
||||
LedgerTestSuite,
|
||||
SemanticTesterLedger
|
||||
}
|
||||
import com.digitalasset.daml.lf.archive.SupportedFileType.DarFile
|
||||
import com.digitalasset.daml.lf.archive.{Decode, UniversalArchiveReader}
|
||||
import com.digitalasset.daml.lf.engine.testing.SemanticTester
|
||||
@ -29,18 +34,15 @@ final class SemanticTests(session: LedgerSession) extends LedgerTestSuite(sessio
|
||||
override val tests: Vector[LedgerTest] =
|
||||
loadedPackages match {
|
||||
case Failure(exception) =>
|
||||
Vector(LedgerTest("SemanticTests", "SemanticTests") { implicit context =>
|
||||
Vector(LedgerTest("SemanticTests", "SemanticTests") { _ =>
|
||||
Future.failed(
|
||||
new RuntimeException("Unable to load the semantic tests package", exception))
|
||||
})
|
||||
case Success((main, packages)) =>
|
||||
for (name <- SemanticTester.scenarios(packages)(main).toVector) yield {
|
||||
LedgerTest(name.toString, name.toString) { implicit context =>
|
||||
LedgerTest(name.toString, name.toString) { ledger =>
|
||||
val tester =
|
||||
new SemanticTester(
|
||||
parties => context.semanticTesterLedger(parties, packages),
|
||||
main,
|
||||
packages)
|
||||
new SemanticTester(new SemanticTesterLedger(ledger, _, packages), main, packages)
|
||||
tester.testScenario(name).recover {
|
||||
case error @ SemanticTesterError(_, message) =>
|
||||
throw new AssertionError(message, error)
|
||||
|
@ -12,11 +12,11 @@ import scala.concurrent.duration.DurationInt
|
||||
final class Time(session: LedgerSession) extends LedgerTestSuite(session) {
|
||||
|
||||
val pass =
|
||||
LedgerTest("PassTime", "Advancing time should return the new time") { implicit context =>
|
||||
LedgerTest("PassTime", "Advancing time should return the new time") { ledger =>
|
||||
for {
|
||||
t1 <- time()
|
||||
_ <- passTime(1.second)
|
||||
t2 <- time()
|
||||
t1 <- ledger.time()
|
||||
_ <- ledger.passTime(1.second)
|
||||
t2 <- ledger.time()
|
||||
travel = Duration.between(t1, t2)
|
||||
} yield
|
||||
assert(
|
||||
|
@ -11,83 +11,90 @@ import scalaz.Tag
|
||||
final class Witnesses(session: LedgerSession) extends LedgerTestSuite(session) {
|
||||
|
||||
private[this] val respectDisclosureRules =
|
||||
LedgerTest("RespectDisclosureRules", "The ledger should respect disclosure rules") {
|
||||
implicit context =>
|
||||
for {
|
||||
Vector(alice, bob, charlie) <- allocateParties(3)
|
||||
LedgerTest("RespectDisclosureRules", "The ledger should respect disclosure rules") { ledger =>
|
||||
for {
|
||||
Vector(alice, bob, charlie) <- ledger.allocateParties(3)
|
||||
|
||||
// Create the Witnesses contract as Alice and get the resulting transaction as seen by all parties
|
||||
(witnessesTransactionId, witnesses) <- createAndGetTransactionId(
|
||||
WitnessesTemplate(alice, bob, charlie))(alice)
|
||||
witnessesTransaction <- transactionTreeById(witnessesTransactionId, alice, bob, charlie)
|
||||
// Create the Witnesses contract as Alice and get the resulting transaction as seen by all parties
|
||||
(witnessesTransactionId, witnesses) <- ledger.createAndGetTransactionId(
|
||||
alice,
|
||||
WitnessesTemplate(alice, bob, charlie))
|
||||
witnessesTransaction <- ledger.transactionTreeById(
|
||||
witnessesTransactionId,
|
||||
alice,
|
||||
bob,
|
||||
charlie)
|
||||
|
||||
// Charlie is not a stakeholder of Witnesses and thus cannot see any such contract unless divulged.
|
||||
// Such contract is divulged by creating a DivulgeWitness with Charlie as a signatory and exercising
|
||||
// a choice as Alice that causes divulgence (in this case, the Witnesses instance previously
|
||||
// created is fetched as part of the transaction).
|
||||
divulgeWitness <- create(DivulgeWitnesses(alice, charlie))(charlie)
|
||||
_ <- exercise(divulgeWitness.contractId.exerciseDivulge(_, witnesses.contractId))(alice)
|
||||
// Charlie is not a stakeholder of Witnesses and thus cannot see any such contract unless divulged.
|
||||
// Such contract is divulged by creating a DivulgeWitness with Charlie as a signatory and exercising
|
||||
// a choice as Alice that causes divulgence (in this case, the Witnesses instance previously
|
||||
// created is fetched as part of the transaction).
|
||||
divulgeWitness <- ledger.create(charlie, DivulgeWitnesses(alice, charlie))
|
||||
_ <- ledger.exercise(alice, divulgeWitness.exerciseDivulge(_, witnesses))
|
||||
|
||||
// A non-consuming choice is exercised with the expectation
|
||||
// that Charlie is now able to exercise a choice on the divulged contract
|
||||
// The tree is fetched from the identifier to ensure we get the witnesses as seen by all parties
|
||||
nonConsuming <- exercise(witnesses.contractId.exerciseWitnessesNonConsumingChoice)(
|
||||
charlie)
|
||||
nonConsumingTree <- transactionTreeById(nonConsuming.transactionId, alice, bob, charlie)
|
||||
// A non-consuming choice is exercised with the expectation
|
||||
// that Charlie is now able to exercise a choice on the divulged contract
|
||||
// The tree is fetched from the identifier to ensure we get the witnesses as seen by all parties
|
||||
nonConsuming <- ledger.exercise(charlie, witnesses.exerciseWitnessesNonConsumingChoice)
|
||||
nonConsumingTree <- ledger.transactionTreeById(
|
||||
nonConsuming.transactionId,
|
||||
alice,
|
||||
bob,
|
||||
charlie)
|
||||
|
||||
// A consuming choice is exercised with the expectation
|
||||
// that Charlie is now able to exercise a choice on the divulged contract
|
||||
// The tree is fetched from the identifier to ensure we get the witnesses as seen by all parties
|
||||
consuming <- exercise(witnesses.contractId.exerciseWitnessesChoice)(charlie)
|
||||
consumingTree <- transactionTreeById(consuming.transactionId, alice, bob, charlie)
|
||||
} yield {
|
||||
// A consuming choice is exercised with the expectation
|
||||
// that Charlie is now able to exercise a choice on the divulged contract
|
||||
// The tree is fetched from the identifier to ensure we get the witnesses as seen by all parties
|
||||
consuming <- ledger.exercise(charlie, witnesses.exerciseWitnessesChoice)
|
||||
consumingTree <- ledger.transactionTreeById(consuming.transactionId, alice, bob, charlie)
|
||||
} yield {
|
||||
|
||||
assert(
|
||||
witnessesTransaction.eventsById.size == 1,
|
||||
s"The transaction for creating the Witness contract should only contain a single event, but has ${witnessesTransaction.eventsById.size}"
|
||||
)
|
||||
val (_, creationEvent) = witnessesTransaction.eventsById.head
|
||||
assert(
|
||||
creationEvent.kind.isCreated,
|
||||
s"The event in the transaction for creating the Witness should be a CreatedEvent, but was ${creationEvent.kind}")
|
||||
assert(
|
||||
witnessesTransaction.eventsById.size == 1,
|
||||
s"The transaction for creating the Witness contract should only contain a single event, but has ${witnessesTransaction.eventsById.size}"
|
||||
)
|
||||
val (_, creationEvent) = witnessesTransaction.eventsById.head
|
||||
assert(
|
||||
creationEvent.kind.isCreated,
|
||||
s"The event in the transaction for creating the Witness should be a CreatedEvent, but was ${creationEvent.kind}")
|
||||
|
||||
val expectedWitnessesOfCreation = Tag.unsubst(Seq(alice, bob)).sorted
|
||||
assert(
|
||||
creationEvent.getCreated.witnessParties.sorted == expectedWitnessesOfCreation,
|
||||
s"The parties for witnessing the CreatedEvent should be ${expectedWitnessesOfCreation}, but were ${creationEvent.getCreated.witnessParties}"
|
||||
)
|
||||
assert(
|
||||
nonConsumingTree.eventsById.size == 1,
|
||||
s"The transaction for exercising the non-consuming choice should only contain a single event, but has ${nonConsumingTree.eventsById.size}"
|
||||
)
|
||||
val (_, nonConsumingEvent) = nonConsumingTree.eventsById.head
|
||||
assert(
|
||||
nonConsumingEvent.kind.isExercised,
|
||||
s"The event in the transaction for exercising the non-consuming choice should be an ExercisedEvent, but was ${nonConsumingEvent.kind}"
|
||||
)
|
||||
val expectedWitnessesOfCreation = Tag.unsubst(Seq(alice, bob)).sorted
|
||||
assert(
|
||||
creationEvent.getCreated.witnessParties.sorted == expectedWitnessesOfCreation,
|
||||
s"The parties for witnessing the CreatedEvent should be ${expectedWitnessesOfCreation}, but were ${creationEvent.getCreated.witnessParties}"
|
||||
)
|
||||
assert(
|
||||
nonConsumingTree.eventsById.size == 1,
|
||||
s"The transaction for exercising the non-consuming choice should only contain a single event, but has ${nonConsumingTree.eventsById.size}"
|
||||
)
|
||||
val (_, nonConsumingEvent) = nonConsumingTree.eventsById.head
|
||||
assert(
|
||||
nonConsumingEvent.kind.isExercised,
|
||||
s"The event in the transaction for exercising the non-consuming choice should be an ExercisedEvent, but was ${nonConsumingEvent.kind}"
|
||||
)
|
||||
|
||||
val expectedWitnessesOfNonConsumingChoice = Tag.unsubst(Seq(alice, charlie)).sorted
|
||||
assert(
|
||||
nonConsumingEvent.getExercised.witnessParties.sorted == expectedWitnessesOfNonConsumingChoice,
|
||||
s"The parties for witnessing the non-consuming ExercisedEvent should be ${expectedWitnessesOfNonConsumingChoice}, but were ${nonConsumingEvent.getCreated.witnessParties}"
|
||||
)
|
||||
assert(
|
||||
consumingTree.eventsById.size == 1,
|
||||
s"The transaction for exercising the consuming choice should only contain a single event, but has ${consumingTree.eventsById.size}"
|
||||
)
|
||||
val expectedWitnessesOfNonConsumingChoice = Tag.unsubst(Seq(alice, charlie)).sorted
|
||||
assert(
|
||||
nonConsumingEvent.getExercised.witnessParties.sorted == expectedWitnessesOfNonConsumingChoice,
|
||||
s"The parties for witnessing the non-consuming ExercisedEvent should be ${expectedWitnessesOfNonConsumingChoice}, but were ${nonConsumingEvent.getCreated.witnessParties}"
|
||||
)
|
||||
assert(
|
||||
consumingTree.eventsById.size == 1,
|
||||
s"The transaction for exercising the consuming choice should only contain a single event, but has ${consumingTree.eventsById.size}"
|
||||
)
|
||||
|
||||
val (_, consumingEvent) = consumingTree.eventsById.head
|
||||
assert(
|
||||
consumingEvent.kind.isExercised,
|
||||
s"The event in the transaction for exercising the consuming choice should be an ExercisedEvent, but was ${consumingEvent.kind}"
|
||||
)
|
||||
val expectedWitnessesOfConsumingChoice = Tag.unsubst(Seq(alice, bob, charlie)).sorted
|
||||
assert(
|
||||
consumingEvent.getExercised.witnessParties.sorted == expectedWitnessesOfConsumingChoice,
|
||||
s"The parties for witnessing the consuming ExercisedEvent should be ${expectedWitnessesOfConsumingChoice}, but were ${consumingEvent.getCreated.witnessParties}"
|
||||
)
|
||||
val (_, consumingEvent) = consumingTree.eventsById.head
|
||||
assert(
|
||||
consumingEvent.kind.isExercised,
|
||||
s"The event in the transaction for exercising the consuming choice should be an ExercisedEvent, but was ${consumingEvent.kind}"
|
||||
)
|
||||
val expectedWitnessesOfConsumingChoice = Tag.unsubst(Seq(alice, bob, charlie)).sorted
|
||||
assert(
|
||||
consumingEvent.getExercised.witnessParties.sorted == expectedWitnessesOfConsumingChoice,
|
||||
s"The parties for witnessing the consuming ExercisedEvent should be ${expectedWitnessesOfConsumingChoice}, but were ${consumingEvent.getCreated.witnessParties}"
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override val tests: Vector[LedgerTest] = Vector(respectDisclosureRules)
|
||||
|
@ -30,7 +30,7 @@ trait TransactionsService {
|
||||
def getLedgerEnd(): Future[Offset]
|
||||
|
||||
/*
|
||||
def getTransactionById(
|
||||
def transactionTreeById(
|
||||
,
|
||||
transactionId: TransactionId,
|
||||
requestingParties: Set[Party]
|
||||
|
Loading…
Reference in New Issue
Block a user