content: {- |
  Module     : Spec.Proposal
  Maintainer : emi@haskell.fyi
  Description: Tests for Proposal policy and validator
  
  Tests for Proposal policy and validator
  -}
  module Spec.Proposal (specs) where
  
  import Sample.Proposal.Advance qualified as Advance
  import Sample.Proposal.Cosign qualified as Cosign
  import Sample.Proposal.Create qualified as Create
  import Sample.Proposal.PrivilegeEscalate qualified as PrivilegeEscalate
  import Sample.Proposal.Unlock qualified as Unlock
  import Sample.Proposal.Vote qualified as Vote
  
  import Test.Specification (
    SpecificationTree,
    group,
   )
  
  -- | Stake specs.
  specs :: [SpecificationTree]
  specs =
    [ group
        "policy (proposal creation)"
        [ Create.mkTestTree
            "legal"
            Create.totallyValidParameters
            True
            True
            True
        , group
            "illegal"
            [ Create.mkTestTree
                "invalid next proposal id"
                Create.invalidOutputGovernorDatumParameters
                True
                False
                True
            , Create.mkTestTree
                "use other's stake"
                Create.useStakeOwnBySomeoneElseParameters
                True
                True
                False
            , Create.mkTestTree
                "altered stake"
                Create.invalidOutputStakeParameters
                True
                False
                False
            , Create.mkTestTree
                "invalid stake locks"
                Create.addInvalidLocksParameters
                True
                True
                False
            , Create.mkTestTree
                "has reached maximum proposals limit"
                Create.exceedMaximumProposalsParameters
                True
                False
                True
            , Create.mkTestTree
                "loose time range"
                Create.timeRangeNotTightParameters
                True
                False
                True
            , Create.mkTestTree
                "open time range"
                Create.timeRangeNotClosedParameters
                True
                False
                True
            , group "invalid proposal status" $
                map
                  ( \ps ->
                      Create.mkTestTree
                        (show ps.proposalStatus)
                        ps
                        True
                        False
                        True
                  )
                  Create.invalidProposalStatusParameters
            , Create.mkTestTree
                "fake SST"
                Create.fakeSSTParameters
                True
                False
                False
            , Create.mkTestTree
                "wrong governor redeemer"
                Create.wrongGovernorRedeemer
                False
                False
                True
            , Create.mkTestTree
                "wrong governor redeemer"
                Create.wrongGovernorRedeemer1
                False
                False
                True
            ]
        ]
    , group
        "validator"
        [ group
            "cosignature"
            [ Cosign.mkTestTree
                "legal"
                Cosign.totallyValid
                (Cosign.Validity True True)
            , group
                "illegal"
                [ Cosign.mkTestTree
                    "insufficient staked amount"
                    Cosign.insufficientStakedAmount
                    (Cosign.Validity False True)
                , Cosign.mkTestTree
                    "proposal locks not updated"
                    Cosign.locksNotUpdated
                    (Cosign.Validity True False)
                , Cosign.mkTestTree
                    "duplicate cosigners"
                    Cosign.duplicateCosigners
                    (Cosign.Validity False True)
                , Cosign.mkTestTree
                    "cosigners not updated"
                    Cosign.cosignersNotUpdated
                    (Cosign.Validity False True)
                , group "cosign after draft" $
                    map
                      ( \b ->
                          Cosign.mkTestTree
                            "(negative test)"
                            b
                            (Cosign.Validity False True)
                      )
                      Cosign.cosignAfterDraft
                ]
            ]
        , group
            "voting"
            [ group
                "legal"
                [ group "different number of stakes" $
                    map
                      ( \s ->
                          group
                            (unwords [show s, "stakes"])
                            [ Vote.mkTestTree
                                "by owner"
                                (Vote.mkValidOwnerVoteBundle s)
                                (Vote.Validity True True)
                            , Vote.mkTestTree
                                "by delegatee"
                                (Vote.mkValidDelegateeVoteBundle s)
                                (Vote.Validity True True)
                            ]
                      )
                      [1, 3, 5, 7, 9]
                , Vote.mkTestTree
                    "transparent non-GT tokens"
                    Vote.transparentAssets
                    (Vote.Validity True True)
                , Vote.mkTestTree
                    "Delegatee vote with own and delegated stakes in one tx"
                    Vote.delegateeVoteWithOwnAndDelegatedStakeBundle
                    (Vote.Validity True True)
                ]
            , group
                "illegal"
                [ Vote.mkTestTree
                    "vote for nonexistent outcome"
                    Vote.voteForNonexistentOutcome
                    (Vote.Validity False True)
                , Vote.mkTestTree
                    "unauthorized tx"
                    Vote.transactionNotAuthorized
                    (Vote.Validity True False)
                , Vote.mkTestTree
                    "no proposal"
                    Vote.noProposal
                    (Vote.Validity False False)
                , Vote.mkTestTree
                    "more than one proposals"
                    Vote.voteForNonexistentOutcome
                    (Vote.Validity False True)
                , Vote.mkTestTree
                    "locks not added"
                    Vote.invalidLocks
                    (Vote.Validity True False)
                , Vote.mkTestTree
                    "attempt to burn stakes"
                    Vote.destroyStakes
                    (Vote.Validity True False)
                , Vote.mkTestTree
                    "insufficient staked amount"
                    Vote.insufficientAmount
                    (Vote.Validity False True)
                , Vote.mkTestTree
                    "insufficient staked amount"
                    Vote.insufficientAmount1
                    (Vote.Validity False True)
                ]
            ]
        , group
            "advancing"
            $ let possibleCosigners = [1, 5, 10]
                  possibleEffects = [1, 2, 5]
               in do
                    cs <- possibleCosigners
                    es <- possibleEffects
  
                    let groupName =
                          unwords
                            [ "with"
                            , show cs
                            , "cosigners"
                            , "and"
                            , show es
                            , "effects"
                            ]
  
                    pure $
                      group
                        groupName
                        [ group
                            "legal"
                            $ let allValid =
                                    Advance.Validity
                                      { forProposalValidator = True
                                      , forStakeValidator = True
                                      , forGovernorValidator = Just True
                                      , forAuthorityTokenPolicy = Just True
                                      }
                                  mkName b =
                                    unwords
                                      [ "from"
                                      , show b.proposalParameters.fromStatus
                                      , "to"
                                      , show b.proposalParameters.toStatus
                                      ]
                               in [ Advance.mkTestTree'
                                      "to next state"
                                      mkName
                                      (Advance.mkValidToNextStateBundles cs es)
                                      allValid
                                  , Advance.mkTestTree'
                                      "to failed state"
                                      mkName
                                      (Advance.mkValidToFailedStateBundles cs es)
                                      allValid
                                  , Advance.mkTestTree'
                                      "to finished state with inline datum"
                                      mkName
                                      (Advance.mkValidToFinishedInlineGATDatumBundles cs es)
                                      allValid
                                  ]
                        , group
                            "illegal"
                            [ Advance.mkTestTree'
                                "advance finished proposals"
                                (const "(negative test)")
                                (Advance.mkFromFinishedBundles cs es)
                                Advance.Validity
                                  { forProposalValidator = False
                                  , forStakeValidator = True
                                  , forGovernorValidator = Just False
                                  , forAuthorityTokenPolicy = Just True
                                  }
                            , Advance.mkTestTree
                                "insufficient cosigns"
                                (Advance.mkInsufficientCosignsBundle cs es)
                                Advance.Validity
                                  { forProposalValidator = False
                                  , forStakeValidator = True
                                  , forGovernorValidator = Nothing
                                  , forAuthorityTokenPolicy = Nothing
                                  }
                            , Advance.mkTestTree
                                "insufficient votes"
                                (Advance.mkInsufficientVotesBundle cs es)
                                Advance.Validity
                                  { forProposalValidator = False
                                  , forStakeValidator = True
                                  , forGovernorValidator = Nothing
                                  , forAuthorityTokenPolicy = Nothing
                                  }
                            , Advance.mkTestTree
                                "ambiguous winning effect"
                                (Advance.mkAmbiguousWinnerBundle cs es)
                                Advance.Validity
                                  { forProposalValidator = False
                                  , forStakeValidator = True
                                  , forGovernorValidator = Nothing
                                  , forAuthorityTokenPolicy = Nothing
                                  }
                            , Advance.mkTestTree'
                                "to next state too late"
                                (\b -> unwords ["from", show b.proposalParameters.fromStatus])
                                (Advance.mkToNextStateTooLateBundles cs es)
                                Advance.Validity
                                  { forProposalValidator = False
                                  , forStakeValidator = True
                                  , forGovernorValidator = Just True
                                  , forAuthorityTokenPolicy = Just True
                                  }
                            , Advance.mkTestTree
                                "forget to mint GATs"
                                (Advance.mkNoGATMintedBundle cs es)
                                Advance.Validity
                                  { forProposalValidator = True
                                  , forStakeValidator = True
                                  , forGovernorValidator = Just False
                                  , forAuthorityTokenPolicy = Nothing
                                  }
                            , Advance.mkTestTree
                                "mint GATs for wrong validators"
                                (Advance.mkMintGATsForWrongEffectsBundle cs es)
                                Advance.Validity
                                  { forProposalValidator = True
                                  , forStakeValidator = True
                                  , forGovernorValidator = Just False
                                  , forAuthorityTokenPolicy = Just True
                                  }
                            , Advance.mkTestTree
                                "mint GATs with bad token name"
                                (Advance.mkMintGATsWithoutTagBundle cs es)
                                Advance.Validity
                                  { forProposalValidator = True
                                  , forStakeValidator = True
                                  , forGovernorValidator = Just False
                                  , forAuthorityTokenPolicy = Just True
                                  }
                            , Advance.mkTestTree
                                "wrong GAT datum"
                                (Advance.mkGATsWithWrongDatumBundle cs es)
                                Advance.Validity
                                  { forProposalValidator = True
                                  , forStakeValidator = True
                                  , forGovernorValidator = Just False
                                  , forAuthorityTokenPolicy = Just True
                                  }
                            , Advance.mkTestTree
                                "invalid governor output datum"
                                (Advance.mkBadGovernorOutputDatumBundle cs es)
                                Advance.Validity
                                  { forProposalValidator = True
                                  , forStakeValidator = True
                                  , forGovernorValidator = Just False
                                  , forAuthorityTokenPolicy = Just True
                                  }
                            , Advance.mkTestTree'
                                "fastforward to finished"
                                (\b -> unwords ["from", show b.proposalParameters.fromStatus])
                                (Advance.mkFastforwardToFinishBundles cs es)
                                Advance.Validity
                                  { forProposalValidator = False
                                  , forStakeValidator = True
                                  , forGovernorValidator = Just False
                                  , forAuthorityTokenPolicy = Just True
                                  }
                            , Advance.mkTestTree
                                "wrong governor redeemer"
                                (Advance.mkBadGovernorRedeemerBundle cs es)
                                Advance.Validity
                                  { forProposalValidator = True
                                  , forStakeValidator = True
                                  , forGovernorValidator = Just False
                                  , forAuthorityTokenPolicy = Just False
                                  }
                            ]
                        ]
        , group "unlocking" $
            let stakeCountCases = [1, 3, 5, 7, 9, 11]
  
                mkSubgroupName nStakes = unwords ["with", show nStakes, "stakes"]
  
                mkLegalGroup nStakes =
                  group
                    (mkSubgroupName nStakes)
                    [ Unlock.mkTestTree
                        "voter: retract votes while voting"
                        (Unlock.mkValidVoterRetractVotes nStakes)
                        (Unlock.Validity True True)
                    , Unlock.mkTestTree
                        "voter: retract votes while voting by delegatee"
                        (Unlock.mkValidDelegateeRetractVotes nStakes)
                        (Unlock.Validity True True)
                    , Unlock.mkTestTree
                        "voter/creator: retract votes while voting"
                        (Unlock.mkValidVoterCreatorRetractVotes nStakes)
                        (Unlock.Validity True True)
                    , Unlock.mkTestTree
                        "creator: remove creator lock after voting"
                        (Unlock.mkValidCreatorRemoveLock nStakes)
                        (Unlock.Validity True True)
                    , Unlock.mkTestTree
                        "Voter: remove lock after voting"
                        (Unlock.mkValidVoterRemoveLockAfterVoting nStakes)
                        (Unlock.Validity True True)
                    ]
  
                mkIllegalGroup nStakes =
                  group
                    (mkSubgroupName nStakes)
                    [ group "retract votes while not voting" $
                        map
                          ( \c ->
                              Unlock.mkTestTree
                                "(negative test)"
                                c
                                (Unlock.Validity False True)
                          )
                          (Unlock.mkRetractVotesWhileNotVoting nStakes)
                    , group "remove creator too early" $
                        map
                          ( \c ->
                              Unlock.mkTestTree
                                "(negative test)"
                                c
                                (Unlock.Validity True False)
                          )
                          (Unlock.mkRemoveCreatorLockBeforeFinished nStakes)
                    , Unlock.mkTestTree
                        "unlock an irrelevant stake"
                        (Unlock.mkUnockIrrelevantStakes nStakes)
                        (Unlock.Validity False False)
                    , Unlock.mkTestTree
                        "creator: retract votes"
                        (Unlock.mkCreatorRetractVotes nStakes)
                        (Unlock.Validity False True)
                    , Unlock.mkTestTree
                        "change output stake value"
                        (Unlock.mkChangeOutputStakeValue nStakes)
                        (Unlock.Validity True False)
                    , Unlock.mkTestTree
                        "use fake stake"
                        (Unlock.mkUseFakeStakes nStakes)
                        (Unlock.Validity False False)
                    , Unlock.mkTestTree
                        "retract votes in cooldown"
                        (Unlock.mkDisrespectCooldown nStakes)
                        (Unlock.Validity True False)
                    ]
  
                legalGroup = group "legal" $ map mkLegalGroup stakeCountCases
                illegalGroup = group "illegal" $ map mkIllegalGroup stakeCountCases
             in [legalGroup, illegalGroup]
        ]
    , group
        "privilege escalate"
        [ PrivilegeEscalate.mkTestTree
            "vote"
            PrivilegeEscalate.Voting
            (PrivilegeEscalate.Validity False False)
        , PrivilegeEscalate.mkTestTree
            "retract votes"
            PrivilegeEscalate.RetractingVotes
            (PrivilegeEscalate.Validity False False)
        ]
    ]
  