208 lines
5.4 KiB
Haskell
208 lines
5.4 KiB
Haskell
module Spec.Spec (
|
|
Specification (..),
|
|
SpecificationExpectation (..),
|
|
SpecificationTree (..),
|
|
group,
|
|
toTestTree,
|
|
getSpecification,
|
|
getSpecificationTree,
|
|
scriptSucceeds,
|
|
scriptFails,
|
|
policySucceedsWith,
|
|
policyFailsWith,
|
|
validatorSucceedsWith,
|
|
validatorFailsWith,
|
|
effectSucceedsWith,
|
|
effectFailsWith,
|
|
) where
|
|
|
|
import Data.Maybe (catMaybes)
|
|
import Plutarch.Api.V1 (PMintingPolicy, PValidator)
|
|
import Plutarch.Builtin (pforgetData)
|
|
import Plutarch.Evaluate (evalScript)
|
|
import Plutarch.Lift (PUnsafeLiftDecl (PLifted))
|
|
import Plutus.V1.Ledger.Api (Script, ScriptContext)
|
|
import PlutusTx.IsData qualified as PlutusTx (ToData)
|
|
import Test.Tasty (TestTree, testGroup)
|
|
import Test.Tasty.HUnit (assertFailure, testCase)
|
|
|
|
data SpecificationExpectation
|
|
= Success
|
|
| Failure
|
|
| FailureWith String
|
|
deriving stock (Show)
|
|
|
|
data Specification = Specification
|
|
{ sName :: String
|
|
, sExpectation :: SpecificationExpectation
|
|
, sScript :: Script
|
|
}
|
|
deriving stock (Show)
|
|
|
|
data SpecificationTree
|
|
= Terminal Specification
|
|
| Group String [SpecificationTree]
|
|
deriving stock (Show)
|
|
|
|
exists :: String -> SpecificationTree -> Bool
|
|
exists s (Terminal (Specification name _ _)) = s == name
|
|
exists s (Group name st) = or (exists s <$> st) || s == name
|
|
|
|
group :: String -> [SpecificationTree] -> SpecificationTree
|
|
group name st
|
|
| or $ exists name <$> st = error $ "Name already exists: " <> name
|
|
| otherwise = Group name st
|
|
|
|
getSpecification :: String -> SpecificationTree -> Maybe Specification
|
|
getSpecification name (Terminal spec@(Specification sn _ _))
|
|
| name == sn = Just spec
|
|
| otherwise = Nothing
|
|
getSpecification name (Group _ st)
|
|
| length specs == 1 = Just $ head specs
|
|
| otherwise = Nothing
|
|
where
|
|
specs = catMaybes $ getSpecification name <$> st
|
|
|
|
getSpecificationTree :: String -> SpecificationTree -> Maybe SpecificationTree
|
|
getSpecificationTree name specTree@(Group gn st)
|
|
| gn == name = Just specTree
|
|
| length trees == 1 = Just $ head trees
|
|
| otherwise = Nothing
|
|
where
|
|
trees = catMaybes $ getSpecificationTree name <$> st
|
|
getSpecificationTree _ _ = Nothing
|
|
|
|
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
|
|
(res, _budget, traces) = evalScript script
|
|
ts = " Traces: " <> show 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 ()
|
|
|
|
scriptSucceeds :: String -> Script -> SpecificationTree
|
|
scriptSucceeds name script = Terminal $ Specification name Success script
|
|
|
|
scriptFails :: String -> Script -> SpecificationTree
|
|
scriptFails name script = Terminal $ Specification name Failure script
|
|
|
|
policySucceedsWith ::
|
|
( PLift redeemer
|
|
, PlutusTx.ToData (PLifted redeemer)
|
|
) =>
|
|
String ->
|
|
ClosedTerm PMintingPolicy ->
|
|
PLifted redeemer ->
|
|
ScriptContext ->
|
|
SpecificationTree
|
|
policySucceedsWith tag policy redeemer scriptContext =
|
|
scriptSucceeds tag $
|
|
compile
|
|
( policy
|
|
# pforgetData (pconstantData redeemer)
|
|
# pconstant scriptContext
|
|
)
|
|
|
|
policyFailsWith ::
|
|
( PLift redeemer
|
|
, PlutusTx.ToData (PLifted redeemer)
|
|
) =>
|
|
String ->
|
|
ClosedTerm PMintingPolicy ->
|
|
PLifted redeemer ->
|
|
ScriptContext ->
|
|
SpecificationTree
|
|
policyFailsWith tag policy redeemer scriptContext =
|
|
scriptFails tag $
|
|
compile
|
|
( policy
|
|
# pforgetData (pconstantData redeemer)
|
|
# pconstant scriptContext
|
|
)
|
|
|
|
validatorSucceedsWith ::
|
|
( PLift datum
|
|
, PlutusTx.ToData (PLifted datum)
|
|
, PLift redeemer
|
|
, PlutusTx.ToData (PLifted redeemer)
|
|
) =>
|
|
String ->
|
|
ClosedTerm PValidator ->
|
|
PLifted datum ->
|
|
PLifted redeemer ->
|
|
ScriptContext ->
|
|
SpecificationTree
|
|
validatorSucceedsWith tag validator datum redeemer scriptContext =
|
|
scriptSucceeds tag $
|
|
compile
|
|
( validator
|
|
# pforgetData (pconstantData datum)
|
|
# pforgetData (pconstantData redeemer)
|
|
# pconstant scriptContext
|
|
)
|
|
|
|
validatorFailsWith ::
|
|
( PLift datum
|
|
, PlutusTx.ToData (PLifted datum)
|
|
, PLift redeemer
|
|
, PlutusTx.ToData (PLifted redeemer)
|
|
) =>
|
|
String ->
|
|
ClosedTerm PValidator ->
|
|
PLifted datum ->
|
|
PLifted redeemer ->
|
|
ScriptContext ->
|
|
SpecificationTree
|
|
validatorFailsWith tag validator datum redeemer scriptContext =
|
|
scriptFails tag $
|
|
compile
|
|
( validator
|
|
# pforgetData (pconstantData datum)
|
|
# pforgetData (pconstantData redeemer)
|
|
# pconstant scriptContext
|
|
)
|
|
|
|
effectSucceedsWith ::
|
|
( PLift datum
|
|
, PlutusTx.ToData (PLifted datum)
|
|
) =>
|
|
String ->
|
|
ClosedTerm PValidator ->
|
|
PLifted datum ->
|
|
ScriptContext ->
|
|
SpecificationTree
|
|
effectSucceedsWith tag eff datum = validatorSucceedsWith tag eff datum ()
|
|
|
|
effectFailsWith ::
|
|
( PLift datum
|
|
, PlutusTx.ToData (PLifted datum)
|
|
) =>
|
|
String ->
|
|
ClosedTerm PValidator ->
|
|
PLifted datum ->
|
|
ScriptContext ->
|
|
SpecificationTree
|
|
effectFailsWith tag eff datum = validatorFailsWith tag eff datum ()
|