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:
Stefano Baghino 2019-08-16 16:24:00 +02:00 committed by GitHub
parent 6a0ebc9f5e
commit eb965866cc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 583 additions and 847 deletions

View File

@ -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)
}

View File

@ -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}...")

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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].")
}
}

View File

@ -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))
}

View File

@ -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.")
}

View File

@ -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(

View File

@ -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(

View File

@ -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"
)
}
}
}

View File

@ -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)

View File

@ -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)

View File

@ -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(

View File

@ -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)

View File

@ -30,7 +30,7 @@ trait TransactionsService {
def getLedgerEnd(): Future[Offset]
/*
def getTransactionById(
def transactionTreeById(
,
transactionId: TransactionId,
requestingParties: Set[Party]