wireform-derive-0.1.0.0: test/Test/Derive/Aeson.hs
{-# LANGUAGE OverloadedStrings #-}
module Test.Derive.Aeson (tests) where
import qualified Data.Aeson as A
import qualified Data.Aeson.Key as Key
import qualified Data.Aeson.KeyMap as KM
import Test.Tasty (TestTree, testGroup)
import Test.Tasty.HUnit (assertBool, assertEqual, testCase, (@?=))
import Test.Derive.Aeson.Instances ()
import Test.Derive.Aeson.Types
tests :: TestTree
tests = testGroup "Aeson deriver"
[ recordTests
, newtypeTests
, enumTests
, sumTests
]
-- ---------------------------------------------------------------------------
-- Record
-- ---------------------------------------------------------------------------
recordTests :: TestTree
recordTests = testGroup "record"
[ testCase "encode applies rename / renameStyle" $ do
let a = Address "1 Main" "Springfield" "12345" "secret"
let v = A.toJSON a
case v of
A.Object o -> do
assertEqual "street key (literal rename)"
(Just (A.String "1 Main"))
(KM.lookup (Key.fromText "street") o)
assertEqual "city key (snake rename)"
(Just (A.String "Springfield"))
(KM.lookup (Key.fromText "addr_city") o)
assertEqual "zip key (strip-prefix + snake)"
(Just (A.String "12345"))
(KM.lookup (Key.fromText "zip") o)
assertBool "internal key skipped under JSON"
(not (KM.member (Key.fromText "addrInternal") o))
_ -> fail "expected JSON object"
, testCase "decode round-trips (skipped field filled by defaults)" $ do
let a = Address "1 Main" "Springfield" "12345" "secret"
case A.fromJSON (A.toJSON a) of
A.Success a' -> do
assertEqual "street" (addrStreet a) (addrStreet a')
assertEqual "city" (addrCity a) (addrCity a')
assertEqual "zip" (addrZip a) (addrZip a')
assertEqual "internal -> defaultAddrInternal"
defaultAddrInternal
(addrInternal a')
A.Error e -> fail ("decode failed: " ++ e)
]
-- ---------------------------------------------------------------------------
-- Newtype
-- ---------------------------------------------------------------------------
newtypeTests :: TestTree
newtypeTests = testGroup "newtype"
[ testCase "encode passes through" $
A.toJSON (UserId 42) @?= A.Number 42
, testCase "round-trip" $ do
case A.fromJSON (A.toJSON (UserId 7)) of
A.Success (UserId n) -> n @?= 7
A.Error e -> fail e
]
-- ---------------------------------------------------------------------------
-- Enum
-- ---------------------------------------------------------------------------
enumTests :: TestTree
enumTests = testGroup "enum"
[ testCase "encode literal-renamed constructor"
(A.toJSON Red @?= A.String "red")
, testCase "encode style-renamed constructor (DarkBlue -> dark-blue)"
(A.toJSON DarkBlue @?= A.String "dark-blue")
, testCase "round-trip Red" $
A.fromJSON (A.String "red") @?= A.Success Red
, testCase "round-trip Green" $
A.fromJSON (A.String "green") @?= A.Success Green
, testCase "round-trip DarkBlue" $
A.fromJSON (A.String "dark-blue") @?= A.Success DarkBlue
, testCase "unknown value fails" $ do
case A.fromJSON (A.String "purple") :: A.Result Color of
A.Error _ -> pure ()
A.Success c -> fail ("unexpected " ++ show c)
]
-- ---------------------------------------------------------------------------
-- Sum
-- ---------------------------------------------------------------------------
sumTests :: TestTree
sumTests = testGroup "sum"
[ testCase "Point -> tag/contents (null payload)" $
A.toJSON Point @?= A.object
[ Key.fromText "tag" A..= A.String "point"
, Key.fromText "contents" A..= A.Null
]
, testCase "Circle -> tag/contents (single payload)" $
A.toJSON (Circle 1.5) @?= A.object
[ Key.fromText "tag" A..= A.String "circle"
, Key.fromText "contents" A..= A.Number 1.5
]
, testCase "Rect (renameStyle SnakeCase) -> tag = \"rect\", array contents" $ do
let v = A.toJSON (Rect 2 3)
case v of
A.Object o -> do
KM.lookup (Key.fromText "tag") o
@?= Just (A.String "rect")
KM.lookup (Key.fromText "contents") o
@?= Just (A.toJSON ([A.Number 2, A.Number 3] :: [A.Value]))
_ -> fail "expected JSON object"
, testCase "round-trip Point" $ rt Point
, testCase "round-trip Circle" $ rt (Circle 2.5)
, testCase "round-trip Rect" $ rt (Rect 4 5)
, testCase "unknown tag fails" $ do
let bad = A.object
[ Key.fromText "tag" A..= A.String "triangle"
, Key.fromText "contents" A..= A.Null
]
case A.fromJSON bad :: A.Result Shape of
A.Error _ -> pure ()
A.Success s -> fail ("unexpected " ++ show s)
]
where
rt :: Shape -> IO ()
rt s = case A.fromJSON (A.toJSON s) of
A.Success s' -> s' @?= s
A.Error e -> fail ("round-trip failed: " ++ e)