Safe Haskell | Safe-Inferred |
---|---|
Language | Haskell2010 |
This module exposes a flexible way to generate test cases that go through a pre-processing -> pre-condition -> execution -> post-condition pipeline.
The intention is to provide an easier interface to re-using predicates and pre-processors between many tests and between different _kinds_ of tests, including nominal and attack cases, as well as unit and property testing.
This module is currently targeted primarily at tests run on the CEK machine. In the future, it may be expanded to be polymorphic in terms of arbitrary "computation". The current computation type is essentially hard-coded to
(Maybe Datum, Redeemer, ScriptContext, Script) -> (Either EvalError Script, ExBudget, Logs)
but this could be expanded so that other types of tests (emulator, integration, serialization, golden) could be run as well.
Synopsis
- data PreProcessor err input
- mkPreProcessor :: (input -> Either err input) -> PreProcessor err input
- preProcess :: [PreProcessor err input] -> input -> Either err input
- data PreCondition err input
- mkPreCondition :: (input -> Maybe err) -> PreCondition err input
- checkPreConditions :: [PreCondition err input] -> input -> Maybe err
- data PostCondition err output
- mkPostCondition :: (output -> Maybe err) -> PostCondition err output
- checkPostConditions :: [PostCondition err output] -> output -> Maybe err
- data PipelinedTestCase errPP errPreC errPostC input output = PipelinedTestCase {
- name :: String
- preProcessors :: [PreProcessor errPP input]
- preConditions :: [PreCondition errPreC input]
- input :: input
- computation :: input -> output
- postConditions :: [PostCondition errPostC output]
- data PipelinedTestErrors errPP errPreC errPostC
- = PipelinedPreProcessorError errPP
- | PipelinedPreConditionError errPreC
- | PipelinedPostConditionError errPostC
- pipelinedUnitCase :: (Show errPP, Show errPreC, Show errPostC) => PipelinedTestCase errPP errPreC errPostC input output -> TestTree
- data TxFCEKCase errPP errPreC errPostC
- data TxFCEKInput = TxFCEKInput {
- cekDatum :: Maybe Datum
- cekRedeemer :: Redeemer
- cekScriptContext :: ScriptContext
- cekScript :: Script
- data TxFCEKOutput = TxFCEKOutput {
- cekResult :: Either EvalError Script
- cekExBudget :: ExBudget
- cekLogs :: [Text]
- mkTxFCEKCase :: forall (errPP :: Type) (errPreC :: Type) (errPostC :: Type). String -> [PreProcessor errPP TxFCEKInput] -> [PreCondition errPreC TxFCEKInput] -> TxFCEKInput -> [PostCondition errPostC TxFCEKOutput] -> TxFCEKCase errPP errPreC errPostC
- txfCEKUnitCase :: forall (errPP :: Type) (errPreC :: Type) (errPostC :: Type). (Show errPP, Show errPreC, Show errPostC) => TxFCEKCase errPP errPreC errPostC -> TestTree
- nominalPostCondition :: ((EvalError, [Text]) -> err) -> PostCondition err TxFCEKOutput
- nominalCaseBasic :: String -> Maybe Datum -> Redeemer -> ScriptContext -> Script -> TxFCEKCase String String String
- attackCaseRegexPostCondition :: ((Maybe Int, [Text]) -> err) -> RE -> PostCondition err TxFCEKOutput
- attackCaseBasicRegex :: String -> RE -> Maybe Datum -> Redeemer -> ScriptContext -> Script -> PreProcessor errPP TxFCEKInput -> TxFCEKCase errPP String String
Polymorphic Types and Functions
PreProcessing
data PreProcessor err input Source #
PreProcessing -- could be used for normalization or attacks
Construction
mkPreProcessor :: (input -> Either err input) -> PreProcessor err input Source #
Elimination
preProcess :: [PreProcessor err input] -> input -> Either err input Source #
Given a list of preprocessors, apply them in order from left to right. Returns the first error encountered, if any.
PreCondition Checking
data PreCondition err input Source #
Checks for after the preprocessing is run. Could be used to check for balancing, etc
TODO: rewrite as Kleisli?
Construction
mkPreCondition :: (input -> Maybe err) -> PreCondition err input Source #
Elimination
checkPreConditions :: [PreCondition err input] -> input -> Maybe err Source #
Check a list of pre-conditions, returning the first error encountered
PostCondition Checking
data PostCondition err output Source #
Represents a partial post-condition predicate on the result of a call to `evalScript :: Script -> (Either EvalError Script, ExBudget, [Text]) ` TODO: Rewrite as Kleisli?
TODO: If its possible to turn budgeting off, we should. These scripts will be run with some logging output, and they wouldn't in production; thus costing is not actually representative of anything useful.
Construction
mkPostCondition :: (output -> Maybe err) -> PostCondition err output Source #
Elimination
checkPostConditions :: [PostCondition err output] -> output -> Maybe err Source #
Check a list of post-conditions from left to right, returning the first error encountered
Test Cases
data PipelinedTestCase errPP errPreC errPostC input output Source #
A type representing a named test case for a transaction family.
NOTE: if the `sc-tools` work ends up panning out, the args
and script
arguments
might get combined to a (Maybe Datum, Redeemer, ScriptContext, Script)
PipelinedTestCase | |
|
data PipelinedTestErrors errPP errPreC errPostC Source #
A sum type to collect the errors of a pipelined test case
PipelinedPreProcessorError errPP | |
PipelinedPreConditionError errPreC | |
PipelinedPostConditionError errPostC |
pipelinedUnitCase :: (Show errPP, Show errPreC, Show errPostC) => PipelinedTestCase errPP errPreC errPostC input output -> TestTree Source #
Behavior of this function:
- 1.) All pre-processors are run in order from left to right on the arguments. If any pre-processor returns a Left, then the resulting test case fails with the appropriate error message.
- 2.) All pre-condition checks are run on the pre-processed arguments. If any pre-condition checks return Nothing, then the resulting test case fails with the appropriate error meesage.
- 3.) The pre-processed arguments are fed to the script.
- 4.) The results of (3) are checked against each post-condition. If any of the post-condition checks fail, the test case fails with the appropriate error message
- 5.) If none of the above checks cause a failure, the test succeeds.
TxFCEKMachinery
Generic TxF TestCase
data TxFCEKCase errPP errPreC errPostC Source #
A sum type of the errors for a TxFCEKCase
data TxFCEKInput Source #
The arguments needed to run a YTxP-style transaction family (single script) on the CEK Machine. Includes a datum (for validators only), a redeemer, script context, and the script itself.
TxFCEKInput | |
|
Instances
Show TxFCEKInput Source # | |
Defined in Cardano.TestUtils | |
Eq TxFCEKInput Source # | |
Defined in Cardano.TestUtils (==) :: TxFCEKInput -> TxFCEKInput -> Bool Source # (/=) :: TxFCEKInput -> TxFCEKInput -> Bool Source # |
data TxFCEKOutput Source #
The data produced by the CEK machine when run against a TxFCEKInput.
TxFCEKOutput | |
|
Instances
Show TxFCEKOutput Source # | |
Defined in Cardano.TestUtils | |
Eq TxFCEKOutput Source # | |
Defined in Cardano.TestUtils (==) :: TxFCEKOutput -> TxFCEKOutput -> Bool Source # (/=) :: TxFCEKOutput -> TxFCEKOutput -> Bool Source # |
:: forall (errPP :: Type) (errPreC :: Type) (errPostC :: Type). String | Name of the test case |
-> [PreProcessor errPP TxFCEKInput] | List of pre-processors over something like (Maybe Datum, Redeemer, ScriptContext, Script) |
-> [PreCondition errPreC TxFCEKInput] | List of pre-condition checks over something like (Maybe Datum, Redeemer, ScriptContext, Script) |
-> TxFCEKInput | Something like (Maybe Datum, Redeemer, ScriptContext, Script) |
-> [PostCondition errPostC TxFCEKOutput] | Post-conditions over something like (Either EvalError Script, ExBudget, [Text]) |
-> TxFCEKCase errPP errPreC errPostC |
Create a TxFCEKCase by filling in the computation via running the script.
txfCEKUnitCase :: forall (errPP :: Type) (errPreC :: Type) (errPostC :: Type). (Show errPP, Show errPreC, Show errPostC) => TxFCEKCase errPP errPreC errPostC -> TestTree Source #
Nominal Tests
nominalPostCondition :: ((EvalError, [Text]) -> err) -> PostCondition err TxFCEKOutput Source #
A post condition for checking whether the script execution succeeded or failed You must supply a way to turn a generic evaluation error into your domain-specific error type
:: String | Name of the test case |
-> Maybe Datum | Datum to apply to the script. Set this to Nothing unless you are testing a validator |
-> Redeemer | Redeemer |
-> ScriptContext | Nominal context to apply Nominal |
-> Script | Nominal to apply |
-> TxFCEKCase String String String |
A basic nominal case unit test. Only checks if the script succeeds; does not do pre-processing, pre-condition checking, or other post-condition checks.
Throws a generic String error (not domain-specific)
Attack Tests
attackCaseRegexPostCondition Source #
:: ((Maybe Int, [Text]) -> err) | A way to go from the number of matches (if unequal to 1) and the logs to a domain-specific error |
-> RE | The regular expression to match against. It must match exactly once for the post-condition check to pass |
-> PostCondition err TxFCEKOutput |
The attackCaseRegex-style functions will fail with - A (Nothing, [Text]) if the script succeeds. [Text] in the tuple contains the logs. - A (Just Int, [Text]) if the script fails, but the regex does not match exactly once. The int contains the number of matches
:: String | Name of the test case |
-> RE | Expected Failure String Match; TODO: This can be improved. Maybe use a discriminated error sum type with a injective string mapping? |
-> Maybe Datum | Datum to apply to the script. Set this to Nothing unless you are testing a validator |
-> Redeemer | Redeemer |
-> ScriptContext | Nominal context to apply attack |
-> Script | The script to execute |
-> PreProcessor errPP TxFCEKInput | Attack to apply |
-> TxFCEKCase errPP String String |
Generate an "attack case" test tree, given a name, an expected failure condition, the arguments to the script, an attack, and the script itself.
The expected failure condition is matched as a regex. It must match the logs exactly once.