highjson-0.4.0.0: test/Data/JsonSpec.hs
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
module Data.JsonSpec where
import Data.HighJson
import Control.Lens.TH
import Data.Aeson (eitherDecode, encode)
import Data.Typeable
import Test.Hspec
import Test.QuickCheck
import qualified Data.Text as T
data SomeDummy
= SomeDummy
{ sd_int :: Int
, sd_bool :: Bool
, sd_text :: T.Text
, sd_either :: Either Bool T.Text
, sd_maybe :: Maybe Int
} deriving (Show, Eq)
someDummySpec :: RecordTypeSpec SomeDummy '[Int, Bool, T.Text, Either Bool T.Text, Maybe Int]
someDummySpec =
recSpec "Some Dummy" Nothing SomeDummy $
"int" .= sd_int
:& "bool" .= sd_bool
:& "text" .= sd_text
:& "either" .= sd_either
:& "maybe" .= sd_maybe
instance ToJSON SomeDummy where
toJSON = jsonSerializer someDummySpec
toEncoding = jsonEncoder someDummySpec
instance FromJSON SomeDummy where
parseJSON = jsonParser someDummySpec
newtype SomeText = SomeText { unSomeText :: T.Text }
instance Arbitrary SomeText where
arbitrary = SomeText . T.pack <$> listOf1 (choose ('A', 'Z'))
instance Arbitrary SomeDummy where
arbitrary =
SomeDummy
<$> arbitrary
<*> arbitrary
<*> (unSomeText <$> arbitrary)
<*> ebt
<*> arbitrary
where
ebt =
do v <- arbitrary
case v of
Left b -> pure (Left b)
Right (SomeText t) -> pure (Right t)
data SomeNested
= SomeNested
{ sn_list :: [SomeNested]
, sn_obj :: Maybe SomeNested
} deriving (Show, Eq, Typeable)
someNestedSpec :: RecordTypeSpec SomeNested '[[SomeNested], Maybe SomeNested]
someNestedSpec =
recSpec "Nested" Nothing SomeNested $
"list" .= sn_list
:& "obj" .=? sn_obj
instance ToJSON SomeNested where
toJSON = jsonSerializer someNestedSpec
toEncoding = jsonEncoder someNestedSpec
instance FromJSON SomeNested where
parseJSON = jsonParser someNestedSpec
data SomeSumType
= SomeDummyT SomeDummy
| SomeInt Int
| SomeBool Bool
| SomeEnum
deriving (Show, Eq, Typeable)
makePrisms ''SomeSumType
someSumSpec :: SumTypeSpec SomeSumType '[SomeDummy, Int, Bool, ()]
someSumSpec =
sumSpec "SomeSum" Nothing $
"some_dummy" .-> _SomeDummyT
:& "some_int" .-> _SomeInt
:& "some_bool" .-> _SomeBool
:& "some_enum" .-> _SomeEnum
instance ToJSON SomeSumType where
toJSON = jsonSerializer someSumSpec
toEncoding = jsonEncoder someSumSpec
instance FromJSON SomeSumType where
parseJSON = jsonParser someSumSpec
instance Arbitrary SomeSumType where
arbitrary =
oneof
[ SomeDummyT <$> arbitrary
, SomeInt <$> arbitrary
, SomeBool <$> arbitrary
]
data ParamType a
= ParamType
{ pt_key :: a
, pt_val :: T.Text
} deriving (Show, Eq, Typeable)
paramTypeSpec :: FromJSON a => RecordTypeSpec (ParamType a) '[a, T.Text]
paramTypeSpec =
recSpec "Some Param" Nothing ParamType $
"key" .= pt_key
:& "val" .= pt_val
instance (ToJSON a, FromJSON a) => ToJSON (ParamType a) where
toJSON = jsonSerializer paramTypeSpec
toEncoding = jsonEncoder paramTypeSpec
instance (FromJSON a) => FromJSON (ParamType a) where
parseJSON = jsonParser paramTypeSpec
instance Arbitrary a => Arbitrary (ParamType a) where
arbitrary =
ParamType <$> arbitrary <*> (unSomeText <$> arbitrary)
data SomeEnumType
= SomeEnumA
| SomeEnumB
| SomeEnumC
| SomeEnumD
deriving (Show, Eq, Typeable)
makePrisms ''SomeEnumType
someEnumSpec :: EnumTypeSpec SomeEnumType '[(), (), (), ()]
someEnumSpec =
enumSpec "SomeSum" Nothing $
"a" @-> _SomeEnumA
:& "b" @-> _SomeEnumB
:& "c" @-> _SomeEnumC
:& "d" @-> _SomeEnumD
instance ToJSON SomeEnumType where
toJSON = jsonSerializer someEnumSpec
toEncoding = jsonEncoder someEnumSpec
instance FromJSON SomeEnumType where
parseJSON = jsonParser someEnumSpec
instance Arbitrary SomeEnumType where
arbitrary =
oneof
[ pure SomeEnumA
, pure SomeEnumB
, pure SomeEnumC
, pure SomeEnumD
]
spec :: Spec
spec =
describe "Parser and Serialiser" $
do it "Handles custom types correctly" $
do let t1 = SomeDummy 34 True "Teext" (Left False) Nothing
eitherDecode (encode t1) `shouldBe` Right t1
let t2 = SomeDummy 34 True "Teext" (Right "ok") (Just 42)
eitherDecode (encode t2) `shouldBe` Right t2
it "Handles arbitrary custom types correctly" $ property $ \t ->
(eitherDecode . encode) t == Right (t :: SomeDummy)
it "Handles arbitrary custom sum types correctly" $ property $ \t ->
(eitherDecode . encode) t == Right (t :: SomeSumType)
it "Handles arbitrary custom enum types correctly" $ property $ \t ->
(eitherDecode . encode) t == Right (t :: SomeEnumType)
it "Handles arbitrary custom parametrized types correctly" $ property $ \t ->
(eitherDecode . encode) t == Right (t :: ParamType SomeSumType)
it "Handles arbitrary custom parametrized types correctly 2" $ property $ \t ->
(eitherDecode . encode) t == Right (t :: ParamType SomeEnumType)
it "Handles nested types correctly" $
do let nested = SomeNested [SomeNested [] Nothing] (Just $ SomeNested [] Nothing)
eitherDecode (encode nested) `shouldBe` Right nested