packages feed

hedgehog-extras-0.3.0.1: src/Hedgehog/Extras/Aeson.hs

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}

{-# OPTIONS_GHC -Wno-unused-imports #-}
{-# OPTIONS_GHC -Wno-redundant-constraints #-}
{-# OPTIONS_GHC -Wno-unused-local-binds #-}

module Hedgehog.Extras.Aeson
  ( goldenTestJsonValue
  , goldenTestJsonValuePretty
  ) where

import           Control.Monad
import           Control.Monad.IO.Class
import           Data.Aeson
import           Data.Aeson.Encode.Pretty
import           Data.Bool
import           Data.Either
import           Data.Eq
import           Data.Function
import           Data.Functor
import           Data.Maybe
import           Data.Semigroup
import           GHC.Stack
import           Hedgehog
import           System.IO
import           Text.Show

import qualified Data.ByteString.Lazy as LBS
import qualified Hedgehog.Internal.Property as H

writeNewGoldFiles :: Bool
writeNewGoldFiles = False

strictComparison :: Bool
strictComparison = False

goldenTestJsonValue :: forall a. ()
  => Eq a
  => FromJSON a
  => Show a
  => ToJSON a
  => HasCallStack
  => a
  -> FilePath
  -> Property
goldenTestJsonValue x path = withFrozenCallStack $ withTests 1 . property $ do
  bs <- liftIO (LBS.readFile path)
  when writeNewGoldFiles $ liftIO . LBS.writeFile (path <> ".gold") $ encode x
  when strictComparison $ fmap encode (eitherDecode @a bs) === Right bs
  case eitherDecode bs of
    Left  err -> H.failWith Nothing $ "could not decode: " <> show err
    Right x'  -> x === x'

goldenTestJsonValuePretty
  :: forall a. ()
  => Eq a
  => FromJSON a
  => HasCallStack
  => Show a
  => ToJSON a
  => a
  -> FilePath
  -> Property
goldenTestJsonValuePretty x path =
  withFrozenCallStack
    $ withTests 1
    . property
    $ do
        bs <- liftIO (LBS.readFile path)
        -- Sort keys by their order of appearance in the argument list
        -- of `keyOrder`. Keys not in the argument list are moved to the
        -- end, while their order is preserved.
        let
          defConfig' = Config
            { confIndent          = Spaces 4
            , confCompare         = keyOrder ["file", "hash"]
            , confNumFormat       = Generic
            , confTrailingNewline = False
            }
        when writeNewGoldFiles $ liftIO . LBS.writeFile (path <> ".gold") $ encodePretty' defConfig' x
        when strictComparison $ fmap (encodePretty' defConfig') (eitherDecode @a bs) === Right bs
        case eitherDecode bs of
          Left  err -> H.failWith Nothing $ "could not decode: " <> show err
          Right x'  -> x === x'