packages feed

serokell-util-0.9.0: test/Test/Serokell/Util/VerifySpec.hs

module Test.Serokell.Util.VerifySpec
       ( spec
       ) where

import Universum

import Test.Hspec (Expectation, Spec, describe, it, shouldBe)
import Test.Hspec.QuickCheck (prop)
import Test.QuickCheck (Property, (===), (==>))
import Test.QuickCheck.Instances ()

import Data.List.NonEmpty (fromList)

import Serokell.Arbitrary ()

import qualified Serokell.Util.Verify as V

spec :: Spec
spec =
    describe "Verify" $ do
        describe "isVerSuccess" $
            prop description_isVerSuccess isVerSuccessWorks
        describe "isVerSuccess" $
            prop description_isVerFailure isVerFailureWorks
        describe "Monoid VerificationRes" $
            prop description_verificationResIsMonoid verificationResIsMonoid
        describe "verifyGeneric" $ do
            it "verifyGeneric of an empty string always succeeds"
                (V.verifyGeneric [] `shouldBe` V.VerSuccess)
            prop description_verifyGeneric verifyGenericWorks
            prop description_verifyGenericMessageOrder verifyGenericMessageOrder
  where
    description_isVerSuccess =
        "A successful verification should yield True, and an unsuccessful one should\
        \ yield False"
    description_isVerFailure =
        "An unsuccessful verification should yield True, and an unsuccessful one should\
        \ yield False"
    description_verificationResIsMonoid =
       "The set of verification results with the concatenation operation defined in the\
       \ Serokell.Util.Verify module forms a monoid"
    description_verifyGeneric =
        "A list of predicates with a single failure yields a failed verification, and a\
        \ list of predicates with no failures yields a successful verification"
    description_verifyGenericMessageOrder =
        "Error messages in a predicate list are taken from left to right"

isVerSuccessWorks :: V.VerificationRes -> Expectation
isVerSuccessWorks v = case v of
    V.VerSuccess   -> V.isVerSuccess v `shouldBe` True
    V.VerFailure _ -> V.isVerSuccess v `shouldBe` False

-- | The property
-- `isVerFailure = not . isVerSuccess`
-- should hold for all v in VerificationRes
isVerFailureWorks :: V.VerificationRes -> Property
isVerFailureWorks v = (not . V.isVerSuccess $ v) === V.isVerFailure v

verificationResIsMonoid
    :: V.VerificationRes
    -> V.VerificationRes
    -> V.VerificationRes
    -> Bool
verificationResIsMonoid v1 v2 v3 =
    let isAssociative =
            let assoc1 = (v1 <> v2) <> v3
                assoc2 = v1 <> (v2 <> v3)
            in assoc1 == assoc2
        hasIdentity =
            let id1 = mempty <> v1
                id2 = v1 <> mempty
            in (v1 == id1) && (v1 == id2)
    in isAssociative && hasIdentity

-- | The function `verifyGeneric` must have the following properties
-- (any (not . fst) l :: [(Bool, a)]) iff (isVerFailure . verifyGeneric) l
-- (all (identity . fst) l :: [(Bool, a)]) iff (isVerSuccess . verifyGeneric) l

verifyGenericWorks :: [(Bool, Text)] -> Bool
verifyGenericWorks l =
    let someFailure  = any (not . fst) l
        allSuccess   = not someFailure
    in (someFailure == (V.isVerFailure . V.verifyGeneric $ l)) &&
       (allSuccess  == (V.isVerSuccess . V.verifyGeneric $ l))

-- | This property checks that the error messages in a list of predicates are collected
-- from left to right.

verifyGenericMessageOrder :: [(Bool, Text)] -> Property
verifyGenericMessageOrder l =
    let someFailure  = any (not . fst) l
        msgList = foldr (\(b, err) accList ->
                            if not b then err : accList
                                  else accList) [] l
    in someFailure ==>
       ((V.VerFailure $ fromList msgList) == V.verifyGeneric l)