hydra-0.1.0: src/test/haskell/Hydra/Impl/Haskell/Ext/Json/SerdeSpec.hs
-- Note: these tests are dependent on Data.Aeson, both because the Serde depends on Data.Aeson
-- and because of the particular serialization style.
module Hydra.Impl.Haskell.Ext.Json.SerdeSpec where
import Hydra.All
import Hydra.Impl.Haskell.Dsl.Terms
import Hydra.Impl.Haskell.Ext.Json.Serde
import qualified Hydra.Impl.Haskell.Dsl.Types as Types
import Hydra.TestData
import Hydra.TestUtils
import qualified Test.Hspec as H
import qualified Data.List as L
import qualified Test.QuickCheck as QC
import qualified Data.Maybe as Y
checkLiterals :: H.SpecWith ()
checkLiterals = H.describe "Test literal values" $ do
H.it "Booleans become 'true' and 'false'" $ do
QC.property $ \b -> checkSerialization jsonSerdeStr
(TypedTerm Types.boolean $ boolean b)
(if b then "true" else "false")
H.it "int32's become numbers, and are serialized in the obvious way" $ do
QC.property $ \i -> checkSerialization jsonSerdeStr
(TypedTerm Types.int32 $ int32 i)
(show i)
H.it "uint8's and other finite integer types become numbers, and are serialized in the obvious way" $ do
QC.property $ \i -> checkSerialization jsonSerdeStr
(TypedTerm Types.uint8 $ uint8 i)
(show i)
H.it "bigints become numbers" $ do
QC.property $ \i -> checkSerialization jsonSerdeStr
(TypedTerm Types.bigint $ bigint i)
(show i)
checkOptionals :: H.SpecWith ()
checkOptionals = H.describe "Test and document serialization of optionals" $ do
H.it "A 'nothing' becomes 'null' (except when it appears as a field)" $
QC.property $ \mi -> checkSerialization jsonSerdeStr
(TypedTerm
(Types.optional Types.int32)
(optional $ (Just . int32) =<< mi))
(Y.maybe "null" show mi)
H.it "Nested optionals case #1: just x? :: optional<optional<int32>>" $
QC.property $ \mi -> checkSerialization jsonSerdeStr
(TypedTerm
(Types.optional $ Types.optional Types.int32)
(optional $ Just $ optional $ (Just . int32) =<< mi))
("[" ++ Y.maybe "null" show mi ++ "]")
H.it "Nested optionals case #2: nothing :: optional<optional<int32>>" $
QC.property $ \() -> checkSerialization jsonSerdeStr
(TypedTerm
(Types.optional $ Types.optional Types.int32)
(optional Nothing))
"[]"
checkRecordsAndUnions :: H.SpecWith ()
checkRecordsAndUnions = H.describe "Test and document handling of optionals vs. nulls for record and union types" $ do
H.it "Empty records become empty objects" $
QC.property $ \() -> checkSerialization jsonSerdeStr
(TypedTerm Types.unit unit)
"{}"
H.it "Simple records become simple objects" $
QC.property $ \() -> checkSerialization jsonSerdeStr
(TypedTerm latLonType (latlonRecord 37 (negate 122)))
"{\"lat\":37,\"lon\":-122}"
H.it "Optionals are omitted from record objects if 'nothing'" $
QC.property $ \() -> checkSerialization jsonSerdeStr
(TypedTerm
(TypeRecord $ RowType testTypeName Nothing [Types.field "one" $ Types.optional Types.string, Types.field "two" $ Types.optional Types.int32])
(record testTypeName [Field (FieldName "one") $ optional $ Just $ string "test", Field (FieldName "two") $ optional Nothing]))
"{\"one\":\"test\"}"
H.it "Simple unions become simple objects, via records" $
QC.property $ \() -> checkSerialization jsonSerdeStr
(TypedTerm
(TypeUnion $ RowType testTypeName Nothing [Types.field "left" Types.string, Types.field "right" Types.int32])
(union testTypeName $ Field (FieldName "left") $ string "test"))
"{\"left\":\"test\"}"
jsonSerdeIsInformationPreserving :: H.SpecWith ()
jsonSerdeIsInformationPreserving = H.describe "Verify that a round trip from a type+term, to serialized JSON, and back again is a no-op" $ do
H.it "Generate arbitrary type/term pairs, serialize the terms to JSON, deserialize them, and compare" $
QC.property (checkSerdeRoundTrip jsonSerde)
spec :: H.Spec
spec = do
checkLiterals
checkOptionals
checkRecordsAndUnions
-- jsonSerdeIsInformationPreserving -- TODO: restore me