packages feed

json-schema-0.7.4.1: tests/Main.hs

{-# OPTIONS -fno-warn-missing-signatures #-}
{-# LANGUAGE
    CPP
  , DeriveGeneric
  , OverloadedStrings
  , ScopedTypeVariables
  , TemplateHaskell
  , TypeFamilies
  #-}
module Main (main) where

import Data.Aeson hiding (Result)
import Data.Proxy
import GHC.Generics (Generic)
import Generics.Generic.Aeson
import Test.Tasty
import Test.Tasty.HUnit
import Test.Tasty.TH
import qualified Data.Aeson.Types as A

import Data.JSON.Schema
import Data.JSON.Schema.Combinators
import Example ()
import Test.Util
import qualified Data.JSON.Schema as S
import qualified Test.Validate as Validate

data SingleCons = SingleCons deriving (Generic, Show, Eq)
instance ToJSON   SingleCons where toJSON    = gtoJson
instance FromJSON SingleCons where parseJSON = gparseJson
instance JSONSchema SingleCons where schema = gSchema

case_constructorWithoutFields = do
  bidir "\"singleCons\"" SingleCons
  eq (S.Constant (A.String "singleCons"))
     (schema (Proxy :: Proxy SingleCons))
  valid SingleCons

data Record = Record { recordField :: Int } deriving (Generic, Show, Eq)
instance ToJSON   Record where toJSON    = gtoJson
instance FromJSON Record where parseJSON = gparseJson
instance JSONSchema Record where schema = gSchema
case_record = do
  bidir "{\"recordField\":1}" Record { recordField = 1 }
  eq (S.Object [S.Field {S.key = "recordField", S.required = True, S.content = S.Number S.unbounded}])
     (schema (Proxy :: Proxy Record))
  valid Record { recordField = 1 }

data RecordTwoFields = D { d1 :: Int, d2 :: String } deriving (Generic, Show, Eq)
instance ToJSON   RecordTwoFields where toJSON = gtoJson
instance FromJSON RecordTwoFields where parseJSON = gparseJson
instance JSONSchema RecordTwoFields where schema = gSchema
case_recordWithFields = do
  bidir "{\"d1\":1,\"d2\":\"aap\"}"D {d1 = 1, d2 = "aap"}
  eq (S.Object [Field {key = "d1", required = True, content = S.Number S.unbounded}, Field {key = "d2", required = True, content = S.Value S.unboundedLength }])
     (schema (Proxy :: Proxy RecordTwoFields))
  valid D { d1 = 1, d2 = "aap"}

data E = E Int deriving (Generic, Show, Eq)
instance ToJSON   E where toJSON = gtoJson
instance FromJSON E where parseJSON = gparseJson
instance JSONSchema E where schema = gSchema
case_constructorOneField = do
  bidir "1" (E 1)
  eq (S.Number S.unbounded)
     (schema (Proxy :: Proxy E))
  valid $ E 1

data F = F Int String deriving (Generic, Show, Eq)
instance ToJSON   F where toJSON = gtoJson
instance FromJSON F where parseJSON = gparseJson
instance JSONSchema F where schema = gSchema
case_constructorWithFields = do
  bidir "[1,\"aap\"]" (F 1 "aap")
  eq (S.Tuple [S.Number S.unbounded, S.Value S.unboundedLength])
     (schema (Proxy :: Proxy F))
  valid $ F 1 "aap"

data G = G1 Int | G2 String deriving (Generic, Show, Eq)
instance ToJSON   G where toJSON = gtoJson
instance FromJSON G where parseJSON = gparseJson
instance JSONSchema G where schema = gSchema
case_sumConstructorsWithField = do
  bidir "{\"g1\":1}" (G1 1)
  bidir "{\"g2\":\"aap\"}" (G2 "aap")
  eq (S.Choice [S.Object [Field {key = "g1", required = True, content = S.Number S.unbounded}],S.Object [Field {key = "g2", required = True, content = S.Value S.unboundedLength }]])
     (schema (Proxy :: Proxy G))
  valid $ G1 1
  valid $ G2 "aap"

data H = H1 { h1 :: Int } | H2 { h2 :: String } deriving (Generic, Show, Eq)
instance ToJSON   H where toJSON = gtoJson
instance FromJSON H where parseJSON = gparseJson
instance JSONSchema H where schema = gSchema
case_sumRecord = do
  bidir "{\"h1\":{\"h1\":1}}" H1 { h1 = 1 }
  bidir "{\"h2\":{\"h2\":\"aap\"}}" H2 { h2 = "aap" }
  eq (S.Choice [S.Object [Field {key = "h1", required = True, content = S.Object [Field {key = "h1", required = True, content = S.Number S.unbounded}]}],S.Object [Field {key = "h2", required = True, content = S.Object [Field {key = "h2", required = True, content = S.Value S.unboundedLength}]}]])
     (schema (Proxy :: Proxy H))
  valid $ H1 1
  valid $ H2 "aap"

data J = J1 { j1 :: Int, j2 :: String } | J2 deriving (Generic, Show, Eq)
instance ToJSON   J where toJSON = gtoJson
instance FromJSON J where parseJSON = gparseJson
instance JSONSchema J where schema = gSchema
case_sumRecordConstructorWithoutFields = do
  bidir "{\"j1\":{\"j1\":1,\"j2\":\"aap\"}}" J1 {j1 = 1, j2 = "aap"}
  bidir  "{\"j2\":{}}" J2
  eq (S.Choice [S.Object [Field {key = "j1", required = True, content = S.Object [Field {key = "j1", required = True, content = S.Number S.unbounded},Field {key = "j2", required = True, content = S.Value S.unboundedLength }]}],S.Object [Field {key = "j2", required = True, content = S.Object []}]])
     (schema (Proxy :: Proxy J))
  valid $ J1 1 "aap"
  valid $ J2

data L = L1 | L2 Int String deriving (Generic, Show, Eq)
instance ToJSON   L where toJSON = gtoJson
instance FromJSON L where parseJSON = gparseJson
instance JSONSchema L where schema = gSchema
case_sumConstructorWithoutFieldsConstructorWithFields = do
  bidir "{\"l1\":{}}" L1
  bidir "{\"l2\":[1,\"aap\"]}" (L2 1 "aap")
  eq (S.Choice [S.Object [Field {key = "l1", required = True, content = S.Object []}],S.Object [Field {key = "l2", required = True, content = S.Tuple [S.Number S.unbounded, S.Value S.unboundedLength]}]])
     (schema (Proxy :: Proxy L))
  valid L1
  valid (L2 1 "aap")

data M = M1 | M2 Int M deriving (Generic, Show, Eq)
instance ToJSON   M where toJSON = gtoJson
instance FromJSON M where parseJSON = gparseJson
instance JSONSchema M where schema = gSchema
case_sumConstructorWithoutFieldsConstructorWithRecursiveField = do
  let a = M1
  let b = M2 1 M1
  let c = M2 1 (M2 2 M1)
  bidir "{\"m1\":{}}" a
  bidir "{\"m2\":[1,{\"m1\":{}}]}" b
  bidir "{\"m2\":[1,{\"m2\":[2,{\"m1\":{}}]}]}" c
  -- Infinite schema, so we just validate
  valid a
  valid b
  valid c

data N = N1 | N2 { n1 :: Int, n2 :: N } deriving (Generic, Show, Eq)
instance ToJSON   N where toJSON = gtoJson
instance FromJSON N where parseJSON = gparseJson
instance JSONSchema N where schema = gSchema
case_sum_constructorWithoutFields_record = do
  bidir "{\"n1\":{}}" N1
  bidir "{\"n2\":{\"n2\":{\"n1\":{}},\"n1\":1}}" N2 { n1 = 1, n2 = N1 }
  bidir "{\"n2\":{\"n1\":1,\"n2\":{\"n2\":{\"n1\":2,\"n2\":{\"n1\":{}}}}}}" N2 { n1 = 1, n2 = N2 { n1 = 2, n2 = N1 } }
  -- Infinite schema, so we just validate
  valid $ N1
  valid $ N2 1 (N2 2 N1)
  valid $ N2 1 N1

data O = O { o :: [Int] } deriving (Generic, Show, Eq)
instance ToJSON   O where toJSON = gtoJson
instance FromJSON O where parseJSON = gparseJson
instance JSONSchema O where schema = gSchema
case_recordListField = do
  bidir "{\"o\":[1,2,3]}" O {o = [1,2,3]}
  eq (S.Object [Field {key = "o", required = True, content = S.Array S.unboundedLength False (S.Number S.unbounded)}])
     (schema (Proxy :: Proxy O))
  valid $ O [1,2,3]

data P = P [Int] deriving (Generic, Show, Eq)
instance ToJSON   P where toJSON = gtoJson
instance FromJSON P where parseJSON = gparseJson
instance JSONSchema P where schema = gSchema
case_constructorListField = do
  bidir "[1,2,3]" (P [1,2,3])
  eq (S.Array S.unboundedLength False (S.Number S.unbounded))
     (schema (Proxy :: Proxy P))
  valid $ P [1,2,3]

data Q = Q Int Int Int deriving (Generic, Show, Eq)
instance ToJSON   Q where toJSON = gtoJson
instance FromJSON Q where parseJSON = gparseJson
instance JSONSchema Q where schema = gSchema
case_ConstructorSameTypedFields = do
  bidir "[1,2,3]" (Q 1 2 3)
  eq (S.Tuple [S.Number S.unbounded, S.Number S.unbounded, S.Number S.unbounded])
     (schema (Proxy :: Proxy Q))
  valid $ Q 1 2 3

data T = T { r1 :: Maybe Int } deriving (Generic, Show, Eq)
instance ToJSON   T where toJSON = gtoJson
instance FromJSON T where parseJSON = gparseJson
instance JSONSchema T where schema = gSchema
case_RecordMaybeField = do
  bidir "{}" T { r1 = Nothing }
  bidir "{\"r1\":1}" T { r1 = Just 1 }
  eq (S.Object [Field {key = "r1", required = False, content = S.Number S.unbounded}])
     (schema (Proxy :: Proxy T))
  valid $ T Nothing
  valid $ T (Just 1)

data V = V1 | V2 | V3 deriving (Generic, Show, Eq)
instance ToJSON   V where toJSON = gtoJson
instance FromJSON V where parseJSON = gparseJson
instance JSONSchema V where schema = gSchema
case_constructorsWithoutFields = do
  bidir "\"v1\"" V1
  bidir "\"v2\"" V2
  eq (S.Choice [S.Constant (A.String "v1"), S.Constant (A.String "v2"), S.Constant (A.String "v3")])
     (schema (Proxy :: Proxy V))
  valid V1
  valid V2

data W = W { underscore1_ :: Int, _underscore2 :: Int } deriving (Generic, Show, Eq)
instance ToJSON   W where toJSON = gtoJson
instance FromJSON W where parseJSON = gparseJson
instance JSONSchema W where schema = gSchema
case_recordWithUnderscoredFields = do
  bidir "{\"underscore1\":1,\"underscore2\":2}" W {underscore1_ = 1, _underscore2 = 2}
  eq (S.Object [Field {key = "underscore1", required = True, content = S.Number S.unbounded},Field {key = "underscore2", required = True, content = S.Number S.unbounded}])
     (schema (Proxy :: Proxy W))
  valid $ W 1 2

data Strip = Strip { stripA :: Int, strip_B :: Int, stripC_ :: Int, strip :: Int } deriving (Generic, Show, Eq)
stripSettings :: Settings
stripSettings = defaultSettings { stripPrefix = Just "strip" }
instance ToJSON     Strip where toJSON    = gtoJsonWithSettings    stripSettings
instance FromJSON   Strip where parseJSON = gparseJsonWithSettings stripSettings
instance JSONSchema Strip where schema    = gSchemaWithSettings    stripSettings
case_strip = do
  bidir "{\"a\":1,\"b\":2,\"c\":3,\"strip\":4}" Strip { stripA = 1, strip_B = 2, stripC_ = 3, strip = 4 }
  eq (S.Object [ Field { key = "a"    , required = True, content = S.Number S.unbounded }
               , Field { key = "b"    , required = True, content = S.Number S.unbounded }
               , Field { key = "c"    , required = True, content = S.Number S.unbounded }
               , Field { key = "strip", required = True, content = S.Number S.unbounded }
               ])
     (schema (Proxy :: Proxy Strip))
  valid (Strip 1 2 3 4)

data Stat = StatA | StatB (Maybe Prog)
  deriving (Eq, Generic, Show)
data Prog = Prog { aff :: Int }
  deriving (Eq, Generic, Show)
instance ToJSON     Prog where toJSON    = gtoJson
instance FromJSON   Prog where parseJSON = gparseJson
instance JSONSchema Prog where schema    = gSchema
instance ToJSON     Stat where toJSON    = gtoJson
instance FromJSON   Stat where parseJSON = gparseJson
instance JSONSchema Stat where schema    = gSchema
case_stat = do
  let a = StatB (Just Prog { aff = 1 })
  bidir "{\"statB\":{\"aff\":1}}" a
  valid a

  let b = StatB Nothing
  bidir "{\"statB\":null}" b
  eq (field "statA" True empty <|> field "statB" True (nullable $ field "aff" True number))
     (schema (Proxy :: Proxy Stat))
  valid b

-- https://github.com/silkapp/generic-aeson/issues/2
data X = X (Maybe Int) Int deriving (Eq, Generic, Show)
instance ToJSON     X where toJSON    = gtoJson
instance FromJSON   X where parseJSON = gparseJson
instance JSONSchema X where schema    = gSchema
case_constructorWithMaybeField = do
  let a = X (Just 1) 2
  bidir "[1,2]" a
  valid a

  let b = X Nothing 2
  bidir "[null,2]" b
  valid b

#if MIN_VERSION_aeson(0,10,0)
  eq (Left "Error in $: expected Int, encountered Boolean" :: Either String X)
     (eitherDecode "[true,2]")
#else
  eq (Left "when expecting a Int, encountered Boolean instead" :: Either String X)
     (eitherDecode "[true,2]")
#endif

  eq (Tuple [nullable number, number])
     (schema (Proxy :: Proxy X))

data X1 = X1 { x1a :: Maybe Int, x1b :: Int } deriving (Eq, Generic, Show)
instance ToJSON     X1 where toJSON    = gtoJson
instance FromJSON   X1 where parseJSON = gparseJson
instance JSONSchema X1 where schema    = gSchema
case_recordWithMaybeField = do
  let a = X1 { x1a = Just 1, x1b = 2 }
  bidir "{\"x1a\" : 1, \"x1b\" : 2}}" a
  valid a

  let b = X1 Nothing 2
  bidir "{ \"x1b\" : 2 }" b
  eq (Nothing :: Maybe X1)
     (decode "{\"x1a\":true,\"x1b\":2}")
  valid b

  eq (S.Object [Field "x1a" False number, Field "x1b" True number])
     (schema (Proxy :: Proxy X1))

  -- Regression test
  eq (Nothing :: Maybe X1)
     (decode "[true,2]")

-- https://github.com/silkapp/generic-aeson/issues/3
data X2 = X2 { x2 :: Maybe Int }
  deriving (Eq, Generic, Show)
instance ToJSON   X2 where toJSON = gtoJson
instance FromJSON X2 where parseJSON = gparseJson
instance JSONSchema X2 where schema = gSchema
case_recordWithOnlyOneMaybeField = do
  eq (Nothing :: Maybe X2)
     (decode "[{\"x2\":1}]")
  bidir "{\"x2\":1}" X2 { x2 = Just 1 }
  valid $ X2 (Just 1)

data MaybeStringCon = MaybeStringCon (Maybe String)
  deriving (Eq, Generic, Show)
instance ToJSON     MaybeStringCon where toJSON    = gtoJson
instance FromJSON   MaybeStringCon where parseJSON = gparseJson
instance JSONSchema MaybeStringCon where schema    = gSchema
data MaybeString = MaybeString { ms :: Maybe String }
  deriving (Eq, Generic, Show)
instance ToJSON     MaybeString where toJSON    = gtoJson
instance FromJSON   MaybeString where parseJSON = gparseJson
instance JSONSchema MaybeString where schema    = gSchema
case_maybeString = do
  let a = MaybeStringCon (Just "x")
  bidir "\"x\"" a

  eq (nullable value)
     (schema (Proxy :: Proxy MaybeStringCon))

  valid a

  let b = MaybeString { ms = Just "x" }
  bidir "{\"ms\":\"x\"}" b

  valid b

  let c = MaybeString Nothing
  bidir "{}" c

  valid c

  eq (field "ms" False value)
     (schema (Proxy :: Proxy MaybeString))

tests :: TestTree
tests = $testGroupGenerator

main :: IO ()
main = do
  defaultMain $ testGroup "generic-aeson" [tests, Validate.tests]