Skip to content

Commit e3585bb

Browse files
committed
test(ping-pong): unit tests over the contract
1 parent 6e8812b commit e3585bb

File tree

3 files changed

+113
-78
lines changed

3 files changed

+113
-78
lines changed

src/coin-selection/test/Scripts.hs

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,13 @@ module Scripts (
1818
sampleValidatorScript,
1919
pingPongValidatorScript,
2020
spendSample,
21-
spendPingPong,
21+
playPingPongRound,
2222
Sample.SampleRedeemer (..),
2323
PingPong.PingPongRedeemer (..),
2424
PingPong.PingPongState (..),
2525
) where
2626

27+
import Cardano.Api (NetworkId)
2728
import Cardano.Api qualified as C
2829
import Convex.BuildTx (MonadBuildTx)
2930
import Convex.BuildTx qualified as BuildTx
@@ -124,19 +125,36 @@ spendSample redeemer txi =
124125
redeemer
125126
in BuildTx.setScriptsValid >> BuildTx.addInputWithTxBody txi witness
126127

127-
spendPingPong
128+
plutusScript :: (C.IsPlutusScriptLanguage lang) => C.PlutusScript lang -> C.Script lang
129+
plutusScript = C.PlutusScript C.plutusScriptVersion
130+
131+
playPingPongRound
128132
:: forall era m
129-
. (C.IsAlonzoBasedEra era, C.HasScriptLanguageInEra C.PlutusScriptV3 era)
133+
. ( C.IsBabbageBasedEra era
134+
, C.HasScriptLanguageInEra C.PlutusScriptV3 era
135+
)
130136
=> (MonadBuildTx era m)
131-
=> PingPong.PingPongRedeemer
137+
=> NetworkId
138+
-> C.Lovelace
139+
-> PingPong.PingPongRedeemer
132140
-> C.TxIn
133141
-> m ()
134-
spendPingPong redeemer txi =
142+
playPingPongRound networkId value redeemer txi = do
135143
let witness _ =
136144
C.ScriptWitness C.ScriptWitnessForSpending $
137145
BuildTx.buildScriptWitness
138146
pingPongValidatorScript
139147
(C.ScriptDatumForTxIn $ Nothing) -- Just $ toHashableScriptData PingPong.Pinged)
140148
-- (fromIntegral @Int @Integer $ 9898) -- BuildTx.findIndexSpending txi txBody)
141149
redeemer
142-
in BuildTx.setScriptsValid >> BuildTx.addInputWithTxBody txi witness
150+
BuildTx.setScriptsValid >> BuildTx.addInputWithTxBody txi witness
151+
BuildTx.payToScriptInlineDatum
152+
networkId
153+
(C.hashScript (plutusScript Scripts.pingPongValidatorScript))
154+
( case redeemer of
155+
PingPong.Ping -> PingPong.Pinged
156+
PingPong.Pong -> PingPong.Ponged
157+
PingPong.Stop -> PingPong.Stopped
158+
)
159+
C.NoStakeAddress
160+
(C.lovelaceToValue value) -- add to the witness the datum

src/coin-selection/test/Scripts/PingPong.hs

Lines changed: 3 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
-- A plutus validator that only succeeds if the redeemer is identical to the script's input index
99
module Scripts.PingPong (
1010
validator,
11-
validatorSimplified,
1211
PingPongRedeemer (..),
1312
PingPongState (..),
1413
) where
@@ -21,8 +20,8 @@ import PlutusLedgerApi.V3.Contexts (
2120
TxInInfo (TxInInfo, txInInfoResolved),
2221
TxInfo (..),
2322
)
24-
import PlutusTx (Data (..), unstableMakeIsData)
25-
import PlutusTx.AssocMap (Map, lookup, toList)
23+
import PlutusTx (unstableMakeIsData)
24+
import PlutusTx.AssocMap (Map, lookup)
2625
import PlutusTx.Builtins.Internal qualified as BI
2726
import PlutusTx.IsData.Class (UnsafeFromData (unsafeFromBuiltinData))
2827
import PlutusTx.Prelude (BuiltinData, BuiltinUnit)
@@ -39,10 +38,8 @@ showState Pinged = "Pinged "
3938
showState Ponged = "Ponged "
4039
showState Stopped = "Stopped "
4140

42-
-- deriving anyclass (ToJSON, FromJSON)
43-
4441
data PingPongRedeemer = Ping | Pong | Stop
45-
deriving stock (Haskell.Show)
42+
deriving stock (Haskell.Eq, Haskell.Show)
4643

4744
{-# INLINEABLE showAction #-}
4845
showAction :: PingPongRedeemer -> P.BuiltinString
@@ -82,31 +79,11 @@ validator
8279
(Ponged, Stop, Stopped) -> BI.unitval
8380
_ -> P.traceError P.$ "Invalid state transition: " `P.appendString` showState currentState `P.appendString` showAction action `P.appendString` showState nextState
8481

85-
{-# INLINEABLE validatorSimplified #-}
86-
validatorSimplified :: BuiltinData -> BuiltinUnit
87-
validatorSimplified
88-
( unsafeFromBuiltinData ->
89-
ScriptContext
90-
{ scriptContextRedeemer = (unsafeFromBuiltinData P.. getRedeemer -> action :: PingPongRedeemer)
91-
, scriptContextTxInfo =
92-
txInfo@TxInfo
93-
{ -- txInfoInputs = (getStateFromInputs -> currentState)
94-
txInfoOutputs = (getStateFromOutpusts (txInfoData txInfo) -> nextState)
95-
}
96-
}
97-
) = case (action, nextState) of
98-
(Pong, Ponged) -> BI.unitval
99-
(Ping, Pinged) -> BI.unitval
100-
(Stop, Stopped) -> BI.unitval
101-
_ -> P.traceError "Invalid state transition"
102-
10382
{-# INLINEABLE getStateFromInputs #-}
10483
getStateFromInputs :: Map DatumHash Datum -> [TxInInfo] -> PingPongState
10584
getStateFromInputs _ [] = P.traceError "No inputs"
10685
getStateFromInputs txInfoData' (TxInInfo{txInInfoResolved = TxOut{txOutDatum}} : _) = getPingPongState txInfoData' "Datum on input" txOutDatum
10786

108-
-- getStateFromInputs _ _ = P.traceError "Multiple inputs"
109-
11087
{-# INLINEABLE getStateFromOutpusts #-}
11188
getStateFromOutpusts :: Map DatumHash Datum -> [TxOut] -> PingPongState
11289
getStateFromOutpusts _ [] = P.traceError "No outputs"
@@ -119,17 +96,3 @@ getPingPongState _ errorMsg NoOutputDatum = P.traceError P.$ P.appendString erro
11996
getPingPongState datumMap errorMsg (OutputDatumHash hash) = case lookup hash datumMap of
12097
P.Just (unsafeFromBuiltinData P.. getDatum -> state :: PingPongState) -> state
12198
P.Nothing -> P.traceError P.$ P.appendString errorMsg " - OutputDatumHash not found in datum map"
122-
123-
-- getPingPongState errorMsg (OutputDatum datum) =
124-
-- -- let d = getDatum datum
125-
-- P.traceError P.$ errorMsg
126-
127-
{-# INLINEABLE inspectDatum #-}
128-
inspectDatum :: BuiltinData -> P.BuiltinString
129-
inspectDatum d =
130-
case BI.builtinDataToData d of
131-
Constr i _ -> "Constructor " -- `P.appendString` showInt i
132-
Map _ -> "Map"
133-
List _ -> "List"
134-
I _ -> "Integer"
135-
B _ -> "ByteString"

src/coin-selection/test/Spec.hs

Lines changed: 86 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,65 @@ tests =
150150
-- Test tree fails
151151
(\_ -> pure ())
152152
)
153-
, testCase "spend pingPong an output succeeds" (mockchainSucceeds $ failOnError (pingPongScriptTest Scripts.Pong))
153+
, testGroup
154+
"ping-pong"
155+
[ testCase
156+
"Ping and Pong should succeed"
157+
( mockchainSucceeds $
158+
failOnError
159+
(pingPongMultipleRounds Scripts.Pinged [Scripts.Pong])
160+
)
161+
, testCase
162+
"Pong and Ping should succeed"
163+
( mockchainSucceeds $
164+
failOnError (pingPongMultipleRounds Scripts.Ponged [Scripts.Ping])
165+
)
166+
, testCase
167+
"Ping and Ping should fail"
168+
( mockchainFails
169+
(failOnError (pingPongMultipleRounds Scripts.Pinged [Scripts.Ping]))
170+
-- Test tree fails
171+
(\_ -> pure ())
172+
)
173+
, testCase
174+
"Pong and Pong should fail"
175+
( mockchainFails
176+
(failOnError (pingPongMultipleRounds Scripts.Ponged [Scripts.Pong]))
177+
-- Test tree fails
178+
(\_ -> pure ())
179+
)
180+
, testCase
181+
"Stop after Ping should succeed"
182+
( mockchainSucceeds $
183+
failOnError (pingPongMultipleRounds Scripts.Ponged [Scripts.Ping, Scripts.Stop])
184+
)
185+
, testCase
186+
"Stop after Pong should succeed"
187+
( mockchainSucceeds $
188+
failOnError (pingPongMultipleRounds Scripts.Pinged [Scripts.Pong, Scripts.Stop])
189+
)
190+
, testCase
191+
"Stop after Stop should fail"
192+
( mockchainFails
193+
(failOnError (pingPongMultipleRounds Scripts.Stopped [Scripts.Stop]))
194+
-- Test tree fails
195+
(\_ -> pure ())
196+
)
197+
, testCase
198+
"Ping after Stop should fail"
199+
( mockchainFails
200+
(failOnError (pingPongMultipleRounds Scripts.Stopped [Scripts.Ping]))
201+
-- Test tree fails
202+
(\_ -> pure ())
203+
)
204+
, testCase
205+
"Pong after Stop should fail"
206+
( mockchainFails
207+
(failOnError (pingPongMultipleRounds Scripts.Stopped [Scripts.Pong]))
208+
-- Test tree fails
209+
(\_ -> pure ())
210+
)
211+
]
154212
]
155213
, testGroup
156214
"mockchain"
@@ -451,7 +509,7 @@ sampleScriptTest
451509
)
452510
=> Scripts.SampleRedeemer
453511
-> m ()
454-
sampleScriptTest redemer = inBabbage @era $ do
512+
sampleScriptTest redeemer = inBabbage @era $ do
455513
let txBody =
456514
execBuildTx
457515
( BuildTx.payToScriptDatumHash
@@ -465,55 +523,51 @@ sampleScriptTest redemer = inBabbage @era $ do
465523
input <- C.TxIn . C.getTxId . C.getTxBody <$> tryBalanceAndSubmit mempty Wallet.w1 txBody TrailingChange [] <*> pure (C.TxIx 0)
466524

467525
-- Spend!! the outputs in a single transaction
468-
_tx <- tryBalanceAndSubmit mempty Wallet.w1 (execBuildTx $ Scripts.spendSample redemer input) TrailingChange []
526+
_tx <- tryBalanceAndSubmit mempty Wallet.w1 (execBuildTx $ Scripts.spendSample redeemer input) TrailingChange []
469527
pure ()
470528

471-
pingPongScriptTest
529+
pingPongMultipleRounds
472530
:: forall era m
473531
. ( MonadMockchain era m
474532
, MonadError (BalanceTxError era) m
475533
, MonadFail m
476534
, C.IsBabbageBasedEra era
477535
, C.HasScriptLanguageInEra C.PlutusScriptV3 era
478536
)
479-
=> Scripts.PingPongRedeemer
537+
=> Scripts.PingPongState
538+
-> [Scripts.PingPongRedeemer]
480539
-> m ()
481-
pingPongScriptTest redemer = inBabbage @era $ do
540+
pingPongMultipleRounds fstState redeemers = inBabbage @era $ do
541+
let value = 10_000_000
542+
-- this is the inital state and will not be validated
543+
-- we should prepare the state based on what we are about to play
482544
let txBody =
483545
execBuildTx
484546
( BuildTx.payToScriptInlineDatum
485547
Defaults.networkId
486548
(C.hashScript (plutusScript Scripts.pingPongValidatorScript))
487-
Scripts.Pinged
549+
-- we should start with Pinged if redeemer is Pong
550+
-- and Ponged if redeemer is Ping
551+
fstState
488552
C.NoStakeAddress
489-
(C.lovelaceToValue 10_000_000)
553+
(C.lovelaceToValue value)
490554
)
491-
-- here is the locking !!!
492555
tx <- tryBalanceAndSubmit mempty Wallet.w1 txBody TrailingChange []
493-
-- error $ show tx
494-
495-
let input = C.TxIn (C.getTxId $ C.getTxBody tx) (C.TxIx 0)
496-
-- input <- C.TxBody . C.getTxId . C.getTxBody <$> tryBalanceAndSubmit mempty Wallet.w1 txBody TrailingChange [] <*> pure (C.TxIx 0)
497-
498-
-- Spend!! the outputs in a single transaction
499-
_tx <-
500-
tryBalanceAndSubmit
501-
mempty
502-
Wallet.w1
503-
( execBuildTx $ do
504-
Scripts.spendPingPong redemer input
505-
--
506-
-- here we are going to payToScript
507-
BuildTx.payToScriptInlineDatum
508-
Defaults.networkId
509-
(C.hashScript (plutusScript Scripts.pingPongValidatorScript))
510-
Scripts.Ponged
511-
C.NoStakeAddress
512-
(C.lovelaceToValue 10_000_000) -- add to the witness the datum
513-
)
514-
TrailingChange
515-
[]
556+
_ <- play value tx redeemers
516557
pure ()
558+
where
559+
play _ tx [] = pure tx
560+
play value tx (redeemer : xs) = do
561+
newTx <-
562+
tryBalanceAndSubmit
563+
mempty
564+
Wallet.w1
565+
(execBuildTx $ Scripts.playPingPongRound Defaults.networkId value redeemer (getTxIn tx))
566+
TrailingChange
567+
[]
568+
play value newTx xs
569+
570+
getTxIn tx = C.TxIn (C.getTxId $ C.getTxBody tx) (C.TxIx 0)
517571

518572
scriptStakingCredential :: C.StakeCredential
519573
scriptStakingCredential = C.StakeCredentialByScript $ C.hashScript (C.PlutusScript C.PlutusScriptV2 Scripts.v2StakingScript)

0 commit comments

Comments
 (0)