diff --git a/CHANGELOG.md b/CHANGELOG.md index d4588759..eb91a353 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ constrained yet. - PrettyCooked option `pcOptPrintLog`, which is a boolean, to turn on or off the log display in the pretty printer. The default value is `True`. +- Capability to test the result of a mockchain run based on the log entries. ### Removed @@ -52,6 +53,7 @@ * it now displays when the user specifies useless collateral utxos. * it is not visible from outside of `cooked-validators` - Dependency to cardano-api bumped to 8.46. +- The whole testing API has been revamped ### Fixed diff --git a/src/Cooked/MockChain/Testing.hs b/src/Cooked/MockChain/Testing.hs index 89d4735b..1456cafd 100644 --- a/src/Cooked/MockChain/Testing.hs +++ b/src/Cooked/MockChain/Testing.hs @@ -12,15 +12,15 @@ import Cooked.MockChain.UtxoState import Cooked.Pretty import Data.Default import Data.Text qualified as T -import Debug.Trace import Ledger qualified import Test.QuickCheck qualified as QC import Test.Tasty.HUnit qualified as HU --- | This module provides a common interface for HUnit and QuickCheck tests. We --- do so by abstracting uses of 'HU.Assertion' and 'QC.Property' for @(IsProp --- prop) => prop@, then provide instances for both @HU.Asserton@ and --- @QC.Property@. +-- * Common interface between HUnit and QuickCheck + +-- | 'IsProp' is a common interface for HUnit and QuickCheck tests. It abstracts +-- uses of 'HU.Assertion' and 'QC.Property' for @(IsProp prop) => prop@, then +-- provide instances for both @HU.Asserton@ and @QC.Property@. class IsProp prop where -- | Displays the string to the user in case of failure testCounterexample :: String -> prop -> prop @@ -65,180 +65,11 @@ infixr 2 .||. (.||.) :: (IsProp prop) => prop -> prop -> prop a .||. b = testDisjoin [a, b] --- | Ensure that all results produced by the staged mockchain /succeed/, --- starting from the default initial distribution -testSucceeds :: (IsProp prop) => PrettyCookedOpts -> StagedMockChain a -> prop -testSucceeds pcOpts = testSucceedsFrom pcOpts def - --- | Ensure that all results produced by the staged mockchain /fail/ and that a --- predicate holds over the error. --- --- To test that validation fails, use --- > testFails def (isCekEvaluationFailure def) e -testFails :: (IsProp prop, Show a) => PrettyCookedOpts -> (MockChainError -> prop) -> StagedMockChain a -> prop -testFails pcOpts predi = testFailsFrom pcOpts predi def - --- | Ensure that all results produced by the staged mockchain succeed starting --- from some initial distribution but doesn't impose any additional condition on --- success. Use 'testSucceedsFrom'' for that. -testSucceedsFrom :: - (IsProp prop) => - PrettyCookedOpts -> - InitialDistribution -> - StagedMockChain a -> - prop -testSucceedsFrom pcOpts = testSucceedsFrom' pcOpts (\_ _ -> testSuccess) - --- | Ensure that all results produced by the staged mockchain succeed starting --- from some initial distribution. Additionally impose a condition over the --- resulting state and value. -testSucceedsFrom' :: - (IsProp prop) => - PrettyCookedOpts -> - (a -> UtxoState -> prop) -> - InitialDistribution -> - StagedMockChain a -> - prop -testSucceedsFrom' pcOpts prop = testAllSatisfiesFrom pcOpts (either (testFailureMsg . renderString (prettyCookedOpt pcOpts)) (uncurry prop)) - --- | Ensure that all results produced by the staged mockchain /fail/ starting --- from some initial distribution. -testFailsFrom :: - (IsProp prop, Show a) => - PrettyCookedOpts -> - (MockChainError -> prop) -> - InitialDistribution -> - StagedMockChain a -> - prop -testFailsFrom pcOpts predi = - testAllSatisfiesFrom - pcOpts - (either predi (testFailureMsg . renderString (prettyCookedOpt pcOpts))) - --- | Is satisfied when the given 'MockChainError' is wrapping a --- @CekEvaluationFailure@. This is particularly important when writing negative --- tests. For example, if we are simulating an attack and writing a test with --- 'testFailsFrom', we might have made a mistake in the attack, yielding a test --- that fails for reasons such as @ValueLessThanMinAda@ or @ValueNotPreserved@, --- which does not rule out the attack being caught by the validator script. For --- these scenarios it is paramount to rely on @testFailsFrom' --- isCekEvaluationFailure@ instead. -isCekEvaluationFailure :: (IsProp prop) => PrettyCookedOpts -> MockChainError -> prop -isCekEvaluationFailure _ (MCEValidationError _ (Ledger.ScriptFailure _)) = testSuccess -isCekEvaluationFailure pcOpts e = testFailureMsg $ "Expected 'CekEvaluationFailure', got: " ++ renderString (prettyCookedOpt pcOpts) e - --- | Similar to 'isCekEvaluationFailure', but enables us to check for a specific --- error message in the error. -isCekEvaluationFailureWithMsg :: (IsProp prop) => PrettyCookedOpts -> (String -> Bool) -> MockChainError -> prop -isCekEvaluationFailureWithMsg _ f (MCEValidationError _ (Ledger.ScriptFailure (Ledger.EvaluationError msgs _))) - | any (f . T.unpack) msgs = testSuccess -isCekEvaluationFailureWithMsg pcOpts _ e = testFailureMsg $ "Expected 'CekEvaluationFailure' with specific messages, got: " ++ renderString (prettyCookedOpt pcOpts) e - --- | Ensure that all results produced by the set of traces encoded by the --- 'StagedMockChain' satisfy the given predicate. If you wish to build custom --- predicates you can use 'testSatisfiesFrom'' directly and see --- 'testBinaryRelatedBy' as an example. -testAllSatisfiesFrom :: - forall prop a. - (IsProp prop) => - PrettyCookedOpts -> - (Either MockChainError (a, UtxoState) -> prop) -> - InitialDistribution -> - StagedMockChain a -> - prop -testAllSatisfiesFrom pcOpts f = testSatisfiesFrom' (testAll go) - where - go :: MockChainReturn a UtxoState -> prop - go (prop, mcLog) = testCounterexample (renderString (prettyCookedOpt pcOpts) mcLog) (f prop) - --- | Asserts that the given 'StagedMockChain' produces exactly two outcomes, --- both of which are successful and have their resulting states related by a --- given predicate. A typical usage would look like: --- --- > testBinaryRelatedBy equalModuloAda myInitDistr $ do --- > x <- trPrepare --- > execOption1 x <|> execOption2 x -testBinaryRelatedBy :: - (IsProp prop) => - PrettyCookedOpts -> - (UtxoState -> UtxoState -> prop) -> - InitialDistribution -> - StagedMockChain a -> - prop -testBinaryRelatedBy pcOpts rel = testSatisfiesFrom' $ \case - [(ra, ta), (rb, tb)] -> case (ra, rb) of - (Right resA, Right resB) -> rel (snd resA) (snd resB) - (Left errA, Right _) -> - testFailureMsg $ concat ["Expected two outcomes, the first failed with:", renderString (prettyCookedOpt pcOpts) errA, "\n", renderString (prettyCookedOpt pcOpts) ta] - (Right _, Left errB) -> - testFailureMsg $ concat ["Expected two outcomes, the second failed with:", renderString (prettyCookedOpt pcOpts) errB, "\n", renderString (prettyCookedOpt pcOpts) tb] - (Left errA, Left errB) -> - testFailureMsg $ - concat - [ "Expected two outcomes, both failed with:", - renderString (prettyCookedOpt pcOpts) errA, - "; ", - renderString (prettyCookedOpt pcOpts) errB, - "\n First: ", - renderString (prettyCookedOpt pcOpts) ta, - "\nSecond: ", - renderString (prettyCookedOpt pcOpts) tb - ] - xs -> testFailureMsg $ "Expected exactly two outcomes, received: " ++ show (length xs) - --- | Generalizes 'testBinaryRelatedBy', asserting that the given --- 'StagedMockChain' produces more than two outcomes, say @[x,y,z,w]@, all of --- which are successful (i.e. are not a 'MockChainError') and these states are --- in the same equivalence class of (~); that is, they satisfy: --- --- > x ~ y && x ~ z && x ~ z && x ~ w --- --- Because @(~)@ should be symmetric and transitive we can estabilish that these --- states all belong to the same equivalence class. This function does /not/ --- check each pointwise case. -testOneEquivClass :: - (IsProp prop) => - PrettyCookedOpts -> - (UtxoState -> UtxoState -> prop) -> - InitialDistribution -> - StagedMockChain a -> - prop -testOneEquivClass pcOpts rel = testSatisfiesFrom' $ \case - [] -> testFailureMsg "Expected two of more outcomes, received: 0" - [_] -> testFailureMsg "Expected two of more outcomes, received: 1" - ((Left errX, tx) : _) -> testFailureMsg $ concat ["First outcome is a failure: ", renderString (prettyCookedOpt pcOpts) errX, "\n", renderString (prettyCookedOpt pcOpts) tx] - ((Right resX, _) : xs) -> go (snd resX) xs - where - -- we can flag a success here because 'xs' above is guarnateed to have at - -- least one element since we ruled out the empty and the singleton lists in - -- the \case - go _resX [] = testSuccess - go _resX ((Left errY, ty) : _) = testFailureMsg $ concat ["An outcome is a failure: ", renderString (prettyCookedOpt pcOpts) errY, "\n", renderString (prettyCookedOpt pcOpts) ty] - go resX ((Right (_, resY), _) : ys) = testConjoin [rel resX resY, go resX ys] - --- | Asserts that the results produced by running the given 'StagedMockChain' --- from some speficied 'InitialDistribution' satisfy a given assertion. In this --- case, the predicate gets the trace descriptions that led to each potential --- outcome and is responsible for calling 'testCounterexample' communicate these --- to the user. --- --- Although this function is mainly used internally, as a building block for the --- simpler predicates, it can be useful in building some custom --- predicates. Check 'testAllSatisfiesFrom' or 'testBinaryRelatedBy' for --- examples on using this. -testSatisfiesFrom' :: - ([MockChainReturn a UtxoState] -> prop) -> - InitialDistribution -> - StagedMockChain a -> - prop -testSatisfiesFrom' predi i0 = predi . interpretAndRunWith (runMockChainTFrom i0) - --- * 'TestResult' instances - --- Catches a HUnit test failure, if the test fails. +-- | Catches a HUnit test failure, if the test fails. assertionToMaybe :: HU.Assertion -> IO (Maybe HU.HUnitFailure) assertionToMaybe = flip E.catches [E.Handler $ return . Just] . (>> return Nothing) +-- | HUnit instance of 'IsProp' instance IsProp HU.Assertion where testCounterexample msg = maybe testSuccess (E.throw . adjustMsg) <=< assertionToMaybe where @@ -259,6 +90,7 @@ instance IsProp HU.Assertion where testSuccess = return () +-- | QuickCheck instance of 'IsProp' instance IsProp QC.Property where testCounterexample = QC.counterexample testFailure = QC.property False @@ -271,14 +103,9 @@ instance IsProp QC.Property where forAll :: (Show a) => QC.Gen a -> (a -> QC.Property) -> QC.Property forAll = QC.forAll --- TODO: Discuss this instance; its here to enable us to easily run things in a --- repl but I'm not sure whether to ignore the counterexample messages or not. -instance IsProp Bool where - testCounterexample msg False = trace msg False - testCounterexample _ True = True - testConjoin = and - testDisjoin = or +-- * Extra HUnit assertions +-- | Asserts whether a set is a subset of another one, both given as lists. assertSubset :: (Show a, Eq a) => [a] -> [a] -> HU.Assertion assertSubset l r = testConjoin @@ -295,6 +122,7 @@ assertSubset l r = l ) +-- | Asserts whether 2 sets are equal, both given as lists. assertSameSets :: (Show a, Eq a) => [a] -> [a] -> HU.Assertion assertSameSets l r = HU.assertBool @@ -302,3 +130,151 @@ assertSameSets l r = (length l == length r) .&&. assertSubset l r .&&. assertSubset r l + +-- * Testing mockchain traces + +-- | Data structure to test a mockchain trace +data Test a prop = Test + { -- | The mockchain trace to test + testTrace :: StagedMockChain a, + -- | The initial distribution from which the trace should be run + testInitDist :: InitialDistribution, + -- | The property should hold in case of failure + testErrorProp :: PrettyCookedOpts -> MockChainError -> [MockChainLogEntry] -> prop, + -- | The property that should hold in case of success + testResultProp :: PrettyCookedOpts -> a -> UtxoState -> [MockChainLogEntry] -> prop, + -- | The printing option that should be use to render test results + testPrettyOpts :: PrettyCookedOpts + } + +-- | A test template which expects a success from a trace +mustSucceedTest :: (IsProp prop) => StagedMockChain a -> Test a prop +mustSucceedTest trace = + Test + { testTrace = trace, + testInitDist = def, + testErrorProp = \opts res _ -> testFailureMsg $ renderString (prettyCookedOpt opts) res, + testResultProp = \_ _ _ _ -> testSuccess, + testPrettyOpts = def + } + +-- | A test template which expects a failure from a trace +mustFailTest :: (IsProp prop, Show a) => StagedMockChain a -> Test a prop +mustFailTest trace = + Test + { testTrace = trace, + testInitDist = def, + testErrorProp = \_ _ _ -> testSuccess, + testResultProp = \opts a res _ -> testFailureMsg $ renderString (prettyCookedOpt opts) (a, res), + testPrettyOpts = def + } + +-- | A test template with no particular requirement on the trace +emptyTest :: (IsProp prop) => StagedMockChain a -> Test a prop +emptyTest trace = + Test + { testTrace = trace, + testInitDist = def, + testErrorProp = \_ _ _ -> testSuccess, + testResultProp = \_ _ _ _ -> testSuccess, + testPrettyOpts = def + } + +-- | Appending an initial distribution to a test +withInitDist :: (IsProp prop) => Test a prop -> InitialDistribution -> Test a prop +withInitDist test initDist = test {testInitDist = initDist} + +-- | Appending printing options to a test +withPrettyOpts :: (IsProp prop) => Test a prop -> PrettyCookedOpts -> Test a prop +withPrettyOpts test opts = test {testPrettyOpts = opts} + +-- | Appending a predicate over the log to a test. This will be used both in +-- case of success or failure of the trace. +withJournalPred :: (IsProp prop) => Test a prop -> ([MockChainLogEntry] -> prop) -> Test a prop +withJournalPred test journalPred = + test + { testErrorProp = \opts err journal -> testErrorProp test opts err journal .&&. journalPred journal, + testResultProp = \opts val state journal -> testResultProp test opts val state journal .&&. journalPred journal + } + +-- | Appending a predicate over the return value and state, which will be used +-- in case of success of the trace. +withValueAndStatePred :: (IsProp prop) => Test a prop -> (a -> UtxoState -> prop) -> Test a prop +withValueAndStatePred test resultPred = + test + { testResultProp = \opts val state journal -> testResultProp test opts val state journal .&&. resultPred val state + } + +-- | Appending a predicate over the return value, which will be used in case of +-- success of the trace. +withValuePred :: (IsProp prop) => Test a prop -> (a -> prop) -> Test a prop +withValuePred test valuePred = withValueAndStatePred test $ \val _ -> valuePred val + +-- | Appending a predicate over the return state, which will be used in case of +-- success of the trace. +withStatePred :: (IsProp prop) => Test a prop -> (UtxoState -> prop) -> Test a prop +withStatePred test statePred = withValueAndStatePred test $ \_ st -> statePred st + +-- | Appending a predicate over an error which uses the printing options, which +-- will be used in case of failure of the trace. +withPrettyAndErrorPred :: (IsProp prop) => Test a prop -> (PrettyCookedOpts -> MockChainError -> prop) -> Test a prop +withPrettyAndErrorPred test errorPred = test {testErrorProp = \opts err journal -> testErrorProp test opts err journal .&&. errorPred opts err} + +withErrorPred :: (IsProp prop) => Test a prop -> (MockChainError -> prop) -> Test a prop +withErrorPred test errorPred = withPrettyAndErrorPred test $ \_ err -> errorPred err + +-- | This takes a test and transforms it into an actual test case in prop. +testToProp :: (IsProp prop) => Test a prop -> prop +testToProp Test {..} = + let innerProp (res, mcLog) = + case res of + Left err -> testErrorProp testPrettyOpts err mcLog + Right (result, state) -> testResultProp testPrettyOpts result state mcLog + in testAll + (\ret@(_, mcLog) -> testCounterexample (renderString (prettyCookedOpt testPrettyOpts) mcLog) (innerProp ret)) + (interpretAndRunWith (runMockChainTFrom testInitDist) testTrace) + +-- | Ensure that all results produced by the staged mockchain /succeed/, +-- starting from the default initial distribution +testSucceeds :: (IsProp prop) => StagedMockChain a -> prop +testSucceeds = testToProp . mustSucceedTest + +-- | Ensure that all results produced by the staged mockchain /fail/ +testFails :: (IsProp prop, Show a) => StagedMockChain a -> prop +testFails = testToProp . mustFailTest + +-- | A property to ensure a phase 1 failure +isPhase1Failure :: (IsProp prop) => PrettyCookedOpts -> MockChainError -> prop +isPhase1Failure _ (MCEValidationError Ledger.Phase1 _) = testSuccess +isPhase1Failure pcOpts e = testFailureMsg $ "Expected phase 1 evaluation failure, got: " ++ renderString (prettyCookedOpt pcOpts) e + +-- | A test that succeeds when the trace results in a phase 1 failure +testFailsInPhase1 :: (IsProp prop, Show a) => StagedMockChain a -> prop +testFailsInPhase1 = testToProp . (`withPrettyAndErrorPred` isPhase1Failure) . mustFailTest + +-- | A property to ensure a phase 2 failure +isPhase2Failure :: (IsProp prop) => PrettyCookedOpts -> MockChainError -> prop +isPhase2Failure _ (MCEValidationError Ledger.Phase2 _) = testSuccess +isPhase2Failure pcOpts e = testFailureMsg $ "Expected phase 2 evaluation failure, got: " ++ renderString (prettyCookedOpt pcOpts) e + +-- | A test that succeeds when the trace results in a phase 2 failure +testFailsInPhase2 :: (IsProp prop, Show a) => StagedMockChain a -> prop +testFailsInPhase2 = testToProp . (`withPrettyAndErrorPred` isPhase2Failure) . mustFailTest + +-- | Same as 'isPhase1Failure' with an added predicate on the text error +isPhase1FailureWithMsg :: (IsProp prop) => (String -> Bool) -> PrettyCookedOpts -> MockChainError -> prop +isPhase1FailureWithMsg f _ (MCEValidationError Ledger.Phase1 (Ledger.CardanoLedgerValidationError text)) | f $ T.unpack text = testSuccess +isPhase1FailureWithMsg _ pcOpts e = testFailureMsg $ "Expected phase 1 evaluation failure with constrained messages, got: " ++ renderString (prettyCookedOpt pcOpts) e + +-- | Same as 'testFailsInPhase1' with an added predicate on the text error +testFailsInPhase1WithMsg :: (IsProp prop, Show a) => (String -> Bool) -> StagedMockChain a -> prop +testFailsInPhase1WithMsg f = testToProp . (`withPrettyAndErrorPred` isPhase1FailureWithMsg f) . mustFailTest + +-- | Same as 'isPhase2Failure' with an added predicate over the text error +isPhase2FailureWithMsg :: (IsProp prop) => (String -> Bool) -> PrettyCookedOpts -> MockChainError -> prop +isPhase2FailureWithMsg f _ (MCEValidationError Ledger.Phase2 (Ledger.ScriptFailure (Ledger.EvaluationError texts _))) | any (f . T.unpack) texts = testSuccess +isPhase2FailureWithMsg _ pcOpts e = testFailureMsg $ "Expected phase 2 evaluation failure with constrained messages, got: " ++ renderString (prettyCookedOpt pcOpts) e + +-- | Same as 'testFailsInPhase2' with an added predicate over the text error +testFailsInPhase2WithMsg :: (IsProp prop, Show a) => (String -> Bool) -> StagedMockChain a -> prop +testFailsInPhase2WithMsg f = testToProp . (`withPrettyAndErrorPred` isPhase2FailureWithMsg f) . mustFailTest diff --git a/src/Cooked/MockChain/UtxoState.hs b/src/Cooked/MockChain/UtxoState.hs index 5100257e..c0398ae4 100644 --- a/src/Cooked/MockChain/UtxoState.hs +++ b/src/Cooked/MockChain/UtxoState.hs @@ -14,7 +14,7 @@ import Data.Function (on) import Data.List qualified as List import Data.Map.Strict (Map) import Data.Map.Strict qualified as Map -import Plutus.Script.Utils.Value qualified as Script +import PlutusLedgerApi.V1.Value qualified as Api import PlutusLedgerApi.V3 qualified as Api -- | A description of who owns what in a blockchain. Owners are addresses and @@ -51,7 +51,7 @@ data UtxoPayload = UtxoPayload instance Eq UtxoPayloadSet where (UtxoPayloadSet xs) == (UtxoPayloadSet ys) = xs' == ys' where - k (UtxoPayload ref val dat rs) = (ref, Script.flattenValue val, dat, rs) + k (UtxoPayload ref val dat rs) = (ref, Api.flattenValue val, dat, rs) xs' = List.sortBy (compare `on` k) xs ys' = List.sortBy (compare `on` k) ys diff --git a/tests/Cooked/Attack/DatumHijackingSpec.hs b/tests/Cooked/Attack/DatumHijackingSpec.hs index 9f86b29b..0f471c6d 100644 --- a/tests/Cooked/Attack/DatumHijackingSpec.hs +++ b/tests/Cooked/Attack/DatumHijackingSpec.hs @@ -227,30 +227,25 @@ tests = @=? fst <$> skelOut x2 (1 ==) ], testCase "careful validator" $ - testFails - def - (isCekEvaluationFailure def) - ( somewhere - ( datumHijackingAttack @DHContract - ( \(ConcreteOutput v _ d _ _) -> - Script.validatorHash v == Script.validatorHash carefulValidator - && d == TxSkelOutInlineDatum SecondLock - ) - (const True) - ) - (datumHijackingTrace carefulValidator) - ), + testFailsInPhase2 $ + somewhere + ( datumHijackingAttack @DHContract + ( \(ConcreteOutput v _ d _ _) -> + Script.validatorHash v == Script.validatorHash carefulValidator + && d == TxSkelOutInlineDatum SecondLock + ) + (const True) + ) + (datumHijackingTrace carefulValidator), testCase "careless validator" $ - testSucceeds - def - ( somewhere - ( datumHijackingAttack @DHContract - ( \(ConcreteOutput v _ d _ _) -> - Script.validatorHash v == Script.validatorHash carelessValidator - && d == TxSkelOutInlineDatum SecondLock - ) - (const True) - ) - (datumHijackingTrace carelessValidator) - ) + testSucceeds $ + somewhere + ( datumHijackingAttack @DHContract + ( \(ConcreteOutput v _ d _ _) -> + Script.validatorHash v == Script.validatorHash carelessValidator + && d == TxSkelOutInlineDatum SecondLock + ) + (const True) + ) + (datumHijackingTrace carelessValidator) ] diff --git a/tests/Cooked/Attack/DupTokenSpec.hs b/tests/Cooked/Attack/DupTokenSpec.hs index 2f6866eb..82acdddb 100644 --- a/tests/Cooked/Attack/DupTokenSpec.hs +++ b/tests/Cooked/Attack/DupTokenSpec.hs @@ -114,17 +114,14 @@ tests = testCase "careful minting policy" $ let tName = Script.tokenName "MockToken" pol = carefulPolicy tName 1 - in testFails - def - (isCekEvaluationFailure def) - ( somewhere - (dupTokenAttack (\_ n -> n + 1) (wallet 6)) - (dupTokenTrace pol tName 1 (wallet 1)) - ), + in testFailsInPhase2 $ + somewhere + (dupTokenAttack (\_ n -> n + 1) (wallet 6)) + (dupTokenTrace pol tName 1 (wallet 1)), testCase "careless minting policy" $ let tName = Script.tokenName "MockToken" pol = carelessPolicy - in testSucceeds def $ + in testSucceeds $ somewhere (dupTokenAttack (\_ n -> n + 1) (wallet 6)) (dupTokenTrace pol tName 1 (wallet 1)), diff --git a/tests/Cooked/BalancingSpec.hs b/tests/Cooked/BalancingSpec.hs index a3833248..496e55f4 100644 --- a/tests/Cooked/BalancingSpec.hs +++ b/tests/Cooked/BalancingSpec.hs @@ -179,61 +179,71 @@ reachingMagic = do } } -type ResProp prop = TestBalancingOutcome -> prop +type ResProp = TestBalancingOutcome -> Assertion -hasFee :: (IsProp prop) => Integer -> ResProp prop +hasFee :: Integer -> ResProp hasFee fee (_, _, fee', _, _) = testBool $ fee == fee' -additionalOutsNb :: (IsProp prop) => Int -> ResProp prop +additionalOutsNb :: Int -> ResProp additionalOutsNb ao (txSkel1, txSkel2, _, _, _) = testBool $ length (txSkelOuts txSkel2) - length (txSkelOuts txSkel1) == ao -insNb :: (IsProp prop) => Int -> ResProp prop +insNb :: Int -> ResProp insNb is (_, TxSkel {..}, _, _, _) = testBool $ length txSkelIns == is -colInsNb :: (IsProp prop) => Int -> ResProp prop +colInsNb :: Int -> ResProp colInsNb cis (_, _, _, Nothing, _) = testBool $ cis == 0 colInsNb cis (_, _, _, Just (refs, _), _) = testBool $ cis == length refs -retOutsNb :: (IsProp prop) => Int -> ResProp prop +retOutsNb :: Int -> ResProp retOutsNb ros (_, _, _, _, refs) = testBool $ ros == length refs -testBalancingSucceedsWith :: String -> [ResProp Assertion] -> StagedMockChain TestBalancingOutcome -> TestTree -testBalancingSucceedsWith msg props smc = testCase msg $ testSucceedsFrom' def (\res _ -> testConjoin $ ($ res) <$> props) initialDistributionBalancing smc +testBalancingSucceedsWith :: String -> [ResProp] -> StagedMockChain TestBalancingOutcome -> TestTree +testBalancingSucceedsWith msg props run = + testCase msg $ + testToProp $ + mustSucceedTest run + `withInitDist` initialDistributionBalancing + `withValuePred` \res -> testConjoin (($ res) <$> props) -failsAtBalancingWith :: (IsProp prop) => Api.Value -> Wallet -> MockChainError -> prop +failsAtBalancingWith :: Api.Value -> Wallet -> MockChainError -> Assertion failsAtBalancingWith val' wal' (MCEUnbalanceable wal val _) = testBool $ val' == val && wal' == wal failsAtBalancingWith _ _ _ = testBool False -failsAtBalancing :: (IsProp prop) => MockChainError -> prop +failsAtBalancing :: MockChainError -> Assertion failsAtBalancing MCEUnbalanceable {} = testBool True failsAtBalancing _ = testBool False -failsWithTooLittleFee :: (IsProp prop) => MockChainError -> prop +failsWithTooLittleFee :: MockChainError -> Assertion failsWithTooLittleFee (MCEValidationError Ledger.Phase1 (Ledger.CardanoLedgerValidationError text)) = testBool $ isInfixOf "FeeTooSmallUTxO" text failsWithTooLittleFee _ = testBool False -failsWithValueNotConserved :: (IsProp prop) => MockChainError -> prop +failsWithValueNotConserved :: MockChainError -> Assertion failsWithValueNotConserved (MCEValidationError Ledger.Phase1 (Ledger.CardanoLedgerValidationError text)) = testBool $ isInfixOf "ValueNotConserved" text failsWithValueNotConserved _ = testBool False -failsWithEmptyTxIns :: (IsProp prop) => MockChainError -> prop +failsWithEmptyTxIns :: MockChainError -> Assertion failsWithEmptyTxIns (MCEGenerationError (TxBodyError _ Cardano.TxBodyEmptyTxIns)) = testBool True failsWithEmptyTxIns _ = testBool False -failsAtCollateralsWith :: (IsProp prop) => Integer -> MockChainError -> prop +failsAtCollateralsWith :: Integer -> MockChainError -> Assertion failsAtCollateralsWith fee' (MCENoSuitableCollateral fee percentage val) = testBool $ fee == fee' && val == Script.lovelace (1 + (fee * percentage) `div` 100) failsAtCollateralsWith _ _ = testBool False -failsAtCollaterals :: (IsProp prop) => MockChainError -> prop +failsAtCollaterals :: MockChainError -> Assertion failsAtCollaterals MCENoSuitableCollateral {} = testBool True failsAtCollaterals _ = testBool False -failsLackOfCollateralWallet :: (IsProp prop) => MockChainError -> prop +failsLackOfCollateralWallet :: MockChainError -> Assertion failsLackOfCollateralWallet (FailWith msg) = testBool $ "Can't select collateral utxos from a balancing wallet because it does not exist." == msg failsLackOfCollateralWallet _ = testBool False testBalancingFailsWith :: (Show a) => String -> (MockChainError -> Assertion) -> StagedMockChain a -> TestTree -testBalancingFailsWith msg p smc = testCase msg $ testFailsFrom def p initialDistributionBalancing smc +testBalancingFailsWith msg p smc = + testCase msg $ + testToProp $ + mustFailTest smc + `withInitDist` initialDistributionBalancing + `withErrorPred` p tests :: TestTree tests = @@ -345,7 +355,8 @@ tests = testGroup "Manual balancing with auto fee" [ testCase "Auto fee with manual balancing yields maximum fee" $ - testSucceedsFrom def initialDistributionBalancing noBalanceMaxFee + testToProp $ + mustSucceedTest noBalanceMaxFee `withInitDist` initialDistributionBalancing ], testGroup "Auto balancing with auto fee" @@ -383,71 +394,67 @@ tests = id ), testCase "Auto fee are minimal: less fee will lead to strictly smaller fee than Cardano's estimate" $ - testSucceedsFrom' - def - ( \(feeBalanced, feeBalanced', feeBalancedManual, feeBalancedManual') _ -> - testBool $ - feeBalanced' <= feeBalanced && feeBalancedManual' > feeBalancedManual - ) - initialDistributionBalancing - balanceReduceFee, + testToProp $ + mustSucceedTest balanceReduceFee + `withInitDist` initialDistributionBalancing + `withValuePred` \(feeBalanced, feeBalanced', feeBalancedManual, feeBalancedManual') -> + testBool $ feeBalanced' <= feeBalanced && feeBalancedManual' > feeBalancedManual, testCase "The auto-fee process can sometimes recover from a temporary balancing error..." $ - testSucceedsFrom - def - initialDistributionBalancing - ( simplePaymentToBob - 103_650_000 - 0 - 0 - 0 - False - id - ), + testToProp $ + mustSucceedTest + ( simplePaymentToBob + 103_650_000 + 0 + 0 + 0 + False + id + ) + `withInitDist` initialDistributionBalancing, testCase "... but not always" $ - testFailsFrom - def - failsAtBalancing - initialDistributionBalancing - ( simplePaymentToBob - 104_000_000 - 0 - 0 - 0 - False - id - ), + testToProp $ + mustFailTest + ( simplePaymentToBob + 104_000_000 + 0 + 0 + 0 + False + id + ) + `withInitDist` initialDistributionBalancing + `withErrorPred` failsAtBalancing, testCase "The auto-fee process can recover from a temporary collateral error..." $ - testSucceedsFrom - def - initialDistributionBalancing - ( testingBalancingTemplate - (Script.ada 142) - mempty - emptySearch - emptySearch - (aliceNAdaUtxos 2) - True - id - ), + testToProp $ + mustSucceedTest + ( testingBalancingTemplate + (Script.ada 142) + mempty + emptySearch + emptySearch + (aliceNAdaUtxos 2) + True + id + ) + `withInitDist` initialDistributionBalancing, testCase "... but not always" $ - testFailsFrom - def - failsAtCollaterals - initialDistributionBalancing - ( testingBalancingTemplate - (Script.ada 142) - mempty - (utxosAtSearch alice) - emptySearch - (aliceNAdaUtxos 1) - True - id - ), + testToProp $ + mustFailTest + ( testingBalancingTemplate + (Script.ada 142) + mempty + (utxosAtSearch alice) + emptySearch + (aliceNAdaUtxos 1) + True + id + ) + `withInitDist` initialDistributionBalancing + `withErrorPred` failsAtCollaterals, testCase "Reaching magical spot with the exact balance during auto fee computation" $ - testSucceedsFrom - def - initialDistributionBalancing - reachingMagic + testToProp $ + mustSucceedTest reachingMagic + `withInitDist` initialDistributionBalancing ], testGroup "Auto balancing with manual fee" diff --git a/tests/Cooked/BasicUsageSpec.hs b/tests/Cooked/BasicUsageSpec.hs index 7a5a1a65..f9c1cfcc 100644 --- a/tests/Cooked/BasicUsageSpec.hs +++ b/tests/Cooked/BasicUsageSpec.hs @@ -67,9 +67,9 @@ tests :: TestTree tests = testGroup "Basic usage" - [ testCase "Payment from alice to bob, with auto-balancing" $ testSucceedsFrom def def (pkToPk alice bob 10), - testCase "Circular payments of 10 Script.ada between alice bob and carrie" $ testSucceedsFrom def def multiplePksToPks, - testCase "Minting quick tokens" $ testSucceedsFrom def def mintingQuickValue, - testCase "Paying to the always true validator" $ testSucceedsFrom def def payToAlwaysTrueValidator, - testCase "Consuming the always true validator" $ testSucceedsFrom def def consumeAlwaysTrueValidator + [ testCase "Payment from alice to bob, with auto-balancing" $ testSucceeds $ pkToPk alice bob 10, + testCase "Circular payments of 10 Script.ada between alice bob and carrie" $ testSucceeds multiplePksToPks, + testCase "Minting quick tokens" $ testSucceeds mintingQuickValue, + testCase "Paying to the always true validator" $ testSucceeds payToAlwaysTrueValidator, + testCase "Consuming the always true validator" $ testSucceeds consumeAlwaysTrueValidator ] diff --git a/tests/Cooked/InitialDistributionSpec.hs b/tests/Cooked/InitialDistributionSpec.hs index d586035a..81fa0845 100644 --- a/tests/Cooked/InitialDistributionSpec.hs +++ b/tests/Cooked/InitialDistributionSpec.hs @@ -2,7 +2,6 @@ module Cooked.InitialDistributionSpec where import Control.Monad import Cooked -import Data.Default import Data.Map qualified as Map import Data.Maybe (catMaybes) import Plutus.Script.Utils.Value qualified as Script @@ -52,7 +51,12 @@ tests = testGroup "Initial distributions" [ testCase "Reading datums placed in the initial distribution, inlined or hashed" $ - testSucceedsFrom' def (\results _ -> testBool $ results == [10, 10]) initialDistributionWithDatum getValueFromInitialDatum, + testToProp $ + mustSucceedTest getValueFromInitialDatum + `withInitDist` initialDistributionWithDatum + `withValuePred` (testBool . (== [10, 10])), testCase "Spending a script placed as a reference script in the initial distribution" $ - testSucceedsFrom def initialDistributionWithReferenceScript spendReferenceAlwaysTrueValidator + testToProp $ + mustSucceedTest spendReferenceAlwaysTrueValidator + `withInitDist` initialDistributionWithReferenceScript ] diff --git a/tests/Cooked/InlineDatumsSpec.hs b/tests/Cooked/InlineDatumsSpec.hs index 0a82d35d..85b4da1d 100644 --- a/tests/Cooked/InlineDatumsSpec.hs +++ b/tests/Cooked/InlineDatumsSpec.hs @@ -200,19 +200,19 @@ tests = [ testGroup "validator expects an inline datum..." [ testCase "...and gets an inline datum, expecting success" $ - testSucceeds def $ + testSucceeds $ spendOutputTestTrace True (inputDatumValidator True), testCase "...and gets a datum hash, expecting script failure" $ - testFails def (isCekEvaluationFailure def) $ + testFailsInPhase2 $ spendOutputTestTrace False (inputDatumValidator True) ], testGroup "validator expects a datum hash..." [ testCase "...and gets an inline datum, expecting script failure" $ - testFails def (isCekEvaluationFailure def) $ + testFailsInPhase2 $ spendOutputTestTrace True (inputDatumValidator False), testCase "...and gets a datum hash, expecting success" $ - testSucceeds def $ + testSucceeds $ spendOutputTestTrace False (inputDatumValidator False) ] ], @@ -221,37 +221,37 @@ tests = [ testGroup "validator expects a regular datum..." [ testCase "...and gets a regular datum, expecting success" $ - testSucceeds def $ + testSucceeds $ continuingOutputTestTrace Datum (outputDatumValidator Datum), testCase "...and gets an inline datum, expecting script failure" $ - testFails def (isCekEvaluationFailure def) $ + testFailsInPhase2 $ continuingOutputTestTrace Inline (outputDatumValidator Datum), testCase "...and gets a datum hash, expecting script failure" $ - testFails def (isCekEvaluationFailure def) $ + testFailsInPhase2 $ continuingOutputTestTrace OnlyHash (outputDatumValidator Datum) ], testGroup "validator expects an inline datum..." [ testCase "...and gets a regular datum, expecting script failure" $ - testFails def (isCekEvaluationFailure def) $ + testFailsInPhase2 $ continuingOutputTestTrace Datum (outputDatumValidator Inline), testCase "...and gets an inline datum, expecting success" $ - testSucceeds def $ + testSucceeds $ continuingOutputTestTrace Inline (outputDatumValidator Inline), testCase "...and gets a datum hash, expecting script failure" $ - testFails def (isCekEvaluationFailure def) $ + testFailsInPhase2 $ continuingOutputTestTrace OnlyHash (outputDatumValidator Inline) ], testGroup "validator expects a datum hash..." [ testCase "...and gets a regular datum, expecting script failure" $ - testFails def (isCekEvaluationFailure def) $ + testFailsInPhase2 $ continuingOutputTestTrace Datum (outputDatumValidator OnlyHash), testCase "...and gets an inline datum, expecting script failure" $ - testFails def (isCekEvaluationFailure def) $ + testFailsInPhase2 $ continuingOutputTestTrace Inline (outputDatumValidator OnlyHash), testCase "...and gets a datum hash, expecting success" $ - testSucceeds def $ + testSucceeds $ continuingOutputTestTrace OnlyHash (outputDatumValidator OnlyHash) ] ] diff --git a/tests/Cooked/MinAdaSpec.hs b/tests/Cooked/MinAdaSpec.hs index a24b5432..6a38c2fb 100644 --- a/tests/Cooked/MinAdaSpec.hs +++ b/tests/Cooked/MinAdaSpec.hs @@ -3,7 +3,6 @@ module Cooked.MinAdaSpec where import Control.Monad import Cooked import Data.Default -import Ledger.Index qualified as Ledger import Optics.Core ((^.)) import Plutus.Script.Utils.Ada qualified as Script import Plutus.Script.Utils.Value qualified as Script @@ -36,13 +35,6 @@ tests :: TestTree tests = testGroup "MinAda auto adjustment of transaction outputs" - [ testCase "adjusted transaction passes" $ testSucceeds def paymentWithMinAda, - testCase "adjusted transaction contains minimal amount" - $ testFails - def - ( \case - MCEValidationError Ledger.Phase1 _ -> testSuccess - _ -> testFailure - ) - $ paymentWithMinAda >>= paymentWithoutMinAda . (+ (-1)) + [ testCase "adjusted transaction passes" $ testSucceeds paymentWithMinAda, + testCase "adjusted transaction contains minimal amount" $ testFailsInPhase1 $ paymentWithMinAda >>= paymentWithoutMinAda . (+ (-1)) ] diff --git a/tests/Cooked/ProposingScriptSpec.hs b/tests/Cooked/ProposingScriptSpec.hs index 63d118aa..f2b53b39 100644 --- a/tests/Cooked/ProposingScriptSpec.hs +++ b/tests/Cooked/ProposingScriptSpec.hs @@ -2,9 +2,7 @@ module Cooked.ProposingScriptSpec where import Control.Monad import Cooked -import Data.Default import Data.Map qualified as Map -import Ledger.Index qualified as Ledger import Plutus.Script.Utils.Scripts qualified as Script import Plutus.Script.Utils.Value qualified as Script import PlutusLedgerApi.V3 qualified as Api @@ -60,21 +58,21 @@ tests = testGroup "Proposing scripts" [ testCase "The always True proposing script succeeds" $ - testSucceeds def $ + testSucceeds $ testProposingScript alwaysTrueProposingValidator (TxGovActionTreasuryWithdrawals Map.empty), testCase "The always True proposing script suceeds as a reference script" $ - testSucceeds def $ + testSucceeds $ testProposingRefScript alwaysTrueProposingValidator (TxGovActionTreasuryWithdrawals Map.empty), testCase "The always False proposing script fails" $ - testFails def (isCekEvaluationFailure def) $ + testFailsInPhase2 $ testProposingScript alwaysFalseProposingValidator (TxGovActionTreasuryWithdrawals Map.empty), testCase "A more advanced proposing script can succeed" $ - testSucceeds def $ + testSucceeds $ testProposingScript checkProposingScript (TxGovActionParameterChange [FeePerByte 100]), testCase "A more advanced proposing script can succeed as a reference script" $ - testSucceeds def $ + testSucceeds $ testProposingRefScript checkProposingScript (TxGovActionParameterChange [FeePerByte 100]), testCase "Proposing scripts are restricted to parameter changes or treasury withdrawals" $ - testFails def (\case (MCEValidationError Ledger.Phase1 _) -> testBool True; _ -> testBool False) $ + testFailsInPhase1 $ testProposingScript alwaysFalseProposingValidator TxGovActionNoConfidence ] diff --git a/tests/Cooked/ReferenceInputsSpec.hs b/tests/Cooked/ReferenceInputsSpec.hs index cad6e437..f4a8c0a6 100644 --- a/tests/Cooked/ReferenceInputsSpec.hs +++ b/tests/Cooked/ReferenceInputsSpec.hs @@ -2,7 +2,6 @@ module Cooked.ReferenceInputsSpec where import Control.Monad import Cooked -import Data.Default import Data.Map qualified as Map import Data.Set qualified as Set import Plutus.Script.Utils.Typed qualified as Script @@ -147,6 +146,6 @@ tests :: Tasty.TestTree tests = Tasty.testGroup "Reference inputs" - [ Tasty.testCase "We can reference an input that can't be spent" (testSucceeds def trace1), - Tasty.testCase "We can decode the datum hash from a reference input" (testSucceeds def trace2) + [ Tasty.testCase "We can reference an input that can't be spent" $ testSucceeds trace1, + Tasty.testCase "We can decode the datum hash from a reference input" $ testSucceeds trace2 ] diff --git a/tests/Cooked/ReferenceScriptsSpec.hs b/tests/Cooked/ReferenceScriptsSpec.hs index 254f1ecc..45d47f0b 100644 --- a/tests/Cooked/ReferenceScriptsSpec.hs +++ b/tests/Cooked/ReferenceScriptsSpec.hs @@ -7,7 +7,6 @@ import Data.Default import Data.Map qualified as Map import Data.Maybe import Data.Set qualified as Set -import Ledger.Index qualified as Ledger import Optics.Core import Plutus.Script.Utils.Ada qualified as Script import Plutus.Script.Utils.Scripts qualified as Script @@ -144,113 +143,95 @@ tests = [ testGroup "putting reference scripts on chain and retrieving them" $ let theRefScript = alwaysFalseValidator theRefScriptHash = toScriptHash theRefScript - in [ testCase "on a public key output" - $ testSucceedsFrom' - def - ( \mScriptHash _ -> - testCounterexample "the script hash on the retrieved output is wrong" $ - Just theRefScriptHash .==. mScriptHash - ) - def - $ putRefScriptOnWalletOutput (wallet 3) theRefScript - >>= retrieveRefScriptHash, - testCase "on a script output" - $ testSucceedsFrom' - def - ( \mScriptHash _ -> - testCounterexample "the script hash on the retrieved output is wrong" $ - Just theRefScriptHash .==. mScriptHash - ) - def - $ putRefScriptOnScriptOutput alwaysTrueValidator theRefScript - >>= retrieveRefScriptHash, - testCase "retrieving the complete script from its hash" - $ testSucceedsFrom' - def - ( \mOut _ -> case mOut of - Nothing -> testFailure - Just out -> Just (Script.vValidatorScript theRefScript) .==. out ^. outputReferenceScriptL - ) - def - $ putRefScriptOnWalletOutput (wallet 3) theRefScript - >>= fmap fromJust . txOutByRef - >>= resolveReferenceScript + in [ testCase "on a public key output" $ + testToProp $ + mustSucceedTest + ( putRefScriptOnWalletOutput (wallet 3) theRefScript + >>= retrieveRefScriptHash + ) + `withValuePred` ( testCounterexample "the script hash on the retrieved output is wrong" + . (Just theRefScriptHash .==.) + ), + testCase "on a script output" $ + testToProp $ + mustSucceedTest + ( putRefScriptOnScriptOutput alwaysTrueValidator theRefScript + >>= retrieveRefScriptHash + ) + `withValuePred` ( testCounterexample "the script hash on the retrieved output is wrong" + . (Just theRefScriptHash .==.) + ), + testCase "retrieving the complete script from its hash" $ + testToProp $ + mustSucceedTest + ( putRefScriptOnWalletOutput (wallet 3) theRefScript + >>= fmap fromJust . txOutByRef + >>= resolveReferenceScript + ) + `withValuePred` maybe testFailure ((Just (Script.vValidatorScript theRefScript) .==.) . (^. outputReferenceScriptL)) ], testGroup "checking the presence of reference scripts on the TxInfo" [ testCase "fail if wrong reference script" - $ testFails - def - ( isCekEvaluationFailureWithMsg - def - (== "there is no reference input with the correct script hash") - ) + $ testFailsInPhase2WithMsg + (== "there is no reference input with the correct script hash") $ putRefScriptOnWalletOutput (wallet 3) alwaysFalseValidator >>= checkReferenceScriptOnOref (toScriptHash alwaysTrueValidator), testCase "succeed if correct reference script" $ - testSucceeds def $ + testSucceeds $ putRefScriptOnWalletOutput (wallet 3) alwaysTrueValidator >>= checkReferenceScriptOnOref (toScriptHash alwaysTrueValidator) ], testGroup "using reference scripts" - [ testCase "fail from transaction generation for missing reference scripts" - $ testFailsFrom - def - ( \case + [ testCase "fail from transaction generation for missing reference scripts" $ + testToProp $ + mustFailTest + ( do + (consumedOref, _) : _ <- + runUtxoSearch $ + utxosAtSearch (wallet 1) + `filterWithPred` ((`Script.geq` Script.lovelaceValueOf 42_000_000) . outputValue) + oref : _ <- + validateTxSkel' + txSkelTemplate + { txSkelOuts = [paysScript (alwaysTrueValidator @MockContract) () (Script.ada 42)], + txSkelIns = Map.singleton consumedOref txSkelEmptyRedeemer, + txSkelSigners = [wallet 1] + } + void $ + validateTxSkel + txSkelTemplate + { txSkelIns = Map.singleton oref (txSkelSomeRedeemerAndReferenceScript consumedOref ()), + txSkelSigners = [wallet 1] + } + ) + `withErrorPred` \case MCEUnknownOutRefError "lookupUtxos: unknown TxOutRef" _ -> testSuccess - _ -> testFailure - ) - def - $ do - (consumedOref, _) : _ <- - runUtxoSearch $ - utxosAtSearch (wallet 1) - `filterWithPred` ((`Script.geq` Script.lovelaceValueOf 42_000_000) . outputValue) - oref : _ <- - validateTxSkel' - txSkelTemplate - { txSkelOuts = [paysScript (alwaysTrueValidator @MockContract) () (Script.ada 42)], - txSkelIns = Map.singleton consumedOref txSkelEmptyRedeemer, - txSkelSigners = [wallet 1] - } - void $ - validateTxSkel - txSkelTemplate - { txSkelIns = Map.singleton oref (txSkelSomeRedeemerAndReferenceScript consumedOref ()), - txSkelSigners = [wallet 1] - }, - testCase "fail from transaction generation for mismatching reference scripts" - $ testFailsFrom - def - ( \case + _ -> testFailure, + testCase "fail from transaction generation for mismatching reference scripts" $ + testToProp $ + mustFailTest + ( do + scriptOref <- putRefScriptOnWalletOutput (wallet 3) alwaysFalseValidator + oref : _ <- + validateTxSkel' + txSkelTemplate + { txSkelOuts = [paysScript (alwaysTrueValidator @MockContract) () (Script.ada 42)], + txSkelSigners = [wallet 1] + } + void $ + validateTxSkel + txSkelTemplate + { txSkelIns = Map.singleton oref (txSkelSomeRedeemerAndReferenceScript scriptOref ()), + txSkelSigners = [wallet 1] + } + ) + `withErrorPred` \case MCEGenerationError err -> err .==. GenerateTxErrorGeneral "toPlutusScriptOrReferenceInput: Wrong reference script hash." - _ -> testFailure - ) - def - $ do - scriptOref <- putRefScriptOnWalletOutput (wallet 3) alwaysFalseValidator - oref : _ <- - validateTxSkel' - txSkelTemplate - { txSkelOuts = [paysScript (alwaysTrueValidator @MockContract) () (Script.ada 42)], - txSkelSigners = [wallet 1] - } - void $ - validateTxSkel - txSkelTemplate - { txSkelIns = Map.singleton oref (txSkelSomeRedeemerAndReferenceScript scriptOref ()), - txSkelSigners = [wallet 1] - }, - testCase "phase 1 - fail if using a reference script with 'txSkelSomeRedeemer'" - $ testFailsFrom - def - ( \case - MCEValidationError Ledger.Phase1 _ -> testSuccess - _ -> testFailure - ) - def - $ do + _ -> testFailure, + testCase "phase 1 - fail if using a reference script with 'txSkelSomeRedeemer'" $ + testFailsInPhase1 $ do scriptOref <- putRefScriptOnWalletOutput (wallet 3) alwaysTrueValidator oref : _ <- validateTxSkel' @@ -267,38 +248,28 @@ tests = }, testCase "fail if reference script's requirement is violated" - $ testFailsFrom - (def {pcOptPrintTxHashes = True}) - ( isCekEvaluationFailureWithMsg - def - (== "the required signer is missing") - ) - def + $ testFailsInPhase2WithMsg (== "the required signer is missing") $ useReferenceScript (wallet 1) (requireSignerValidator $ walletPKHash $ wallet 2), testCase "succeed if reference script's requirement is met" $ - testSucceeds def $ + testSucceeds $ useReferenceScript (wallet 1) (requireSignerValidator $ walletPKHash $ wallet 1) ], testGroup "referencing minting policies" [ testCase "succeed if given a reference minting policy" $ - testSucceeds def $ + testSucceeds $ referenceMint quickCurrencyPolicyV3 quickCurrencyPolicyV3 0, - testCase "fail if given the wrong reference minting policy" - $ testFails - def - ( \case + testCase "fail if given the wrong reference minting policy" $ + testToProp $ + mustFailTest (referenceMint permanentCurrencyPolicyV3 quickCurrencyPolicyV3 0) + `withErrorPred` \case MCEGenerationError (GenerateTxErrorGeneral err) -> err .==. "toPlutusScriptOrReferenceInput: Wrong reference script hash." - _ -> testFailure - ) - $ referenceMint permanentCurrencyPolicyV3 quickCurrencyPolicyV3 0, - testCase "fail if referencing the wrong utxo" - $ testFails - def - ( \case + _ -> testFailure, + testCase "fail if referencing the wrong utxo" $ + testToProp $ + mustFailTest (referenceMint quickCurrencyPolicyV3 quickCurrencyPolicyV3 1) + `withErrorPred` \case MCEGenerationError (GenerateTxErrorGeneral err) -> err .==. "toPlutusScriptOrReferenceInput: Can't resolve reference script utxo." _ -> testFailure - ) - $ referenceMint quickCurrencyPolicyV3 quickCurrencyPolicyV3 1 ] ] diff --git a/tests/Cooked/WithdrawalsSpec.hs b/tests/Cooked/WithdrawalsSpec.hs index def3e0e8..8d5ed934 100644 --- a/tests/Cooked/WithdrawalsSpec.hs +++ b/tests/Cooked/WithdrawalsSpec.hs @@ -2,7 +2,6 @@ module Cooked.WithdrawalsSpec where import Control.Monad import Cooked -import Data.Default import Plutus.Script.Utils.Ada qualified as Script import Plutus.Script.Utils.Scripts qualified as Script import PlutusLedgerApi.V3 qualified as Api @@ -47,9 +46,9 @@ tests = testGroup "Withdrawing scripts" [ testCase "We can use a withdrawing script" $ - testSucceeds def $ + testSucceeds $ testWithdrawingScript 2 2, testCase "But the script might fail" $ - testFailsFrom def (isCekEvaluationFailure def) def $ + testFailsInPhase2 $ testWithdrawingScript 2 1 ]