add tests for advancement from draft phrase; refactoring
This commit is contained in:
parent
894bba2874
commit
701340688a
12 changed files with 1402 additions and 1078 deletions
|
|
@ -1,5 +1,3 @@
|
|||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
{- |
|
||||
Module : Spec.Proposal
|
||||
Maintainer : emi@haskell.fyi
|
||||
|
|
@ -11,45 +9,19 @@ module Spec.Proposal (specs) where
|
|||
|
||||
import Agora.Proposal (
|
||||
Proposal (..),
|
||||
ProposalDatum (..),
|
||||
ProposalId (ProposalId),
|
||||
ProposalRedeemer (..),
|
||||
ProposalStatus (..),
|
||||
ProposalThresholds (..),
|
||||
ProposalVotes (ProposalVotes),
|
||||
ResultTag (ResultTag),
|
||||
cosigners,
|
||||
effects,
|
||||
emptyVotesFor,
|
||||
proposalId,
|
||||
status,
|
||||
thresholds,
|
||||
votes,
|
||||
)
|
||||
import Agora.Proposal.Scripts (proposalPolicy, proposalValidator)
|
||||
import Agora.Proposal.Time (
|
||||
ProposalStartingTime (ProposalStartingTime),
|
||||
)
|
||||
import Agora.Stake (
|
||||
ProposalLock (ProposalLock),
|
||||
StakeDatum (StakeDatum),
|
||||
StakeRedeemer (PermitVote, WitnessStake),
|
||||
)
|
||||
import Agora.Stake.Scripts (stakeValidator)
|
||||
import Data.Default.Class (Default (def))
|
||||
import Data.Tagged (Tagged (Tagged), untag)
|
||||
import PlutusLedgerApi.V1 (ScriptContext (..), ScriptPurpose (..))
|
||||
import PlutusTx.AssocMap qualified as AssocMap
|
||||
import Agora.Proposal.Scripts (proposalPolicy)
|
||||
import Sample.Proposal qualified as Proposal
|
||||
import Sample.Proposal.Advance qualified as Advance
|
||||
import Sample.Proposal.Cosign qualified as Cosign
|
||||
import Sample.Proposal.UnlockStake qualified as UnlockStake
|
||||
import Sample.Shared (signer, signer2)
|
||||
import Sample.Shared qualified as Shared (proposal, stake)
|
||||
import Sample.Proposal.Vote qualified as Vote
|
||||
import Sample.Shared qualified as Shared (proposal)
|
||||
import Test.Specification (
|
||||
SpecificationTree,
|
||||
group,
|
||||
policySucceedsWith,
|
||||
validatorFailsWith,
|
||||
validatorSucceedsWith,
|
||||
)
|
||||
|
||||
-- | Stake specs.
|
||||
|
|
@ -67,279 +39,133 @@ specs =
|
|||
"validator"
|
||||
[ group
|
||||
"cosignature"
|
||||
[ validatorSucceedsWith
|
||||
"proposal"
|
||||
(proposalValidator Shared.proposal)
|
||||
( ProposalDatum
|
||||
{ proposalId = ProposalId 0
|
||||
, effects =
|
||||
AssocMap.fromList
|
||||
[ (ResultTag 0, AssocMap.empty)
|
||||
, (ResultTag 1, AssocMap.empty)
|
||||
]
|
||||
, status = Draft
|
||||
, cosigners = [signer]
|
||||
, thresholds = def
|
||||
, votes =
|
||||
emptyVotesFor $
|
||||
AssocMap.fromList
|
||||
[ (ResultTag 0, AssocMap.empty)
|
||||
, (ResultTag 1, AssocMap.empty)
|
||||
]
|
||||
, timingConfig = def
|
||||
, startingTime = ProposalStartingTime 0
|
||||
}
|
||||
)
|
||||
(Cosign [signer2])
|
||||
(ScriptContext (Proposal.cosignProposal [signer2]) (Spending Proposal.proposalRef))
|
||||
, validatorSucceedsWith
|
||||
"stake"
|
||||
(stakeValidator Shared.stake)
|
||||
(StakeDatum (Tagged 50_000_000) signer2 [])
|
||||
WitnessStake
|
||||
(ScriptContext (Proposal.cosignProposal [signer2]) (Spending Proposal.stakeRef))
|
||||
]
|
||||
$ let cosignerCases = [1, 5, 10]
|
||||
|
||||
mkLegalGroup nCosigners =
|
||||
Cosign.mkTestTree
|
||||
("with " <> show nCosigners <> " cosigners")
|
||||
(Cosign.validCosignNParameters nCosigners)
|
||||
True
|
||||
legalGroup =
|
||||
group "legal" $
|
||||
map mkLegalGroup cosignerCases
|
||||
|
||||
mkIllegalStatusNotDraftGroup nCosigners =
|
||||
group ("with " <> show nCosigners <> " cosigners") $
|
||||
map
|
||||
( \ps ->
|
||||
Cosign.mkTestTree
|
||||
("status: " <> show ps.proposalStatus)
|
||||
ps
|
||||
False
|
||||
)
|
||||
(Cosign.statusNotDraftCosignNParameters nCosigners)
|
||||
illegalStatusNotDraftGroup =
|
||||
group "proposal status not Draft" $
|
||||
map mkIllegalStatusNotDraftGroup cosignerCases
|
||||
|
||||
illegalGroup =
|
||||
group
|
||||
"illegal"
|
||||
[ Cosign.mkTestTree
|
||||
"duplicate cosigners"
|
||||
Cosign.duplicateCosignersParameters
|
||||
False
|
||||
, Cosign.mkTestTree
|
||||
"altered output stake"
|
||||
Cosign.invalidStakeOutputParameters
|
||||
False
|
||||
, illegalStatusNotDraftGroup
|
||||
]
|
||||
in [legalGroup, illegalGroup]
|
||||
, group
|
||||
"voting"
|
||||
[ validatorSucceedsWith
|
||||
"proposal"
|
||||
(proposalValidator Shared.proposal)
|
||||
( ProposalDatum
|
||||
{ proposalId = ProposalId 42
|
||||
, effects =
|
||||
AssocMap.fromList
|
||||
[ (ResultTag 0, AssocMap.empty)
|
||||
, (ResultTag 1, AssocMap.empty)
|
||||
[ Vote.mkTestTree "legal" Vote.validVoteParameters True
|
||||
-- TODO: add negative test cases
|
||||
]
|
||||
, group "advancing" $
|
||||
let mkFromDraft nCosigners =
|
||||
let name = "with " <> show nCosigners <> " cosigner(s)"
|
||||
|
||||
legalGroup =
|
||||
group
|
||||
"legal"
|
||||
[ Advance.mkTestTree
|
||||
"to next state"
|
||||
( head $
|
||||
Advance.advanceToNextStateInTimeParameters
|
||||
nCosigners
|
||||
)
|
||||
True
|
||||
, Advance.mkTestTree
|
||||
"to failed state"
|
||||
( head $
|
||||
Advance.advanceToFailedStateDueToTimeoutParameters
|
||||
nCosigners
|
||||
)
|
||||
True
|
||||
]
|
||||
, status = VotingReady
|
||||
, cosigners = [signer]
|
||||
, thresholds = def
|
||||
, votes =
|
||||
ProposalVotes
|
||||
( AssocMap.fromList
|
||||
[ (ResultTag 0, 42)
|
||||
, (ResultTag 1, 4242)
|
||||
]
|
||||
|
||||
illegalGroup =
|
||||
group
|
||||
"illegal"
|
||||
[ Advance.mkTestTree
|
||||
"insufficient cosigns"
|
||||
(Advance.insufficientCosignsParameters nCosigners)
|
||||
False
|
||||
, Advance.mkTestTree
|
||||
"invalid stake output"
|
||||
(head $ Advance.invalidOutputStakeParameters nCosigners)
|
||||
False
|
||||
]
|
||||
in group name [legalGroup, illegalGroup]
|
||||
|
||||
draftGroup = group "from draft" $ map mkFromDraft [1, 5, 10]
|
||||
|
||||
legalGroup =
|
||||
group
|
||||
"legal"
|
||||
[ group "advance to next state" $
|
||||
map
|
||||
( \ps ->
|
||||
let name = "from: " <> show ps.fromStatus
|
||||
in Advance.mkTestTree name ps True
|
||||
)
|
||||
, timingConfig = def
|
||||
, startingTime = ProposalStartingTime 0
|
||||
}
|
||||
)
|
||||
(Vote (ResultTag 0))
|
||||
( ScriptContext
|
||||
( Proposal.voteOnProposal
|
||||
Proposal.VotingParameters
|
||||
{ Proposal.voteFor = ResultTag 0
|
||||
, Proposal.voteCount = 27
|
||||
}
|
||||
)
|
||||
(Spending Proposal.proposalRef)
|
||||
)
|
||||
, validatorSucceedsWith
|
||||
"stake"
|
||||
(stakeValidator Shared.stake)
|
||||
( StakeDatum
|
||||
(Tagged 27)
|
||||
signer
|
||||
[ ProposalLock (ResultTag 0) (ProposalId 0)
|
||||
, ProposalLock (ResultTag 2) (ProposalId 1)
|
||||
(tail $ Advance.advanceToNextStateInTimeParameters 1)
|
||||
, group "advance to failed state" $
|
||||
map
|
||||
( \ps ->
|
||||
let name = "from: " <> show ps.fromStatus
|
||||
in Advance.mkTestTree name ps True
|
||||
)
|
||||
(tail $ Advance.advanceToFailedStateDueToTimeoutParameters 1)
|
||||
]
|
||||
)
|
||||
(PermitVote $ ProposalLock (ResultTag 0) (ProposalId 42))
|
||||
( ScriptContext
|
||||
( Proposal.voteOnProposal
|
||||
Proposal.VotingParameters
|
||||
{ Proposal.voteFor = ResultTag 0
|
||||
, Proposal.voteCount = 27
|
||||
}
|
||||
)
|
||||
(Spending Proposal.stakeRef)
|
||||
)
|
||||
]
|
||||
, group
|
||||
"advancing"
|
||||
[ group "successfully advance to next state" $
|
||||
map
|
||||
( \(name, initialState) ->
|
||||
validatorSucceedsWith
|
||||
name
|
||||
(proposalValidator Shared.proposal)
|
||||
( ProposalDatum
|
||||
{ proposalId = ProposalId 0
|
||||
, effects =
|
||||
AssocMap.fromList
|
||||
[ (ResultTag 0, AssocMap.empty)
|
||||
, (ResultTag 1, AssocMap.empty)
|
||||
]
|
||||
, status = initialState
|
||||
, cosigners = [signer]
|
||||
, thresholds = def
|
||||
, votes =
|
||||
ProposalVotes
|
||||
( AssocMap.fromList
|
||||
[
|
||||
( ResultTag 0
|
||||
, case initialState of
|
||||
Draft -> 0
|
||||
_ -> untag (def :: ProposalThresholds).execute + 1
|
||||
)
|
||||
, (ResultTag 1, 0)
|
||||
]
|
||||
)
|
||||
, timingConfig = def
|
||||
, startingTime = ProposalStartingTime 0
|
||||
}
|
||||
)
|
||||
AdvanceProposal
|
||||
( ScriptContext
|
||||
( Proposal.advanceProposalSuccess
|
||||
Proposal.TransitionParameters
|
||||
{ Proposal.initialProposalStatus = initialState
|
||||
, Proposal.proposalStartingTime = ProposalStartingTime 0
|
||||
}
|
||||
)
|
||||
(Spending Proposal.proposalRef)
|
||||
)
|
||||
)
|
||||
[ ("Draft -> VotringReady", Draft)
|
||||
, ("VotingReady -> Locked", VotingReady)
|
||||
, ("Locked -> Finished", Locked)
|
||||
]
|
||||
, group "successfully advance to failed state: timeout" $
|
||||
map
|
||||
( \(name, initialState) ->
|
||||
validatorSucceedsWith
|
||||
name
|
||||
(proposalValidator Shared.proposal)
|
||||
( ProposalDatum
|
||||
{ proposalId = ProposalId 0
|
||||
, effects =
|
||||
AssocMap.fromList
|
||||
[ (ResultTag 0, AssocMap.empty)
|
||||
, (ResultTag 1, AssocMap.empty)
|
||||
]
|
||||
, status = initialState
|
||||
, cosigners = [signer]
|
||||
, thresholds = def
|
||||
, votes =
|
||||
ProposalVotes
|
||||
( AssocMap.fromList
|
||||
[
|
||||
( ResultTag 0
|
||||
, case initialState of
|
||||
Draft -> 0
|
||||
_ -> untag (def :: ProposalThresholds).vote + 1
|
||||
)
|
||||
, (ResultTag 1, 0)
|
||||
]
|
||||
)
|
||||
, timingConfig = def
|
||||
, startingTime = ProposalStartingTime 0
|
||||
}
|
||||
)
|
||||
AdvanceProposal
|
||||
( ScriptContext
|
||||
( Proposal.advanceProposalFailureTimeout
|
||||
Proposal.TransitionParameters
|
||||
{ Proposal.initialProposalStatus = initialState
|
||||
, Proposal.proposalStartingTime = ProposalStartingTime 0
|
||||
}
|
||||
)
|
||||
(Spending Proposal.proposalRef)
|
||||
)
|
||||
)
|
||||
[ ("Draft -> Finished", Draft)
|
||||
, ("VotingReady -> Finished", VotingReady)
|
||||
, ("Locked -> Finished", Locked)
|
||||
]
|
||||
, validatorFailsWith
|
||||
"illegal: insufficient votes"
|
||||
(proposalValidator Shared.proposal)
|
||||
( ProposalDatum
|
||||
{ proposalId = ProposalId 0
|
||||
, effects =
|
||||
AssocMap.fromList
|
||||
[ (ResultTag 0, AssocMap.empty)
|
||||
, (ResultTag 1, AssocMap.empty)
|
||||
]
|
||||
, status = VotingReady
|
||||
, cosigners = [signer]
|
||||
, thresholds = def
|
||||
, votes =
|
||||
ProposalVotes
|
||||
( AssocMap.fromList
|
||||
[ (ResultTag 0, 1)
|
||||
, (ResultTag 1, 0)
|
||||
]
|
||||
)
|
||||
, timingConfig = def
|
||||
, startingTime = ProposalStartingTime 0
|
||||
}
|
||||
)
|
||||
AdvanceProposal
|
||||
( ScriptContext
|
||||
Proposal.advanceProposalInsufficientVotes
|
||||
(Spending Proposal.proposalRef)
|
||||
)
|
||||
, validatorFailsWith
|
||||
"illegal: initial state is Finished"
|
||||
(proposalValidator Shared.proposal)
|
||||
( ProposalDatum
|
||||
{ proposalId = ProposalId 0
|
||||
, effects =
|
||||
AssocMap.fromList
|
||||
[ (ResultTag 0, AssocMap.empty)
|
||||
, (ResultTag 1, AssocMap.empty)
|
||||
]
|
||||
, status = Finished
|
||||
, cosigners = [signer]
|
||||
, thresholds = def
|
||||
, votes =
|
||||
ProposalVotes
|
||||
( AssocMap.fromList
|
||||
[ (ResultTag 0, untag (def :: ProposalThresholds).vote + 1)
|
||||
, (ResultTag 1, 0)
|
||||
]
|
||||
)
|
||||
, timingConfig = def
|
||||
, startingTime = ProposalStartingTime 0
|
||||
}
|
||||
)
|
||||
AdvanceProposal
|
||||
( ScriptContext
|
||||
Proposal.advanceFinishedProposal
|
||||
(Spending Proposal.proposalRef)
|
||||
)
|
||||
, validatorFailsWith
|
||||
"illegal: with stake input"
|
||||
(proposalValidator Shared.proposal)
|
||||
( ProposalDatum
|
||||
{ proposalId = ProposalId 0
|
||||
, effects =
|
||||
AssocMap.fromList
|
||||
[ (ResultTag 0, AssocMap.empty)
|
||||
, (ResultTag 1, AssocMap.empty)
|
||||
]
|
||||
, status = VotingReady
|
||||
, cosigners = [signer]
|
||||
, thresholds = def
|
||||
, votes =
|
||||
ProposalVotes
|
||||
( AssocMap.fromList
|
||||
[ (ResultTag 0, 0)
|
||||
, (ResultTag 1, 0)
|
||||
]
|
||||
)
|
||||
, timingConfig = def
|
||||
, startingTime = ProposalStartingTime 0
|
||||
}
|
||||
)
|
||||
AdvanceProposal
|
||||
( ScriptContext
|
||||
Proposal.advanceProposalWithInvalidOutputStake
|
||||
(Spending Proposal.proposalRef)
|
||||
)
|
||||
]
|
||||
|
||||
illegalGroup =
|
||||
group
|
||||
"illegal"
|
||||
[ Advance.mkTestTree
|
||||
"insufficient votes"
|
||||
Advance.insufficientVotesParameters
|
||||
False
|
||||
, Advance.mkTestTree
|
||||
"initial state is Finished"
|
||||
Advance.advanceFromFinishedParameters
|
||||
False
|
||||
, group
|
||||
"invalid stake output"
|
||||
$ do
|
||||
nStake <- [1, 5]
|
||||
ps <- tail $ Advance.invalidOutputStakeParameters nStake
|
||||
|
||||
let name =
|
||||
"from " <> show ps.fromStatus <> "with "
|
||||
<> show nStake
|
||||
<> " stakes"
|
||||
|
||||
pure $ Advance.mkTestTree name ps False
|
||||
]
|
||||
in [draftGroup, legalGroup, illegalGroup]
|
||||
, group "unlocking" $ do
|
||||
proposalCount <- [1, 42]
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue