aeson-schemas-1.3.4: test/Tests/SumType.hs
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE TypeApplications #-}
module Tests.SumType where
import Data.Aeson (ToJSON, eitherDecode, encode)
import Data.Proxy (Proxy (..))
import Data.String (fromString)
import Test.Tasty
import Test.Tasty.Golden
import Test.Tasty.HUnit
import Test.Tasty.QuickCheck
import Data.Aeson.Schema.Utils.Sum (SumType (..), fromSumType)
type SpecialJSON = SumType '[Bool, Int, [String]]
toSpecialJSON :: ToJSON a => a -> SpecialJSON
toSpecialJSON = either (error . ("Invalid SpecialJSON: " ++) . show) id . toSpecialJSON'
toSpecialJSON' :: ToJSON a => a -> Either String SpecialJSON
toSpecialJSON' = eitherDecode . encode
{- Tests -}
test :: TestTree
test =
testGroup
"SumType"
[ testCase "Sanity checks" $
-- this should compile
let values =
[ Here True
, Here False
, There (Here 1)
, There (Here 10)
, There (There (Here []))
, There (There (Here ["a"]))
] ::
[SpecialJSON]
in values @?= values
, testDecode
, testFromSumType
]
testDecode :: TestTree
testDecode =
testGroup
"Decode SumType"
[ testProperty "branch 1" $ \(b :: Bool) ->
toSpecialJSON' b === Right (Here b)
, testProperty "branch 2" $ \(x :: Int) ->
toSpecialJSON' x === Right (There (Here x))
, testProperty "branch 3" $ \(l :: [String]) ->
toSpecialJSON' l === Right (There (There (Here l)))
, goldenVsString "invalid SumType" "test/goldens/sumtype_decode_invalid.golden" $
case toSpecialJSON' [True] of
Right v -> error $ "Unexpectedly decoded value: " ++ show v
Left e -> pure $ fromString e
]
testFromSumType :: TestTree
testFromSumType =
testGroup
"fromSumType"
[ testProperty "branch 0 valid" $ \b ->
fromSumType (Proxy @0) (toSpecialJSON b) === Just b
, testProperty "branch 0 invalid" $
forAll (specialJSONExcept 0) $ \(branch, value) ->
labelBranch branch $ fromSumType (Proxy @0) value === Nothing
, testProperty "branch 1 valid" $ \x ->
fromSumType (Proxy @1) (toSpecialJSON x) === Just x
, testProperty "branch 1 invalid" $
forAll (specialJSONExcept 1) $ \(branch, value) ->
labelBranch branch $ fromSumType (Proxy @1) value === Nothing
, testProperty "branch 2 valid" $ \l ->
fromSumType (Proxy @2) (toSpecialJSON l) === Just l
, testProperty "branch 2 invalid" $
forAll (specialJSONExcept 2) $ \(branch, value) ->
labelBranch branch $ fromSumType (Proxy @2) value === Nothing
]
where
specialJSONExcept :: Int -> Gen (Int, SpecialJSON)
specialJSONExcept validBranch =
let fmapFst (a, gen) = (a,) <$> gen
in oneof $
map fmapFst $
filter
((/= validBranch) . fst)
[ (0, toSpecialJSON <$> arbitrary @Bool)
, (1, toSpecialJSON <$> arbitrary @Int)
, (2, toSpecialJSON <$> arbitrary @[String])
]
labelBranch branch = label $ "branch " ++ show branch