packages feed

relude-0.7.0.0: test/Test/Relude/Extra/Validation/Property.hs

{- HLINT ignore "Alternative law, right identity" -}
{- HLINT ignore "Alternative law, left identity" -}
{- HLINT ignore "Monoid law, right identity" -}
{- HLINT ignore "Monoid law, left identity" -}
{- HLINT ignore "Use <$>" -}

module Test.Relude.Extra.Validation.Property
       ( validationLaws
       ) where

import Relude
import Relude.Extra.Validation

import Hedgehog (Gen, Group (..), Property, forAll, forAllWith, property, (===))

import qualified Hedgehog.Gen as Gen
import qualified Hedgehog.Range as Range

validationLaws :: [Group]
validationLaws =
    [ validationSemigroupProps
    , validationMonoidProps
    , validationApplicativeProps
    , validationAlternativeProps
    ]

----------------------------------------------------------------------------
-- Generators
----------------------------------------------------------------------------

genFunction :: Gen (Int -> Int)
genFunction = Gen.element [(+), (*), const] <*> genSmallInt

genSmallInt :: Gen Int
genSmallInt = Gen.int (Range.linear (-10) 10)

genSmallText :: Gen Text
genSmallText = Gen.text (Range.linear 3 10) Gen.unicode

asValidation :: Gen a -> Gen (Validation [Text] a)
asValidation gen = Gen.choice
    [ Success <$> gen
    , Failure <$> Gen.list (Range.linear 1 5) genSmallText
    ]

----------------------------------------------------------------------------
-- Property helpers
----------------------------------------------------------------------------

checkAssociativityFor
    :: (Show a, Eq a) => Gen a -> (a -> a -> a) -> Property
checkAssociativityFor gen op = property $ do
    a <- forAll gen
    b <- forAll gen
    c <- forAll gen
    a `op` (b `op` c) === (a `op` b) `op` c

----------------------------------------------------------------------------
-- Semigroup instance properties
----------------------------------------------------------------------------

validationSemigroupProps :: Group
validationSemigroupProps =
    Group "Semigroup instance for Validation property tests"
        [ ("associativity:", prop_semigroupAssociativity)
        ]

prop_semigroupAssociativity :: Property
prop_semigroupAssociativity =
    checkAssociativityFor (asValidation genSmallText) (<>)

----------------------------------------------------------------------------
-- Monoid instance properties
----------------------------------------------------------------------------

validationMonoidProps :: Group
validationMonoidProps =
    Group "Monoid instance for Validation property tests"
        [ ("right identity:", prop_monoidRightIdentity)
        , ("left identity:", prop_monoidLeftIdentity)
        ]

prop_monoidRightIdentity :: Property
prop_monoidRightIdentity = property $ do
    x <- forAll $ asValidation genSmallText
    x <> mempty === x

prop_monoidLeftIdentity :: Property
prop_monoidLeftIdentity = property $ do
    x <- forAll $ asValidation genSmallText
    mempty <> x === x

----------------------------------------------------------------------------
-- Applicative instance properties
----------------------------------------------------------------------------

validationApplicativeProps :: Group
validationApplicativeProps =
    Group "Applicative instance for Validation property tests"
        [ ("identity:", prop_applicativeIdentity)
        , ("composition:", prop_applicativeComposition)
        , ("homomorphism:", prop_applicativeHomomorphism)
        , ("interchange:", prop_applicativeInterchange)
        , ("u *> v == (id <$ u) <*> v", prop_applicativeApplyRight)
        , ("u <* v == liftA2 const u v", prop_applicativeApplyLeft)
        ]

prop_applicativeIdentity :: Property
prop_applicativeIdentity = property $ do
    vx <- forAll $ asValidation genSmallText
    (pure id <*> vx) === vx

prop_applicativeComposition :: Property
prop_applicativeComposition = property $ do
    vf <- forAllWith (const "f") $ asValidation genFunction
    vg <- forAllWith (const "g") $ asValidation genFunction
    vx <- forAll $ asValidation genSmallInt
    (pure (.) <*> vf <*> vg <*> vx) === (vf <*> (vg <*> vx))

prop_applicativeHomomorphism :: Property
prop_applicativeHomomorphism = property $ do
    f <- forAllWith (const "f") genFunction
    x <- forAll genSmallInt
    (pure f <*> pure x) === pure @(Validation [Text]) (f x)

prop_applicativeInterchange :: Property
prop_applicativeInterchange = property $ do
    vf <- forAllWith (const "f") $ asValidation genFunction
    x <- forAll genSmallInt
    (vf <*> pure x) === (pure ($ x) <*> vf)

prop_applicativeApplyRight :: Property
prop_applicativeApplyRight = property $ do
    vy <- forAll $ asValidation genSmallInt
    vx <- forAll $ asValidation genSmallInt
    (vy *> vx) === ((id <$ vy) <*> vx)

prop_applicativeApplyLeft :: Property
prop_applicativeApplyLeft = property $ do
    vy <- forAll $ asValidation genSmallInt
    vx <- forAll $ asValidation genSmallInt
    (vy <* vx) === liftA2 const vy vx

----------------------------------------------------------------------------
-- Alternative instance properties
----------------------------------------------------------------------------

validationAlternativeProps :: Group
validationAlternativeProps =
    Group "Alternative instance for Validation property tests"
        [ ("associativity:", prop_alternativeAssociativity)
        , ("right identity:", prop_alternativeRightIdentity)
        , ("left identity:", prop_alternativeLeftIdentity)
        ]

prop_alternativeAssociativity :: Property
prop_alternativeAssociativity =
    checkAssociativityFor (asValidation genSmallText) (<|>)

prop_alternativeRightIdentity :: Property
prop_alternativeRightIdentity = property $ do
    x <- forAll $ asValidation genSmallText
    (x <|> empty) === x

prop_alternativeLeftIdentity :: Property
prop_alternativeLeftIdentity = property $ do
    x <- forAll $ asValidation genSmallText
    (empty <|> x) === x