From 2300e875f5cfcba873e8be7d87b336cdd885a857 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Mon, 4 Nov 2024 09:57:29 +0000 Subject: [PATCH] Migrate Daml3ScriptTrySubmit test to also run on 1.15 --- sdk/daml-script/test/BUILD.bazel | 12 ++ .../test/daml/Daml3ScriptTrySubmit15.daml | 203 ++++++++++++++++++ .../script/test/Daml3ScriptTestRunner.scala | 39 ++++ 3 files changed, 254 insertions(+) create mode 100644 sdk/daml-script/test/daml/Daml3ScriptTrySubmit15.daml create mode 100644 sdk/daml-script/test/src/com/digitalasset/daml/lf/engine/script/test/Daml3ScriptTestRunner.scala diff --git a/sdk/daml-script/test/BUILD.bazel b/sdk/daml-script/test/BUILD.bazel index 40b516cbf4c8..317a21cd66c2 100644 --- a/sdk/daml-script/test/BUILD.bazel +++ b/sdk/daml-script/test/BUILD.bazel @@ -215,6 +215,17 @@ da_scala_library( ], ) +daml_compile( + name = "try-submit-test-1.15", + srcs = [":daml/Daml3ScriptTrySubmit15.daml"], + dependencies = [ + "//daml-script/daml3:daml3-script-1.15.dar", + ], + project_name = "try-submit-test", + target = "1.15", + version = "1.0.0", +) + daml_compile( name = "upgrade-test-lib", srcs = [":daml/UpgradeTestLib.daml"], @@ -260,6 +271,7 @@ da_scala_test_suite( data = [ ":script-test.dar", ":script-test-no-ledger.dar", + ":try-submit-test-1.15.dar", "//daml-script/runner:daml-script-binary", "//daml-script/test:retrointerface.dar", "//daml-script/test:template.dar", diff --git a/sdk/daml-script/test/daml/Daml3ScriptTrySubmit15.daml b/sdk/daml-script/test/daml/Daml3ScriptTrySubmit15.daml new file mode 100644 index 000000000000..8ee2ab1b2f4e --- /dev/null +++ b/sdk/daml-script/test/daml/Daml3ScriptTrySubmit15.daml @@ -0,0 +1,203 @@ +-- Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved. +-- SPDX-License-Identifier: Apache-2.0 + +{-# LANGUAGE ApplicativeDo #-} + +module Daml3ScriptTrySubmit15 where + +import Daml.Script +import Daml.Script.Internal +import DA.Assert +import DA.Exception +import DA.Text + +template T1 with + t1_p : Party + where + signatory t1_p + + key t1_p : Party + maintainer key + +template T2 with + t2_p : Party + moreThan3 : Int + where + signatory t2_p + ensure moreThan3 > 3 + +template T3 with + t3_p : Party + ms : [Party] + where + signatory t3_p + + key (t3_p, ms) : (Party, [Party]) + maintainer key._2 + +template FetchByKeyT3 with + fetcher : Party + fetchKey : (Party, [Party]) + where + signatory fetcher + + choice DoFetch : (ContractId T3, T3) + controller fetcher + do fetchByKey @T3 fetchKey + +template T4 with + t4_p : Party + keyParties : [Party] + where + signatory t4_p + + key (t4_p, keyParties) : (Party, [Party]) + maintainer key._1 + +-- ContractNotFound: Cannot test contract not found with IDELedger, no way to get an invalid contract ID + +contractKeyNotFound : Script () +contractKeyNotFound = script do + alice <- allocateParty "alice" + res <- alice `trySubmit` exerciseByKeyCmd @T1 alice Archive + case res of + Left (ContractKeyNotFound (fromAnyContractKey @T1 -> Some shouldBeAlice)) -> shouldBeAlice === alice + Left e -> error $ "contractKeyNotFound incorrect error: " <> show e + Right _ -> error "contractKeyNotFound incorrectly succeeded" + +authorizationError : Script () +authorizationError = script do + alice <- allocateParty "alice" + bob <- allocateParty "bob" + res <- alice `trySubmit` createCmd T1 with t1_p = bob + case res of + Left (AuthorizationError err) -> + assert $ ("requires authorizers " <> partyToText bob <> ", but only " <> partyToText alice <> " were given") `isInfixOf` err + Left e -> error $ "authorizationError incorrect error: " <> show e + Right _ -> error "authorizationError incorrectly succeeded" + +contractNotActive : Script () +contractNotActive = script do + alice <- allocateParty "alice" + cid <- alice `submit` createCmd T1 with t1_p = alice + res <- alice `trySubmit` do + exerciseCmd cid Archive + exerciseCmd cid Archive + pure () + case res of + Left e@(ContractNotFound _ mbAdditionalInfo) -> + + case mbAdditionalInfo of + None -> error "contractNotActive no additional info" + Some additionalInfo -> + case isNotActive additionalInfo of + None -> error "contractNotActive additional info is not NotActive variant" + Some anyCid -> + case fromAnyContractId @T1 anyCid of + None -> error $ "contractNotActive cannot find contract with id: " <> show e + Some shouldBeCid -> shouldBeCid === cid + Left e -> error $ "contractNotActive incorrect error: " <> show e + Right _ -> error "contractNotActive incorrectly succeeded" + +-- TODO[SW] Behaviour between Canton and IDE Ledger here is different - canton will give a NotFound, whereas IDE gives a NotActive. +-- Changes to IDE needed to match canton behavior, alongside restructing of NotFound to provide more IDE context. +-- As such, we don't run this test yet + +-- contractNotActiveSeparate : Script () +-- contractNotActiveSeparate = script do +-- alice <- allocateParty "alice" +-- cid <- alice `submit` createCmd T1 with t1_p = alice +-- alice `submit` exerciseCmd cid Archive +-- res <- alice `trySubmit` exerciseCmd cid Archive +-- case res of +-- Left (ContractNotActive (fromAnyContractId @T1 -> Some shouldBeCid)) -> shouldBeCid === cid +-- Left e -> error $ "contractNotActive incorrect error: " <> show e +-- Right _ -> error "contractNotActive incorrectly succeeded" + +-- DisclosedContractKeyHashingError: Cannot test DisclosedContractKeyHashingError yet, scripts does not support this functionality + +duplicateContractKey : Script () +duplicateContractKey = script do + alice <- allocateParty "alice" + alice `submit` createCmd T1 with t1_p = alice + res <- alice `trySubmit` createCmd T1 with t1_p = alice + case res of + Left (DuplicateContractKey (Some (fromAnyContractKey @T1 -> Some shouldBeAlice))) -> shouldBeAlice === alice + -- Key is often not provided when using canton + Left (DuplicateContractKey None) -> pure () + Left e -> error $ "duplicateContractKey incorrect error: " <> show e + Right _ -> error "duplicateContractKey incorrectly succeeded" + +-- InconsistentContractKey: I don't know how to trigger an InconsistentContractKey error + +unhandledException : Script () +unhandledException = script do + alice <- allocateParty "alice" + res <- alice `trySubmit` createCmd T2 with t2_p = alice, moreThan3 = 2 + case res of + Left (UnhandledException (Some (fromAnyException @PreconditionFailed -> Some pfError))) -> assert $ "Template precondition violated" `isInfixOf` pfError.message + Left e -> error $ "unhandledException incorrect error: " <> show e + Right _ -> error "unhandledException incorrectly succeeded" + +-- UserError: Only throwable pre LF 1.14, which daml3-script doesn't support. Consider dropping the error +-- but keep in mind that daml3-script may be usable against a ledger running older contracts, so it might be possible +-- TODO: verify this + +-- TemplatePreconditionViolated: Same as above + +createEmptyContractKeyMaintainers : Script () +createEmptyContractKeyMaintainers = script do + alice <- allocateParty "alice" + let t3 = T3 with t3_p = alice, ms = [] + res <- alice `trySubmit` createCmd t3 + case res of + Left (CreateEmptyContractKeyMaintainers (fromAnyTemplate @T3 -> Some shouldBeT3)) -> shouldBeT3 === t3 + Left e -> error $ "createEmptyContractKeyMaintainers incorrect error: " <> show e + Right _ -> error "createEmptyContractKeyMaintainers incorrectly succeeded" + +fetchEmptyContractKeyMaintainers : Script () +fetchEmptyContractKeyMaintainers = script do + alice <- allocateParty "alice" + let t3Key = (alice, []) + res <- alice `trySubmit` createAndExerciseCmd (FetchByKeyT3 with fetcher = alice, fetchKey = t3Key) DoFetch + case res of + Left (FetchEmptyContractKeyMaintainers (fromAnyContractKey @T3 -> Some shouldBeT3Key)) -> shouldBeT3Key === t3Key + Left e -> error $ "fetchEmptyContractKeyMaintainers incorrect error: " <> show e + Right _ -> error "fetchEmptyContractKeyMaintainers incorrectly succeeded" + +wronglyTypedContract : Script () +wronglyTypedContract = script do + alice <- allocateParty "alice" + t1Cid <- alice `submit` createCmd T1 with t1_p = alice + let t2Cid = coerceContractId @T1 @T2 t1Cid + res <- alice `trySubmit` exerciseCmd t2Cid Archive + case res of + Left (WronglyTypedContract (fromAnyContractId @T1 -> Some shouldBeT1Cid) expectedTypeRep actualTypeRep) -> do + shouldBeT1Cid === t1Cid + expectedTypeRep === templateTypeRep @T2 + actualTypeRep === templateTypeRep @T1 + Left e -> error $ "wronglyTypedContract incorrect error: " <> show e + Right _ -> error "wronglyTypedContract incorrectly succeeded" + +-- ContractDoesNotImplementInterface: Can't do this from daml-script with IDELedger +-- ContractDoesNotImplementRequiringInterface: Can't do this from daml-script with IDELedger +-- NonComparableValues: Can't do this from daml-script with IDELedger +-- ContractIdInContractKey: Can't do this from daml-script with IDELedger +-- ContractIdComparability: Can't do this from daml-script with IDELedger + +-- UnknownError: Shouldn't be testable, we should know all the possible errors. +-- Can reproduce right now with a timeout, or package vetting test. + +-- Using contractKeyNotFound with an enormous key +truncatedError : Script () +truncatedError = script do + alice <- allocateParty "alice" + let t4Key = (alice, replicate 10000 alice) + res <- alice `trySubmit` exerciseByKeyCmd @T4 t4Key Archive + case res of + -- We specifically throw a fixed string to be caught by the real Ledger tests + Left (TruncatedError "ContractKeyNotFound" _) -> error "EXPECTED_TRUNCATED_ERROR" + -- We silently accept this case for the IDE Ledger + Left (ContractKeyNotFound (fromAnyContractKey @T4 -> Some shouldBeT4Key)) | shouldBeT4Key == t4Key -> pure () + Left e -> error $ "contractKeyNotFound incorrect error: " <> show e + Right _ -> error "contractKeyNotFound incorrectly succeeded" diff --git a/sdk/daml-script/test/src/com/digitalasset/daml/lf/engine/script/test/Daml3ScriptTestRunner.scala b/sdk/daml-script/test/src/com/digitalasset/daml/lf/engine/script/test/Daml3ScriptTestRunner.scala new file mode 100644 index 000000000000..ac158049458b --- /dev/null +++ b/sdk/daml-script/test/src/com/digitalasset/daml/lf/engine/script/test/Daml3ScriptTestRunner.scala @@ -0,0 +1,39 @@ +// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package com.daml.lf.engine.script + +import com.daml.bazeltools.BazelRunfiles +import org.scalatest.Suite + +import java.nio.file.Paths + +class Daml3ScriptTestRunner extends DamlScriptTestRunner { + self: Suite => + + val trySubmitTestDarPath = + Paths.get(BazelRunfiles.rlocation("daml-script/test/try-submit-test-1.15.dar")) + + override lazy val darFiles = List(trySubmitTestDarPath) + + val expectedContractNotActiveResponse = + """FAILURE (com.daml.lf.engine.free.InterpretationError: Error: Unhandled Daml exception: DA.Exception.GeneralError:GeneralError@XXXXXXXX{ message = "contractNotActive no additional info" })""" + + "daml-script command line" should { + "pick up all scripts and returns somewhat sensible outputs for daml3-script features" in + assertDamlScriptRunnerResult( + trySubmitTestDarPath, + f"""Daml3ScriptTrySubmit15:authorizationError SUCCESS + |Daml3ScriptTrySubmit15:contractKeyNotFound SUCCESS + |Daml3ScriptTrySubmit15:contractNotActive ${expectedContractNotActiveResponse} + |Daml3ScriptTrySubmit15:createEmptyContractKeyMaintainers SUCCESS + |Daml3ScriptTrySubmit15:duplicateContractKey SUCCESS + |Daml3ScriptTrySubmit15:fetchEmptyContractKeyMaintainers SUCCESS + |Daml3ScriptTrySubmit15:truncatedError FAILURE (com.daml.lf.engine.free.InterpretationError: Error: Unhandled Daml exception: DA.Exception.GeneralError:GeneralError@XXXXXXXX{ message = "EXPECTED_TRUNCATED_ERROR" }) + |Daml3ScriptTrySubmit15:unhandledException SUCCESS + |Daml3ScriptTrySubmit15:wronglyTypedContract SUCCESS + |""".stripMargin, + ) + + } +}