2
2
3
3
module Spec.HydraAuctionOnchain.Validators.StandingBid (spec ) where
4
4
5
+ import Data.Bool (bool )
6
+ import Data.Default (Default (def ))
5
7
import Data.List (singleton )
8
+ import HydraAuctionOnchain.Errors.StandingBid (PStandingBidError (.. ))
6
9
import HydraAuctionOnchain.Scripts (compileScript )
7
10
import HydraAuctionOnchain.Validators.StandingBid (standingBidValidator )
8
11
import Plutarch (Script )
@@ -16,7 +19,7 @@ import PlutusLedgerApi.V2
16
19
, Credential (ScriptCredential )
17
20
, CurrencySymbol
18
21
, Datum (Datum )
19
- , OutputDatum (OutputDatum )
22
+ , OutputDatum (NoOutputDatum , OutputDatum )
20
23
, Redeemer (Redeemer )
21
24
, ScriptContext (.. )
22
25
, ScriptHash
@@ -30,7 +33,7 @@ import PlutusLedgerApi.V2
30
33
, toData
31
34
)
32
35
import PlutusTx.AssocMap qualified as AMap (singleton )
33
- import Spec.HydraAuctionOnchain.Expectations (shouldSucceed )
36
+ import Spec.HydraAuctionOnchain.Expectations (shouldFail , shouldFailWithError , shouldSucceed )
34
37
import Spec.HydraAuctionOnchain.Helpers (mkStandingBidTokenValue )
35
38
import Spec.HydraAuctionOnchain.QuickCheck.Gen
36
39
( genKeyPair
@@ -40,29 +43,137 @@ import Spec.HydraAuctionOnchain.QuickCheck.Gen
40
43
, genValidNewBidState
41
44
, vkey
42
45
)
46
+ import Spec.HydraAuctionOnchain.QuickCheck.Modifiers (GenNonAdaValue (GenNonAdaValue ))
43
47
import Spec.HydraAuctionOnchain.Types.AuctionTerms (AuctionTerms , biddingPeriod )
44
48
import Spec.HydraAuctionOnchain.Types.Redeemers (StandingBidRedeemer (NewBidRedeemer ))
45
- import Spec.HydraAuctionOnchain.Types.StandingBidState (StandingBidState )
46
- import Test.QuickCheck (Arbitrary (arbitrary ), Property )
49
+ import Spec.HydraAuctionOnchain.Types.StandingBidState (StandingBidState ( StandingBidState ) )
50
+ import Test.QuickCheck (Arbitrary (arbitrary ), NonZero ( NonZero ), Property , resize , suchThat )
47
51
import Test.Tasty (TestTree , testGroup )
52
+
53
+ -- import Test.Tasty.ExpectedFailure (ignoreTest)
48
54
import Test.Tasty.QuickCheck (testProperty )
49
55
50
56
spec :: TestTree
51
57
spec =
52
- testGroup
53
- " StandingBid"
54
- [ testGroup
55
- " NewBidRedeemer"
56
- [ testProperty " Succeeds" prop_newBid_validInput_succeeds
58
+ testGroup " StandingBid" $
59
+ [ testGroup " NewBidRedeemer" $
60
+ [ testProperty " Succeeds if transaction is valid" $
61
+ prop_validInput_succeeds
62
+ , testProperty " Fails if standing bid input does not exist" $
63
+ prop_missingStandingBidInput_fails
64
+ , testProperty " Fails if there are multiple standing bid inputs" $
65
+ prop_multipleStandingBidInputs_fails
66
+ , testProperty " Fails if standing bid input does not contain standing bid token" $
67
+ prop_standingBidInputMissingToken_fails
68
+ , testProperty " Fails if transaction mints or burns tokens" $
69
+ prop_mintsBurnsValue_fails
70
+ , testProperty " Fails if standing bid output does not exist" $
71
+ prop_newBid_missingStandingBidOutput_fails
72
+ , testProperty " Fails if there are multiple standing bid outputs" $
73
+ prop_multipleStandingBidOutputs_fails
74
+ , testProperty " Fails if standing bid output does not contain standing bid token" $
75
+ prop_newBid_standingBidOutputMissingToken_fails
76
+ , testProperty " Fails if new bid state datum is missing" $
77
+ prop_newBid_missingNewBidStateDatum_fails
78
+ , testProperty " Fails if new bid state datum is invalid" $
79
+ prop_newBid_invalidNewBidStateDatum_fails
80
+ , testProperty " Fails if new bid state is empty" $
81
+ prop_newBid_emptyNewBidState_fails
57
82
]
58
83
]
59
84
60
85
--------------------------------------------------------------------------------
61
- -- NewBid
86
+ -- NewBid Properties
62
87
--------------------------------------------------------------------------------
63
88
64
- prop_newBid_validInput_succeeds :: NewBidTestContext -> Property
65
- prop_newBid_validInput_succeeds testContext = shouldSucceed $ testNewBid testContext
89
+ prop_validInput_succeeds :: NewBidTestContext -> Property
90
+ prop_validInput_succeeds testContext =
91
+ shouldSucceed $
92
+ testNewBid testContext def
93
+
94
+ prop_missingStandingBidInput_fails :: NewBidTestContext -> Property
95
+ prop_missingStandingBidInput_fails testContext =
96
+ shouldFailWithError StandingBid'Error'MissingStandingBidInput $
97
+ testNewBid testContext $
98
+ def
99
+ { standingBidInputMode = StandingBidInputMissing
100
+ }
101
+
102
+ prop_multipleStandingBidInputs_fails :: NewBidTestContext -> Property
103
+ prop_multipleStandingBidInputs_fails testContext =
104
+ shouldFailWithError StandingBid'Error'TooManyOwnScriptInputs $
105
+ testNewBid testContext $
106
+ def
107
+ { standingBidInputMode = MultipleStandingBidInputs
108
+ }
109
+
110
+ prop_standingBidInputMissingToken_fails :: NewBidTestContext -> Property
111
+ prop_standingBidInputMissingToken_fails testContext =
112
+ shouldFailWithError StandingBid'Error'OwnInputMissingToken $
113
+ testNewBid testContext $
114
+ def
115
+ { standingBidInputContainsToken = False
116
+ }
117
+
118
+ prop_mintsBurnsValue_fails :: NewBidTestContext -> Property
119
+ prop_mintsBurnsValue_fails testContext =
120
+ shouldFailWithError StandingBid'Error'UnexpectedTokensMintedBurned $
121
+ testNewBid testContext $
122
+ def
123
+ { mintsBurnsValue = True
124
+ }
125
+
126
+ prop_newBid_missingStandingBidOutput_fails :: NewBidTestContext -> Property
127
+ prop_newBid_missingStandingBidOutput_fails testContext =
128
+ shouldFailWithError StandingBid'NewBid'Error'MissingOwnOutput $
129
+ testNewBid testContext $
130
+ def
131
+ { standingBidOutputMode = StandingBidOutputMissing
132
+ }
133
+
134
+ prop_multipleStandingBidOutputs_fails :: NewBidTestContext -> Property
135
+ prop_multipleStandingBidOutputs_fails testContext =
136
+ shouldFailWithError StandingBid'NewBid'Error'MissingOwnOutput $
137
+ testNewBid testContext $
138
+ def
139
+ { standingBidOutputMode = MultipleStandingBidOutputs
140
+ }
141
+
142
+ prop_newBid_standingBidOutputMissingToken_fails :: NewBidTestContext -> Property
143
+ prop_newBid_standingBidOutputMissingToken_fails testContext =
144
+ shouldFailWithError StandingBid'NewBid'Error'OwnOutputMissingToken $
145
+ testNewBid testContext $
146
+ def
147
+ { standingBidOutputContainsToken = False
148
+ }
149
+
150
+ prop_newBid_missingNewBidStateDatum_fails :: NewBidTestContext -> Property
151
+ prop_newBid_missingNewBidStateDatum_fails testContext =
152
+ shouldFailWithError StandingBid'NewBid'Error'FailedToDecodeNewBid $
153
+ testNewBid testContext $
154
+ def
155
+ { newBidStateMode = NewBidStateMissingDatum
156
+ }
157
+
158
+ prop_newBid_invalidNewBidStateDatum_fails :: NewBidTestContext -> Property
159
+ prop_newBid_invalidNewBidStateDatum_fails testContext =
160
+ shouldFail $
161
+ testNewBid testContext $
162
+ def
163
+ { newBidStateMode = NewBidStateInvalidDatum
164
+ }
165
+
166
+ prop_newBid_emptyNewBidState_fails :: NewBidTestContext -> Property
167
+ prop_newBid_emptyNewBidState_fails testContext =
168
+ shouldFailWithError StandingBid'NewBid'Error'InvalidNewBidState $
169
+ testNewBid testContext $
170
+ def
171
+ { newBidStateMode = NewBidStateEmpty
172
+ }
173
+
174
+ --------------------------------------------------------------------------------
175
+ -- NewBid Test Context
176
+ --------------------------------------------------------------------------------
66
177
67
178
data NewBidTestContext = NewBidTestContext
68
179
{ auctionCs :: CurrencySymbol
@@ -72,6 +183,8 @@ data NewBidTestContext = NewBidTestContext
72
183
, txInfoTemplate :: TxInfo
73
184
, standingBidInputOref :: TxOutRef
74
185
, scriptAddress :: Address
186
+ , invalidNewBidStateDatum :: Datum
187
+ , invalidMintValue :: Value
75
188
}
76
189
deriving stock (Show , Eq )
77
190
@@ -85,10 +198,60 @@ instance Arbitrary NewBidTestContext where
85
198
txInfoTemplate <- genTxInfoTemplate
86
199
standingBidInputOref <- arbitrary @ TxOutRef
87
200
scriptAddress <- flip Address Nothing . ScriptCredential <$> arbitrary @ ScriptHash
201
+ invalidNewBidStateDatum <- resize 10 $ arbitrary @ Datum
202
+ GenNonAdaValue @ NonZero invalidMintValue <- arbitrary `suchThat` ((/=) mempty )
88
203
pure NewBidTestContext {.. }
89
204
90
- testNewBid :: NewBidTestContext -> Script
91
- testNewBid NewBidTestContext {.. } =
205
+ --------------------------------------------------------------------------------
206
+ -- NewBid Test Constraints
207
+ --------------------------------------------------------------------------------
208
+
209
+ data NewBidTestConstraints = NewBidTestConstraints
210
+ { standingBidInputMode :: StandingBidInputMode
211
+ , standingBidInputContainsToken :: Bool
212
+ , mintsBurnsValue :: Bool
213
+ , standingBidOutputMode :: StandingBidOutputMode
214
+ , standingBidOutputContainsToken :: Bool
215
+ , newBidStateMode :: NewBidStateMode
216
+ }
217
+ deriving stock (Show , Eq )
218
+
219
+ instance Default NewBidTestConstraints where
220
+ def =
221
+ NewBidTestConstraints
222
+ { standingBidInputMode = StandingBidInputValid
223
+ , standingBidInputContainsToken = True
224
+ , mintsBurnsValue = False
225
+ , standingBidOutputMode = StandingBidOutputValid
226
+ , standingBidOutputContainsToken = True
227
+ , newBidStateMode = NewBidStateValid
228
+ }
229
+
230
+ data StandingBidInputMode
231
+ = StandingBidInputValid
232
+ | StandingBidInputMissing
233
+ | MultipleStandingBidInputs
234
+ deriving stock (Show , Eq )
235
+
236
+ data StandingBidOutputMode
237
+ = StandingBidOutputValid
238
+ | StandingBidOutputMissing
239
+ | MultipleStandingBidOutputs
240
+ deriving stock (Show , Eq )
241
+
242
+ data NewBidStateMode
243
+ = NewBidStateValid
244
+ | NewBidStateEmpty
245
+ | NewBidStateMissingDatum
246
+ | NewBidStateInvalidDatum
247
+ deriving stock (Show , Eq )
248
+
249
+ --------------------------------------------------------------------------------
250
+ -- NewBid Test
251
+ --------------------------------------------------------------------------------
252
+
253
+ testNewBid :: NewBidTestContext -> NewBidTestConstraints -> Script
254
+ testNewBid NewBidTestContext {.. } NewBidTestConstraints {.. } =
92
255
let
93
256
standingBidTokenValue :: Value
94
257
standingBidTokenValue = mkStandingBidTokenValue auctionCs
@@ -98,7 +261,7 @@ testNewBid NewBidTestContext {..} =
98
261
TxInInfo standingBidInputOref $
99
262
TxOut
100
263
{ txOutAddress = scriptAddress
101
- , txOutValue = standingBidTokenValue
264
+ , txOutValue = bool mempty standingBidTokenValue standingBidInputContainsToken
102
265
, txOutDatum = OutputDatum $ Datum $ dataToBuiltinData $ toData oldBidState
103
266
, txOutReferenceScript = Nothing
104
267
}
@@ -107,8 +270,17 @@ testNewBid NewBidTestContext {..} =
107
270
standingBidOutput =
108
271
TxOut
109
272
{ txOutAddress = scriptAddress
110
- , txOutValue = standingBidTokenValue
111
- , txOutDatum = OutputDatum $ Datum $ dataToBuiltinData $ toData newBidState
273
+ , txOutValue = bool mempty standingBidTokenValue standingBidOutputContainsToken
274
+ , txOutDatum =
275
+ case newBidStateMode of
276
+ NewBidStateValid ->
277
+ OutputDatum $ Datum $ dataToBuiltinData $ toData newBidState
278
+ NewBidStateEmpty ->
279
+ OutputDatum $ Datum $ dataToBuiltinData $ toData $ StandingBidState Nothing
280
+ NewBidStateMissingDatum ->
281
+ NoOutputDatum
282
+ NewBidStateInvalidDatum ->
283
+ OutputDatum invalidNewBidStateDatum
112
284
, txOutReferenceScript = Nothing
113
285
}
114
286
@@ -126,8 +298,17 @@ testNewBid NewBidTestContext {..} =
126
298
ScriptContext
127
299
{ scriptContextTxInfo =
128
300
txInfoTemplate
129
- { txInfoInputs = singleton standingBidInput
130
- , txInfoOutputs = singleton standingBidOutput
301
+ { txInfoInputs =
302
+ case standingBidInputMode of
303
+ StandingBidInputValid -> singleton standingBidInput
304
+ StandingBidInputMissing -> mempty
305
+ MultipleStandingBidInputs -> replicate 2 standingBidInput
306
+ , txInfoOutputs =
307
+ case standingBidOutputMode of
308
+ StandingBidOutputValid -> singleton standingBidOutput
309
+ StandingBidOutputMissing -> mempty
310
+ MultipleStandingBidOutputs -> replicate 2 standingBidOutput
311
+ , txInfoMint = bool mempty invalidMintValue mintsBurnsValue
131
312
, txInfoValidRange = biddingPeriod auctionTerms
132
313
, txInfoRedeemers = AMap. singleton scriptPurpose redeemer
133
314
}
0 commit comments