packages feed

cleveland-0.1.0: lorentz-test/Test/Lorentz/Tickets/Helpers.hs

-- SPDX-FileCopyrightText: 2021 Tocqueville Group
--
-- SPDX-License-Identifier: LicenseRef-MIT-TQ

-- | Tests on tickets helpers from "Lorentz.Tickets" module.
module Test.Lorentz.Tickets.Helpers
  ( test_ActionAuth
  , test_TokenAllowance
  ) where

import Test.Tasty (TestTree, testGroup)

import qualified Lorentz as L
import Lorentz.Value
import Morley.Util.Named
import Test.Cleveland
import Test.Cleveland.Lorentz.Ticketer

import Test.Lorentz.Tickets.Contracts

test_ActionAuth :: [TestTree]
test_ActionAuth =
  let prepare
        :: MonadCleveland caps m
        => m ( ContractHandle (Ticket Integer) Bool ()
             , ContractHandle (L.View_ ((), (Natural, Integer)) (Ticket Integer)) () ()
             )
      prepare = do
        ticketer <- originateSimple "ticketer" () (ticketerContract' L.drop)
        launcher <- originateSimple "launcher" False (atomicMissileLaunchContract $ toTAddress ticketer)
        return (launcher, ticketer)
  in
  [ testScenario "Normal scenario works" $ scenario do
      (launcher, ticketer) <- prepare

      callViaTicketer ticketer 1 12345 launcher ()

  , testScenario "Bad ticket data causes failure" $ scenario do
      (launcher, ticketer) <- prepare

      callViaTicketer ticketer 1 123 launcher ()
        & expectFailedWith [mt|Bad code|]

  , testScenario "Bad tokens number causes failure" $ scenario do
      (launcher, ticketer) <- prepare

      callViaTicketer ticketer 0 12345 launcher ()
        & expectCustomErrorNoArg #nOT_SINGLE_TICKET_TOKEN

      callViaTicketer ticketer 3 12345 launcher ()
        & expectCustomErrorNoArg #nOT_SINGLE_TICKET_TOKEN

  , testScenario "Wrong ticketer causes failure" $ scenario do
      (launcher, _ticketer) <- prepare
      fakeTicketer <- originateSimple "fake-ticketer" () (ticketerContract' L.drop)

      callViaTicketer fakeTicketer 3 12345 launcher ()
        & expectCustomErrorNoArg #wRONG_TICKETER

  ]

test_TokenAllowance :: [TestTree]
test_TokenAllowance =
  let prepare
        :: MonadCleveland caps m
        => m ( ContractHandle PermitTokensParameter PermitTokensStorage ()
             , ContractHandle (L.View_ ((), (Natural, ())) (Ticket ())) () ()
             )
      prepare = do
        ticketer <- originateSimple "ticketer" () (ticketerContract' L.drop)
        let initStorage = PermitTokensStorage Nothing (toAddress ticketer)
        token <- originateSimple "token" initStorage permitTokensContract
        return (token, ticketer)
  in
  [ testGroup "Allowances"

    [ testScenario "Permitting tokens works" $ scenario do
        -- TODO: Would be actually nice to try hedgehog here once it is available for cleveland

        (token, ticketer) <- prepare

        callViaTicketer ticketer 5 () (callingAddress token (Call @"PermitAllow")) ()
        callViaTicketer ticketer 3 () (callingAddress token (Call @"PermitAllow")) ()

        call token (Call @"PermitGet") (L.mkVoid ())
          & expectError (L.VoidResult @Natural 8)

    , testScenario "Permitting 0 tokens works" $ scenario do
        (token, ticketer) <- prepare

        callViaTicketer ticketer 0 () (callingAddress token (Call @"PermitAllow")) ()

        call token (Call @"PermitGet") (L.mkVoid ())
          & expectError (L.VoidResult @Natural 0)

    , testScenario "Permitting tokens from wrong ticketer fails" $ scenario do
        (token, _ticketer) <- prepare
        fakeTicketer <- originateSimple "fake-ticketer" () (ticketerContract' L.drop)

        callViaTicketer fakeTicketer 1 () (callingAddress token (Call @"PermitAllow")) ()
          & expectCustomErrorNoArg #wRONG_TICKETER

    ]

  , testGroup "Spendings"

    [ testScenario "Spending tokens works" $ scenario do
        (token, ticketer) <- prepare

        callViaTicketer ticketer 5 () (callingAddress token (Call @"PermitAllow")) ()
        call token (Call @"PermitSpend") 3

        call token (Call @"PermitGet") (L.mkVoid ())
          & expectError (L.VoidResult @Natural 2)

    , testScenario "Spending too many tokens fails" $ scenario do
        (token, ticketer) <- prepare

        callViaTicketer ticketer 5 () (callingAddress token (Call @"PermitAllow")) ()
        call token (Call @"PermitSpend") 8
          & expectCustomError #insufficient_tokens_permitted_by_ticket
              (#permitted :! 5, #spent :! 8)

    , testScenario "Can repeatedly exhaust all allowances" $ scenario do
        (token, ticketer) <- prepare

        callViaTicketer ticketer 5 () (callingAddress token (Call @"PermitAllow")) ()
        call token (Call @"PermitSpend") 3
        call token (Call @"PermitSpend") 2
        callViaTicketer ticketer 0 () (callingAddress token (Call @"PermitAllow")) ()
        call token (Call @"PermitSpend") 0
        callViaTicketer ticketer 1 () (callingAddress token (Call @"PermitAllow")) ()
        call token (Call @"PermitSpend") 1
        callViaTicketer ticketer 3 () (callingAddress token (Call @"PermitAllow")) ()

        call token (Call @"PermitGet") (L.mkVoid ())
          & expectError (L.VoidResult @Natural 3)

    ]

    , testGroup "Admin change"

    [ testScenario "Admin change takes effect" $ scenario do
        (token, ticketer) <- prepare
        ticketer2 <- originateSimple "ticketer2" () (ticketerContract' L.drop)

        callViaTicketer ticketer 5 () (callingAddress token (Call @"PermitAllow")) ()
        call token (Call @"PermitSetAdmin") (toAddress ticketer2)

        call token (Call @"PermitGet") (L.mkVoid ())
          & expectError (L.VoidResult @Natural 0)

        callViaTicketer ticketer 5 () (callingAddress token (Call @"PermitAllow")) ()
          & expectCustomErrorNoArg #wRONG_TICKETER

        callViaTicketer ticketer2 5 () (callingAddress token (Call @"PermitAllow")) ()
        call token (Call @"PermitGet") (L.mkVoid ())
          & expectError (L.VoidResult @Natural 5)

    ]
  ]

{- We do not cover 'allowancesContract' with tests since there is nothing
special there comparing to 'permitTokensContract', the contract exists only to
demonstrate implementation for a contract that stores an entire map of tickets.
-}
_untestedContract1 :: [TestTree]
_untestedContract1 = const [] allowancesContract