packages feed

servant-quickcheck-0.1.0.0: src/Servant/QuickCheck/Internal/Equality.hs

module Servant.QuickCheck.Internal.Equality where

import           Data.Aeson           (Value, decode, decodeStrict)
import           Data.ByteString      (ByteString)
import qualified Data.ByteString.Lazy as LB
import           Data.Function        (on)
import           Network.HTTP.Client  (Response (..), equivCookieJar, responseBody)
import           Prelude.Compat

newtype ResponseEquality b = ResponseEquality {getResponseEquality :: Response b -> Response b -> Bool}

instance Semigroup (ResponseEquality b) where
  ResponseEquality a <> ResponseEquality b = ResponseEquality $ \x y ->
    a x y && b x y

instance Monoid (ResponseEquality b) where
  mempty = ResponseEquality $ \_ _ -> True
  mappend = (<>)

{- | Use `Eq` instance for `Response`

/Since 0.0.0.0/
-}
allEquality :: (Eq b) => ResponseEquality b
allEquality = ResponseEquality $ \respa respb ->
  and
    [ responseStatus respa == responseStatus respb
    , responseVersion respa == responseVersion respb
    , responseHeaders respa == responseHeaders respb
    , responseBody respa == responseBody respb
    , responseCookieJar respa `equivCookieJar` responseCookieJar respb
    ]

{- | ByteString `Eq` instance over the response body.

/Since 0.0.0.0/
-}
bodyEquality :: (Eq b) => ResponseEquality b
bodyEquality = ResponseEquality ((==) `on` responseBody)

{- | Equality as 'Value'. This means that if two bodies are equal as JSON
(e.g., insignificant whitespace difference) they are considered equal.

/Since 0.0.3.0/
-}
jsonEquality :: (JsonEq b) => ResponseEquality b
jsonEquality = ResponseEquality (jsonEq `on` responseBody)

class JsonEq a where
  decode' :: a -> Maybe Value
  jsonEq :: a -> a -> Bool
  jsonEq first second = compareDecodedResponses (decode' first) (decode' second)

instance JsonEq LB.ByteString where
  decode' = decode

instance JsonEq ByteString where
  decode' = decodeStrict

compareDecodedResponses :: Maybe Value -> Maybe Value -> Bool
compareDecodedResponses resp1 resp2 =
  case resp1 of
    Nothing -> False -- if decoding fails we assume failure
    (Just r1) -> case resp2 of
      Nothing   -> False -- another decode failure
      (Just r2) -> r1 == r2