commit ec70bfd539fe2e27fd48f5f76395400287ac72d7
Author: Seungheon Oh <seungheon.ooh@gmail.com>
Date: Tue Oct 18 18:58:59 2022 -0500
use LSE
commit 25fff9b3ad1f2dde4cd7cf36977530b06a87d23c
Merge: 01cd3aa 1821dd6
Author: Seungheon Oh <seungheon.ooh@gmail.com>
Date: Tue Oct 18 18:17:45 2022 -0500
Merge branch 'staging' into seungheonoh/ply
commit 01cd3aa7a235e6fe6658246ca1026fa26dc71a83
Author: Hongrui Fang <chfanghr@gmail.com>
Date: Tue Oct 11 12:02:03 2022 +0800
update benchmark
commit a8513244892ce33cfdc9edf8cd501c4985ae8008
Author: Hongrui Fang <chfanghr@gmail.com>
Date: Tue Oct 11 11:59:22 2022 +0800
fix tests
commit 20ca40823485c2e2f78253643cf4453ac7b7ddd5
Author: Hongrui Fang <chfanghr@gmail.com>
Date: Tue Oct 11 11:57:37 2022 +0800
better import
commit a19fe49424210891bd03db71e4083fc1e0edfd98
Author: Hongrui Fang <chfanghr@gmail.com>
Date: Tue Oct 11 11:08:20 2022 +0800
update flake inputs
commit c93b21f1f9441e5c6f54525bf7c6a54757ec36cc
Author: Seungheon Oh <seungheon.ooh@gmail.com>
Date: Mon Oct 10 12:54:12 2022 -0500
tried to make tests pass
commit 1046ae1237299a33c58b48661bdb6d325a22147e
Merge: 2bf4e36 363bd83
Author: Seungheon Oh <seungheon.ooh@gmail.com>
Date: Mon Oct 10 12:18:48 2022 -0500
Merge branch 'staging' into seungheonoh/ply
commit 2bf4e3627c1b229f58078695082da85c80efd560
Author: Seungheon Oh <seungheon.ooh@gmail.com>
Date: Mon Oct 10 10:48:36 2022 -0500
remove junkpile
commit a1dbc9ad9e531fe0d0a0480c4aef9cf9ffa90f1d
Author: Seungheon Oh <seungheon.ooh@gmail.com>
Date: Mon Oct 10 10:47:25 2022 -0500
versions
commit 4542a06ac733858297d3a48c53368fad19dedc43
Author: Seungheon Oh <seungheon.ooh@gmail.com>
Date: Thu Oct 6 22:57:48 2022 -0500
script exporting interface
commit 6bd8c1a1d57e4bf9dc25c3068a9c8eae6bf6a19d
Author: Seungheon Oh <seungheon.ooh@gmail.com>
Date: Thu Oct 6 22:58:41 2022 -0500
fixed tests
commit d3ce2cf95633d336f3e621833677bd5bf10ee2c8
Author: Seungheon Oh <seungheon.ooh@gmail.com>
Date: Sun Oct 2 00:55:18 2022 -0500
fixed tests
commit 1ae64c9f692652b77b0506013853b2ba44267c65
Author: Seungheon Oh <seungheon.ooh@gmail.com>
Date: Sat Oct 1 13:28:20 2022 -0500
linker
commit db88cb75c7b74843141ad8ab4e6522b66d0dcfbc
Author: Seungheon Oh <seungheon.ooh@gmail.com>
Date: Sat Oct 1 01:03:50 2022 -0500
exporting scripts
commit 6389fce28e885a8a7f8669629c266f59c0edb51f
Author: Seungheon Oh <seungheon.ooh@gmail.com>
Date: Sat Oct 1 00:51:49 2022 -0500
made scripts parameterized on the script level
commit aea1e518a8890550bdebd0e5251da11d915c53a9
Author: Seungheon Oh <seungheon.ooh@gmail.com>
Date: Wed Sep 28 19:53:29 2022 -0500
Use `TypedScriptEnvelope` for `Agora.Bootstrap`
320 lines
8.3 KiB
Haskell
320 lines
8.3 KiB
Haskell
{- |
|
|
Module : Test.Specification
|
|
Maintainer : seungheon.ooh@gmail.com
|
|
Description: Helpers to build Specification for testing and bench-marking
|
|
|
|
Constructors for building a specification for Plutarch scripts:
|
|
|
|
- 'policySucceedsWith': checks that a minting policy succeeds.
|
|
|
|
- 'policyFailsWith': checks that a minting policy fails.
|
|
|
|
- 'validatorSucceedsWith': checks that validator succeeds.
|
|
|
|
- 'validatorFailsWith': checks that validator fails.
|
|
|
|
- 'effectSucceedsWith': checks that effect succeeds.
|
|
|
|
- 'effectFailsWith': checks that effect fails.
|
|
|
|
- 'scriptSucceeds': checks that an arbitrary script does not
|
|
`perror`.
|
|
|
|
- 'scriptFails': checks that an arbitrary script `perror`s out.
|
|
-}
|
|
module Test.Specification (
|
|
-- * Structures
|
|
Specification (..),
|
|
SpecificationExpectation (..),
|
|
SpecificationTree (..),
|
|
|
|
-- * Spec helpers
|
|
group,
|
|
getSpecification,
|
|
getSpecificationTree,
|
|
|
|
-- * Spec builders
|
|
scriptSucceeds,
|
|
scriptFails,
|
|
policySucceedsWith,
|
|
policyFailsWith,
|
|
validatorSucceedsWith,
|
|
validatorFailsWith,
|
|
effectSucceedsWith,
|
|
effectFailsWith,
|
|
testValidator,
|
|
testPolicy,
|
|
|
|
-- * Converters
|
|
toTestTree,
|
|
) where
|
|
|
|
import Control.Composition ((.**), (.***))
|
|
import Data.Coerce (coerce)
|
|
import Data.Text qualified as Text
|
|
import Plutarch.Evaluate (evalScript)
|
|
import PlutusLedgerApi.V1.Scripts (
|
|
Context (Context),
|
|
applyMintingPolicyScript,
|
|
applyValidator,
|
|
)
|
|
import PlutusLedgerApi.V2 (
|
|
Datum (..),
|
|
MintingPolicy,
|
|
Redeemer (Redeemer),
|
|
Script,
|
|
ScriptContext,
|
|
ToData (toBuiltinData),
|
|
Validator,
|
|
)
|
|
import PlutusTx.IsData qualified as PlutusTx (ToData)
|
|
import Test.Tasty (TestTree, testGroup)
|
|
import Test.Tasty.HUnit (assertFailure, testCase)
|
|
|
|
{- | Expectations upon execution of script
|
|
@Success@ indicates a successful execution.
|
|
@Failure@ inidcates a faulty execution.
|
|
@FailureWith@ indicates a faulty execution but with expected reason for failure.
|
|
-}
|
|
data SpecificationExpectation
|
|
= Success
|
|
| Failure
|
|
| FailureWith String
|
|
deriving stock (Show)
|
|
|
|
{- | Unit of specification. @Specification@ holds name, expectation, and
|
|
script to be tested or executed later on.
|
|
-}
|
|
data Specification = Specification
|
|
{ sName :: String
|
|
, sExpectation :: SpecificationExpectation
|
|
, sScript :: Script
|
|
}
|
|
deriving stock (Show)
|
|
|
|
-- | Tree-structure to group alike specifications--modeled after @TestTree@ from tasty.
|
|
data SpecificationTree
|
|
= Terminal Specification
|
|
| Group String [SpecificationTree]
|
|
deriving stock (Show)
|
|
|
|
{- | Checks if given name exists in @SpecificationTree@ as either
|
|
group name or specification name.
|
|
-}
|
|
exists :: String -> SpecificationTree -> Bool
|
|
exists s (Terminal (Specification name _ _)) = s == name
|
|
exists s (Group name st) = or (exists s <$> st) || s == name
|
|
|
|
-- | Groups alike @SpecificationTree@s into a bigger tree.
|
|
group :: String -> [SpecificationTree] -> SpecificationTree
|
|
group name st
|
|
| or $ exists name <$> st = error $ "Name already exists: " <> name
|
|
| otherwise = Group name st
|
|
|
|
-- | Query specific @Specification@ from a tree.
|
|
getSpecification :: String -> SpecificationTree -> [Specification]
|
|
getSpecification name (Terminal spec@(Specification sn _ _))
|
|
| name == sn = [spec]
|
|
| otherwise = []
|
|
getSpecification name (Group _ st) = mconcat $ getSpecification name <$> st
|
|
|
|
-- | Query specific @SpecificationTree@ from a tree.
|
|
getSpecificationTree :: String -> SpecificationTree -> [SpecificationTree]
|
|
getSpecificationTree name specTree@(Group gn st)
|
|
| gn == name = [specTree]
|
|
| otherwise = mconcat $ getSpecificationTree name <$> st
|
|
getSpecificationTree _ _ = []
|
|
|
|
-- | Convert @SpecificationTree@ into @TestTree@ to be used as a unit test.
|
|
toTestTree :: SpecificationTree -> TestTree
|
|
toTestTree (Group name st) = testGroup name $ toTestTree <$> st
|
|
toTestTree (Terminal (Specification name expectation script)) =
|
|
testCase name $ do
|
|
case expectation of
|
|
Success -> onSuccess
|
|
Failure -> onFailure
|
|
FailureWith s -> onFailureWith s
|
|
where
|
|
beautifyTraces =
|
|
Text.unpack
|
|
. Text.intercalate "\n"
|
|
. map (" " <>)
|
|
(res, _budget, traces) = evalScript script
|
|
ts = " Traces:\n" <> beautifyTraces traces
|
|
onSuccess = case res of
|
|
Left e ->
|
|
assertFailure $
|
|
show e <> ts
|
|
_ -> pure ()
|
|
onFailure = case res of
|
|
Right v ->
|
|
assertFailure $
|
|
"Expected failure, but succeeded. "
|
|
<> show v
|
|
<> ts
|
|
_ -> pure ()
|
|
onFailureWith _s = case res of -- TODO: check Trace for this
|
|
Right v ->
|
|
assertFailure $
|
|
"Expected failure, but succeeded. "
|
|
<> show v
|
|
<> ts
|
|
_ -> pure ()
|
|
|
|
-- | Check that an arbitrary script doesn't error when evaluated, given a name.
|
|
scriptSucceeds :: String -> Script -> SpecificationTree
|
|
scriptSucceeds name script = Terminal $ Specification name Success script
|
|
|
|
-- | Check that an arbitrary script **does** error when evaluated, given a name.
|
|
scriptFails :: String -> Script -> SpecificationTree
|
|
scriptFails name script = Terminal $ Specification name Failure script
|
|
|
|
mkContext :: ScriptContext -> Context
|
|
mkContext = Context . toBuiltinData
|
|
|
|
mkRedeemer ::
|
|
forall redeemer.
|
|
(PlutusTx.ToData redeemer) =>
|
|
redeemer ->
|
|
Redeemer
|
|
mkRedeemer = Redeemer . toBuiltinData
|
|
|
|
mkDatum ::
|
|
forall datum.
|
|
(PlutusTx.ToData datum) =>
|
|
datum ->
|
|
Datum
|
|
mkDatum = Datum . toBuiltinData
|
|
|
|
applyMintingPolicy' ::
|
|
(PlutusTx.ToData redeemer) =>
|
|
MintingPolicy ->
|
|
redeemer ->
|
|
ScriptContext ->
|
|
Script
|
|
applyMintingPolicy' policy redeemer scriptContext =
|
|
applyMintingPolicyScript
|
|
(mkContext scriptContext)
|
|
policy
|
|
(mkRedeemer redeemer)
|
|
|
|
applyValidator' ::
|
|
( PlutusTx.ToData datum
|
|
, PlutusTx.ToData redeemer
|
|
) =>
|
|
Validator ->
|
|
datum ->
|
|
redeemer ->
|
|
ScriptContext ->
|
|
Script
|
|
applyValidator' validator datum redeemer scriptContext =
|
|
applyValidator
|
|
(mkContext scriptContext)
|
|
validator
|
|
(mkDatum datum)
|
|
(mkRedeemer redeemer)
|
|
|
|
-- | Check that a policy script succeeds, given a name and arguments.
|
|
policySucceedsWith ::
|
|
(PlutusTx.ToData redeemer) =>
|
|
String ->
|
|
MintingPolicy ->
|
|
redeemer ->
|
|
ScriptContext ->
|
|
SpecificationTree
|
|
policySucceedsWith tag =
|
|
scriptSucceeds tag .** applyMintingPolicy'
|
|
|
|
-- | Check that a policy script fails, given a name and arguments.
|
|
policyFailsWith ::
|
|
(PlutusTx.ToData redeemer) =>
|
|
String ->
|
|
MintingPolicy ->
|
|
redeemer ->
|
|
ScriptContext ->
|
|
SpecificationTree
|
|
policyFailsWith tag =
|
|
scriptFails tag .** applyMintingPolicy'
|
|
|
|
-- | Check that a validator script succeeds, given a name and arguments.
|
|
validatorSucceedsWith ::
|
|
( PlutusTx.ToData datum
|
|
, PlutusTx.ToData redeemer
|
|
) =>
|
|
String ->
|
|
Validator ->
|
|
datum ->
|
|
redeemer ->
|
|
ScriptContext ->
|
|
SpecificationTree
|
|
validatorSucceedsWith tag =
|
|
scriptSucceeds tag .*** applyValidator'
|
|
|
|
-- | Check that a validator script fails, given a name and arguments.
|
|
validatorFailsWith ::
|
|
( PlutusTx.ToData datum
|
|
, PlutusTx.ToData redeemer
|
|
) =>
|
|
String ->
|
|
Validator ->
|
|
datum ->
|
|
redeemer ->
|
|
ScriptContext ->
|
|
SpecificationTree
|
|
validatorFailsWith tag =
|
|
scriptFails tag .*** applyValidator'
|
|
|
|
-- | Check that an effect succeeds, given a name and argument.
|
|
effectSucceedsWith ::
|
|
( PlutusTx.ToData datum
|
|
) =>
|
|
String ->
|
|
Validator ->
|
|
datum ->
|
|
ScriptContext ->
|
|
SpecificationTree
|
|
effectSucceedsWith tag eff datum = validatorSucceedsWith tag (coerce eff) datum ()
|
|
|
|
-- | Check that an effect fails, given a name and argument.
|
|
effectFailsWith ::
|
|
( PlutusTx.ToData datum
|
|
) =>
|
|
String ->
|
|
Validator ->
|
|
datum ->
|
|
ScriptContext ->
|
|
SpecificationTree
|
|
effectFailsWith tag eff datum = validatorFailsWith tag (coerce eff) datum ()
|
|
|
|
-- | Test a validator, given the expectation as a boolean value.
|
|
testValidator ::
|
|
forall datum redeemer.
|
|
(PlutusTx.ToData datum, PlutusTx.ToData redeemer) =>
|
|
-- | Is this test case expected to succeed?
|
|
Bool ->
|
|
String ->
|
|
Validator ->
|
|
datum ->
|
|
redeemer ->
|
|
ScriptContext ->
|
|
SpecificationTree
|
|
testValidator isValid =
|
|
if isValid
|
|
then validatorSucceedsWith
|
|
else validatorFailsWith
|
|
|
|
-- | Test a policy, given the expectation as a boolean value.
|
|
testPolicy ::
|
|
forall redeemer.
|
|
(PlutusTx.ToData redeemer) =>
|
|
-- | Is this test case expected to succeed?
|
|
Bool ->
|
|
String ->
|
|
MintingPolicy ->
|
|
redeemer ->
|
|
ScriptContext ->
|
|
SpecificationTree
|
|
testPolicy isValid =
|
|
if isValid
|
|
then policySucceedsWith
|
|
else policyFailsWith
|