content: {-# LANGUAGE QuasiQuotes #-}
  
  {- |
  Module     : Spec.Stake
  Maintainer : emi@haskell.fyi
  Description: Tests for Stake policy and validator
  
  Tests for Stake policy and validator
  -}
  module Spec.Stake (specs) where
  
  import Agora.Stake (
    StakeDatum (StakeDatum),
    StakeRedeemer (DepositWithdraw),
   )
  import PlutusLedgerApi.V1 (Credential (PubKeyCredential))
  import Sample.Shared (stakeValidator)
  import Sample.Stake (
    DepositWithdrawExample (
      DepositWithdrawExample,
      delta,
      startAmount
    ),
    signer,
   )
  import Sample.Stake qualified as Stake (
    stakeDepositWithdraw,
   )
  import Sample.Stake.Create qualified as Create
  import Sample.Stake.Destroy qualified as Destroy
  import Sample.Stake.SetDelegate qualified as SetDelegate
  import Sample.Stake.UnauthorizedMintingExploit qualified as UnauthorizedMintingExploit
  import Test.Specification (
    SpecificationTree,
    group,
    validatorFailsWith,
    validatorSucceedsWith,
   )
  
  -- | The SpecificationTree exported by this module.
  specs :: [SpecificationTree]
  specs =
    [ group
        "policy"
        [ group
            "create"
            [ group
                "valid"
                [ Create.mkTestCase
                    "stake owner: pub key"
                    Create.ownerIsPubKeyTotallyValid
                    True
                , Create.mkTestCase
                    "stake owner: script"
                    Create.ownerIsScriptTotallyValid
                    True
                ]
            , group
                "invalid"
                [ Create.mkTestCase
                    "mint more than one sst in one tx"
                    Create.createMoreThanOneStake
                    False
                , Create.mkTestCase
                    "spend stake while minting SST"
                    Create.spendStake
                    False
                , Create.mkTestCase
                    "wrong staked amount"
                    Create.unexpectedStakedAmount
                    False
                , Create.mkTestCase
                    "no stake datum"
                    Create.noStakeDatum
                    False
                , Create.mkTestCase
                    "bad stake datum"
                    Create.malformedStakeDatum
                    False
                , Create.mkTestCase
                    "not authorized by owner"
                    Create.notAuthorizedByOwner
                    False
                , Create.mkTestCase
                    "delegatee not empty"
                    Create.setDelegatee
                    False
                , Create.mkTestCase
                    "have locks"
                    Create.alreadyHasLocks
                    False
                ]
            ]
        ]
    , group
        "validator"
        [ group
            "destroy"
            [ group
                "legal"
                [ Destroy.mkTestTree
                    "One stake"
                    Destroy.oneStake
                    (Destroy.Validity (Just True) True)
                , Destroy.mkTestTree
                    "Multiple stake"
                    Destroy.multipleStakes
                    (Destroy.Validity (Just True) True)
                ]
            , group
                "illegal"
                [ Destroy.mkTestTree
                    "Destroy only one stake to steal SST"
                    Destroy.stealSST
                    (Destroy.Validity (Just False) False)
                , Destroy.mkTestTree
                    "Destroy nothing to steal SST"
                    Destroy.stealSST1
                    (Destroy.Validity Nothing False)
                , Destroy.mkTestTree
                    "Steal SST"
                    Destroy.stealSST3
                    (Destroy.Validity (Just False) False)
                , Destroy.mkTestTree
                    "Destroy locked stakes"
                    Destroy.lockedStakes
                    (Destroy.Validity (Just True) False)
                , Destroy.mkTestTree
                    "not authorized by owner"
                    Destroy.notAuthorized
                    (Destroy.Validity (Just True) False)
                , Destroy.mkTestTree
                    "not authorized by owner"
                    Destroy.authorizedByDelegatee
                    (Destroy.Validity (Just True) False)
                ]
            ]
        , validatorSucceedsWith
            "stakeDepositWithdraw deposit"
            stakeValidator
            (StakeDatum 100_000 (PubKeyCredential signer) Nothing [])
            (DepositWithdraw 100_000)
            (Stake.stakeDepositWithdraw $ DepositWithdrawExample {startAmount = 100_000, delta = 100_000})
        , validatorSucceedsWith
            "stakeDepositWithdraw withdraw"
            stakeValidator
            (StakeDatum 100_000 (PubKeyCredential signer) Nothing [])
            (DepositWithdraw $ negate 100_000)
            (Stake.stakeDepositWithdraw $ DepositWithdrawExample {startAmount = 100_000, delta = negate 100_000})
        , validatorFailsWith
            "stakeDepositWithdraw negative GT"
            stakeValidator
            (StakeDatum 100_000 (PubKeyCredential signer) Nothing [])
            (DepositWithdraw 1_000_000)
            (Stake.stakeDepositWithdraw $ DepositWithdrawExample {startAmount = 100_000, delta = negate 1_000_000})
        , group
            "set delegate"
            [ SetDelegate.mkTestCase
                "override existing delegate"
                SetDelegate.overrideExistingDelegateParameters
                True
            , SetDelegate.mkTestCase
                "remove existing delegate"
                SetDelegate.clearDelegateParameters
                True
            , SetDelegate.mkTestCase
                "set delegate to something"
                SetDelegate.setDelegateParameters
                True
            , SetDelegate.mkTestCase
                "owner doesn't sign the transaction"
                SetDelegate.ownerDoesntSignParameters
                False
            , SetDelegate.mkTestCase
                "delegate to the owner"
                SetDelegate.delegateToOwnerParameters
                False
            , SetDelegate.mkTestCase
                "invalid output stake"
                SetDelegate.invalidOutputStakeDatumParameters
                False
            ]
        , group
            "unauthorized SST minting exploit"
            $ map
              ( UnauthorizedMintingExploit.mkTestCase
                  "(negative test)"
                  . UnauthorizedMintingExploit.Parameters
              )
              [1 .. 20]
        ]
    ]
  