-
Notifications
You must be signed in to change notification settings - Fork 205
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add conformance test for deeply nested values (#10319)
CHANGELOG_BEGIN CHANGELOG_END
- Loading branch information
1 parent
faf479e
commit 63739fa
Showing
5 changed files
with
307 additions
and
0 deletions.
There are no files selected for viewing
177 changes: 177 additions & 0 deletions
177
...pi-test-tool/src/main/scala/com/daml/ledger/api/testtool/suites/DeeplyNestedValueIT.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
// Copyright (c) 2021 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package com.daml.ledger.api.testtool.suites | ||
|
||
import com.daml.ledger.api.refinements.ApiTypes.Party | ||
import com.daml.ledger.api.testtool.infrastructure.Allocation._ | ||
import com.daml.ledger.api.testtool.infrastructure.Assertions._ | ||
import com.daml.ledger.api.testtool.infrastructure.LedgerTestSuite | ||
import com.daml.ledger.api.testtool.infrastructure.participant.ParticipantTestContext | ||
import com.daml.ledger.test.semantic.DeeplyNestedValue._ | ||
import com.daml.ledger.client.binding.Primitive | ||
import io.grpc.Status | ||
|
||
import scala.annotation.tailrec | ||
import scala.concurrent.{ExecutionContext, Future} | ||
import scala.util.Success | ||
|
||
final class DeeplyNestedValueIT extends LedgerTestSuite { | ||
|
||
@tailrec | ||
private[this] def toNat(i: Long, acc: Nat = Nat.Z(())): Nat = | ||
if (i == 0) acc else toNat(i - 1, Nat.S(acc)) | ||
|
||
private[this] def waitForTransactionId( | ||
alpha: ParticipantTestContext, | ||
party: Party, | ||
command: Primitive.Update[_], | ||
)(implicit | ||
ec: ExecutionContext | ||
): Future[Either[Throwable, String]] = | ||
alpha | ||
.submitAndWaitForTransactionId( | ||
alpha.submitAndWaitRequest(party, command.command) | ||
) | ||
.transform(x => Success(x.toEither)) | ||
|
||
private[this] def camlCase(s: String) = | ||
s.split(" ").iterator.map(_.capitalize).mkString("") | ||
|
||
List[Long](46, 100, 101, 110, 200).foreach { nesting => | ||
val accepted = nesting <= 100 | ||
val result = if (accepted) "Accept" else "Reject" | ||
|
||
// Once converted to Nat, `n` will have a nesting `nesting`. | ||
// Note that Nat.Z(()) has nesting 1. | ||
val n = nesting - 1 | ||
|
||
// Choice argument are always wrapped in a record | ||
val nChoiceArgument = n - 1 | ||
|
||
// The nesting of the payload of a `Contract` is one more than the nat it contains | ||
val nContract = n - 1 | ||
|
||
// The nesting of the key of a `ContractWithKey` is one more than the nat it contains | ||
val nKey = n - 1 | ||
|
||
def test[T](description: String)( | ||
update: ExecutionContext => ( | ||
ParticipantTestContext, | ||
Party, | ||
) => Future[Either[Throwable, T]] | ||
) = | ||
super.test( | ||
result + camlCase(description) + nesting.toString, | ||
s"${result.toLowerCase}s $description with a nesting of $nesting", | ||
allocate(SingleParty), | ||
)(implicit ec => { case Participants(Participant(alpha, party)) => | ||
update(ec)(alpha, party).map { | ||
case Right(_) if accepted => () | ||
case Left(err: Throwable) if !accepted => | ||
assertGrpcError(err, Status.Code.INVALID_ARGUMENT, None) | ||
case otherwise => | ||
fail("Unexpected " + otherwise.fold(err => s"failure: $err", _ => "success")) | ||
} | ||
}) | ||
|
||
test("create command") { implicit ec => (alpha, party) => | ||
waitForTransactionId(alpha, party, Contract(party, nContract, toNat(nContract)).create) | ||
} | ||
|
||
test("exercise command") { implicit ec => (alpha, party) => | ||
for { | ||
handler <- alpha.create(party, Handler(party)) | ||
result <- waitForTransactionId( | ||
alpha, | ||
party, | ||
handler.exerciseDestruct(party, toNat(nChoiceArgument)), | ||
) | ||
} yield result | ||
} | ||
|
||
test("create argument in CreateAndExercise command") { implicit ec => (alpha, party) => | ||
waitForTransactionId( | ||
alpha, | ||
party, | ||
Contract(party, nContract, toNat(nContract)).createAnd | ||
.exerciseArchive(party), | ||
) | ||
} | ||
|
||
test("choice argument in CreateAndExercise command") { implicit ec => (alpha, party) => | ||
waitForTransactionId( | ||
alpha, | ||
party, | ||
Handler(party).createAnd.exerciseDestruct(party, toNat(nChoiceArgument)), | ||
) | ||
} | ||
|
||
test("exercise argument") { implicit ec => (alpha, party) => | ||
for { | ||
handler <- alpha.create(party, Handler(party)) | ||
result <- | ||
waitForTransactionId( | ||
alpha, | ||
party, | ||
handler.exerciseConstructThenDestruct(party, nChoiceArgument), | ||
) | ||
} yield result | ||
} | ||
|
||
test("exercise output") { implicit ec => (alpha, party) => | ||
for { | ||
handler <- alpha.create(party, Handler(party)) | ||
result <- | ||
waitForTransactionId(alpha, party, handler.exerciseConstruct(party, n)) | ||
} yield result | ||
} | ||
|
||
test("create argument") { implicit ec => (alpha, party) => | ||
for { | ||
handler <- alpha.create(party, Handler(party)) | ||
result <- waitForTransactionId(alpha, party, handler.exerciseCreate(party, nContract)) | ||
} yield result | ||
} | ||
|
||
test("contract key") { implicit ec => (alpha, party) => | ||
for { | ||
handler <- alpha.create(party, Handler(party)) | ||
result <- waitForTransactionId(alpha, party, handler.exerciseCreateKey(party, nKey)) | ||
} yield result | ||
} | ||
|
||
if (accepted) { | ||
// Because we cannot create contracts with nesting > 100, | ||
// it does not make sense to test fetch of those kinds of contracts. | ||
test("fetch by key") { implicit ec => (alpha, party) => | ||
for { | ||
handler <- alpha.create(party, Handler(party)) | ||
_ <- alpha.exercise(party, handler.exerciseCreateKey(_, nKey)) | ||
result <- waitForTransactionId(alpha, party, handler.exerciseFetchByKey(party, nKey)) | ||
} yield result | ||
} | ||
} | ||
|
||
test("failing lookup by key") { implicit ec => (alpha, party) => | ||
for { | ||
handler <- alpha.create(party, Handler(party)) | ||
result <- waitForTransactionId(alpha, party, handler.exerciseLookupByKey(party, nKey)) | ||
} yield result | ||
} | ||
|
||
if (accepted) { | ||
// Because we cannot create contracts with key nesting > 100, | ||
// it does not make sens to test successful lookup for those keys. | ||
test("successful lookup by key") { implicit ec => (alpha, party) => | ||
for { | ||
handler <- alpha.create(party, Handler(party)) | ||
_ <- alpha.exercise(party, handler.exerciseCreateKey(_, nKey)) | ||
result <- | ||
waitForTransactionId(alpha, party, handler.exerciseLookupByKey(party, nKey)) | ||
} yield result | ||
} | ||
} | ||
|
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
88 changes: 88 additions & 0 deletions
88
ledger/test-common/src/main/daml/semantic/DeeplyNestedValue.daml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
-- Copyright (c) 2021 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved. | ||
-- SPDX-License-Identifier: Apache-2.0 | ||
|
||
module DeeplyNestedValue where | ||
|
||
data Nat = Z | S Nat | ||
deriving (Eq, Show) | ||
|
||
construct: Int -> Nat -> Nat | ||
construct x acc | x <= 0 = acc | ||
construct x acc = construct (x-1) (S acc) | ||
|
||
toNat : Int -> Nat | ||
toNat x = construct x Z | ||
|
||
destruct: Nat -> Int -> Int | ||
destruct Z acc = acc | ||
destruct (S n) acc = destruct n (acc + 1) | ||
|
||
toInt: Nat -> Int | ||
toInt x = destruct x 0 | ||
|
||
template Contract | ||
with | ||
party: Party | ||
i: Int | ||
n: Nat | ||
where | ||
signatory party | ||
|
||
template ContractWithKey | ||
with | ||
party: Party | ||
i: Int | ||
where | ||
signatory party | ||
key (party, toNat i): (Party, Nat) | ||
maintainer key._1 | ||
|
||
template Handler | ||
with | ||
party : Party | ||
where | ||
signatory party | ||
controller party can | ||
nonconsuming Construct : Nat | ||
with i : Int | ||
do | ||
pure $ toNat i | ||
nonconsuming Destruct : Int | ||
with n: Nat | ||
do pure $ toInt n | ||
nonconsuming ConstructThenDestruct : Int | ||
with | ||
i: Int | ||
do | ||
exercise self Destruct with n = toNat i | ||
nonconsuming Create: ContractId Contract | ||
with | ||
i : Int | ||
do | ||
create Contract with | ||
party = party | ||
i = i | ||
n = toNat i | ||
nonconsuming CreateKey: ContractId ContractWithKey | ||
with | ||
i : Int | ||
do | ||
create ContractWithKey with | ||
party = party | ||
i = i | ||
nonconsuming Fetch: Contract | ||
with cid: ContractId Contract | ||
do | ||
fetch cid | ||
nonconsuming FetchByKey: ContractId ContractWithKey | ||
with | ||
i: Int | ||
do | ||
(cid, _) <- fetchByKey @ContractWithKey (party, toNat i) | ||
pure cid | ||
nonconsuming LookupByKey: Optional (ContractId ContractWithKey) | ||
with | ||
i: Int | ||
do | ||
lookupByKey @ContractWithKey (party, toNat i) | ||
|