packages feed

slack-web-1.5.0.0: tests/JSONGolden.hs

{-# LANGUAGE TemplateHaskell #-}

module JSONGolden (oneGoldenTestDecode, oneGoldenTestEncode) where

import Data.Aeson (eitherDecode)
import Data.Aeson.Encode.Pretty qualified as AP
import Data.ByteString.Lazy qualified as LBS
import Data.Text (stripEnd)
import Data.Text.IO qualified as T
import Language.Haskell.TH (Exp (..), Lit (..))
import Language.Haskell.TH.Syntax.Compat (makeRelativeToProject)
import Test.Hspec.Core.Spec (SpecM)
import Test.Hspec.Golden
import TestImport
import Text.Pretty.Simple (pShowNoColor)
import Type.Reflection

-- this requires the filepath hacking like this so it can be run from arbitrary
-- working directories
filename :: Text -> Text -> FilePath
filename tycon name = $(LitE . StringL <$> makeRelativeToProject "tests/golden") </> unpack tycon </> unpack name

typeName :: forall a. Typeable a => Text
typeName = pack . tyConName . typeRepTyCon $ typeRep @a

goldenTestDecode :: forall a. (FromJSON a, Show a, Typeable a) => Text -> LByteString -> Golden Text
goldenTestDecode name rawInput = do
  let output = either error id $ eitherDecode @a rawInput
      theTypeName = typeName @a
   in Golden
        { output = toStrict . pShowNoColor $ output
        , encodePretty = unpack
        , writeToFile = T.writeFile
        , -- deal with vim related EOF
          readFromFile = \fname -> stripEnd <$> T.readFile fname
        , goldenFile = filename theTypeName name ++ ".golden"
        , actualFile = Just $ filename theTypeName name ++ ".actual"
        , failFirstTime = True
        }

goldenTestEncode :: forall a. (ToJSON a, Typeable a) => Text -> a -> Golden Text
goldenTestEncode name value = do
  let output = AP.encodePretty @a value
      theTypeName = typeName @a
   in Golden
        { output = cs output
        , encodePretty = unpack
        , writeToFile = T.writeFile
        , -- deal with vim related EOF
          readFromFile = \fname -> stripEnd <$> T.readFile fname
        , goldenFile = filename theTypeName name ++ ".golden.json"
        , actualFile = Just $ filename theTypeName name ++ ".actual.json"
        , failFirstTime = True
        }

oneGoldenTestEncode :: forall a. (ToJSON a, Typeable a) => Text -> a -> SpecM () ()
oneGoldenTestEncode name value = do
  it (unpack name) $ goldenTestEncode @a name value

oneGoldenTestDecode :: forall a. (FromJSON a, Show a, Typeable a) => Text -> SpecM () ()
oneGoldenTestDecode name = do
  input <- runIO . LBS.readFile $ filename (typeName @a) name <> ".json"
  it (unpack name) $ goldenTestDecode @a name input