packages feed

cleveland-0.1.1: morley-test/Test/Interpreter/Compare.hs

-- SPDX-FileCopyrightText: 2021 Oxhead Alpha
-- SPDX-License-Identifier: LicenseRef-MIT-OA

-- | Module, containing spec to test compare.tz contract.
module Test.Interpreter.Compare
  ( test_compare
  , test_Equality_vs_compare
  ) where

import Hedgehog (MonadTest, discard, forAll, property, withDiscards, withTests, (===))
import Test.Tasty (TestTree)
import Test.Tasty.Hedgehog (testProperty)

import Hedgehog.Gen.Michelson.Typed
import Hedgehog.Gen.Michelson.Untyped qualified as U
import Hedgehog.Gen.Tezos.Core (genMutez)
import Morley.Michelson.Interpret (ContractReturn)
import Morley.Michelson.Typed (ToT, fromVal)
import Morley.Michelson.Typed qualified as T
import Morley.Tezos.Core (Mutez)
import Test.Cleveland
import Test.Cleveland.Lorentz.Import (embedContract)
import Test.Cleveland.Util (failedTest, genTuple2)

import Test.Util.Contracts

type Param = (Mutez, Mutez)
type ContractResult = ContractReturn (ToT [Bool])

-- | Spec to test compare.tz contract.
test_compare :: [TestTree]
test_compare =
    [ testScenarioOnEmulator "success test" $ myScenario (10, 11)
    , testProperty "Random check" $
        withTests 200 $ property $ do
          inputParam <- forAll $ genTuple2 genMutez genMutez
          testScenarioProps $ myScenario inputParam
    ]
  where
    contract = $$(embedContract @Param @[Bool] @() (inContractsDir "tezos_examples/macros/compare.tz"))

    myScenario :: Monad m => Param -> Scenario m
    myScenario inp = scenario do
      handle <- originateSimple "compare" [] contract
      call handle CallDefault inp
      getStorage handle @@== mkExpected inp
      pure ()

    initStorage :: [Bool]
    initStorage = []

    mkExpected :: Param -> [Bool]
    mkExpected (a, b) = [a == b, a > b, a < b, a >= b, a <= b]

    validate
      :: MonadTest m
      => [Bool]
      -> ContractResult
      -> m ()
    validate e (Right ([], fromVal -> l), _) = l === e
    validate _ (Left _, _) = failedTest "Unexpected fail of script."
    validate _ _ = failedTest "Invalid result got."

{-# ANN module ("HLint: ignore Redundant compare" :: Text) #-}

-- | This test is necessary because there exist two notions of comparable
-- values - in Michelson world (works only for specific types) and in
-- Haskell world (works for all values).
-- And at the moment of writing we define 'Ord' instance for 'Value' as in
-- Michelson, while 'Eq' instance is produced by Haskell means automatically.
--
-- In various places we rely on the fact that instance produced by Haskell
-- matches the Michelson definitions, so we have to ensure that on comparable
-- types (in terms of Michelson) our @Eq@ and @Ord@ instances match.
test_Equality_vs_compare :: TestTree
test_Equality_vs_compare =
  testProperty "Eq and Ord instances on Value match" $
    withDiscards 200 $ property $ do
      T.AsUTypeExt st _ <- forAll U.genValidType
      case (T.checkComparability st, T.checkOpPresence st, T.getWTP' st) of
        (T.CanBeCompared, T.OpAbsent, Right T.Dict) -> do
          value1 <- forAll $ genValue' st
          value2 <- forAll $ genValue' st
          forM_ ((,) <$> [value1, value2] <*> [value1, value2]) $ \(v1, v2) ->
            (compare v1 v2 == EQ) === (v1 == v2)
        _ -> discard