jordan-0.1.0.0: test/Jordan/SpecDefs.hs
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE RankNTypes #-}
module Jordan.SpecDefs
where
import Control.Applicative (Alternative((<|>)))
import Data.Functor.Contravariant
import Data.Functor.Contravariant.Divisible
import Data.String (IsString(..))
import Data.Text (Text, pack)
import GHC.Generics (Generic)
import Jordan.FromJSON.Class
import Jordan.ToJSON.Class
import Test.Hspec (Arg, Example, Spec, SpecWith, describe, it, shouldBe)
import Text.RawString.QQ
newtype BasicStruct
= BasicStruct { bar :: () }
deriving (Show, Eq, Ord)
goodBasic :: BasicStruct
goodBasic = BasicStruct ()
instance FromJSON BasicStruct where
fromJSON = (BasicStruct <$> fromNull) <> fromObject
where
fromObject = parseObject "TestItems.BasicStruct.Output" $
BasicStruct <$> parseField "bar"
fromNull = parseNull
instance ToJSON BasicStruct where
toJSON = serializeObject "TestItems.BasicStruct.Input" $ writeField "bar" serializeNull
basicNull = pack [r| null |]
basicOneField = pack [r| { "bar": null } |]
basicEscaped = pack [r| { "\u0062\u0061\u0072": null } |]
basicPartialEscape = pack [r| { "\u0062a\u0072": null } |]
basicExtraFields = pack [r| { "whatever": null, "bar": null, "baz": null } |]
basicArray = pack [r| [null, { "whatever": null, "bar": null }, null, null] |]
data TwoFields
= TwoFields { one :: (), two :: () }
deriving (Show, Eq, Ord, Generic)
goodTwo :: TwoFields
goodTwo = TwoFields () ()
instance FromJSON TwoFields where
fromJSON = parseObject "TwoFields" $ TwoFields <$> parseField "one" <*> parseField "two"
twoSimple = pack [r| { "one": null, "two": null } |]
twoScramble = pack [r| { "two": null, "one": null } |]
twoExtra = pack [r|
{
"ignore": [],
"one": null,
"bad": {},
"another": [1,2,3],
"yetAgain": null,
"two": null,
"three": null }
|]
twoScrambleExtra = pack [r|
{
"ignore": null,
"bad": null,
"two": null,
"another": null,
"yetAgain": null,
"one": null
}
|]
data GenericStruct
= GenericStruct
{ firstLabel :: ()
, secondLabel :: [()]
} deriving (Show, Read, Eq, Ord, Generic)
instance FromJSON GenericStruct
genericDefault = pack [r| { "firstLabel": null, "secondLabel": [null] } |]
data GenericSum
= GenericBasic BasicStruct
| GenericTwo TwoFields
deriving (Show, Eq, Ord, Generic)
instance FromJSON GenericSum
sumBasic = pack [r| { "GenericBasic": null } |]
sumBasicObj = pack [r| { "GenericBasic": { "bar": null } } |]
sumTwo = pack [r| { "GenericTwo": { "one": null, "two": null, "bar": null } } |]
mismatchType = pack [r| { "GenericTwo": null } |]
basicWritingSpec
:: (forall val. (ToJSON val, Show val) => val -> Text)
-> Spec
basicWritingSpec writeJSON = do
describe "writing basic primitives" $ do
it "writes nulls" $
writeJSON () `shouldBe` "null"
it "writes True to true" $
writeJSON True `shouldBe` "true"
it "writes False to false" $
writeJSON False `shouldBe` "false"
it "writes an empty array" $
writeJSON ([] :: [()]) `shouldBe` "[]"
it "writes an array with one item" $ do
writeJSON [()] `shouldBe` "[null]"
it "writes an array with two items" $ do
writeJSON [(),()] `shouldBe` "[null,null]"
it "writes the number 1" $ do
writeJSON (1 :: Int) `shouldBe` "1.0"
it "writes the number 1.5" $ do
writeJSON (1.5 :: Double) `shouldBe` "1.5"
describe "writing text" $ do
it "writes with no escapes" $ do
writeJSON ("foo" :: Text) `shouldBe` [r|"foo"|]
it "writes with an escaped quote" $ do
writeJSON ("foo\"" :: Text) `shouldBe` [r|"foo\""|]
it "writes with an escaped backslash" $ do
writeJSON ("foo\\" :: Text) `shouldBe` [r|"foo\\"|]
describe "writing a basic object" $ do
it "writes properly" $ do
writeJSON goodBasic `shouldBe` [r|{"bar": null}|]
basicParsingSpec
:: (Example a)
=> (forall val. (FromJSON val, Show val, Eq val) => Text -> val -> a)
-> SpecWith (Arg a)
basicParsingSpec parseMatch = do
describe "parsing basic structure" $ do
it "parses true" $
"true" `parseMatch` True
it "parses false" $
"false" `parseMatch` False
it "parses a raw null" $
"null" `parseMatch` ()
it "parses an empty array of nulls" $
"[]" `parseMatch` ([] :: [()])
it "parses a one-null array" $ do
"[null ]" `parseMatch` [()]
it "parses a two-null array with weird spaces" $ do
"[null\n,null]" `parseMatch` [(), ()]
it "parses null to a basic struct" $
basicNull `parseMatch` goodBasic
it "parses with field" $
basicOneField `parseMatch` goodBasic
it "parses with two fields" $
basicExtraFields `parseMatch` goodBasic
it "parses with a fully-escaped field" $
basicEscaped `parseMatch` goodBasic
it "parses with a partially-escaped field" $
basicPartialEscape `parseMatch` goodBasic
it "parses an array" $
basicArray `parseMatch` replicate 4 goodBasic
describe "string parsing" $ do
it "parses with an escaped backslash" $ do
"\"foo\\\\\"" `parseMatch` ("foo\\" :: Text)
it "parses with an escaped quote" $ do
"\"foo\\\"\"" `parseMatch` ("foo\"" :: Text)
it "parses with unicode" $ do
"\"foo\\u2795\"" `parseMatch` ("foo➕" :: Text)
it "parses a text with no escapes" $ do
[r|"foo"|] `parseMatch` ("foo" :: Text)
it "parses a text with an escaped backslash" $ do
[r|"foo\\\\"|] `parseMatch` ([r|foo\\|] :: Text)
describe "parsing a two-field object" $ do
it "parses with only required fields" $
twoSimple `parseMatch` goodTwo
it "parses with only required fields in wrong order" $
twoScramble `parseMatch` goodTwo
it "parses with lots of extra crap" $
twoExtra `parseMatch` goodTwo
it "parses in a weird order with extra crap" $
twoScrambleExtra `parseMatch` goodTwo
describe "parsing a generically-derived object" $ do
it "parses correctly in the basic case" $
genericDefault `parseMatch` GenericStruct () [()]
describe "parsing a generally-derived sum object" $ do
it "parses first correctly" $
sumBasic `parseMatch` GenericBasic (BasicStruct ())
it "parses first correctrly when using other alternate" $
sumBasicObj `parseMatch` GenericBasic (BasicStruct ())
it "parses second correctly" $
sumTwo `parseMatch` GenericTwo (TwoFields () ())