mirror of
https://github.com/digital-asset/daml.git
synced 2024-09-20 09:17:43 +03:00
Conformance Tests for Interface Subscriptions [DPP-1159] (#14750)
* Conformance Tests for Interface Subscriptions [DPP-1159] CHANGELOG_BEGIN CHANGELOG_END
This commit is contained in:
parent
24268b25ae
commit
1f7dd3c018
@ -4,10 +4,12 @@
|
||||
package com.daml.ledger.api.testtool.infrastructure.participant
|
||||
|
||||
import java.time.Instant
|
||||
|
||||
import com.daml.ledger.api.refinements.ApiTypes.TemplateId
|
||||
import com.daml.ledger.api.testtool.infrastructure.Endpoint
|
||||
import com.daml.ledger.api.testtool.infrastructure.participant.ParticipantTestContext.CompletionResponse
|
||||
import com.daml.ledger.api.testtool.infrastructure.participant.ParticipantTestContext.{
|
||||
CompletionResponse,
|
||||
IncludeInterfaceView,
|
||||
}
|
||||
import com.daml.ledger.api.testtool.infrastructure.time.DelayMechanism
|
||||
import com.daml.ledger.api.v1.active_contracts_service.GetActiveContractsRequest
|
||||
import com.daml.ledger.api.v1.admin.config_management_service.{
|
||||
@ -43,13 +45,14 @@ import com.daml.ledger.api.v1.ledger_configuration_service.LedgerConfiguration
|
||||
import com.daml.ledger.api.v1.ledger_offset.LedgerOffset
|
||||
import com.daml.ledger.api.v1.package_service.{GetPackageResponse, PackageStatus}
|
||||
import com.daml.ledger.api.v1.transaction.{Transaction, TransactionTree}
|
||||
import com.daml.ledger.api.v1.transaction_filter.{Filters, TransactionFilter}
|
||||
import com.daml.ledger.api.v1.transaction_service.{
|
||||
GetTransactionByEventIdRequest,
|
||||
GetTransactionByIdRequest,
|
||||
GetTransactionsRequest,
|
||||
GetTransactionsResponse,
|
||||
}
|
||||
import com.daml.ledger.api.v1.value.{Identifier, Value}
|
||||
import com.daml.ledger.api.v1.value.Value
|
||||
import com.daml.ledger.client.binding.{Primitive, Template}
|
||||
import com.daml.lf.data.Ref.HexString
|
||||
import com.google.protobuf.ByteString
|
||||
@ -139,24 +142,36 @@ trait ParticipantTestContext extends UserManagementTestContext {
|
||||
): Future[(Option[LedgerOffset], Vector[CreatedEvent])]
|
||||
def activeContractsRequest(
|
||||
parties: Seq[Primitive.Party],
|
||||
templateIds: Seq[Identifier] = Seq.empty,
|
||||
templateIds: Seq[TemplateId] = Seq.empty,
|
||||
interfaceFilters: Seq[(TemplateId, IncludeInterfaceView)] = Seq.empty,
|
||||
): GetActiveContractsRequest
|
||||
def activeContracts(parties: Primitive.Party*): Future[Vector[CreatedEvent]]
|
||||
def activeContractsByTemplateId(
|
||||
templateIds: Seq[Identifier],
|
||||
templateIds: Seq[TemplateId],
|
||||
parties: Primitive.Party*
|
||||
): Future[Vector[CreatedEvent]]
|
||||
|
||||
/** Create a [[GetTransactionsRequest]] with a set of [[Party]] objects.
|
||||
/** Create a [[TransactionFilter]] with a set of [[Party]] objects.
|
||||
* You should use this only when you need to tweak the request of [[flatTransactions]]
|
||||
* or [[transactionTrees]], otherwise use the shortcut override that allows you to
|
||||
* directly pass a set of [[Party]]
|
||||
*/
|
||||
def getTransactionsRequest(
|
||||
def transactionFilter(
|
||||
parties: Seq[Primitive.Party],
|
||||
templateIds: Seq[TemplateId] = Seq.empty,
|
||||
interfaceFilters: Seq[(TemplateId, IncludeInterfaceView)] = Seq.empty,
|
||||
): TransactionFilter
|
||||
|
||||
def filters(
|
||||
templateIds: Seq[TemplateId] = Seq.empty,
|
||||
interfaceFilters: Seq[(TemplateId, IncludeInterfaceView)] = Seq.empty,
|
||||
): Filters
|
||||
|
||||
def getTransactionsRequest(
|
||||
transactionFilter: TransactionFilter,
|
||||
begin: LedgerOffset = referenceOffset,
|
||||
): GetTransactionsRequest
|
||||
|
||||
def transactionStream(
|
||||
request: GetTransactionsRequest,
|
||||
responseObserver: StreamObserver[GetTransactionsResponse],
|
||||
@ -377,6 +392,7 @@ trait ParticipantTestContext extends UserManagementTestContext {
|
||||
}
|
||||
|
||||
object ParticipantTestContext {
|
||||
type IncludeInterfaceView = Boolean
|
||||
|
||||
case class CompletionResponse(completion: Completion, offset: LedgerOffset, recordTime: Instant)
|
||||
|
||||
|
@ -4,11 +4,13 @@
|
||||
package com.daml.ledger.api.testtool.infrastructure.participant
|
||||
|
||||
import java.time.{Clock, Instant}
|
||||
|
||||
import com.daml.ledger.api.refinements.ApiTypes.TemplateId
|
||||
import com.daml.ledger.api.testtool.infrastructure.Eventually.eventually
|
||||
import com.daml.ledger.api.testtool.infrastructure.ProtobufConverters._
|
||||
import com.daml.ledger.api.testtool.infrastructure.participant.ParticipantTestContext.CompletionResponse
|
||||
import com.daml.ledger.api.testtool.infrastructure.participant.ParticipantTestContext.{
|
||||
CompletionResponse,
|
||||
IncludeInterfaceView,
|
||||
}
|
||||
import com.daml.ledger.api.testtool.infrastructure.time.{
|
||||
DelayMechanism,
|
||||
StaticTimeDelayMechanism,
|
||||
@ -71,7 +73,12 @@ import com.daml.ledger.api.v1.ledger_offset.LedgerOffset
|
||||
import com.daml.ledger.api.v1.package_service._
|
||||
import com.daml.ledger.api.v1.testing.time_service.{GetTimeRequest, GetTimeResponse, SetTimeRequest}
|
||||
import com.daml.ledger.api.v1.transaction.{Transaction, TransactionTree}
|
||||
import com.daml.ledger.api.v1.transaction_filter.{Filters, InclusiveFilters, TransactionFilter}
|
||||
import com.daml.ledger.api.v1.transaction_filter.{
|
||||
Filters,
|
||||
InclusiveFilters,
|
||||
InterfaceFilter,
|
||||
TransactionFilter,
|
||||
}
|
||||
import com.daml.ledger.api.v1.transaction_service.{
|
||||
GetLedgerEndRequest,
|
||||
GetTransactionByEventIdRequest,
|
||||
@ -79,7 +86,7 @@ import com.daml.ledger.api.v1.transaction_service.{
|
||||
GetTransactionsRequest,
|
||||
GetTransactionsResponse,
|
||||
}
|
||||
import com.daml.ledger.api.v1.value.{Identifier, Value}
|
||||
import com.daml.ledger.api.v1.value.Value
|
||||
import com.daml.ledger.client.binding.Primitive.Party
|
||||
import com.daml.ledger.client.binding.{Primitive, Template}
|
||||
import com.daml.lf.data.Ref
|
||||
@ -116,8 +123,6 @@ final class SingleParticipantTestContext private[participant] (
|
||||
extends ParticipantTestContext {
|
||||
private val logger = ContextualizedLogger.get(getClass)
|
||||
|
||||
import SingleParticipantTestContext._
|
||||
|
||||
private[this] val identifierPrefix =
|
||||
s"$applicationId-$endpointId-$identifierSuffix"
|
||||
|
||||
@ -274,11 +279,12 @@ final class SingleParticipantTestContext private[participant] (
|
||||
|
||||
override def activeContractsRequest(
|
||||
parties: Seq[Party],
|
||||
templateIds: Seq[Identifier] = Seq.empty,
|
||||
templateIds: Seq[TemplateId] = Seq.empty,
|
||||
interfaceFilters: Seq[(TemplateId, IncludeInterfaceView)] = Seq.empty,
|
||||
): GetActiveContractsRequest =
|
||||
new GetActiveContractsRequest(
|
||||
ledgerId = ledgerId,
|
||||
filter = transactionFilter(Tag.unsubst(parties), templateIds),
|
||||
filter = Some(transactionFilter(parties, templateIds, interfaceFilters)),
|
||||
verbose = true,
|
||||
)
|
||||
|
||||
@ -286,24 +292,50 @@ final class SingleParticipantTestContext private[participant] (
|
||||
activeContractsByTemplateId(Seq.empty, parties: _*)
|
||||
|
||||
override def activeContractsByTemplateId(
|
||||
templateIds: Seq[Identifier],
|
||||
templateIds: Seq[TemplateId],
|
||||
parties: Party*
|
||||
): Future[Vector[CreatedEvent]] =
|
||||
activeContracts(activeContractsRequest(parties, templateIds)).map(_._2)
|
||||
|
||||
override def getTransactionsRequest(
|
||||
def transactionFilter(
|
||||
parties: Seq[Party],
|
||||
templateIds: Seq[TemplateId] = Seq.empty,
|
||||
begin: LedgerOffset = referenceOffset,
|
||||
): GetTransactionsRequest =
|
||||
new GetTransactionsRequest(
|
||||
ledgerId = ledgerId,
|
||||
begin = Some(begin),
|
||||
end = Some(end),
|
||||
filter = transactionFilter(Tag.unsubst(parties), Tag.unsubst(templateIds)),
|
||||
verbose = true,
|
||||
interfaceFilters: Seq[(TemplateId, IncludeInterfaceView)] = Seq.empty,
|
||||
): TransactionFilter =
|
||||
new TransactionFilter(
|
||||
parties.map(party => party.unwrap -> filters(templateIds, interfaceFilters)).toMap
|
||||
)
|
||||
|
||||
def filters(
|
||||
templateIds: Seq[TemplateId] = Seq.empty,
|
||||
interfaceFilters: Seq[(TemplateId, IncludeInterfaceView)] = Seq.empty,
|
||||
): Filters = new Filters(
|
||||
if (templateIds.isEmpty && interfaceFilters.isEmpty) None
|
||||
else
|
||||
Some(
|
||||
new InclusiveFilters(
|
||||
templateIds = templateIds.map(Tag.unwrap).toSeq,
|
||||
interfaceFilters = interfaceFilters.map { case (id, includeInterfaceView) =>
|
||||
new InterfaceFilter(
|
||||
Some(Tag.unwrap(id)),
|
||||
includeInterfaceView = includeInterfaceView,
|
||||
)
|
||||
}.toSeq,
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
def getTransactionsRequest(
|
||||
transactionFilter: TransactionFilter,
|
||||
begin: LedgerOffset = referenceOffset,
|
||||
): GetTransactionsRequest = new GetTransactionsRequest(
|
||||
ledgerId = ledgerId,
|
||||
begin = Some(begin),
|
||||
end = Some(end),
|
||||
filter = Some(transactionFilter),
|
||||
verbose = true,
|
||||
)
|
||||
|
||||
private def transactions[Res](
|
||||
n: Int,
|
||||
request: GetTransactionsRequest,
|
||||
@ -327,14 +359,14 @@ final class SingleParticipantTestContext private[participant] (
|
||||
templateId: TemplateId,
|
||||
parties: Party*
|
||||
): Future[Vector[Transaction]] =
|
||||
flatTransactions(getTransactionsRequest(parties, Seq(templateId)))
|
||||
flatTransactions(getTransactionsRequest(transactionFilter(parties, Seq(templateId))))
|
||||
|
||||
override def flatTransactions(request: GetTransactionsRequest): Future[Vector[Transaction]] =
|
||||
transactions(request, services.transaction.getTransactions)
|
||||
.map(_.flatMap(_.transactions))
|
||||
|
||||
override def flatTransactions(parties: Party*): Future[Vector[Transaction]] =
|
||||
flatTransactions(getTransactionsRequest(parties))
|
||||
flatTransactions(getTransactionsRequest(transactionFilter(parties)))
|
||||
|
||||
override def flatTransactions(
|
||||
take: Int,
|
||||
@ -344,20 +376,20 @@ final class SingleParticipantTestContext private[participant] (
|
||||
.map(_.flatMap(_.transactions))
|
||||
|
||||
override def flatTransactions(take: Int, parties: Party*): Future[Vector[Transaction]] =
|
||||
flatTransactions(take, getTransactionsRequest(parties))
|
||||
flatTransactions(take, getTransactionsRequest(transactionFilter(parties)))
|
||||
|
||||
override def transactionTreesByTemplateId(
|
||||
templateId: TemplateId,
|
||||
parties: Party*
|
||||
): Future[Vector[TransactionTree]] =
|
||||
transactionTrees(getTransactionsRequest(parties, Seq(templateId)))
|
||||
transactionTrees(getTransactionsRequest(transactionFilter(parties, Seq(templateId))))
|
||||
|
||||
override def transactionTrees(request: GetTransactionsRequest): Future[Vector[TransactionTree]] =
|
||||
transactions(request, services.transaction.getTransactionTrees)
|
||||
.map(_.flatMap(_.transactions))
|
||||
|
||||
override def transactionTrees(parties: Party*): Future[Vector[TransactionTree]] =
|
||||
transactionTrees(getTransactionsRequest(parties))
|
||||
transactionTrees(getTransactionsRequest(transactionFilter(parties)))
|
||||
|
||||
override def transactionTrees(
|
||||
take: Int,
|
||||
@ -367,7 +399,7 @@ final class SingleParticipantTestContext private[participant] (
|
||||
.map(_.flatMap(_.transactions))
|
||||
|
||||
override def transactionTrees(take: Int, parties: Party*): Future[Vector[TransactionTree]] =
|
||||
transactionTrees(take, getTransactionsRequest(parties))
|
||||
transactionTrees(take, getTransactionsRequest(transactionFilter(parties)))
|
||||
|
||||
override def getTransactionByIdRequest(
|
||||
transactionId: String,
|
||||
@ -752,19 +784,3 @@ final class SingleParticipantTestContext private[participant] (
|
||||
private def reservePartyNames(n: Int): Future[Vector[Party]] =
|
||||
Future.successful(Vector.fill(n)(Party(nextPartyHintId())))
|
||||
}
|
||||
|
||||
private[testtool] object SingleParticipantTestContext {
|
||||
|
||||
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)): _*)))
|
||||
|
||||
}
|
||||
|
@ -5,9 +5,9 @@ package com.daml.ledger.api.testtool.infrastructure.participant
|
||||
|
||||
import java.time.Instant
|
||||
import java.util.concurrent.TimeoutException
|
||||
|
||||
import com.daml.ledger.api.refinements.ApiTypes.TemplateId
|
||||
import com.daml.ledger.api.testtool.infrastructure.Endpoint
|
||||
import com.daml.ledger.api.testtool.infrastructure.participant.ParticipantTestContext.IncludeInterfaceView
|
||||
import com.daml.ledger.api.testtool.infrastructure.time.{DelayMechanism, Durations}
|
||||
import com.daml.ledger.api.v1.active_contracts_service.GetActiveContractsRequest
|
||||
import com.daml.ledger.api.v1.admin.config_management_service.{
|
||||
@ -43,13 +43,14 @@ import com.daml.ledger.api.v1.ledger_configuration_service.LedgerConfiguration
|
||||
import com.daml.ledger.api.v1.ledger_offset.LedgerOffset
|
||||
import com.daml.ledger.api.v1.package_service.{GetPackageResponse, PackageStatus}
|
||||
import com.daml.ledger.api.v1.transaction.{Transaction, TransactionTree}
|
||||
import com.daml.ledger.api.v1.transaction_filter.{Filters, TransactionFilter}
|
||||
import com.daml.ledger.api.v1.transaction_service.{
|
||||
GetTransactionByEventIdRequest,
|
||||
GetTransactionByIdRequest,
|
||||
GetTransactionsRequest,
|
||||
GetTransactionsResponse,
|
||||
}
|
||||
import com.daml.ledger.api.v1.value.{Identifier, Value}
|
||||
import com.daml.ledger.api.v1.value.Value
|
||||
import com.daml.ledger.client.binding.{Primitive, Template}
|
||||
import com.daml.lf.data.Ref.HexString
|
||||
import com.daml.timer.Delayed
|
||||
@ -150,22 +151,29 @@ class TimeoutParticipantTestContext(timeoutScaleFactor: Double, delegate: Partic
|
||||
)
|
||||
override def activeContractsRequest(
|
||||
parties: Seq[Primitive.Party],
|
||||
templateIds: Seq[Identifier],
|
||||
): GetActiveContractsRequest = delegate.activeContractsRequest(parties, templateIds)
|
||||
templateIds: Seq[TemplateId],
|
||||
interfaceFilters: Seq[(TemplateId, IncludeInterfaceView)] = Seq.empty,
|
||||
): GetActiveContractsRequest =
|
||||
delegate.activeContractsRequest(parties, templateIds, interfaceFilters)
|
||||
override def activeContracts(parties: Primitive.Party*): Future[Vector[CreatedEvent]] =
|
||||
withTimeout(s"Active contracts for parties $parties", delegate.activeContracts(parties: _*))
|
||||
override def activeContractsByTemplateId(
|
||||
templateIds: Seq[Identifier],
|
||||
templateIds: Seq[TemplateId],
|
||||
parties: Primitive.Party*
|
||||
): Future[Vector[CreatedEvent]] = withTimeout(
|
||||
s"Active contracts by template ids $templateIds for parties $parties",
|
||||
delegate.activeContractsByTemplateId(templateIds, parties: _*),
|
||||
)
|
||||
override def getTransactionsRequest(
|
||||
|
||||
def transactionFilter(
|
||||
parties: Seq[Primitive.Party],
|
||||
templateIds: Seq[TemplateId],
|
||||
templateIds: Seq[TemplateId] = Seq.empty,
|
||||
interfaceFilters: Seq[(TemplateId, IncludeInterfaceView)] = Seq.empty,
|
||||
) = delegate.transactionFilter(parties, templateIds, interfaceFilters)
|
||||
override def getTransactionsRequest(
|
||||
transactionFilter: TransactionFilter,
|
||||
begin: LedgerOffset,
|
||||
): GetTransactionsRequest = delegate.getTransactionsRequest(parties, templateIds, begin)
|
||||
): GetTransactionsRequest = delegate.getTransactionsRequest(transactionFilter, begin)
|
||||
override def transactionStream(
|
||||
request: GetTransactionsRequest,
|
||||
responseObserver: StreamObserver[GetTransactionsResponse],
|
||||
@ -496,4 +504,8 @@ class TimeoutParticipantTestContext(timeoutScaleFactor: Double, delegate: Partic
|
||||
)
|
||||
}
|
||||
|
||||
override def filters(
|
||||
templateIds: Seq[TemplateId],
|
||||
interfaceFilters: Seq[(TemplateId, IncludeInterfaceView)],
|
||||
): Filters = delegate.filters(templateIds, interfaceFilters)
|
||||
}
|
||||
|
@ -119,7 +119,7 @@ class ActiveContractsServiceIT extends LedgerTestSuite {
|
||||
)(implicit ec => { case Participants(Participant(ledger, party)) =>
|
||||
for {
|
||||
(dummy, _, _) <- createDummyContracts(party, ledger)
|
||||
activeContracts <- ledger.activeContractsByTemplateId(Seq(Dummy.id.unwrap), party)
|
||||
activeContracts <- ledger.activeContractsByTemplateId(Seq(Dummy.id), party)
|
||||
} yield {
|
||||
assert(
|
||||
activeContracts.size == 1,
|
||||
@ -185,7 +185,7 @@ class ActiveContractsServiceIT extends LedgerTestSuite {
|
||||
ledger.activeContractsRequest(Seq(party))
|
||||
)
|
||||
dummyWithParam <- ledger.create(party, DummyWithParam(party))
|
||||
request = ledger.getTransactionsRequest(Seq(party))
|
||||
request = ledger.getTransactionsRequest(ledger.transactionFilter(Seq(party)))
|
||||
fromOffset = request.update(_.begin := offset)
|
||||
transactions <- ledger.flatTransactions(fromOffset)
|
||||
} yield {
|
||||
@ -253,9 +253,9 @@ class ActiveContractsServiceIT extends LedgerTestSuite {
|
||||
allContractsForAlice <- ledger.activeContracts(alice)
|
||||
allContractsForBob <- ledger.activeContracts(bob)
|
||||
allContractsForAliceAndBob <- ledger.activeContracts(alice, bob)
|
||||
dummyContractsForAlice <- ledger.activeContractsByTemplateId(Seq(Dummy.id.unwrap), alice)
|
||||
dummyContractsForAlice <- ledger.activeContractsByTemplateId(Seq(Dummy.id), alice)
|
||||
dummyContractsForAliceAndBob <- ledger.activeContractsByTemplateId(
|
||||
Seq(Dummy.id.unwrap),
|
||||
Seq(Dummy.id),
|
||||
alice,
|
||||
bob,
|
||||
)
|
||||
|
@ -467,7 +467,7 @@ final class ContractKeysIT extends LedgerTestSuite {
|
||||
_ <- synchronize(ledger1, ledger2)
|
||||
} yield ()
|
||||
})
|
||||
import scalaz.syntax.tag._
|
||||
|
||||
test(
|
||||
"CKDisclosedContractKeyReusabilityAsSubmitter",
|
||||
"Subsequent disclosed contracts can use the same contract key (disclosure because of submitting)",
|
||||
@ -488,7 +488,7 @@ final class ContractKeysIT extends LedgerTestSuite {
|
||||
|
||||
_ <- synchronize(ledger1, ledger2)
|
||||
|
||||
Seq(withKey1Event) <- ledger1.activeContractsByTemplateId(List(WithKey.id.unwrap), party1)
|
||||
Seq(withKey1Event) <- ledger1.activeContractsByTemplateId(List(WithKey.id), party1)
|
||||
withKey1 = ContractId.apply[WithKey](withKey1Event.contractId)
|
||||
// Archive the disclosed contract
|
||||
_ <- ledger1.exercise(party1, withKey1.exerciseArchive())
|
||||
|
@ -110,14 +110,14 @@ class ParticipantPruningIT extends LedgerTestSuite {
|
||||
|
||||
transactionsAfterPrune <- participant.transactionTrees(
|
||||
participant
|
||||
.getTransactionsRequest(parties = Seq(submitter))
|
||||
.getTransactionsRequest(participant.transactionFilter(parties = Seq(submitter)))
|
||||
.update(_.begin := offsetToPruneUpTo)
|
||||
)
|
||||
|
||||
cannotReadAnymore <- participant
|
||||
.transactionTrees(
|
||||
participant
|
||||
.getTransactionsRequest(parties = Seq(submitter))
|
||||
.getTransactionsRequest(participant.transactionFilter(parties = Seq(submitter)))
|
||||
.update(_.begin := offsetOfSecondToLastPrunedTransaction)
|
||||
)
|
||||
.mustFail("attempting to read transactions before the pruning cut-off")
|
||||
@ -156,14 +156,14 @@ class ParticipantPruningIT extends LedgerTestSuite {
|
||||
|
||||
txAfterPrune <- participant.flatTransactions(
|
||||
participant
|
||||
.getTransactionsRequest(parties = Seq(submitter))
|
||||
.getTransactionsRequest(participant.transactionFilter(parties = Seq(submitter)))
|
||||
.update(_.begin := offsetToPruneUpTo)
|
||||
)
|
||||
|
||||
cannotReadAnymore <- participant
|
||||
.flatTransactions(
|
||||
participant
|
||||
.getTransactionsRequest(parties = Seq(submitter))
|
||||
.getTransactionsRequest(participant.transactionFilter(parties = Seq(submitter)))
|
||||
.update(_.begin := offsetOfSecondToLastPrunedTransaction)
|
||||
)
|
||||
.mustFail("attempting to read transactions before the pruning cut-off")
|
||||
@ -452,7 +452,7 @@ class ParticipantPruningIT extends LedgerTestSuite {
|
||||
|
||||
transactionsAfterPrune <- participant.transactionTrees(
|
||||
participant
|
||||
.getTransactionsRequest(parties = Seq(submitter))
|
||||
.getTransactionsRequest(participant.transactionFilter(parties = Seq(submitter)))
|
||||
.update(_.begin := offsetToPruneUpTo)
|
||||
)
|
||||
|
||||
@ -462,7 +462,7 @@ class ParticipantPruningIT extends LedgerTestSuite {
|
||||
|
||||
transactionsAfterRedundantPrune <- participant.transactionTrees(
|
||||
participant
|
||||
.getTransactionsRequest(parties = Seq(submitter))
|
||||
.getTransactionsRequest(participant.transactionFilter(parties = Seq(submitter)))
|
||||
.update(_.begin := offsetToPruneUpTo)
|
||||
)
|
||||
|
||||
@ -476,7 +476,7 @@ class ParticipantPruningIT extends LedgerTestSuite {
|
||||
|
||||
transactionsAfterSecondPrune <- participant.transactionTrees(
|
||||
participant
|
||||
.getTransactionsRequest(parties = Seq(submitter))
|
||||
.getTransactionsRequest(participant.transactionFilter(parties = Seq(submitter)))
|
||||
.update(_.begin := offsetToPruneUpToInSecondRealPrune)
|
||||
)
|
||||
|
||||
@ -542,11 +542,17 @@ class ParticipantPruningIT extends LedgerTestSuite {
|
||||
_ <- participant.prune(offsetToPruneUpTo)
|
||||
|
||||
emptyRangeAtBegin = participant
|
||||
.getTransactionsRequest(parties = Seq(submitter), begin = participant.begin)
|
||||
.getTransactionsRequest(
|
||||
participant.transactionFilter(parties = Seq(submitter)),
|
||||
begin = participant.begin,
|
||||
)
|
||||
.update(_.end := participant.begin)
|
||||
|
||||
emptyRangeInPrunedSpace = participant
|
||||
.getTransactionsRequest(parties = Seq(submitter), begin = offsetInPrunedRange)
|
||||
.getTransactionsRequest(
|
||||
participant.transactionFilter(parties = Seq(submitter)),
|
||||
begin = offsetInPrunedRange,
|
||||
)
|
||||
.update(_.end := offsetInPrunedRange)
|
||||
|
||||
emptyBeginTreesWillFail <- participant.transactionTrees(emptyRangeAtBegin)
|
||||
@ -802,7 +808,10 @@ class ParticipantPruningIT extends LedgerTestSuite {
|
||||
} yield ()
|
||||
})
|
||||
trees <- participant.transactionTrees(
|
||||
participant.getTransactionsRequest(parties = Seq(submitter), begin = endOffsetAtTestStart)
|
||||
participant.getTransactionsRequest(
|
||||
participant.transactionFilter(parties = Seq(submitter)),
|
||||
begin = endOffsetAtTestStart,
|
||||
)
|
||||
)
|
||||
} yield trees
|
||||
|
||||
|
@ -29,13 +29,13 @@ class TransactionServiceCorrectnessIT extends LedgerTestSuite {
|
||||
_ <- Future.sequence(Vector.fill(transactionsToSubmit)(ledger.create(party, Dummy(party))))
|
||||
endAfterFirstSection <- ledger.currentEnd()
|
||||
firstSectionRequest = ledger
|
||||
.getTransactionsRequest(Seq(party))
|
||||
.getTransactionsRequest(ledger.transactionFilter(Seq(party)))
|
||||
.update(_.end := endAfterFirstSection)
|
||||
firstSection <- ledger.flatTransactions(firstSectionRequest)
|
||||
_ <- Future.sequence(Vector.fill(transactionsToSubmit)(ledger.create(party, Dummy(party))))
|
||||
endAfterSecondSection <- ledger.currentEnd()
|
||||
secondSectionRequest = ledger
|
||||
.getTransactionsRequest(Seq(party))
|
||||
.getTransactionsRequest(ledger.transactionFilter(Seq(party)))
|
||||
.update(_.begin := endAfterFirstSection, _.end := endAfterSecondSection)
|
||||
secondSection <- ledger.flatTransactions(secondSectionRequest)
|
||||
fullSequence <- ledger.flatTransactions(party)
|
||||
|
@ -69,7 +69,7 @@ class TransactionServiceOutputsIT extends LedgerTestSuite {
|
||||
)(implicit ec => { case Participants(Participant(ledger, party)) =>
|
||||
for {
|
||||
_ <- ledger.create(party, Dummy(party))
|
||||
request = ledger.getTransactionsRequest(Seq(party))
|
||||
request = ledger.getTransactionsRequest(ledger.transactionFilter(Seq(party)))
|
||||
verboseTransactions <- ledger.flatTransactions(request.update(_.verbose := true))
|
||||
verboseTransactionTrees <- ledger.transactionTrees(request.update(_.verbose := true))
|
||||
nonVerboseTransactions <- ledger.flatTransactions(request.update(_.verbose := false))
|
||||
|
@ -20,7 +20,7 @@ class TransactionServiceStreamsIT extends LedgerTestSuite {
|
||||
"An empty stream should be served when getting transactions from and to the beginning of the ledger",
|
||||
allocate(SingleParty),
|
||||
)(implicit ec => { case Participants(Participant(ledger, party)) =>
|
||||
val request = ledger.getTransactionsRequest(Seq(party))
|
||||
val request = ledger.getTransactionsRequest(ledger.transactionFilter(Seq(party)))
|
||||
val fromAndToBegin = request.update(_.begin := ledger.begin, _.end := ledger.begin)
|
||||
for {
|
||||
transactions <- ledger.flatTransactions(fromAndToBegin)
|
||||
@ -37,7 +37,7 @@ class TransactionServiceStreamsIT extends LedgerTestSuite {
|
||||
"An empty stream of trees should be served when getting transactions from and to the beginning of the ledger",
|
||||
allocate(SingleParty),
|
||||
)(implicit ec => { case Participants(Participant(ledger, party)) =>
|
||||
val request = ledger.getTransactionsRequest(Seq(party))
|
||||
val request = ledger.getTransactionsRequest(ledger.transactionFilter(Seq(party)))
|
||||
val fromAndToBegin = request.update(_.begin := ledger.begin, _.end := ledger.begin)
|
||||
for {
|
||||
transactions <- ledger.transactionTrees(fromAndToBegin)
|
||||
@ -56,7 +56,7 @@ class TransactionServiceStreamsIT extends LedgerTestSuite {
|
||||
)(implicit ec => { case Participants(Participant(ledger, party)) =>
|
||||
for {
|
||||
_ <- ledger.create(party, Dummy(party))
|
||||
request = ledger.getTransactionsRequest(Seq(party))
|
||||
request = ledger.getTransactionsRequest(ledger.transactionFilter(Seq(party)))
|
||||
endToEnd = request.update(_.begin := ledger.end, _.end := ledger.end)
|
||||
transactions <- ledger.flatTransactions(endToEnd)
|
||||
} yield {
|
||||
@ -75,7 +75,7 @@ class TransactionServiceStreamsIT extends LedgerTestSuite {
|
||||
for {
|
||||
_ <- ledger.create(party, Dummy(party))
|
||||
futureOffset <- ledger.offsetBeyondLedgerEnd()
|
||||
request = ledger.getTransactionsRequest(Seq(party))
|
||||
request = ledger.getTransactionsRequest(ledger.transactionFilter(Seq(party)))
|
||||
beyondEnd = request.update(_.begin := futureOffset, _.optionalEnd := None)
|
||||
failure <- ledger.flatTransactions(beyondEnd).mustFail("subscribing past the ledger end")
|
||||
} yield {
|
||||
@ -95,7 +95,7 @@ class TransactionServiceStreamsIT extends LedgerTestSuite {
|
||||
for {
|
||||
_ <- ledger.create(party, Dummy(party))
|
||||
futureOffset <- ledger.offsetBeyondLedgerEnd()
|
||||
request = ledger.getTransactionsRequest(Seq(party))
|
||||
request = ledger.getTransactionsRequest(ledger.transactionFilter(Seq(party)))
|
||||
beyondEnd = request.update(_.begin := futureOffset, _.optionalEnd := None)
|
||||
failure <- ledger.transactionTrees(beyondEnd).mustFail("subscribing past the ledger end")
|
||||
} yield {
|
||||
|
@ -17,7 +17,7 @@ class TransactionServiceValidationIT extends LedgerTestSuite {
|
||||
"A query with an empty transaction filter should be rejected with an INVALID_ARGUMENT status",
|
||||
allocate(SingleParty),
|
||||
)(implicit ec => { case Participants(Participant(ledger, party)) =>
|
||||
val request = ledger.getTransactionsRequest(Seq(party))
|
||||
val request = ledger.getTransactionsRequest(ledger.transactionFilter(Seq(party)))
|
||||
val requestWithEmptyFilter = request.update(_.filter.filtersByParty := Map.empty)
|
||||
for {
|
||||
failure <- ledger
|
||||
@ -41,7 +41,7 @@ class TransactionServiceValidationIT extends LedgerTestSuite {
|
||||
earlier <- ledger.currentEnd()
|
||||
_ <- ledger.create(party, Dummy(party))
|
||||
later <- ledger.currentEnd()
|
||||
request = ledger.getTransactionsRequest(Seq(party))
|
||||
request = ledger.getTransactionsRequest(ledger.transactionFilter(Seq(party)))
|
||||
invalidRequest = request.update(_.begin := later, _.end := earlier)
|
||||
failure <- ledger
|
||||
.flatTransactions(invalidRequest)
|
||||
@ -62,7 +62,7 @@ class TransactionServiceValidationIT extends LedgerTestSuite {
|
||||
)(implicit ec => { case Participants(Participant(ledger, party)) =>
|
||||
val invalidLedgerId = "DEFINITELY_NOT_A_VALID_LEDGER_IDENTIFIER"
|
||||
val invalidRequest = ledger
|
||||
.getTransactionsRequest(Seq(party))
|
||||
.getTransactionsRequest(ledger.transactionFilter(Seq(party)))
|
||||
.update(_.ledgerId := invalidLedgerId)
|
||||
for {
|
||||
failure <- ledger
|
||||
@ -84,7 +84,7 @@ class TransactionServiceValidationIT extends LedgerTestSuite {
|
||||
)(implicit ec => { case Participants(Participant(ledger, party)) =>
|
||||
val invalidLedgerId = "DEFINITELY_NOT_A_VALID_LEDGER_IDENTIFIER"
|
||||
val invalidRequest = ledger
|
||||
.getTransactionsRequest(Seq(party))
|
||||
.getTransactionsRequest(ledger.transactionFilter(Seq(party)))
|
||||
.update(_.ledgerId := invalidLedgerId)
|
||||
for {
|
||||
failure <- ledger
|
||||
|
@ -8,7 +8,10 @@ import com.daml.ledger.api.tls.TlsConfiguration
|
||||
|
||||
package object v1_dev {
|
||||
def default(timeoutScaleFactor: Double): Vector[LedgerTestSuite] =
|
||||
v1_14.default(timeoutScaleFactor) :+ new InterfaceIT
|
||||
v1_14.default(timeoutScaleFactor) ++ Vector(
|
||||
new InterfaceIT,
|
||||
new InterfaceSubscriptionsIT,
|
||||
)
|
||||
|
||||
def optional(tlsConfig: Option[TlsConfiguration]): Vector[LedgerTestSuite] =
|
||||
v1_14.optional(tlsConfig)
|
||||
|
@ -0,0 +1,584 @@
|
||||
// Copyright (c) 2022 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package com.daml.ledger.api.testtool.suites.v1_dev
|
||||
|
||||
import com.daml.error.definitions.LedgerApiErrors
|
||||
import com.daml.ledger.api.refinements.ApiTypes.TemplateId
|
||||
import com.daml.ledger.api.testtool.infrastructure.Allocation.{
|
||||
Participant,
|
||||
Participants,
|
||||
Parties,
|
||||
SingleParty,
|
||||
allocate,
|
||||
}
|
||||
import com.daml.ledger.api.testtool.infrastructure.Assertions._
|
||||
import com.daml.ledger.api.testtool.infrastructure.LedgerTestSuite
|
||||
import com.daml.ledger.api.testtool.infrastructure.TransactionHelpers._
|
||||
import com.daml.ledger.api.v1.event.Event.Event
|
||||
import com.daml.ledger.api.v1.event.{CreatedEvent, InterfaceView}
|
||||
import com.daml.ledger.api.v1.transaction.Transaction
|
||||
import com.daml.ledger.api.v1.transaction_filter.TransactionFilter
|
||||
import com.daml.ledger.api.v1.value.{Identifier, Record}
|
||||
import com.daml.ledger.test.semantic.InterfaceViews._
|
||||
import scalaz.Tag
|
||||
|
||||
class InterfaceSubscriptionsIT extends LedgerTestSuite {
|
||||
|
||||
test(
|
||||
"ISTransactionsBasic",
|
||||
"Basic functionality for interface subscriptions on transaction streams",
|
||||
allocate(SingleParty),
|
||||
)(implicit ec => { case Participants(Participant(ledger, party)) =>
|
||||
import ledger._
|
||||
for {
|
||||
c1 <- create(party, T1(party, 1))
|
||||
c2 <- create(party, T2(party, 2))
|
||||
c3 <- create(party, T3(party, 3))
|
||||
_ <- create(party, T4(party, 4))
|
||||
transactions <- flatTransactions(
|
||||
getTransactionsRequest(
|
||||
transactionFilter(
|
||||
Seq(party),
|
||||
Seq(T1.id),
|
||||
Seq((I.id, true)),
|
||||
)
|
||||
)
|
||||
)
|
||||
events = transactions.flatMap(createdEvents)
|
||||
} yield basicAssertions(c1.toString, c2.toString, c3.toString, events)
|
||||
})
|
||||
|
||||
test(
|
||||
"ISAcsBasic",
|
||||
"Basic functionality for interface subscriptions on ACS streams",
|
||||
allocate(SingleParty),
|
||||
)(implicit ec => { case Participants(Participant(ledger, party)) =>
|
||||
import ledger._
|
||||
for {
|
||||
c1 <- create(party, T1(party, 1))
|
||||
c2 <- create(party, T2(party, 2))
|
||||
c3 <- create(party, T3(party, 3))
|
||||
_ <- create(party, T4(party, 4))
|
||||
(_, createdEvents) <- activeContracts(
|
||||
activeContractsRequest(Seq(party), Seq(T1.id), Seq((I.id, true)))
|
||||
)
|
||||
} yield basicAssertions(c1.toString, c2.toString, c3.toString, createdEvents)
|
||||
})
|
||||
|
||||
private def basicAssertions(
|
||||
c1: String,
|
||||
c2: String,
|
||||
c3: String,
|
||||
createdEvents: Vector[CreatedEvent],
|
||||
): Unit = {
|
||||
assertLength("3 transactions found", 3, createdEvents)
|
||||
|
||||
// T1
|
||||
val createdEvent1 = createdEvents(0)
|
||||
assertLength("Create event 1 has a view", 1, createdEvent1.interfaceViews)
|
||||
assertEquals(
|
||||
"Create event 1 template ID",
|
||||
createdEvent1.templateId.get.toString,
|
||||
Tag.unwrap(T1.id).toString,
|
||||
)
|
||||
assertEquals("Create event 1 contract ID", createdEvent1.contractId, c1)
|
||||
assertViewEquals(createdEvent1.interfaceViews, Tag.unwrap(I.id)) { value =>
|
||||
assertLength("View1 has 2 fields", 2, value.fields)
|
||||
assertEquals("View1.a", value.fields(0).getValue.getInt64, 1)
|
||||
assertEquals("View1.b", value.fields(1).getValue.getBool, true)
|
||||
assert(
|
||||
value.fields.forall(_.label.nonEmpty),
|
||||
"Expected a view with labels (verbose)",
|
||||
)
|
||||
}
|
||||
assertEquals(
|
||||
"Create event 1 createArguments must NOT be empty",
|
||||
createdEvent1.createArguments.isEmpty,
|
||||
false,
|
||||
)
|
||||
assert(
|
||||
createdEvent1.getCreateArguments.fields.forall(_.label.nonEmpty),
|
||||
"Expected a contract with labels (verbose)",
|
||||
)
|
||||
assertEquals(
|
||||
"Create event 1 should have a contract key defined",
|
||||
createdEvent1.contractKey.isDefined,
|
||||
true,
|
||||
)
|
||||
|
||||
// T2
|
||||
val createdEvent2 = createdEvents(1)
|
||||
assertLength("Create event 2 has a view", 1, createdEvent2.interfaceViews)
|
||||
assertEquals(
|
||||
"Create event 2 template ID",
|
||||
createdEvent2.templateId.get.toString,
|
||||
Tag.unwrap(T2.id).toString,
|
||||
)
|
||||
assertEquals("Create event 2 contract ID", createdEvent2.contractId, c2)
|
||||
assertViewEquals(createdEvent2.interfaceViews, Tag.unwrap(I.id)) { value =>
|
||||
assertLength("View2 has 2 fields", 2, value.fields)
|
||||
assertEquals("View2.a", value.fields(0).getValue.getInt64, 2)
|
||||
assertEquals("View2.b", value.fields(1).getValue.getBool, false)
|
||||
}
|
||||
assertEquals(
|
||||
"Create event 2 createArguments must be empty",
|
||||
createdEvent2.createArguments.isEmpty,
|
||||
true,
|
||||
)
|
||||
assertEquals(
|
||||
"Create event 2 should have a contract key empty, as no match by template_id",
|
||||
createdEvent2.contractKey.isEmpty,
|
||||
true,
|
||||
)
|
||||
|
||||
// T3
|
||||
val createdEvent3 = createdEvents(2)
|
||||
assertLength("Create event 3 has a view", 1, createdEvent3.interfaceViews)
|
||||
assertEquals(
|
||||
"Create event 3 template ID",
|
||||
createdEvent3.templateId.get.toString,
|
||||
Tag.unwrap(T3.id).toString,
|
||||
)
|
||||
assertEquals("Create event 3 contract ID", createdEvent3.contractId, c3)
|
||||
assertViewFailed(createdEvent3.interfaceViews, Tag.unwrap(I.id))
|
||||
assertEquals(
|
||||
"Create event 3 createArguments must be empty",
|
||||
createdEvent3.createArguments.isEmpty,
|
||||
true,
|
||||
)
|
||||
}
|
||||
|
||||
test(
|
||||
"ISMultipleWitness",
|
||||
"Multiple witness",
|
||||
allocate(Parties(2)),
|
||||
)(implicit ec => { case Participants(Participant(ledger, party1, party2)) =>
|
||||
import ledger._
|
||||
for {
|
||||
c <- create(party1, T6(party1, party2))
|
||||
mergedTransactions <- flatTransactions(
|
||||
getTransactionsRequest(
|
||||
new TransactionFilter(
|
||||
Map(
|
||||
party1.toString -> filters(Seq.empty, Seq((I.id, true))),
|
||||
party2.toString -> filters(Seq.empty, Seq((I2.id, true))),
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
party1Transactions <- flatTransactions(
|
||||
getTransactionsRequest(
|
||||
new TransactionFilter(
|
||||
Map(
|
||||
party1.toString -> filters(Seq.empty, Seq((I.id, true)))
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
} yield {
|
||||
assertLength("single transaction found", 1, mergedTransactions)
|
||||
val createdEvent1 = createdEvents(mergedTransactions(0)).head
|
||||
assertEquals("Create event 1 contract ID", createdEvent1.contractId, c.toString)
|
||||
assertViewEquals(createdEvent1.interfaceViews, Tag.unwrap(I.id)) { value =>
|
||||
assertLength("View1 has 2 fields", 2, value.fields)
|
||||
assertEquals("View1.a", value.fields(0).getValue.getInt64, 6)
|
||||
assertEquals("View1.b", value.fields(1).getValue.getBool, true)
|
||||
}
|
||||
assertViewEquals(createdEvent1.interfaceViews, Tag.unwrap(I2.id)) { value =>
|
||||
assertLength("View2 has 1 field", 1, value.fields)
|
||||
assertEquals("View2.c", value.fields(0).getValue.getInt64, 7)
|
||||
}
|
||||
|
||||
assertLength("single transaction found", 1, party1Transactions)
|
||||
val createdEvent2 = createdEvents(party1Transactions(0)).head
|
||||
assertEquals("Create event 1 contract ID", createdEvent2.contractId, c.toString)
|
||||
assertLength("single view found", 1, createdEvent2.interfaceViews)
|
||||
assertViewEquals(createdEvent2.interfaceViews, Tag.unwrap(I.id)) { value =>
|
||||
assertLength("View1 has 2 fields", 2, value.fields)
|
||||
assertEquals("View1.a", value.fields(0).getValue.getInt64, 6)
|
||||
assertEquals("View1.b", value.fields(1).getValue.getBool, true)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
test(
|
||||
"ISMultipleViews",
|
||||
"Multiple interface views populated for one event",
|
||||
allocate(SingleParty),
|
||||
)(implicit ec => { case Participants(Participant(ledger, party)) =>
|
||||
import ledger._
|
||||
for {
|
||||
c <- create(party, T5(party, 31337))
|
||||
transactions <- flatTransactions(
|
||||
getTransactionsRequest(
|
||||
transactionFilter(
|
||||
Seq(party),
|
||||
Seq.empty,
|
||||
Seq((I.id, true), (I2.id, true)),
|
||||
)
|
||||
)
|
||||
)
|
||||
} yield {
|
||||
assertLength("single transaction found", 1, transactions)
|
||||
val createdEvent = createdEvents(transactions(0)).head
|
||||
assertEquals("Create event 1 contract ID", createdEvent.contractId, c.toString)
|
||||
assertViewEquals(createdEvent.interfaceViews, Tag.unwrap(I.id)) { value =>
|
||||
assertLength("View1 has 2 fields", 2, value.fields)
|
||||
assertEquals("View1.a", value.fields(0).getValue.getInt64, 31337)
|
||||
assertEquals("View1.b", value.fields(1).getValue.getBool, true)
|
||||
}
|
||||
assertViewEquals(createdEvent.interfaceViews, Tag.unwrap(I2.id)) { value =>
|
||||
assertLength("View2 has 1 field", 1, value.fields)
|
||||
assertEquals("View2.c", value.fields(0).getValue.getInt64, 1)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
test(
|
||||
"ISTransactionsIrrelevantTransactions",
|
||||
"Subscribing on transaction stream by interface with no relevant transactions",
|
||||
allocate(SingleParty),
|
||||
)(implicit ec => { case Participants(Participant(ledger, party)) =>
|
||||
import ledger._
|
||||
for {
|
||||
_ <- create(party, T4(party, 4))
|
||||
transactions <- flatTransactions(
|
||||
getTransactionsRequest(
|
||||
transactionFilter(Seq(party), Seq.empty, Seq((INoTemplate.id, true)))
|
||||
)
|
||||
)
|
||||
} yield {
|
||||
assertLength("0 transactions should be found", 0, transactions)
|
||||
()
|
||||
}
|
||||
})
|
||||
|
||||
test(
|
||||
"ISTransactionsDuplicateInterfaceFilters",
|
||||
"Subscribing on transaction stream by interface with duplicate filters and not verbose",
|
||||
allocate(SingleParty),
|
||||
)(implicit ec => { case Participants(Participant(ledger, party)) =>
|
||||
import ledger._
|
||||
for {
|
||||
c1 <- create(party, T1(party, 1))
|
||||
c2 <- create(party, T2(party, 2))
|
||||
c3 <- create(party, T3(party, 3))
|
||||
_ <- create(party, T4(party, 4))
|
||||
transactions <- flatTransactions(
|
||||
getTransactionsRequest(
|
||||
transactionFilter(
|
||||
Seq(party),
|
||||
Seq(T1.id),
|
||||
Seq((I.id, false), (I.id, true)),
|
||||
)
|
||||
)
|
||||
.update(_.verbose := false)
|
||||
)
|
||||
} yield {
|
||||
val createdEvent1 = createdEvents(transactions(0)).head
|
||||
assertEquals("Create event 1 contract ID", createdEvent1.contractId, c1.toString)
|
||||
val createdEvent2 = createdEvents(transactions(1)).head
|
||||
assertEquals("Create event 2 contract ID", createdEvent2.contractId, c2.toString)
|
||||
// Expect view to be delivered even though there is an ambiguous
|
||||
// includeInterfaceView flag set to true and false at the same time (true wins)
|
||||
assertViewEquals(createdEvent2.interfaceViews, Tag.unwrap(I.id)) { value =>
|
||||
assertLength("View2 has 2 fields", 2, value.fields)
|
||||
assertEquals("View2.a", value.fields(0).getValue.getInt64, 2)
|
||||
assertEquals("View2.b", value.fields(1).getValue.getBool, false)
|
||||
assert(
|
||||
value.fields.forall(_.label.isEmpty),
|
||||
s"Expected a view with no labels (verbose = false)",
|
||||
)
|
||||
}
|
||||
val createdEvent3 = createdEvents(transactions(2)).head
|
||||
assertEquals("Create event 3 contract ID", createdEvent3.contractId, c3.toString)
|
||||
}
|
||||
})
|
||||
|
||||
test(
|
||||
"ISTransactionsDuplicateTemplateFilters",
|
||||
"Subscribing on transaction stream by template with duplicate filters",
|
||||
allocate(SingleParty),
|
||||
)(implicit ec => { case Participants(Participant(ledger, party)) =>
|
||||
import ledger._
|
||||
for {
|
||||
c1 <- create(party, T1(party, 1))
|
||||
c2 <- create(party, T2(party, 2))
|
||||
c3 <- create(party, T3(party, 3))
|
||||
_ <- create(party, T4(party, 4))
|
||||
transactions <- flatTransactions(
|
||||
getTransactionsRequest(
|
||||
transactionFilter(
|
||||
Seq(party),
|
||||
Seq(T1.id, T1.id),
|
||||
Seq((I.id, true)),
|
||||
)
|
||||
)
|
||||
)
|
||||
} yield {
|
||||
val createdEvent1 = createdEvents(transactions(0)).head
|
||||
assertEquals("Create event 1 contract ID", createdEvent1.contractId, c1.toString)
|
||||
assertEquals(
|
||||
"Create event 1 createArguments must NOT be empty",
|
||||
createdEvent1.createArguments.isEmpty,
|
||||
false,
|
||||
)
|
||||
val createdEvent2 = createdEvents(transactions(1)).head
|
||||
assertEquals("Create event 2 contract ID", createdEvent2.contractId, c2.toString)
|
||||
// Expect view to be delivered even though there is an ambiguous
|
||||
// includeInterfaceView flag set to true and false at the same time.
|
||||
assertViewEquals(createdEvent2.interfaceViews, Tag.unwrap(I.id)) { value =>
|
||||
assertLength("View2 has 2 fields", 2, value.fields)
|
||||
assertEquals("View2.a", value.fields(0).getValue.getInt64, 2)
|
||||
assertEquals("View2.b", value.fields(1).getValue.getBool, false)
|
||||
}
|
||||
val createdEvent3 = createdEvents(transactions(2)).head
|
||||
assertEquals("Create event 3 contract ID", createdEvent3.contractId, c3.toString)
|
||||
}
|
||||
})
|
||||
|
||||
test(
|
||||
"ISTransactionsNoIncludedView",
|
||||
"Subscribing on transaction stream by interface or template without included views",
|
||||
allocate(SingleParty),
|
||||
)(implicit ec => { case Participants(Participant(ledger, party)) =>
|
||||
import ledger._
|
||||
for {
|
||||
c1 <- create(party, T1(party, 1))
|
||||
_ <- create(party, T2(party, 2))
|
||||
_ <- create(party, T3(party, 3))
|
||||
_ <- create(party, T4(party, 4))
|
||||
transactions <- flatTransactions(
|
||||
getTransactionsRequest(
|
||||
transactionFilter(
|
||||
Seq(party),
|
||||
Seq(T1.id),
|
||||
Seq((I.id, false)),
|
||||
)
|
||||
)
|
||||
)
|
||||
} yield {
|
||||
assertLength("3 transactions found", 3, transactions)
|
||||
val interfaceViewCount: Int =
|
||||
transactions.flatMap(createdEvents).map(_.interfaceViews.size).sum
|
||||
assertEquals("No views have been computed and produced", 0, interfaceViewCount)
|
||||
val createArgumentsCount: Int =
|
||||
transactions.flatMap(createdEvents).map(_.createArguments.isDefined).count(_ == true)
|
||||
assertEquals("Only single create arguments must be delivered", 1, createArgumentsCount)
|
||||
|
||||
// T1
|
||||
val createdEvent1 = createdEvents(transactions(0)).head
|
||||
assertEquals(
|
||||
"Create event 1 template ID",
|
||||
createdEvent1.templateId.get.toString,
|
||||
Tag.unwrap(T1.id).toString,
|
||||
)
|
||||
assertEquals("Create event 1 contract ID", createdEvent1.contractId, c1.toString)
|
||||
assertEquals(
|
||||
"Create event 1 createArguments must NOT be empty",
|
||||
createdEvent1.createArguments.isEmpty,
|
||||
false,
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
test(
|
||||
"ISTransactionsEquivalentFilters",
|
||||
"Subscribing by interface or all implementing templates gives the same result",
|
||||
allocate(SingleParty),
|
||||
)(implicit ec => { case Participants(Participant(ledger, party)) =>
|
||||
import ledger._
|
||||
val allImplementations = Seq(T1.id, T2.id, T3.id)
|
||||
for {
|
||||
_ <- create(party, T1(party, 1))
|
||||
_ <- create(party, T2(party, 2))
|
||||
_ <- create(party, T3(party, 3))
|
||||
_ <- create(party, T4(party, 4))
|
||||
// 1. Subscribe by the interface
|
||||
transactions1 <- flatTransactions(
|
||||
getTransactionsRequest(
|
||||
transactionFilter(Seq(party), Seq.empty, Seq((I.id, true)))
|
||||
)
|
||||
)
|
||||
// 2. Subscribe by all implementing templates
|
||||
transactions2 <- flatTransactions(
|
||||
getTransactionsRequest(
|
||||
transactionFilter(Seq(party), allImplementations, Seq.empty)
|
||||
)
|
||||
)
|
||||
// 3. Subscribe by both the interface and all templates (redundant filters)
|
||||
transactions3 <- flatTransactions(
|
||||
getTransactionsRequest(
|
||||
transactionFilter(
|
||||
Seq(party),
|
||||
allImplementations,
|
||||
Seq((I.id, true)),
|
||||
)
|
||||
)
|
||||
)
|
||||
} yield {
|
||||
assertLength("3 transactions found", 3, transactions1)
|
||||
assertEquals(
|
||||
"1 and 2 find the same transactions (but not the same views)",
|
||||
transactions1.map(_.transactionId),
|
||||
transactions2.map(_.transactionId),
|
||||
)
|
||||
assertEquals(
|
||||
"2 and 3 find the same contract_arguments (but not the same views)",
|
||||
transactions2.map(updateTransaction()),
|
||||
transactions3.map(updateTransaction(emptyView = true)),
|
||||
)
|
||||
assertEquals(
|
||||
"1 and 3 produce the same views (but not the same create arguments)",
|
||||
transactions1.map(updateTransaction()),
|
||||
transactions3.map(updateTransaction(emptyContractKey = true, emptyCreateArguments = true)),
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
test(
|
||||
"ISTransactionsUnknownTemplateOrInterface",
|
||||
"Subscribing on transaction stream by an unknown template fails",
|
||||
allocate(SingleParty),
|
||||
)(implicit ec => { case Participants(Participant(ledger, party)) =>
|
||||
val unknownTemplate = TemplateId(Tag.unwrap(I.id).copy(entityName = "IDoesNotExist"))
|
||||
val unknownInterface = TemplateId(Tag.unwrap(I.id).copy(entityName = "IDoesNotExist"))
|
||||
import ledger._
|
||||
for {
|
||||
_ <- create(party, T1(party, 1))
|
||||
failure <- flatTransactions(
|
||||
getTransactionsRequest(
|
||||
transactionFilter(Seq(party), Seq(unknownTemplate), Seq.empty)
|
||||
)
|
||||
)
|
||||
.mustFail("subscribing with an unknown template")
|
||||
failure2 <- flatTransactions(
|
||||
getTransactionsRequest(
|
||||
transactionFilter(Seq(party), Seq.empty, Seq((unknownInterface, true)))
|
||||
)
|
||||
)
|
||||
.mustFail("subscribing with an unknown interface")
|
||||
} yield {
|
||||
assertGrpcError(
|
||||
failure,
|
||||
LedgerApiErrors.RequestValidation.InvalidArgument,
|
||||
Some(s"Templates do not exist"),
|
||||
)
|
||||
assertGrpcError(
|
||||
failure2,
|
||||
LedgerApiErrors.RequestValidation.InvalidArgument,
|
||||
Some(s"Interfaces do not exist"),
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
test(
|
||||
"ISTransactionsMultipleParties",
|
||||
"Subscribing on transaction stream by multiple parties",
|
||||
allocate(Parties(2)),
|
||||
)(implicit ec => { case Participants(Participant(ledger, party1, party2)) =>
|
||||
import ledger._
|
||||
for {
|
||||
_ <- create(party1, T6(party1, party2))
|
||||
party1Iface1transactionsWithView <- flatTransactions(
|
||||
getTransactionsRequest(transactionFilter(Seq(party1), Seq.empty, Seq((I.id, true))))
|
||||
)
|
||||
party1Iface1transactionsWithoutView <- flatTransactions(
|
||||
getTransactionsRequest(transactionFilter(Seq(party1), Seq.empty, Seq((I.id, false))))
|
||||
)
|
||||
party2Iface1transactionsWithView <- flatTransactions(
|
||||
getTransactionsRequest(transactionFilter(Seq(party2), Seq.empty, Seq((I.id, true))))
|
||||
)
|
||||
party2Iface1transactionsWithoutView <- flatTransactions(
|
||||
getTransactionsRequest(transactionFilter(Seq(party2), Seq.empty, Seq((I.id, false))))
|
||||
)
|
||||
} yield {
|
||||
assertEquals(
|
||||
party1Iface1transactionsWithView.map(
|
||||
updateTransaction(emptyView = false, emptyWitness = true)
|
||||
),
|
||||
party2Iface1transactionsWithView.map(
|
||||
updateTransaction(emptyView = false, emptyWitness = true)
|
||||
),
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
party1Iface1transactionsWithoutView.map(
|
||||
updateTransaction(emptyView = false, emptyWitness = true)
|
||||
),
|
||||
party2Iface1transactionsWithoutView.map(
|
||||
updateTransaction(emptyView = false, emptyWitness = true)
|
||||
),
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
party1Iface1transactionsWithView.map(
|
||||
updateTransaction(emptyView = true, emptyWitness = true)
|
||||
),
|
||||
party2Iface1transactionsWithoutView.map(
|
||||
updateTransaction(emptyView = false, emptyWitness = true)
|
||||
),
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
private def updateTransaction(
|
||||
emptyView: Boolean = false,
|
||||
emptyWitness: Boolean = false,
|
||||
emptyCreateArguments: Boolean = false,
|
||||
emptyContractKey: Boolean = false,
|
||||
)(tx: com.daml.ledger.api.v1.transaction.Transaction): Transaction = {
|
||||
tx.copy(
|
||||
events = tx.events.map { event =>
|
||||
event.copy(event = event.event match {
|
||||
case created: Event.Created =>
|
||||
created.copy(value =
|
||||
created.value.copy(
|
||||
witnessParties = if (emptyWitness) Seq.empty else created.value.witnessParties,
|
||||
interfaceViews = if (emptyView) Seq.empty else created.value.interfaceViews,
|
||||
contractKey = if (emptyContractKey) None else created.value.contractKey,
|
||||
createArguments = if (emptyCreateArguments) None else created.value.createArguments,
|
||||
)
|
||||
)
|
||||
case other => other
|
||||
})
|
||||
},
|
||||
commandId = "",
|
||||
)
|
||||
}
|
||||
|
||||
private def assertViewFailed(views: Seq[InterfaceView], interfaceId: Identifier): Unit = {
|
||||
val viewSearch = views.find(_.interfaceId.contains(interfaceId))
|
||||
|
||||
val view = assertDefined(viewSearch, "View could not be found")
|
||||
|
||||
val actualInterfaceId = assertDefined(view.interfaceId, "Interface ID is not defined")
|
||||
assertEquals("View has correct interface ID", interfaceId, actualInterfaceId)
|
||||
|
||||
val status = assertDefined(view.viewStatus, "Status is not defined")
|
||||
assertEquals("Status must be failed", status.code, 9)
|
||||
}
|
||||
|
||||
private def assertViewEquals(views: Seq[InterfaceView], interfaceId: Identifier)(
|
||||
checkValue: Record => Unit
|
||||
): Unit = {
|
||||
val viewSearch = views.find(_.interfaceId.contains(interfaceId))
|
||||
|
||||
val view = assertDefined(viewSearch, "View could not be found")
|
||||
|
||||
val viewCount = views.count(_.interfaceId.contains(interfaceId))
|
||||
assertEquals(s"Only one view of interfaceId=$interfaceId must be defined", viewCount, 1)
|
||||
|
||||
val actualInterfaceId = assertDefined(view.interfaceId, "Interface ID is not defined")
|
||||
assertEquals("View has correct interface ID", actualInterfaceId, interfaceId)
|
||||
|
||||
val status = assertDefined(view.viewStatus, "Status is not defined")
|
||||
assertEquals("Status must be successful", status.code, 0)
|
||||
|
||||
val actualValue = assertDefined(view.viewValue, "Value is not defined")
|
||||
checkValue(actualValue)
|
||||
}
|
||||
|
||||
}
|
@ -559,6 +559,23 @@ conformance_test(
|
||||
],
|
||||
)
|
||||
|
||||
# TODO Make sure InterfaceSubscriptionsIT is run on all servers, after Interface feature is on stable lf
|
||||
server_conformance_test(
|
||||
name = "conformance-test-interface-subscriptions",
|
||||
hocon = True,
|
||||
hocon_config = """
|
||||
ledger.participants.default.api-server.seeding = testing-weak
|
||||
""",
|
||||
lf_versions = [
|
||||
"1.dev",
|
||||
],
|
||||
servers = SERVERS,
|
||||
test_tool_args = [
|
||||
"--verbose",
|
||||
"--include=InterfaceSubscriptionsIT",
|
||||
],
|
||||
)
|
||||
|
||||
exports_files([
|
||||
"src/app/resources/logback.xml",
|
||||
])
|
||||
|
104
ledger/test-common/src/main/daml/semantic/InterfaceViews.daml
Normal file
104
ledger/test-common/src/main/daml/semantic/InterfaceViews.daml
Normal file
@ -0,0 +1,104 @@
|
||||
-- Copyright (c) 2022 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
-- SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
module InterfaceViews where
|
||||
|
||||
data View = View with
|
||||
a : Int
|
||||
b : Bool
|
||||
|
||||
data View2 = View2 with
|
||||
c : Int
|
||||
|
||||
data EmptyInterfaceView = EmptyInterfaceView {}
|
||||
|
||||
-- There are 3 interfaces defined:
|
||||
-- 1) An interface implementing "View" view
|
||||
-- 2) An interface implementing "View2" view (for variability)
|
||||
-- 2) An interface with an empty interface view
|
||||
|
||||
interface I where
|
||||
viewtype View
|
||||
|
||||
interface I2 where
|
||||
viewtype View2
|
||||
|
||||
interface INoTemplate where
|
||||
viewtype EmptyInterfaceView
|
||||
|
||||
-- Template defining a contract key, implementing interface "I".
|
||||
template T1
|
||||
with
|
||||
p : Party
|
||||
a : Int
|
||||
where
|
||||
key (p, a): (Party, Int)
|
||||
maintainer key._1
|
||||
signatory p
|
||||
interface instance I for T1 where
|
||||
view = View with
|
||||
a
|
||||
b = True
|
||||
|
||||
-- Template defining a contract key, implementing interface "I", with different values from "T1".
|
||||
template T2
|
||||
with
|
||||
p : Party
|
||||
a : Int
|
||||
where
|
||||
key (p, a): (Party, Int)
|
||||
maintainer key._1
|
||||
signatory p
|
||||
interface instance I for T2 where
|
||||
view = View with
|
||||
a
|
||||
b = False
|
||||
|
||||
-- Template defining a contract key, implementing interface "I", which is crashing.
|
||||
template T3
|
||||
with
|
||||
p : Party
|
||||
a : Int
|
||||
where
|
||||
signatory p
|
||||
interface instance I for T3 where
|
||||
view = error "view crashed"
|
||||
|
||||
-- Template which is unrelated to any of the defined interfaces.
|
||||
template T4
|
||||
with
|
||||
p : Party
|
||||
a : Int
|
||||
where
|
||||
signatory p
|
||||
|
||||
-- Template defining a contract key, implementing 2 interfaces: "I" and "I2"
|
||||
template T5
|
||||
with
|
||||
p : Party
|
||||
a : Int
|
||||
where
|
||||
signatory p
|
||||
interface instance I for T5 where
|
||||
view = View with
|
||||
a
|
||||
b = True
|
||||
interface instance I2 for T5 where
|
||||
view = View2 with
|
||||
c = 1
|
||||
|
||||
-- Template defining a contract key, with multiple witnesses, implementing 2 interfaces: "I" and "I2"
|
||||
template T6
|
||||
with
|
||||
p1 : Party
|
||||
p2 : Party
|
||||
where
|
||||
signatory p1
|
||||
observer p1, p2
|
||||
interface instance I for T6 where
|
||||
view = View with
|
||||
a = 6
|
||||
b = True
|
||||
interface instance I2 for T6 where
|
||||
view = View2 with
|
||||
c = 7
|
Loading…
Reference in New Issue
Block a user