{-# OPTIONS_GHC -Wno-name-shadowing #-}

-- | This modules provides primitives to run tests over mockchain executions and
-- to provide requirements on the the number and results of these runs.
module Cooked.MockChain.Testing where

import Control.Exception qualified as E
import Control.Monad
import Cooked.MockChain.Error
import Cooked.MockChain.Journal
import Cooked.MockChain.Log
import Cooked.MockChain.Runnable
import Cooked.MockChain.State
import Cooked.MockChain.Write
import Cooked.Pretty
import Data.Default
import Data.List (isInfixOf)
import Data.Set qualified as Set
import Data.Text qualified as T
import Ledger qualified
import Plutus.Script.Utils.Address qualified as Script
import PlutusLedgerApi.V1.Value qualified as Api
import Polysemy
import Test.QuickCheck qualified as QC
import Test.Tasty qualified as HU
import Test.Tasty.HUnit qualified as HU
import Test.Tasty.QuickCheck qualified as QC

-- * 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

  -- | Conjunction of a number of results
  testConjoin :: [prop] -> prop

  -- | Disjunction of a number of results
  testDisjoin :: [prop] -> prop

  -- | Flags a failure
  testFailure :: prop
  testFailure = [prop] -> prop
forall prop. IsProp prop => [prop] -> prop
testDisjoin []

  -- | Flags a success
  testSuccess :: prop
  testSuccess = [prop] -> prop
forall prop. IsProp prop => [prop] -> prop
testConjoin []

  -- | Flags a failure with a message
  testFailureMsg :: String -> prop
  testFailureMsg String
msg = String -> prop -> prop
forall prop. IsProp prop => String -> prop -> prop
testCounterexample String
msg prop
forall prop. IsProp prop => prop
testFailure

-- | Turns a boolean into a @prop@
testBool :: (IsProp prop) => Bool -> prop
testBool :: forall prop. IsProp prop => Bool -> prop
testBool Bool
True = prop
forall prop. IsProp prop => prop
testSuccess
testBool Bool
False = prop
forall prop. IsProp prop => prop
testFailure

-- | Turns a boolean into a @prop@, displaying an error message when applicable
testBoolMsg :: (IsProp prop) => String -> Bool -> prop
testBoolMsg :: forall prop. IsProp prop => String -> Bool -> prop
testBoolMsg String
_ Bool
True = prop
forall prop. IsProp prop => prop
testSuccess
testBoolMsg String
msg Bool
False = String -> prop
forall prop. IsProp prop => String -> prop
testFailureMsg String
msg

-- | Ensures all elements of a list satisfy a given @prop@
testAll :: (IsProp prop) => (a -> prop) -> [a] -> prop
testAll :: forall prop a. IsProp prop => (a -> prop) -> [a] -> prop
testAll a -> prop
f = [prop] -> prop
forall prop. IsProp prop => [prop] -> prop
testConjoin ([prop] -> prop) -> ([a] -> [prop]) -> [a] -> prop
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (a -> prop) -> [a] -> [prop]
forall a b. (a -> b) -> [a] -> [b]
map a -> prop
f

-- | Ensures at least one element of a list satisfy a given @prop@
testAny :: (IsProp prop) => (a -> prop) -> [a] -> prop
testAny :: forall prop a. IsProp prop => (a -> prop) -> [a] -> prop
testAny a -> prop
f = [prop] -> prop
forall prop. IsProp prop => [prop] -> prop
testDisjoin ([prop] -> prop) -> ([a] -> [prop]) -> [a] -> prop
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (a -> prop) -> [a] -> [prop]
forall a b. (a -> b) -> [a] -> [b]
map a -> prop
f

infix 4 .==.

-- | Lifts an equality test to a @prop@
(.==.) :: (IsProp prop, Eq a) => a -> a -> prop
a
a .==. :: forall prop a. (IsProp prop, Eq a) => a -> a -> prop
.==. a
b = Bool -> prop
forall prop. IsProp prop => Bool -> prop
testBool (Bool -> prop) -> Bool -> prop
forall a b. (a -> b) -> a -> b
$ a
a a -> a -> Bool
forall a. Eq a => a -> a -> Bool
== a
b

infixr 3 .&&.

-- | Conjunction of two @prop@s
(.&&.) :: (IsProp prop) => prop -> prop -> prop
prop
a .&&. :: forall prop. IsProp prop => prop -> prop -> prop
.&&. prop
b = [prop] -> prop
forall prop. IsProp prop => [prop] -> prop
testConjoin [prop
a, prop
b]

infixr 2 .||.

-- | Disjunction of two @prop@s
(.||.) :: (IsProp prop) => prop -> prop -> prop
prop
a .||. :: forall prop. IsProp prop => prop -> prop -> prop
.||. prop
b = [prop] -> prop
forall prop. IsProp prop => [prop] -> prop
testDisjoin [prop
a, prop
b]

-- | Catches a HUnit test failure, if the test fails.
assertionToMaybe ::
  HU.Assertion ->
  IO (Maybe HU.HUnitFailure)
assertionToMaybe :: Assertion -> IO (Maybe HUnitFailure)
assertionToMaybe = (IO (Maybe HUnitFailure)
 -> [Handler (Maybe HUnitFailure)] -> IO (Maybe HUnitFailure))
-> [Handler (Maybe HUnitFailure)]
-> IO (Maybe HUnitFailure)
-> IO (Maybe HUnitFailure)
forall a b c. (a -> b -> c) -> b -> a -> c
flip IO (Maybe HUnitFailure)
-> [Handler (Maybe HUnitFailure)] -> IO (Maybe HUnitFailure)
forall a. IO a -> [Handler a] -> IO a
E.catches [(HUnitFailure -> IO (Maybe HUnitFailure))
-> Handler (Maybe HUnitFailure)
forall a e. Exception e => (e -> IO a) -> Handler a
E.Handler ((HUnitFailure -> IO (Maybe HUnitFailure))
 -> Handler (Maybe HUnitFailure))
-> (HUnitFailure -> IO (Maybe HUnitFailure))
-> Handler (Maybe HUnitFailure)
forall a b. (a -> b) -> a -> b
$ Maybe HUnitFailure -> IO (Maybe HUnitFailure)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe HUnitFailure -> IO (Maybe HUnitFailure))
-> (HUnitFailure -> Maybe HUnitFailure)
-> HUnitFailure
-> IO (Maybe HUnitFailure)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. HUnitFailure -> Maybe HUnitFailure
forall a. a -> Maybe a
Just] (IO (Maybe HUnitFailure) -> IO (Maybe HUnitFailure))
-> (Assertion -> IO (Maybe HUnitFailure))
-> Assertion
-> IO (Maybe HUnitFailure)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Assertion -> IO (Maybe HUnitFailure) -> IO (Maybe HUnitFailure)
forall a b. IO a -> IO b -> IO b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Maybe HUnitFailure -> IO (Maybe HUnitFailure)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe HUnitFailure
forall a. Maybe a
Nothing)

-- | HUnit instance of 'IsProp'
instance IsProp HU.Assertion where
  testCounterexample :: String -> Assertion -> Assertion
testCounterexample String
msg = Assertion
-> (HUnitFailure -> Assertion) -> Maybe HUnitFailure -> Assertion
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Assertion
forall prop. IsProp prop => prop
testSuccess (HUnitFailure -> Assertion
forall a e. Exception e => e -> a
E.throw (HUnitFailure -> Assertion)
-> (HUnitFailure -> HUnitFailure) -> HUnitFailure -> Assertion
forall b c a. (b -> c) -> (a -> b) -> a -> c
. HUnitFailure -> HUnitFailure
adjustMsg) (Maybe HUnitFailure -> Assertion)
-> (Assertion -> IO (Maybe HUnitFailure)) -> Assertion -> Assertion
forall (m :: * -> *) b c a.
Monad m =>
(b -> m c) -> (a -> m b) -> a -> m c
<=< Assertion -> IO (Maybe HUnitFailure)
assertionToMaybe
    where
      joinMsg :: String -> String
      joinMsg :: String -> String
joinMsg String
rest = String
msg String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
";\n" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
rest

      adjustMsg :: HU.HUnitFailure -> HU.HUnitFailure
      adjustMsg :: HUnitFailure -> HUnitFailure
adjustMsg (HU.HUnitFailure Maybe SrcLoc
loc String
txt) =
        Maybe SrcLoc -> String -> HUnitFailure
HU.HUnitFailure Maybe SrcLoc
loc (String -> String
joinMsg String
txt)

  testFailure :: Assertion
testFailure = String -> Assertion
forall a. HasCallStack => String -> IO a
HU.assertFailure String
""
  testFailureMsg :: String -> Assertion
testFailureMsg = String -> Assertion
forall a. HasCallStack => String -> IO a
HU.assertFailure

  testConjoin :: [Assertion] -> Assertion
testConjoin = [Assertion] -> Assertion
forall (t :: * -> *) (m :: * -> *) a.
(Foldable t, Monad m) =>
t (m a) -> m ()
sequence_

  testDisjoin :: [Assertion] -> Assertion
testDisjoin [] = Assertion
forall prop. IsProp prop => prop
testFailure
  testDisjoin (Assertion
x : [Assertion]
xs) = Assertion -> IO (Maybe HUnitFailure)
assertionToMaybe Assertion
x IO (Maybe HUnitFailure)
-> (Maybe HUnitFailure -> Assertion) -> Assertion
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Assertion
-> (HUnitFailure -> Assertion) -> Maybe HUnitFailure -> Assertion
forall b a. b -> (a -> b) -> Maybe a -> b
maybe ([Assertion] -> Assertion
forall prop. IsProp prop => [prop] -> prop
testDisjoin [Assertion]
xs) HUnitFailure -> Assertion
forall a e. Exception e => e -> a
E.throw

  testSuccess :: Assertion
testSuccess = () -> Assertion
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ()

-- | QuickCheck instance of 'IsProp'
instance IsProp QC.Property where
  testCounterexample :: String -> Property -> Property
testCounterexample = String -> Property -> Property
forall prop. Testable prop => String -> prop -> Property
QC.counterexample
  testFailure :: Property
testFailure = Bool -> Property
forall prop. Testable prop => prop -> Property
QC.property Bool
False
  testSuccess :: Property
testSuccess = Bool -> Property
forall prop. Testable prop => prop -> Property
QC.property Bool
True
  testConjoin :: [Property] -> Property
testConjoin = [Property] -> Property
forall prop. Testable prop => [prop] -> Property
QC.conjoin
  testDisjoin :: [Property] -> Property
testDisjoin = [Property] -> Property
forall prop. Testable prop => [prop] -> Property
QC.disjoin

-- | Here we provide our own universsal quantifier instead of 'QC.forAll', so we
--  can monomorphize it to returning a 'QC.Property'
forAll ::
  (Show a) =>
  QC.Gen a ->
  (a -> QC.Property) ->
  QC.Property
forAll :: forall a. Show a => Gen a -> (a -> Property) -> Property
forAll = Gen a -> (a -> Property) -> Property
forall a prop.
(Show a, Testable prop) =>
Gen a -> (a -> prop) -> Property
QC.forAll

-- * 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 :: forall a. (Show a, Eq a) => [a] -> [a] -> Assertion
assertSubset [a]
l [a]
r =
  [Assertion] -> Assertion
forall prop. IsProp prop => [prop] -> prop
testConjoin
    ( (a -> Assertion) -> [a] -> [Assertion]
forall a b. (a -> b) -> [a] -> [b]
map
        ( \a
x ->
            HasCallStack => String -> Bool -> Assertion
String -> Bool -> Assertion
HU.assertBool
              ( String
"not a subset:\n\n"
                  String -> String -> String
forall a. [a] -> [a] -> [a]
++ a -> String
forall a. Show a => a -> String
show a
x
                  String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"\n\nis not an element of\n\n"
                  String -> String -> String
forall a. [a] -> [a] -> [a]
++ [a] -> String
forall a. Show a => a -> String
show [a]
r
              )
              (Bool -> Assertion) -> Bool -> Assertion
forall a b. (a -> b) -> a -> b
$ a
x a -> [a] -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [a]
r
        )
        [a]
l
    )

-- | Asserts whether 2 sets are equal, both given as lists.
assertSameSets ::
  ( Show a,
    Eq a
  ) =>
  [a] ->
  [a] ->
  HU.Assertion
assertSameSets :: forall a. (Show a, Eq a) => [a] -> [a] -> Assertion
assertSameSets [a]
l [a]
r =
  HasCallStack => String -> Bool -> Assertion
String -> Bool -> Assertion
HU.assertBool
    (String
"expected lists of the same length, got " String -> String -> String
forall a. [a] -> [a] -> [a]
++ Int -> String
forall a. Show a => a -> String
show ([a] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [a]
l) String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" and " String -> String -> String
forall a. [a] -> [a] -> [a]
++ Int -> String
forall a. Show a => a -> String
show ([a] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [a]
r))
    ([a] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [a]
l Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== [a] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [a]
r)
    Assertion -> Assertion -> Assertion
forall prop. IsProp prop => prop -> prop -> prop
.&&. [a] -> [a] -> Assertion
forall a. (Show a, Eq a) => [a] -> [a] -> Assertion
assertSubset [a]
l [a]
r
    Assertion -> Assertion -> Assertion
forall prop. IsProp prop => prop -> prop -> prop
.&&. [a] -> [a] -> Assertion
forall a. (Show a, Eq a) => [a] -> [a] -> Assertion
assertSubset [a]
r [a]
l

-- * Data structure to test mockchain traces

{--
  Note on properties over the log (or list of 'MockChainLogEntry'): our
  'Test' structure does not directly embed a predicate over the log. Instead
  it is embedded in both the failure and success prediates. The reason is
  simple: the log is generated and accessible in both cases and thus it is
  theoretically possible to define predicates that combine requirements over the
  log and the error in case of failure, and the log and the returning
  state and value in the case of success. If the log predicate was a field
  in itself, this link would be broken and it would not be possible to epxress
  complex requirements that involve both the log and other components of the
  returned elements in the mockchain run. Granted, this use cas is extremely
  rare, but it does not mean our API should not reflect this capability.
  However, we also provide 'LogProp' as in most cases predicating over
  the log itself will be sufficient.
--}

-- | Type of properties over failures
type FailureProp prop = PrettyCookedOpts -> [MockChainLogEntry] -> MockChainError -> UtxoState -> prop

-- | Type of properties over successes
type SuccessProp a prop = PrettyCookedOpts -> [MockChainLogEntry] -> a -> UtxoState -> prop

-- | Type of properties over the number of run outcomes. This does not
-- necessitate a 'PrettyCookedOpts' as parameter as an 'Integer' does not
-- contain anything significant that can be pretty printed.
type SizeProp prop = Integer -> prop

-- | Type of properties over the mockchain log
type LogProp prop = PrettyCookedOpts -> [MockChainLogEntry] -> prop

-- | Type of properties over the 'UtxoState'
type StateProp prop = PrettyCookedOpts -> UtxoState -> prop

-- | Type of trace runners
type Runner effs a b = MockChainState -> InitialDistribution -> Sem effs a -> [MockChainReturn b]

-- | Data structure to test a mockchain trace. @a@ is the return typed of the
-- tested trace, @prop@ is the domain in which the properties live. This is not
-- enforced here, but it will often be assumed that @prop@ satisfies 'IsProp'.
data Test effs a b prop = Test
  { -- | The mockchain trace to test, which returns a result of type a
    forall (effs :: EffectRow) a b prop.
Test effs a b prop -> Sem effs a
testTrace :: Sem effs a,
    -- | The runner of the trace, possibly changing the return type
    forall (effs :: EffectRow) a b prop.
Test effs a b prop -> Runner effs a b
testRunner :: Runner effs a b,
    -- | The initial state from which the trace should be run
    forall (effs :: EffectRow) a b prop.
Test effs a b prop -> MockChainState
testInitState :: MockChainState,
    -- | The initial distribution from which the trace should be run
    forall (effs :: EffectRow) a b prop.
Test effs a b prop -> InitialDistribution
testInitDist :: InitialDistribution,
    -- | The requirement on the number of results
    forall (effs :: EffectRow) a b prop.
Test effs a b prop -> SizeProp prop
testSizeProp :: SizeProp prop,
    -- | The property that should hold in case of failure over the resulting
    -- error and the logs emitted during the run
    forall (effs :: EffectRow) a b prop.
Test effs a b prop -> FailureProp prop
testFailureProp :: FailureProp prop,
    -- | The property that should hold in case of success over the returned
    -- result and the final state of the trace, as well as the logs
    forall (effs :: EffectRow) a b prop.
Test effs a b prop -> SuccessProp b prop
testSuccessProp :: SuccessProp b prop,
    -- | The printing options that should be use to render the test results
    forall (effs :: EffectRow) a b prop.
Test effs a b prop -> PrettyCookedOpts
testPrettyOpts :: PrettyCookedOpts
  }

-- | This takes a 'Test' and transforms it into an actual test case in
-- prop. This is the main function justifying the existence of 'Test'. This runs
-- the traces, ensures there is the right number of outcomes and, depending on
-- the nature of these outcomes, either calls 'testFailureProp' or
-- 'testSuccessProp'. It also uses the aliases emitted during the mockchain run
-- to pretty print messages when applicable.
testToProp ::
  ( IsProp prop,
    Show b
  ) =>
  Test effs a b prop ->
  prop
testToProp :: forall prop b (effs :: EffectRow) a.
(IsProp prop, Show b) =>
Test effs a b prop -> prop
testToProp Test {InitialDistribution
Sem effs a
PrettyCookedOpts
MockChainState
SizeProp prop
SuccessProp b prop
FailureProp prop
Runner effs a b
testTrace :: forall (effs :: EffectRow) a b prop.
Test effs a b prop -> Sem effs a
testRunner :: forall (effs :: EffectRow) a b prop.
Test effs a b prop -> Runner effs a b
testInitState :: forall (effs :: EffectRow) a b prop.
Test effs a b prop -> MockChainState
testInitDist :: forall (effs :: EffectRow) a b prop.
Test effs a b prop -> InitialDistribution
testSizeProp :: forall (effs :: EffectRow) a b prop.
Test effs a b prop -> SizeProp prop
testFailureProp :: forall (effs :: EffectRow) a b prop.
Test effs a b prop -> FailureProp prop
testSuccessProp :: forall (effs :: EffectRow) a b prop.
Test effs a b prop -> SuccessProp b prop
testPrettyOpts :: forall (effs :: EffectRow) a b prop.
Test effs a b prop -> PrettyCookedOpts
testTrace :: Sem effs a
testRunner :: Runner effs a b
testInitState :: MockChainState
testInitDist :: InitialDistribution
testSizeProp :: SizeProp prop
testFailureProp :: FailureProp prop
testSuccessProp :: SuccessProp b prop
testPrettyOpts :: PrettyCookedOpts
..} =
  let results :: [MockChainReturn b]
results = Runner effs a b
testRunner MockChainState
testInitState InitialDistribution
testInitDist Sem effs a
testTrace
   in SizeProp prop
testSizeProp (Int -> Integer
forall a. Integral a => a -> Integer
toInteger ([MockChainReturn b] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [MockChainReturn b]
results))
        prop -> prop -> prop
forall prop. IsProp prop => prop -> prop -> prop
.&&. (MockChainReturn b -> prop) -> [MockChainReturn b] -> prop
forall prop a. IsProp prop => (a -> prop) -> [a] -> prop
testAll
          ( \ret :: MockChainReturn b
ret@(MockChainReturn Either MockChainError b
outcome Map TxOutRef (TxSkelOut, Bool)
_ UtxoState
state (MockChainJournal [MockChainLogEntry]
mcLog Map BuiltinByteString String
names [PrettyCookedOpts -> DocCooked]
_ [(String, Bool)]
assertions)) ->
              let pcOpts :: PrettyCookedOpts
pcOpts = Map BuiltinByteString String
-> PrettyCookedOpts -> PrettyCookedOpts
addHashNames Map BuiltinByteString String
names PrettyCookedOpts
testPrettyOpts
               in [prop] -> prop
forall prop. IsProp prop => [prop] -> prop
testConjoin
                    [ [prop] -> prop
forall prop. IsProp prop => [prop] -> prop
testConjoin ([prop] -> prop) -> [prop] -> prop
forall a b. (a -> b) -> a -> b
$ (String -> Bool -> prop) -> (String, Bool) -> prop
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry String -> Bool -> prop
forall prop. IsProp prop => String -> Bool -> prop
testBoolMsg ((String, Bool) -> prop) -> [(String, Bool)] -> [prop]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [(String, Bool)]
assertions,
                      String -> prop -> prop
forall prop. IsProp prop => String -> prop -> prop
testCounterexample
                        ((MockChainReturn b -> DocCooked) -> MockChainReturn b -> String
forall a. (a -> DocCooked) -> a -> String
renderString (PrettyCookedOpts -> MockChainReturn b -> DocCooked
forall a. PrettyCooked a => PrettyCookedOpts -> a -> DocCooked
prettyCookedOpt PrettyCookedOpts
pcOpts) MockChainReturn b
ret)
                        (prop -> prop) -> prop -> prop
forall a b. (a -> b) -> a -> b
$ case Either MockChainError b
outcome of
                          Left MockChainError
err -> FailureProp prop
testFailureProp PrettyCookedOpts
pcOpts [MockChainLogEntry]
mcLog MockChainError
err UtxoState
state
                          Right b
result -> SuccessProp b prop
testSuccessProp PrettyCookedOpts
pcOpts [MockChainLogEntry]
mcLog b
result UtxoState
state
                    ]
          )
          [MockChainReturn b]
results

-- | A convenience helper when using 'HU.Assertion' which allows to replace
-- 'HU.testCase' with 'testCooked' and thus avoid the use of 'testToProp'.
-- Sadly we cannot generalise it with type classes on @prop@ to work for
-- QuichCheck at GHC will never be able to instantiate @prop@.
testCooked ::
  forall effs a b.
  (Show b) =>
  String ->
  Test effs a b HU.Assertion ->
  HU.TestTree
testCooked :: forall (effs :: EffectRow) a b.
Show b =>
String -> Test effs a b Assertion -> TestTree
testCooked String
name = String -> Assertion -> TestTree
HU.testCase String
name (Assertion -> TestTree)
-> (Test effs a b Assertion -> Assertion)
-> Test effs a b Assertion
-> TestTree
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Test effs a b Assertion -> Assertion
forall prop b (effs :: EffectRow) a.
(IsProp prop, Show b) =>
Test effs a b prop -> prop
testToProp

-- | Same as `testCooked` but first assigns the initial distribution template as
-- a starting point to the test
testCookedFromInitDistTemplate ::
  forall effs a b.
  (Show b) =>
  String ->
  Test effs a b HU.Assertion ->
  HU.TestTree
testCookedFromInitDistTemplate :: forall (effs :: EffectRow) a b.
Show b =>
String -> Test effs a b Assertion -> TestTree
testCookedFromInitDistTemplate String
name =
  String -> Test effs a b Assertion -> TestTree
forall (effs :: EffectRow) a b.
Show b =>
String -> Test effs a b Assertion -> TestTree
testCooked String
name (Test effs a b Assertion -> TestTree)
-> (Test effs a b Assertion -> Test effs a b Assertion)
-> Test effs a b Assertion
-> TestTree
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Test effs a b Assertion
-> InitialDistribution -> Test effs a b Assertion
forall (effs :: EffectRow) a b prop.
Test effs a b prop -> InitialDistribution -> Test effs a b prop
`withInitDist` InitialDistribution
initialDistributionTemplate)

-- | Same as 'testCooked', but for 'QC.Property'
testCookedQC ::
  forall effs a b.
  (Show b) =>
  String ->
  Test effs a b QC.Property ->
  HU.TestTree
testCookedQC :: forall (effs :: EffectRow) a b.
Show b =>
String -> Test effs a b Property -> TestTree
testCookedQC String
name = String -> Property -> TestTree
forall a. Testable a => String -> a -> TestTree
QC.testProperty String
name (Property -> TestTree)
-> (Test effs a b Property -> Property)
-> Test effs a b Property
-> TestTree
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Test effs a b Property -> Property
forall prop b (effs :: EffectRow) a.
(IsProp prop, Show b) =>
Test effs a b prop -> prop
testToProp

-- | Same as `testCookedQC` but first assigns the initial distribution template
-- as a starting point to the test
testCookedQCFromInitDistTemplate ::
  forall effs a b.
  (Show b) =>
  String ->
  Test effs a b QC.Property ->
  HU.TestTree
testCookedQCFromInitDistTemplate :: forall (effs :: EffectRow) a b.
Show b =>
String -> Test effs a b Property -> TestTree
testCookedQCFromInitDistTemplate String
name =
  String -> Test effs a b Property -> TestTree
forall (effs :: EffectRow) a b.
Show b =>
String -> Test effs a b Property -> TestTree
testCookedQC String
name (Test effs a b Property -> TestTree)
-> (Test effs a b Property -> Test effs a b Property)
-> Test effs a b Property
-> TestTree
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Test effs a b Property
-> InitialDistribution -> Test effs a b Property
forall (effs :: EffectRow) a b prop.
Test effs a b prop -> InitialDistribution -> Test effs a b prop
`withInitDist` InitialDistribution
initialDistributionTemplate)

-- * Simple test templates

-- | A test template which expects a success from a trace. This test template is
-- built from a trace and a dedicated runner, to be used for runs that do not
-- implement `RunnableMockChain`. One of the intended uses is for running
-- `Cooked.MockChain.Instance.StagedInjectMockChain` when the additional effect
-- results in a extended return value (such as a resulting state).
mustSucceedTest' ::
  (IsProp prop) =>
  Runner effs a b ->
  Sem effs a ->
  Test effs a b prop
mustSucceedTest' :: forall prop (effs :: EffectRow) a b.
IsProp prop =>
Runner effs a b -> Sem effs a -> Test effs a b prop
mustSucceedTest' Runner effs a b
runner Sem effs a
trace =
  Test
    { testTrace :: Sem effs a
testTrace = Sem effs a
trace,
      testRunner :: Runner effs a b
testRunner = Runner effs a b
runner,
      testInitState :: MockChainState
testInitState = MockChainState
forall a. Default a => a
def,
      testInitDist :: InitialDistribution
testInitDist = InitialDistribution
forall a. Default a => a
def,
      testSizeProp :: SizeProp prop
testSizeProp = Integer -> SizeProp prop
forall prop. IsProp prop => Integer -> SizeProp prop
isAtLeastOfSize Integer
1,
      testFailureProp :: FailureProp prop
testFailureProp = \PrettyCookedOpts
_ [MockChainLogEntry]
_ MockChainError
_ UtxoState
_ -> String -> prop
forall prop. IsProp prop => String -> prop
testFailureMsg String
"💀 Unexpected failure!",
      testSuccessProp :: SuccessProp b prop
testSuccessProp = \PrettyCookedOpts
_ [MockChainLogEntry]
_ b
_ UtxoState
_ -> prop
forall prop. IsProp prop => prop
testSuccess,
      testPrettyOpts :: PrettyCookedOpts
testPrettyOpts = PrettyCookedOpts
forall a. Default a => a
def
    }

-- | A test template which expects a success from a `RunnableMockChain` trace.
mustSucceedTest ::
  ( IsProp prop,
    RunnableMockChain effs,
    Member MockChainWrite effs
  ) =>
  Sem effs a ->
  Test effs a a prop
mustSucceedTest :: forall prop (effs :: EffectRow) a.
(IsProp prop, RunnableMockChain effs,
 Member MockChainWrite effs) =>
Sem effs a -> Test effs a a prop
mustSucceedTest = Runner effs a a -> Sem effs a -> Test effs a a prop
forall prop (effs :: EffectRow) a b.
IsProp prop =>
Runner effs a b -> Sem effs a -> Test effs a b prop
mustSucceedTest' (Runner effs a a -> Sem effs a -> Test effs a a prop)
-> Runner effs a a -> Sem effs a -> Test effs a a prop
forall a b. (a -> b) -> a -> b
$ \MockChainState
initState InitialDistribution
initDist ->
  MockChainConf a (MockChainReturn a)
-> Sem effs a -> [MockChainReturn a]
forall (effs :: EffectRow) a b.
(RunnableMockChain effs, Member MockChainWrite effs) =>
MockChainConf a b -> Sem effs a -> [b]
runMockChainFromConf (MockChainConf a (MockChainReturn a)
 -> Sem effs a -> [MockChainReturn a])
-> MockChainConf a (MockChainReturn a)
-> Sem effs a
-> [MockChainReturn a]
forall a b. (a -> b) -> a -> b
$ MockChainState
-> InitialDistribution
-> FunOnMockChainResult a (MockChainReturn a)
-> MockChainConf a (MockChainReturn a)
forall a b.
MockChainState
-> InitialDistribution
-> FunOnMockChainResult a b
-> MockChainConf a b
MockChainConf MockChainState
initState InitialDistribution
initDist FunOnMockChainResult a (MockChainReturn a)
forall a. FunOnMockChainResult a (MockChainReturn a)
unRawMockChainReturn

-- | A test template which expects a failure from a trace. See
-- `mustSucceedTest'` for more information on its intended usage.
mustFailTest' ::
  (IsProp prop) =>
  Runner effs a b ->
  Sem effs a ->
  Test effs a b prop
mustFailTest' :: forall prop (effs :: EffectRow) a b.
IsProp prop =>
Runner effs a b -> Sem effs a -> Test effs a b prop
mustFailTest' Runner effs a b
runner Sem effs a
trace =
  Test
    { testTrace :: Sem effs a
testTrace = Sem effs a
trace,
      testRunner :: Runner effs a b
testRunner = Runner effs a b
runner,
      testInitState :: MockChainState
testInitState = MockChainState
forall a. Default a => a
def,
      testInitDist :: InitialDistribution
testInitDist = InitialDistribution
forall a. Default a => a
def,
      testSizeProp :: SizeProp prop
testSizeProp = prop -> SizeProp prop
forall a b. a -> b -> a
const prop
forall prop. IsProp prop => prop
testSuccess,
      testFailureProp :: FailureProp prop
testFailureProp = \PrettyCookedOpts
_ [MockChainLogEntry]
_ MockChainError
_ UtxoState
_ -> prop
forall prop. IsProp prop => prop
testSuccess,
      testSuccessProp :: SuccessProp b prop
testSuccessProp = \PrettyCookedOpts
_ [MockChainLogEntry]
_ b
_ UtxoState
_ -> String -> prop
forall prop. IsProp prop => String -> prop
testFailureMsg String
"💀 Unexpected success!",
      testPrettyOpts :: PrettyCookedOpts
testPrettyOpts = PrettyCookedOpts
forall a. Default a => a
def
    }

-- | A test template which expects a failure from a `RunnableMockChain` trace.
mustFailTest ::
  ( IsProp prop,
    RunnableMockChain effs,
    Member MockChainWrite effs
  ) =>
  Sem effs a ->
  Test effs a a prop
mustFailTest :: forall prop (effs :: EffectRow) a.
(IsProp prop, RunnableMockChain effs,
 Member MockChainWrite effs) =>
Sem effs a -> Test effs a a prop
mustFailTest = Runner effs a a -> Sem effs a -> Test effs a a prop
forall prop (effs :: EffectRow) a b.
IsProp prop =>
Runner effs a b -> Sem effs a -> Test effs a b prop
mustFailTest' (Runner effs a a -> Sem effs a -> Test effs a a prop)
-> Runner effs a a -> Sem effs a -> Test effs a a prop
forall a b. (a -> b) -> a -> b
$ \MockChainState
initState InitialDistribution
initDist ->
  MockChainConf a (MockChainReturn a)
-> Sem effs a -> [MockChainReturn a]
forall (effs :: EffectRow) a b.
(RunnableMockChain effs, Member MockChainWrite effs) =>
MockChainConf a b -> Sem effs a -> [b]
runMockChainFromConf (MockChainConf a (MockChainReturn a)
 -> Sem effs a -> [MockChainReturn a])
-> MockChainConf a (MockChainReturn a)
-> Sem effs a
-> [MockChainReturn a]
forall a b. (a -> b) -> a -> b
$ MockChainState
-> InitialDistribution
-> FunOnMockChainResult a (MockChainReturn a)
-> MockChainConf a (MockChainReturn a)
forall a b.
MockChainState
-> InitialDistribution
-> FunOnMockChainResult a b
-> MockChainConf a b
MockChainConf MockChainState
initState InitialDistribution
initDist FunOnMockChainResult a (MockChainReturn a)
forall a. FunOnMockChainResult a (MockChainReturn a)
unRawMockChainReturn

-- * Appending elements (in particular requirements) to existing tests

-- | Gives an initial distribution from which the trace will be run
withInitDist ::
  Test effs a b prop ->
  InitialDistribution ->
  Test effs a b prop
withInitDist :: forall (effs :: EffectRow) a b prop.
Test effs a b prop -> InitialDistribution -> Test effs a b prop
withInitDist Test effs a b prop
test InitialDistribution
initDist = Test effs a b prop
test {testInitDist = initDist}

-- | Gives some pretty options to render test messages
withPrettyOpts ::
  Test effs a b prop ->
  PrettyCookedOpts ->
  Test effs a b prop
withPrettyOpts :: forall (effs :: EffectRow) a b prop.
Test effs a b prop -> PrettyCookedOpts -> Test effs a b prop
withPrettyOpts Test effs a b prop
test PrettyCookedOpts
opts = Test effs a b prop
test {testPrettyOpts = opts}

-- | Appends a requirements over the emitted log, which will need to be satisfied
-- both in case of success or failure of the run.
withLogProp ::
  (IsProp prop) =>
  Test effs a b prop ->
  LogProp prop ->
  Test effs a b prop
withLogProp :: forall prop (effs :: EffectRow) a b.
IsProp prop =>
Test effs a b prop -> LogProp prop -> Test effs a b prop
withLogProp Test effs a b prop
test LogProp prop
logProp =
  Test effs a b prop
test
    { testFailureProp = \PrettyCookedOpts
opts [MockChainLogEntry]
log MockChainError
err UtxoState
state ->
        Test effs a b prop
-> PrettyCookedOpts
-> [MockChainLogEntry]
-> MockChainError
-> UtxoState
-> prop
forall (effs :: EffectRow) a b prop.
Test effs a b prop -> FailureProp prop
testFailureProp Test effs a b prop
test PrettyCookedOpts
opts [MockChainLogEntry]
log MockChainError
err UtxoState
state prop -> prop -> prop
forall prop. IsProp prop => prop -> prop -> prop
.&&. LogProp prop
logProp PrettyCookedOpts
opts [MockChainLogEntry]
log,
      testSuccessProp = \PrettyCookedOpts
opts [MockChainLogEntry]
log b
val UtxoState
state ->
        Test effs a b prop
-> PrettyCookedOpts
-> [MockChainLogEntry]
-> b
-> UtxoState
-> prop
forall (effs :: EffectRow) a b prop.
Test effs a b prop -> SuccessProp b prop
testSuccessProp Test effs a b prop
test PrettyCookedOpts
opts [MockChainLogEntry]
log b
val UtxoState
state prop -> prop -> prop
forall prop. IsProp prop => prop -> prop -> prop
.&&. LogProp prop
logProp PrettyCookedOpts
opts [MockChainLogEntry]
log
    }

-- | Appends a requirements over the resulting 'UtxoState', which will need to
-- be satisfied both in case of success or failure of the run.
withStateProp ::
  (IsProp prop) =>
  Test effs a b prop ->
  StateProp prop ->
  Test effs a b prop
withStateProp :: forall prop (effs :: EffectRow) a b.
IsProp prop =>
Test effs a b prop -> StateProp prop -> Test effs a b prop
withStateProp Test effs a b prop
test StateProp prop
stateProp =
  Test effs a b prop
test
    { testFailureProp = \PrettyCookedOpts
opts [MockChainLogEntry]
log MockChainError
err UtxoState
state ->
        Test effs a b prop
-> PrettyCookedOpts
-> [MockChainLogEntry]
-> MockChainError
-> UtxoState
-> prop
forall (effs :: EffectRow) a b prop.
Test effs a b prop -> FailureProp prop
testFailureProp Test effs a b prop
test PrettyCookedOpts
opts [MockChainLogEntry]
log MockChainError
err UtxoState
state prop -> prop -> prop
forall prop. IsProp prop => prop -> prop -> prop
.&&. StateProp prop
stateProp PrettyCookedOpts
opts UtxoState
state,
      testSuccessProp = \PrettyCookedOpts
opts [MockChainLogEntry]
log b
val UtxoState
state ->
        Test effs a b prop
-> PrettyCookedOpts
-> [MockChainLogEntry]
-> b
-> UtxoState
-> prop
forall (effs :: EffectRow) a b prop.
Test effs a b prop -> SuccessProp b prop
testSuccessProp Test effs a b prop
test PrettyCookedOpts
opts [MockChainLogEntry]
log b
val UtxoState
state prop -> prop -> prop
forall prop. IsProp prop => prop -> prop -> prop
.&&. StateProp prop
stateProp PrettyCookedOpts
opts UtxoState
state
    }

-- | Appends a requirement over the resulting value and state of the mockchain
-- run which will need to be satisfied if the run is successful
withSuccessProp ::
  (IsProp prop) =>
  Test effs a b prop ->
  SuccessProp b prop ->
  Test effs a b prop
withSuccessProp :: forall prop (effs :: EffectRow) a b.
IsProp prop =>
Test effs a b prop -> SuccessProp b prop -> Test effs a b prop
withSuccessProp Test effs a b prop
test SuccessProp b prop
successProp =
  Test effs a b prop
test
    { testSuccessProp = \PrettyCookedOpts
opts [MockChainLogEntry]
log b
val UtxoState
state ->
        Test effs a b prop -> SuccessProp b prop
forall (effs :: EffectRow) a b prop.
Test effs a b prop -> SuccessProp b prop
testSuccessProp Test effs a b prop
test PrettyCookedOpts
opts [MockChainLogEntry]
log b
val UtxoState
state prop -> prop -> prop
forall prop. IsProp prop => prop -> prop -> prop
.&&. SuccessProp b prop
successProp PrettyCookedOpts
opts [MockChainLogEntry]
log b
val UtxoState
state
    }

-- | Same as 'withSuccessProp' but only considers the returning value of the run
withResultProp ::
  (IsProp prop) =>
  Test effs a b prop ->
  (b -> prop) ->
  Test effs a b prop
withResultProp :: forall prop (effs :: EffectRow) a b.
IsProp prop =>
Test effs a b prop -> (b -> prop) -> Test effs a b prop
withResultProp Test effs a b prop
test b -> prop
p = Test effs a b prop -> SuccessProp b prop -> Test effs a b prop
forall prop (effs :: EffectRow) a b.
IsProp prop =>
Test effs a b prop -> SuccessProp b prop -> Test effs a b prop
withSuccessProp Test effs a b prop
test (\PrettyCookedOpts
_ [MockChainLogEntry]
_ b
res UtxoState
_ -> b -> prop
p b
res)

-- | Appends a requirement over the resulting number of outcomes of the run
withSizeProp ::
  (IsProp prop) =>
  Test effs a b prop ->
  SizeProp prop ->
  Test effs a b prop
withSizeProp :: forall prop (effs :: EffectRow) a b.
IsProp prop =>
Test effs a b prop -> SizeProp prop -> Test effs a b prop
withSizeProp Test effs a b prop
test SizeProp prop
reqSize =
  Test effs a b prop
test
    { testSizeProp = \Integer
size -> Test effs a b prop -> SizeProp prop
forall (effs :: EffectRow) a b prop.
Test effs a b prop -> SizeProp prop
testSizeProp Test effs a b prop
test Integer
size prop -> prop -> prop
forall prop. IsProp prop => prop -> prop -> prop
.&&. SizeProp prop
reqSize Integer
size
    }

-- | Appends a requirement over the resulting value and state of the mockchain
-- run which will need to be satisfied if the run is successful
withFailureProp ::
  (IsProp prop) =>
  Test effs a b prop ->
  FailureProp prop ->
  Test effs a b prop
withFailureProp :: forall prop (effs :: EffectRow) a b.
IsProp prop =>
Test effs a b prop -> FailureProp prop -> Test effs a b prop
withFailureProp Test effs a b prop
test FailureProp prop
failureProp =
  Test effs a b prop
test
    { testFailureProp = \PrettyCookedOpts
opts [MockChainLogEntry]
log MockChainError
err UtxoState
state ->
        Test effs a b prop -> FailureProp prop
forall (effs :: EffectRow) a b prop.
Test effs a b prop -> FailureProp prop
testFailureProp Test effs a b prop
test PrettyCookedOpts
opts [MockChainLogEntry]
log MockChainError
err UtxoState
state prop -> prop -> prop
forall prop. IsProp prop => prop -> prop -> prop
.&&. FailureProp prop
failureProp PrettyCookedOpts
opts [MockChainLogEntry]
log MockChainError
err UtxoState
state
    }

-- | Same as 'withFailureProp' but only considers the returning error of the run
withErrorProp ::
  (IsProp prop) =>
  Test effs a b prop ->
  (MockChainError -> prop) ->
  Test effs a b prop
withErrorProp :: forall prop (effs :: EffectRow) a b.
IsProp prop =>
Test effs a b prop
-> (MockChainError -> prop) -> Test effs a b prop
withErrorProp Test effs a b prop
test MockChainError -> prop
errorProp = Test effs a b prop -> FailureProp prop -> Test effs a b prop
forall prop (effs :: EffectRow) a b.
IsProp prop =>
Test effs a b prop -> FailureProp prop -> Test effs a b prop
withFailureProp Test effs a b prop
test (\PrettyCookedOpts
_ [MockChainLogEntry]
_ MockChainError
err UtxoState
_ -> MockChainError -> prop
errorProp MockChainError
err)

-- * Specific properties around failures

-- | A property to ensure a phase 1 failure
isPhase1Failure ::
  (IsProp prop) =>
  FailureProp prop
isPhase1Failure :: forall prop. IsProp prop => FailureProp prop
isPhase1Failure PrettyCookedOpts
_ [MockChainLogEntry]
_ (MCEValidationError ValidationPhase
Ledger.Phase1 ValidationError
_) UtxoState
_ = prop
forall prop. IsProp prop => prop
testSuccess
isPhase1Failure PrettyCookedOpts
pcOpts [MockChainLogEntry]
_ MockChainError
e UtxoState
_ =
  String -> prop
forall prop. IsProp prop => String -> prop
testFailureMsg (String -> prop) -> String -> prop
forall a b. (a -> b) -> a -> b
$
    String
"Expected phase 1 evaluation failure, got: "
      String -> String -> String
forall a. [a] -> [a] -> [a]
++ (MockChainError -> DocCooked) -> MockChainError -> String
forall a. (a -> DocCooked) -> a -> String
renderString (PrettyCookedOpts -> MockChainError -> DocCooked
forall a. PrettyCooked a => PrettyCookedOpts -> a -> DocCooked
prettyCookedOpt PrettyCookedOpts
pcOpts) MockChainError
e

-- | A property to ensure a phase 2 failure
isPhase2Failure ::
  (IsProp prop) =>
  FailureProp prop
isPhase2Failure :: forall prop. IsProp prop => FailureProp prop
isPhase2Failure PrettyCookedOpts
_ [MockChainLogEntry]
_ (MCEValidationError ValidationPhase
Ledger.Phase2 ValidationError
_) UtxoState
_ = prop
forall prop. IsProp prop => prop
testSuccess
isPhase2Failure PrettyCookedOpts
pcOpts [MockChainLogEntry]
_ MockChainError
e UtxoState
_ =
  String -> prop
forall prop. IsProp prop => String -> prop
testFailureMsg (String -> prop) -> String -> prop
forall a b. (a -> b) -> a -> b
$
    String
"Expected phase 2 evaluation failure, got: "
      String -> String -> String
forall a. [a] -> [a] -> [a]
++ (MockChainError -> DocCooked) -> MockChainError -> String
forall a. (a -> DocCooked) -> a -> String
renderString (PrettyCookedOpts -> MockChainError -> DocCooked
forall a. PrettyCooked a => PrettyCookedOpts -> a -> DocCooked
prettyCookedOpt PrettyCookedOpts
pcOpts) MockChainError
e

-- | Same as 'isPhase1Failure' with an added predicate on the text error
isPhase1FailureWithMsg ::
  (IsProp prop) =>
  String ->
  FailureProp prop
isPhase1FailureWithMsg :: forall prop. IsProp prop => String -> FailureProp prop
isPhase1FailureWithMsg String
s PrettyCookedOpts
_ [MockChainLogEntry]
_ (MCEValidationError ValidationPhase
Ledger.Phase1 (Ledger.CardanoLedgerValidationError Text
text)) UtxoState
_
  | String
s String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isInfixOf` Text -> String
T.unpack Text
text =
      prop
forall prop. IsProp prop => prop
testSuccess
isPhase1FailureWithMsg String
_ PrettyCookedOpts
pcOpts [MockChainLogEntry]
_ MockChainError
e UtxoState
_ =
  String -> prop
forall prop. IsProp prop => String -> prop
testFailureMsg (String -> prop) -> String -> prop
forall a b. (a -> b) -> a -> b
$
    String
"Expected phase 1 evaluation failure with constrained messages, got: "
      String -> String -> String
forall a. [a] -> [a] -> [a]
++ (MockChainError -> DocCooked) -> MockChainError -> String
forall a. (a -> DocCooked) -> a -> String
renderString (PrettyCookedOpts -> MockChainError -> DocCooked
forall a. PrettyCooked a => PrettyCookedOpts -> a -> DocCooked
prettyCookedOpt PrettyCookedOpts
pcOpts) MockChainError
e

-- | Same as 'isPhase2Failure' with an added predicate over the text error
isPhase2FailureWithMsg ::
  (IsProp prop) =>
  String ->
  FailureProp prop
isPhase2FailureWithMsg :: forall prop. IsProp prop => String -> FailureProp prop
isPhase2FailureWithMsg String
s PrettyCookedOpts
_ [MockChainLogEntry]
_ (MCEValidationError ValidationPhase
Ledger.Phase2 (Ledger.ScriptFailure (Ledger.EvaluationError [Text]
texts String
_))) UtxoState
_
  | (Text -> Bool) -> [Text] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
isInfixOf String
s (String -> Bool) -> (Text -> String) -> Text -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> String
T.unpack) [Text]
texts =
      prop
forall prop. IsProp prop => prop
testSuccess
isPhase2FailureWithMsg String
_ PrettyCookedOpts
pcOpts [MockChainLogEntry]
_ MockChainError
e UtxoState
_ =
  String -> prop
forall prop. IsProp prop => String -> prop
testFailureMsg (String -> prop) -> String -> prop
forall a b. (a -> b) -> a -> b
$
    String
"Expected phase 2 evaluation failure with constrained messages, got: "
      String -> String -> String
forall a. [a] -> [a] -> [a]
++ (MockChainError -> DocCooked) -> MockChainError -> String
forall a. (a -> DocCooked) -> a -> String
renderString (PrettyCookedOpts -> MockChainError -> DocCooked
forall a. PrettyCooked a => PrettyCookedOpts -> a -> DocCooked
prettyCookedOpt PrettyCookedOpts
pcOpts) MockChainError
e

-- * Specific properties around number of outcomes

-- | Ensures the run has an exact given number of outcomes
isOfSize ::
  (IsProp prop) =>
  Integer ->
  SizeProp prop
isOfSize :: forall prop. IsProp prop => Integer -> SizeProp prop
isOfSize Integer
n1 Integer
n2 | Integer
n1 Integer -> Integer -> Bool
forall a. Eq a => a -> a -> Bool
== Integer
n2 = prop
forall prop. IsProp prop => prop
testSuccess
isOfSize Integer
n1 Integer
n2 =
  String -> prop
forall prop. IsProp prop => String -> prop
testFailureMsg (String -> prop) -> String -> prop
forall a b. (a -> b) -> a -> b
$
    String
"Incorrect number of results (expected: "
      String -> String -> String
forall a. Semigroup a => a -> a -> a
<> Integer -> String
forall a. Show a => a -> String
show Integer
n1
      String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
" but got: "
      String -> String -> String
forall a. Semigroup a => a -> a -> a
<> Integer -> String
forall a. Show a => a -> String
show Integer
n2
      String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
")"

-- | Ensures the run has a minimal number of outcomes
isAtLeastOfSize ::
  (IsProp prop) =>
  Integer ->
  SizeProp prop
isAtLeastOfSize :: forall prop. IsProp prop => Integer -> SizeProp prop
isAtLeastOfSize Integer
n1 Integer
n2 | Integer
n1 Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
<= Integer
n2 = prop
forall prop. IsProp prop => prop
testSuccess
isAtLeastOfSize Integer
n1 Integer
n2 =
  String -> prop
forall prop. IsProp prop => String -> prop
testFailureMsg (String -> prop) -> String -> prop
forall a b. (a -> b) -> a -> b
$
    String
"Incorrect number of results (expected at least: "
      String -> String -> String
forall a. Semigroup a => a -> a -> a
<> Integer -> String
forall a. Show a => a -> String
show Integer
n1
      String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
" but got: "
      String -> String -> String
forall a. Semigroup a => a -> a -> a
<> Integer -> String
forall a. Show a => a -> String
show Integer
n2
      String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
")"

-- | Ensures the run has a minimal number of outcomes
isAtMostOfSize ::
  (IsProp prop) =>
  Integer ->
  SizeProp prop
isAtMostOfSize :: forall prop. IsProp prop => Integer -> SizeProp prop
isAtMostOfSize Integer
n1 Integer
n2 | Integer
n1 Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
>= Integer
n2 = prop
forall prop. IsProp prop => prop
testSuccess
isAtMostOfSize Integer
n1 Integer
n2 =
  String -> prop
forall prop. IsProp prop => String -> prop
testFailureMsg (String -> prop) -> String -> prop
forall a b. (a -> b) -> a -> b
$
    String
"Incorrect number of results (expected at most: "
      String -> String -> String
forall a. Semigroup a => a -> a -> a
<> Integer -> String
forall a. Show a => a -> String
show Integer
n1
      String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
" but got: "
      String -> String -> String
forall a. Semigroup a => a -> a -> a
<> Integer -> String
forall a. Show a => a -> String
show Integer
n2
      String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
")"

-- * Specific properties over the log

-- | Ensures a certain event has been emitted. This uses the constructor's name
-- of the 'MockChainLogEntry' by relying on 'show' being lazy.
happened ::
  (IsProp prop) =>
  String ->
  LogProp prop
happened :: forall prop. IsProp prop => String -> LogProp prop
happened String
eventName PrettyCookedOpts
_ [MockChainLogEntry]
log
  | Set String
allEventNames <- [String] -> Set String
forall a. Ord a => [a] -> Set a
Set.fromList ([String] -> String
forall a. HasCallStack => [a] -> a
head ([String] -> String)
-> (MockChainLogEntry -> [String]) -> MockChainLogEntry -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> [String]
words (String -> [String])
-> (MockChainLogEntry -> String) -> MockChainLogEntry -> [String]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. MockChainLogEntry -> String
forall a. Show a => a -> String
show (MockChainLogEntry -> String) -> [MockChainLogEntry] -> [String]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [MockChainLogEntry]
log) =
      if String
eventName String -> Set String -> Bool
forall a. Ord a => a -> Set a -> Bool
`Set.member` Set String
allEventNames
        then prop
forall prop. IsProp prop => prop
testSuccess
        else
          String -> prop
forall prop. IsProp prop => String -> prop
testFailureMsg (String -> prop) -> String -> prop
forall a b. (a -> b) -> a -> b
$
            String
"The event "
              String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String -> String
forall a. Show a => a -> String
show String
eventName
              String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
" did not occur (but those did: "
              String -> String -> String
forall a. Semigroup a => a -> a -> a
<> Set String -> String
forall a. Show a => a -> String
show Set String
allEventNames
              String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
")"

-- | Ensures a certain event has not been emitted. This uses the constructor's
-- name of the 'MockChainLogEntry' by relying on 'show' being lazy.
didNotHappen :: (IsProp prop) => String -> LogProp prop
didNotHappen :: forall prop. IsProp prop => String -> LogProp prop
didNotHappen String
eventName PrettyCookedOpts
_ [MockChainLogEntry]
log | Bool -> Bool
not (String
eventName String -> Set String -> Bool
forall a. Ord a => a -> Set a -> Bool
`Set.member` [String] -> Set String
forall a. Ord a => [a] -> Set a
Set.fromList ([String] -> String
forall a. HasCallStack => [a] -> a
head ([String] -> String)
-> (MockChainLogEntry -> [String]) -> MockChainLogEntry -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> [String]
words (String -> [String])
-> (MockChainLogEntry -> String) -> MockChainLogEntry -> [String]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. MockChainLogEntry -> String
forall a. Show a => a -> String
show (MockChainLogEntry -> String) -> [MockChainLogEntry] -> [String]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [MockChainLogEntry]
log)) = prop
forall prop. IsProp prop => prop
testSuccess
didNotHappen String
eventName PrettyCookedOpts
_ [MockChainLogEntry]
_ =
  String -> prop
forall prop. IsProp prop => String -> prop
testFailureMsg (String -> prop) -> String -> prop
forall a b. (a -> b) -> a -> b
$
    String
"The event "
      String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String -> String
forall a. Show a => a -> String
show String
eventName
      String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
" was forbidden but occurred nonetheless"

-- * Specific properties over successes

-- | Ensures that the given addresses satisfy certain amount requirements over a
-- list of given asset classes in the end of the run
isAtAddress ::
  ( IsProp prop,
    Script.ToAddress addr,
    Show addr
  ) =>
  [(addr, [(Api.AssetClass, Integer -> Bool)])] ->
  SuccessProp a prop
isAtAddress :: forall prop addr a.
(IsProp prop, ToAddress addr, Show addr) =>
[(addr, [(AssetClass, Integer -> Bool)])] -> SuccessProp a prop
isAtAddress [(addr, [(AssetClass, Integer -> Bool)])]
addressesReqs PrettyCookedOpts
_ [MockChainLogEntry]
_ a
_ UtxoState
utxoState =
  ((addr, [(AssetClass, Integer -> Bool)]) -> prop)
-> [(addr, [(AssetClass, Integer -> Bool)])] -> prop
forall prop a. IsProp prop => (a -> prop) -> [a] -> prop
testAll
    ( \(addr
w, [(AssetClass, Integer -> Bool)]
assetsReqs) ->
        ((AssetClass, Integer -> Bool) -> prop)
-> [(AssetClass, Integer -> Bool)] -> prop
forall prop a. IsProp prop => (a -> prop) -> [a] -> prop
testAll
          ( \(AssetClass
ac, Integer -> Bool
nbReq) ->
              let amount :: Integer
amount = Value -> AssetClass -> Integer
Api.assetClassValueOf (addr -> UtxoState -> Value
forall a. ToAddress a => a -> UtxoState -> Value
holdsInState addr
w UtxoState
utxoState) AssetClass
ac
               in if Integer -> Bool
nbReq Integer
amount
                    then prop
forall prop. IsProp prop => prop
testSuccess
                    else
                      String -> prop
forall prop. IsProp prop => String -> prop
testFailureMsg (String -> prop) -> String -> prop
forall a b. (a -> b) -> a -> b
$
                        String
"Unsatisfied quantity requirement for "
                          String -> String -> String
forall a. Semigroup a => a -> a -> a
<> addr -> String
forall a. Show a => a -> String
show addr
w
                          String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
" over asset class "
                          String -> String -> String
forall a. Semigroup a => a -> a -> a
<> AssetClass -> String
forall a. Show a => a -> String
show AssetClass
ac
          )
          [(AssetClass, Integer -> Bool)]
assetsReqs
    )
    [(addr, [(AssetClass, Integer -> Bool)])]
addressesReqs

-- | Ensures that a given address possesses exactly a certain amount of a given
-- asset class in the end of the run
possesses ::
  ( IsProp prop,
    Script.ToAddress addr,
    Show addr
  ) =>
  addr ->
  Api.AssetClass ->
  Integer ->
  SuccessProp a prop
possesses :: forall prop addr a.
(IsProp prop, ToAddress addr, Show addr) =>
addr -> AssetClass -> Integer -> SuccessProp a prop
possesses addr
w AssetClass
ac Integer
n = [(addr, [(AssetClass, Integer -> Bool)])] -> SuccessProp a prop
forall prop addr a.
(IsProp prop, ToAddress addr, Show addr) =>
[(addr, [(AssetClass, Integer -> Bool)])] -> SuccessProp a prop
isAtAddress [(addr
w, [(AssetClass
ac, (Integer -> Integer -> Bool
forall a. Eq a => a -> a -> Bool
== Integer
n))])]

-- * Advanced test templates

{--
  Note on advanced templates:

  The idea here is definely not to have a huge number of combinations of
  predicates and bundle them into templates. This has been attempted in the past
  and is never a good idea. However, there are a few more advanced template that
  make sense because they will occur a lot when testing smart contracts. Only
  those should appear in the following list. For all the other templates, the
  pattern @testCaseCookedXX $ template testName trace `withXXX` myPropicate@
  should be advocated.

--}

-- | A test template expecting a Phase 2 failure for arbitrary runs
mustFailInPhase2Test' ::
  (IsProp prop) =>
  Runner effs a b ->
  Sem effs a ->
  Test effs a b prop
mustFailInPhase2Test' :: forall prop (effs :: EffectRow) a b.
IsProp prop =>
Runner effs a b -> Sem effs a -> Test effs a b prop
mustFailInPhase2Test' Runner effs a b
runner Sem effs a
trace =
  Runner effs a b -> Sem effs a -> Test effs a b prop
forall prop (effs :: EffectRow) a b.
IsProp prop =>
Runner effs a b -> Sem effs a -> Test effs a b prop
mustFailTest' Runner effs a b
runner Sem effs a
trace Test effs a b prop -> FailureProp prop -> Test effs a b prop
forall prop (effs :: EffectRow) a b.
IsProp prop =>
Test effs a b prop -> FailureProp prop -> Test effs a b prop
`withFailureProp` FailureProp prop
forall prop. IsProp prop => FailureProp prop
isPhase2Failure

-- | A test template expecting a Phase 2 failure for `RunnableMockChain` runs
mustFailInPhase2Test ::
  ( IsProp prop,
    RunnableMockChain effs,
    Member MockChainWrite effs
  ) =>
  Sem effs a ->
  Test effs a a prop
mustFailInPhase2Test :: forall prop (effs :: EffectRow) a.
(IsProp prop, RunnableMockChain effs,
 Member MockChainWrite effs) =>
Sem effs a -> Test effs a a prop
mustFailInPhase2Test Sem effs a
trace =
  Sem effs a -> Test effs a a prop
forall prop (effs :: EffectRow) a.
(IsProp prop, RunnableMockChain effs,
 Member MockChainWrite effs) =>
Sem effs a -> Test effs a a prop
mustFailTest Sem effs a
trace Test effs a a prop -> FailureProp prop -> Test effs a a prop
forall prop (effs :: EffectRow) a b.
IsProp prop =>
Test effs a b prop -> FailureProp prop -> Test effs a b prop
`withFailureProp` FailureProp prop
forall prop. IsProp prop => FailureProp prop
isPhase2Failure

-- | A test template expecting a specific phase 2 error message for arbitrary
-- runs
mustFailInPhase2WithMsgTest' ::
  (IsProp prop) =>
  String ->
  Runner effs a b ->
  Sem effs a ->
  Test effs a b prop
mustFailInPhase2WithMsgTest' :: forall prop (effs :: EffectRow) a b.
IsProp prop =>
String -> Runner effs a b -> Sem effs a -> Test effs a b prop
mustFailInPhase2WithMsgTest' String
msg Runner effs a b
runner Sem effs a
trace =
  Runner effs a b -> Sem effs a -> Test effs a b prop
forall prop (effs :: EffectRow) a b.
IsProp prop =>
Runner effs a b -> Sem effs a -> Test effs a b prop
mustFailTest' Runner effs a b
runner Sem effs a
trace Test effs a b prop -> FailureProp prop -> Test effs a b prop
forall prop (effs :: EffectRow) a b.
IsProp prop =>
Test effs a b prop -> FailureProp prop -> Test effs a b prop
`withFailureProp` String -> FailureProp prop
forall prop. IsProp prop => String -> FailureProp prop
isPhase2FailureWithMsg String
msg

-- | A test template expecting a specific phase 2 error message for
-- `RunnableMockChain` runs
mustFailInPhase2WithMsgTest ::
  ( IsProp prop,
    RunnableMockChain effs,
    Member MockChainWrite effs
  ) =>
  String ->
  Sem effs a ->
  Test effs a a prop
mustFailInPhase2WithMsgTest :: forall prop (effs :: EffectRow) a.
(IsProp prop, RunnableMockChain effs,
 Member MockChainWrite effs) =>
String -> Sem effs a -> Test effs a a prop
mustFailInPhase2WithMsgTest String
msg Sem effs a
trace =
  Sem effs a -> Test effs a a prop
forall prop (effs :: EffectRow) a.
(IsProp prop, RunnableMockChain effs,
 Member MockChainWrite effs) =>
Sem effs a -> Test effs a a prop
mustFailTest Sem effs a
trace Test effs a a prop -> FailureProp prop -> Test effs a a prop
forall prop (effs :: EffectRow) a b.
IsProp prop =>
Test effs a b prop -> FailureProp prop -> Test effs a b prop
`withFailureProp` String -> FailureProp prop
forall prop. IsProp prop => String -> FailureProp prop
isPhase2FailureWithMsg String
msg

-- | A test template expecting a Phase 1 failure
mustFailInPhase1Test' ::
  (IsProp prop) =>
  Runner effs a b ->
  Sem effs a ->
  Test effs a b prop
mustFailInPhase1Test' :: forall prop (effs :: EffectRow) a b.
IsProp prop =>
Runner effs a b -> Sem effs a -> Test effs a b prop
mustFailInPhase1Test' Runner effs a b
runner Sem effs a
trace =
  Runner effs a b -> Sem effs a -> Test effs a b prop
forall prop (effs :: EffectRow) a b.
IsProp prop =>
Runner effs a b -> Sem effs a -> Test effs a b prop
mustFailTest' Runner effs a b
runner Sem effs a
trace Test effs a b prop -> FailureProp prop -> Test effs a b prop
forall prop (effs :: EffectRow) a b.
IsProp prop =>
Test effs a b prop -> FailureProp prop -> Test effs a b prop
`withFailureProp` FailureProp prop
forall prop. IsProp prop => FailureProp prop
isPhase1Failure

-- | A test template expecting a Phase 1 failure for `RunnableMockChain` runs
mustFailInPhase1Test ::
  ( IsProp prop,
    RunnableMockChain effs,
    Member MockChainWrite effs
  ) =>
  Sem effs a ->
  Test effs a a prop
mustFailInPhase1Test :: forall prop (effs :: EffectRow) a.
(IsProp prop, RunnableMockChain effs,
 Member MockChainWrite effs) =>
Sem effs a -> Test effs a a prop
mustFailInPhase1Test Sem effs a
trace =
  Sem effs a -> Test effs a a prop
forall prop (effs :: EffectRow) a.
(IsProp prop, RunnableMockChain effs,
 Member MockChainWrite effs) =>
Sem effs a -> Test effs a a prop
mustFailTest Sem effs a
trace Test effs a a prop -> FailureProp prop -> Test effs a a prop
forall prop (effs :: EffectRow) a b.
IsProp prop =>
Test effs a b prop -> FailureProp prop -> Test effs a b prop
`withFailureProp` FailureProp prop
forall prop. IsProp prop => FailureProp prop
isPhase1Failure

-- | A test template expecting a specific phase 1 error message
mustFailInPhase1WithMsgTest' ::
  (IsProp prop) =>
  String ->
  Runner effs a b ->
  Sem effs a ->
  Test effs a b prop
mustFailInPhase1WithMsgTest' :: forall prop (effs :: EffectRow) a b.
IsProp prop =>
String -> Runner effs a b -> Sem effs a -> Test effs a b prop
mustFailInPhase1WithMsgTest' String
msg Runner effs a b
runner Sem effs a
trace =
  Runner effs a b -> Sem effs a -> Test effs a b prop
forall prop (effs :: EffectRow) a b.
IsProp prop =>
Runner effs a b -> Sem effs a -> Test effs a b prop
mustFailTest' Runner effs a b
runner Sem effs a
trace Test effs a b prop -> FailureProp prop -> Test effs a b prop
forall prop (effs :: EffectRow) a b.
IsProp prop =>
Test effs a b prop -> FailureProp prop -> Test effs a b prop
`withFailureProp` String -> FailureProp prop
forall prop. IsProp prop => String -> FailureProp prop
isPhase1FailureWithMsg String
msg

-- | A test template expecting a specific phase 1 error message for
-- `RunnableMockChain` runs
mustFailInPhase1WithMsgTest ::
  ( IsProp prop,
    RunnableMockChain effs,
    Member MockChainWrite effs
  ) =>
  String ->
  Sem effs a ->
  Test effs a a prop
mustFailInPhase1WithMsgTest :: forall prop (effs :: EffectRow) a.
(IsProp prop, RunnableMockChain effs,
 Member MockChainWrite effs) =>
String -> Sem effs a -> Test effs a a prop
mustFailInPhase1WithMsgTest String
msg Sem effs a
trace =
  Sem effs a -> Test effs a a prop
forall prop (effs :: EffectRow) a.
(IsProp prop, RunnableMockChain effs,
 Member MockChainWrite effs) =>
Sem effs a -> Test effs a a prop
mustFailTest Sem effs a
trace Test effs a a prop -> FailureProp prop -> Test effs a a prop
forall prop (effs :: EffectRow) a b.
IsProp prop =>
Test effs a b prop -> FailureProp prop -> Test effs a b prop
`withFailureProp` String -> FailureProp prop
forall prop. IsProp prop => String -> FailureProp prop
isPhase1FailureWithMsg String
msg

-- | A test template expecting a certain number of successful outcomes for
-- arbitrary runs
mustSucceedWithSizeTest' ::
  (IsProp prop) =>
  Integer ->
  Runner effs a b ->
  Sem effs a ->
  Test effs a b prop
mustSucceedWithSizeTest' :: forall prop (effs :: EffectRow) a b.
IsProp prop =>
Integer -> Runner effs a b -> Sem effs a -> Test effs a b prop
mustSucceedWithSizeTest' Integer
size Runner effs a b
runner Sem effs a
trace =
  Runner effs a b -> Sem effs a -> Test effs a b prop
forall prop (effs :: EffectRow) a b.
IsProp prop =>
Runner effs a b -> Sem effs a -> Test effs a b prop
mustSucceedTest' Runner effs a b
runner Sem effs a
trace Test effs a b prop -> SizeProp prop -> Test effs a b prop
forall prop (effs :: EffectRow) a b.
IsProp prop =>
Test effs a b prop -> SizeProp prop -> Test effs a b prop
`withSizeProp` Integer -> SizeProp prop
forall prop. IsProp prop => Integer -> SizeProp prop
isOfSize Integer
size

-- | A test template expecting a certain number of successful outcomes for
-- `RunnableMockChain` runs
mustSucceedWithSizeTest ::
  ( IsProp prop,
    RunnableMockChain effs,
    Member MockChainWrite effs
  ) =>
  Integer ->
  Sem effs a ->
  Test effs a a prop
mustSucceedWithSizeTest :: forall prop (effs :: EffectRow) a.
(IsProp prop, RunnableMockChain effs,
 Member MockChainWrite effs) =>
Integer -> Sem effs a -> Test effs a a prop
mustSucceedWithSizeTest Integer
size Sem effs a
trace =
  Sem effs a -> Test effs a a prop
forall prop (effs :: EffectRow) a.
(IsProp prop, RunnableMockChain effs,
 Member MockChainWrite effs) =>
Sem effs a -> Test effs a a prop
mustSucceedTest Sem effs a
trace Test effs a a prop -> SizeProp prop -> Test effs a a prop
forall prop (effs :: EffectRow) a b.
IsProp prop =>
Test effs a b prop -> SizeProp prop -> Test effs a b prop
`withSizeProp` Integer -> SizeProp prop
forall prop. IsProp prop => Integer -> SizeProp prop
isOfSize Integer
size

-- | A test template expecting a certain number of unsuccessful outcomes for
-- arbitrary runs
mustFailWithSizeTest' ::
  (IsProp prop) =>
  Integer ->
  Runner effs a b ->
  Sem effs a ->
  Test effs a b prop
mustFailWithSizeTest' :: forall prop (effs :: EffectRow) a b.
IsProp prop =>
Integer -> Runner effs a b -> Sem effs a -> Test effs a b prop
mustFailWithSizeTest' Integer
size Runner effs a b
runner Sem effs a
trace =
  Runner effs a b -> Sem effs a -> Test effs a b prop
forall prop (effs :: EffectRow) a b.
IsProp prop =>
Runner effs a b -> Sem effs a -> Test effs a b prop
mustFailTest' Runner effs a b
runner Sem effs a
trace Test effs a b prop -> SizeProp prop -> Test effs a b prop
forall prop (effs :: EffectRow) a b.
IsProp prop =>
Test effs a b prop -> SizeProp prop -> Test effs a b prop
`withSizeProp` Integer -> SizeProp prop
forall prop. IsProp prop => Integer -> SizeProp prop
isOfSize Integer
size

-- | A test template expecting a certain number of unsuccessful outcomes for
-- `RunnableMockChain` runs
mustFailWithSizeTest ::
  ( IsProp prop,
    RunnableMockChain effs,
    Member MockChainWrite effs
  ) =>
  Integer ->
  Sem effs a ->
  Test effs a a prop
mustFailWithSizeTest :: forall prop (effs :: EffectRow) a.
(IsProp prop, RunnableMockChain effs,
 Member MockChainWrite effs) =>
Integer -> Sem effs a -> Test effs a a prop
mustFailWithSizeTest Integer
size Sem effs a
trace =
  Sem effs a -> Test effs a a prop
forall prop (effs :: EffectRow) a.
(IsProp prop, RunnableMockChain effs,
 Member MockChainWrite effs) =>
Sem effs a -> Test effs a a prop
mustFailTest Sem effs a
trace Test effs a a prop -> SizeProp prop -> Test effs a a prop
forall prop (effs :: EffectRow) a b.
IsProp prop =>
Test effs a b prop -> SizeProp prop -> Test effs a b prop
`withSizeProp` Integer -> SizeProp prop
forall prop. IsProp prop => Integer -> SizeProp prop
isOfSize Integer
size