Safe Haskell | None |
---|---|
Language | Haskell2010 |
Module containing some utilities for testing Michelson contracts using Haskell testing frameworks (hspec and QuickCheck in particular). It's Morley testing EDSL.
Synopsis
- specWithContract :: (Each '[KnownT] [cp, st], HasCallStack) => FilePath -> ((Contract, Contract cp st) -> Spec) -> Spec
- specWithTypedContract :: (Each '[KnownT] [cp, st], HasCallStack) => FilePath -> (Contract cp st -> Spec) -> Spec
- specWithUntypedContract :: FilePath -> (Contract -> Spec) -> Spec
- testTreesWithContract :: (Each '[KnownT] [cp, st], HasCallStack) => FilePath -> ((Contract, Contract cp st) -> IO [TestTree]) -> IO [TestTree]
- testTreesWithUntypedContract :: HasCallStack => FilePath -> (Contract -> IO [TestTree]) -> IO [TestTree]
- testTreesWithTypedContract :: (Each '[KnownT] [cp, st], HasCallStack) => FilePath -> (Contract cp st -> IO [TestTree]) -> IO [TestTree]
- concatTestTrees :: [IO [TestTree]] -> IO [TestTree]
- importContract :: forall cp st. Each '[KnownT] [cp, st] => FilePath -> IO (Contract, Contract cp st)
- importUntypedContract :: FilePath -> IO Contract
- type ContractReturn st = (Either MichelsonFailed ([Operation], Value st), InterpreterState)
- type ContractPropValidator st prop = ContractReturn st -> prop
- contractProp :: (IsoValue param, IsoValue storage, ToT param ~ cp, ToT storage ~ st, ParameterScope cp) => Contract cp st -> ContractPropValidator st prop -> ContractEnv -> param -> storage -> prop
- contractPropVal :: ParameterScope cp => Contract cp st -> ContractPropValidator st prop -> ContractEnv -> Value cp -> Value st -> prop
- validateSuccess :: HasCallStack => ContractPropValidator st Expectation
- validateStorageIs :: IsoValue st => st -> ContractPropValidator (ToT st) Assertion
- validateMichelsonFailsWith :: IsoValue v => v -> ContractPropValidator st Expectation
- type IntegrationalScenario = IntegrationalScenarioM ()
- type IntegrationalScenarioM = StateT InternalState (Except ScenarioError)
- integrationalTestExpectation :: HasCallStack => IntegrationalScenario -> Expectation
- integrationalTestProp :: MonadTest m => IntegrationalScenario -> m ()
- originate :: Contract -> Text -> Value -> Mutez -> IntegrationalScenarioM Address
- tOriginate :: (ParameterScope cp, StorageScope st) => Contract cp st -> Text -> Value st -> Mutez -> IntegrationalScenarioM Address
- transfer :: TxData -> Address -> IntegrationalScenarioM ()
- tTransfer :: forall arg. ParameterScope arg => ("from" :! Address) -> ("to" :! Address) -> Mutez -> EpName -> Value arg -> IntegrationalScenarioM ()
- integrationalFail :: TestError -> IntegrationalScenarioM anything
- unexpectedInterpreterError :: ExecutorError -> Text -> IntegrationalScenarioM a
- setMaxSteps :: RemainingSteps -> IntegrationalScenarioM ()
- setNow :: Timestamp -> IntegrationalScenarioM ()
- rewindTime :: Integer -> IntegrationalScenarioM ()
- withSender :: Address -> IntegrationalScenarioM a -> IntegrationalScenarioM a
- setChainId :: ChainId -> IntegrationalScenarioM ()
- branchout :: HasCallStack => [(Text, IntegrationalScenario)] -> IntegrationalScenario
- (?-) :: Text -> a -> (Text, a)
- expectNoUpdates :: IntegrationalScenario
- expectNoStorageUpdates :: IntegrationalScenario
- expectStorageUpdate :: Address -> (Value -> Either TestError ()) -> IntegrationalScenario
- expectStorageUpdateConst :: Address -> Value -> IntegrationalScenario
- expectBalance :: Address -> Mutez -> IntegrationalScenario
- expectStorage :: Address -> (Value -> Either TestError ()) -> IntegrationalScenario
- expectStorageConst :: Address -> Value -> IntegrationalScenario
- tExpectStorageConst :: forall st. StorageScope st => Address -> Value st -> IntegrationalScenario
- attempt :: IntegrationalScenarioM a -> IntegrationalScenarioM (Either ExecutorError a)
- expectError :: IntegrationalScenarioM a -> IntegrationalScenarioM ExecutorError
- catchExpectedError :: IntegrationalScenarioM a -> (ExecutorError -> IntegrationalScenarioM b) -> IntegrationalScenarioM b
- expectGasExhaustion :: ExecutorError -> IntegrationalScenario
- expectMichelsonFailed :: (MichelsonFailed -> Bool) -> Address -> ExecutorError -> IntegrationalScenario
- data TxData = TxData {}
- genesisAddress :: Address
- failedTest :: (HasCallStack, MonadTest m) => Text -> m ()
- succeededTest :: MonadTest m => m ()
- eitherIsLeft :: (Show b, MonadTest m, HasCallStack) => Either a b -> m ()
- eitherIsRight :: (Show a, MonadTest m, HasCallStack) => Either a b -> m ()
- total :: (MonadTest m, NFData a, HasCallStack) => a -> m a
- meanTimeUpperBoundProp :: (KnownDivRat unit Second, KnownUnitName unit, HasCallStack) => Time unit -> (a -> b) -> a -> Property
- meanTimeUpperBoundPropNF :: (KnownDivRat unit Second, KnownUnitName unit, HasCallStack, NFData b) => Time unit -> (a -> b) -> a -> Property
- mcs :: RatioNat -> Time Microsecond
- ms :: RatioNat -> Time Millisecond
- sec :: RatioNat -> Time Second
- minute :: RatioNat -> Time Minute
- runDocTests :: HasCallStack => [DocTest] -> ContractDoc -> [TestTree]
- testDocBasic :: [DocTest]
- excludeDocTests :: [DocTest] -> [DocTest] -> [DocTest]
- dummyContractEnv :: ContractEnv
- minTimestamp :: Timestamp
- maxTimestamp :: Timestamp
- midTimestamp :: Timestamp
- integrationalTestProperty :: IntegrationalScenario -> Property
- failedProp :: Text -> Property
- succeededProp :: Property
- qcIsLeft :: Show b => Either a b -> Property
- qcIsRight :: Show a => Either a b -> Property
Importing a contract
specWithContract :: (Each '[KnownT] [cp, st], HasCallStack) => FilePath -> ((Contract, Contract cp st) -> Spec) -> Spec Source #
Import contract and use it in the spec. Both versions of contract are passed to the callback function (untyped and typed).
If contract's import fails, a spec with single failing expectation will be generated (so tests will likely run unexceptionally, but a failing result will notify about problem).
specWithTypedContract :: (Each '[KnownT] [cp, st], HasCallStack) => FilePath -> (Contract cp st -> Spec) -> Spec Source #
A version of specWithContract
which passes only the typed
representation of the contract.
testTreesWithContract :: (Each '[KnownT] [cp, st], HasCallStack) => FilePath -> ((Contract, Contract cp st) -> IO [TestTree]) -> IO [TestTree] Source #
Import contract and use to create test trees. Both versions of contract are passed to the callback function (untyped and typed).
If contract's import fails, a tree with single failing test will be generated (so test tree will likely be generated unexceptionally, but a failing result will notify about problem).
testTreesWithUntypedContract :: HasCallStack => FilePath -> (Contract -> IO [TestTree]) -> IO [TestTree] Source #
Like testTreesWithContract
but supplies only untyped contract.
testTreesWithTypedContract :: (Each '[KnownT] [cp, st], HasCallStack) => FilePath -> (Contract cp st -> IO [TestTree]) -> IO [TestTree] Source #
Like testTreesWithContract
but supplies only typed contract.
importContract :: forall cp st. Each '[KnownT] [cp, st] => FilePath -> IO (Contract, Contract cp st) Source #
Import contract from a given file path.
This function reads file, parses and type checks a contract. Within the typechecking we assume that no contracts are originated, otherwise a type checking error will be caused.
This function may throw IOException
and ImportContractError
.
Unit testing
type ContractReturn st = (Either MichelsonFailed ([Operation], Value st), InterpreterState) Source #
type ContractPropValidator st prop = ContractReturn st -> prop Source #
Type for contract execution validation.
It's a function which is supplied with contract execution output (failure or new storage with operation list).
Function returns a property which type is designated by type variable prop
and might be Property
or Expectation
or anything else relevant.
contractProp :: (IsoValue param, IsoValue storage, ToT param ~ cp, ToT storage ~ st, ParameterScope cp) => Contract cp st -> ContractPropValidator st prop -> ContractEnv -> param -> storage -> prop Source #
ContractCode's property tester against given input. Takes contract environment, initial storage and parameter, interprets contract on this input and invokes validation function.
contractPropVal :: ParameterScope cp => Contract cp st -> ContractPropValidator st prop -> ContractEnv -> Value cp -> Value st -> prop Source #
Version of contractProp
which takes Val
as arguments instead
of regular Haskell values.
This function assumes that contract has no explicit default entrypoints and you always have to construct parameter manually; if you need to test contract calling specific entrypoints, use integrational testing defined by Michelson.Test.Integrational module.
validateSuccess :: HasCallStack => ContractPropValidator st Expectation Source #
ContractPropValidator
that expects a successful termination.
validateStorageIs :: IsoValue st => st -> ContractPropValidator (ToT st) Assertion Source #
ContractPropValidator
that expects contract execution to
succeed and update storage to a particular constant value.
validateMichelsonFailsWith :: IsoValue v => v -> ContractPropValidator st Expectation Source #
ContractPropValidator
that expects a given failure.
Integrational testing
Testing engine
type IntegrationalScenario = IntegrationalScenarioM () Source #
type IntegrationalScenarioM = StateT InternalState (Except ScenarioError) Source #
A monad inside which integrational tests can be described using do-notation.
integrationalTestExpectation :: HasCallStack => IntegrationalScenario -> Expectation Source #
Integrational test that executes given operations and validates
them. It can fail using Expectation
capability.
It starts with initGState
and some reasonable dummy values for
gas limit and current timestamp. You can update blockchain state
by performing some operations.
integrationalTestProp :: MonadTest m => IntegrationalScenario -> m () Source #
Integrational test similar to integrationalTestExpectation
.
It can fail using Property
capability.
It can be used with Hedgehog's forAll
to make a
property-based test with arbitrary data.
originate :: Contract -> Text -> Value -> Mutez -> IntegrationalScenarioM Address Source #
Originate a contract with given initial storage and balance. Its address is returned.
tOriginate :: (ParameterScope cp, StorageScope st) => Contract cp st -> Text -> Value st -> Mutez -> IntegrationalScenarioM Address Source #
Like originate
, but for typed contract and value.
transfer :: TxData -> Address -> IntegrationalScenarioM () Source #
Transfer tokens to a given address.
tTransfer :: forall arg. ParameterScope arg => ("from" :! Address) -> ("to" :! Address) -> Mutez -> EpName -> Value arg -> IntegrationalScenarioM () Source #
Similar to transfer
, for typed values.
Note that it works with untyped Address
and does not check that
entrypoint with given name is present and has the expected type.
Passed value must correspond to the entrypoint argument type, not
the parameter type of the contract (and must be unit for implicit
accounts).
integrationalFail :: TestError -> IntegrationalScenarioM anything Source #
Just fail with given error.
unexpectedInterpreterError :: ExecutorError -> Text -> IntegrationalScenarioM a Source #
Fail a test because an interpreter error happened unexpectedly, with the given reason.
setMaxSteps :: RemainingSteps -> IntegrationalScenarioM () Source #
Make all further interpreter calls use the given gas limit.
setNow :: Timestamp -> IntegrationalScenarioM () Source #
Make all further interpreter calls use the given timestamp as the current one.
rewindTime :: Integer -> IntegrationalScenarioM () Source #
Increase current time by the given number of seconds.
withSender :: Address -> IntegrationalScenarioM a -> IntegrationalScenarioM a Source #
Pretend that given address initiates all the transfers within the
code block (i.e. SENDER
instruction will return this address).
setChainId :: ChainId -> IntegrationalScenarioM () Source #
Make all further interpreter calls use the given chain id.
branchout :: HasCallStack => [(Text, IntegrationalScenario)] -> IntegrationalScenario Source #
Execute multiple testing scenarios independently, basing them on scenario built till this point.
The following property holds for this function:
pre >> branchout [a, b, c] = branchout [pre >> a, pre >> b, pre >> c]
.
In case of property failure in one of the branches no following branch is executed.
Providing empty list of scenarios to this function causes error;
we do not require NonEmpty
here though for convenience.
(?-) :: Text -> a -> (Text, a) infixr 0 Source #
Make a tuple with name without extra syntactic noise.
Validators
expectNoUpdates :: IntegrationalScenario Source #
Check that there were no updates.
expectNoStorageUpdates :: IntegrationalScenario Source #
Check that there were no storage updates.
expectStorageUpdate :: Address -> (Value -> Either TestError ()) -> IntegrationalScenario Source #
Check that storage value is updated for given address. Takes a predicate that is used to check the value.
It works even if updates are not filtered (i. e. a value can be updated more than once).
expectStorageUpdateConst :: Address -> Value -> IntegrationalScenario Source #
Like expectStorageUpdate
, but expects a constant.
expectBalance :: Address -> Mutez -> IntegrationalScenario Source #
Check that eventually address has some particular balance.
expectStorage :: Address -> (Value -> Either TestError ()) -> IntegrationalScenario Source #
Check that storage value satisfies the given predicate.
expectStorageConst :: Address -> Value -> IntegrationalScenario Source #
Check that eventually address has some particular storage value.
tExpectStorageConst :: forall st. StorageScope st => Address -> Value st -> IntegrationalScenario Source #
Similar to expectStorageConst
, for typed stuff.
Errors
attempt :: IntegrationalScenarioM a -> IntegrationalScenarioM (Either ExecutorError a) Source #
Attempt to run an action and return its result or, if interpretation fails, an error.
expectError :: IntegrationalScenarioM a -> IntegrationalScenarioM ExecutorError Source #
Run an action that is expected to fail. If the action fails, the test succeeds and the error is returned. If the action succeeds, the test fails.
catchExpectedError :: IntegrationalScenarioM a -> (ExecutorError -> IntegrationalScenarioM b) -> IntegrationalScenarioM b Source #
Run an action that is expected to fail.
In action
:
If the action fails, catchExpectedError
ff
is applied to the error.
If the action succeeds, the test fails.
expectGasExhaustion :: ExecutorError -> IntegrationalScenario Source #
Check that interpreter failed due to gas exhaustion.
expectMichelsonFailed :: (MichelsonFailed -> Bool) -> Address -> ExecutorError -> IntegrationalScenario Source #
Expect that interpretation of contract with given address ended with [FAILED].
Various
Data associated with a particular transaction.
TxData | |
|
genesisAddress :: Address Source #
One of genesis addresses.
General utilities
failedTest :: (HasCallStack, MonadTest m) => Text -> m () Source #
A Property
that always fails with given message.
succeededTest :: MonadTest m => m () Source #
A Property
that always succeeds.
eitherIsLeft :: (Show b, MonadTest m, HasCallStack) => Either a b -> m () Source #
The Property
holds on `Left a`.
eitherIsRight :: (Show a, MonadTest m, HasCallStack) => Either a b -> m () Source #
The Property
holds on `Right b`.
total :: (MonadTest m, NFData a, HasCallStack) => a -> m a Source #
Checks that a value is total, i.e., doesn't crash when evaluated, by reducing it to its normal form.
Equivalent to QuickCheck's total
.
meanTimeUpperBoundProp :: (KnownDivRat unit Second, KnownUnitName unit, HasCallStack) => Time unit -> (a -> b) -> a -> Property Source #
Benchmarks the given function and checks that the mean time to evaluate to weak head normal form is under the given amount of time.
This test fails if the benchmark takes longer than 30 seconds to run.
meanTimeUpperBoundPropNF :: (KnownDivRat unit Second, KnownUnitName unit, HasCallStack, NFData b) => Time unit -> (a -> b) -> a -> Property Source #
Benchmarks the given function and checks that the mean time to evaluate to normal form is under the given amount of time.
This test aborts and fails if the benchmark takes longer than 120 seconds to run.
Re-exports
These functions from Time are re-exported here to make it convenient to call
meanTimeUpperBoundProp
and meanTimeUpperBoundPropNF
.
mcs :: RatioNat -> Time Microsecond #
Creates Microsecond
from given Natural
.
>>>
mcs 42
42mcs
ms :: RatioNat -> Time Millisecond #
Creates Millisecond
from given Natural
.
>>>
ms 42
42ms
Autodoc testing
runDocTests :: HasCallStack => [DocTest] -> ContractDoc -> [TestTree] Source #
Finalize test suites.
testDocBasic :: [DocTest] Source #
Base properties which should comply for all documentations.
excludeDocTests :: [DocTest] -> [DocTest] -> [DocTest] Source #
Calling excludeDocTests tests toExclude
returns all test suites from
tests
which are not present in toExclude
.
Dummy values
dummyContractEnv :: ContractEnv Source #
Dummy ContractEnv
with some reasonable hardcoded values. You
can override values you are interested in using record update
syntax.
Arbitrary data
minTimestamp :: Timestamp Source #
Minimal (earliest) timestamp used for Arbitrary (CValue 'CTimestamp)
maxTimestamp :: Timestamp Source #
Maximal (latest) timestamp used for Arbitrary (CValue 'CTimestamp)
midTimestamp :: Timestamp Source #
Median of minTimestamp
and maxTimestamp
.
Useful for testing (exactly half of generated dates will be before and after
this date).
Deprecated
integrationalTestProperty :: IntegrationalScenario -> Property Source #
Deprecated: Use integrationalTestProp
instead.
Integrational test similar to integrationalTestExpectation
.
It can fail using Property
capability.
It can be used with QuickCheck's forAll
to make a
property-based test with arbitrary data.
failedProp :: Text -> Property Source #
Deprecated: Use failedtest
instead.
A Property
that always fails with given message.
succeededProp :: Property Source #
Deprecated: Use succeededTest
instead.
A Property
that always succeeds.