{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE OverloadedLists #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE FlexibleInstances #-}
module Main where
import Control.Lens hiding ((.=))
import Control.Monad
import Data.Aeson as A
import Data.Aeson.Types (parseEither)
import Data.Aeson.Unit
import Data.Aeson.WithField
import Data.Monoid
import Data.Proxy
import Data.Scientific (scientific)
import Data.Swagger
import Data.Swagger.Lens
import Data.Swagger.Internal.Schema
import Data.Text
import Data.Text.Arbitrary
import Data.Typeable
import Test.Tasty
import Test.Tasty.HUnit
import Test.Tasty.QuickCheck as QC
import qualified Data.Map.Strict as Map
import qualified Data.Vector as V
instance (Arbitrary a, Arbitrary b) => Arbitrary (WithField s a b) where
arbitrary = WithField <$> arbitrary <*> arbitrary
instance (Arbitrary a, Arbitrary b) => Arbitrary (WithFields a b) where
arbitrary = WithFields <$> arbitrary <*> arbitrary
instance Arbitrary a => Arbitrary (OnlyField s a) where
arbitrary = OnlyField <$> arbitrary
main :: IO ()
main = defaultMain tests
tests :: TestTree
tests = testGroup "Tests" [qcProperties, unitTests]
qcProperties :: TestTree
qcProperties = testGroup "Properties" [
withFieldProps
, withFieldsProps
, onlyFieldProps
, unitDataProps
]
unitTests :: TestTree
unitTests = testGroup "Unit tests" [
withFieldTests
, withFieldsTests
, onlyFieldTests
, unitDataTests
]
data TestObj = TestObj !Text
deriving (Eq, Show)
instance ToJSON TestObj where
toJSON (TestObj t) = object ["field" .= t]
instance FromJSON TestObj where
parseJSON (Object o) = TestObj <$> o .: "field"
parseJSON _ = mzero
instance ToSchema TestObj where
declareNamedSchema prx = do
t <- declareSchema (Proxy :: Proxy Text)
return $ NamedSchema (Just "TestObj") $ mempty
& type_ .~ Just SwaggerObject
& properties .~ [("field", Inline t)]
& required .~ ["field"]
instance Arbitrary TestObj where
arbitrary = TestObj <$> arbitrary
withFieldTests :: TestTree
withFieldTests = testGroup "WithField tests" [
testsToJSON
, testsFromJSON
, testsToSchema
, testsRoundtip
]
where
testsToJSON = testGroup "toJSON" [
testCase "Inline mode: atomic field" $ do
let expected = object ["a" .= (0 :: Int), "field" .= ("val" :: Text)]
let actual = toJSON (WithField 0 (TestObj "val") :: WithField "a" Int TestObj)
expected @=? actual
, testCase "Inline mode: complex field" $ do
let expected = object [
"a" .= object ["field" .= ("key" :: Text)]
, "field" .= ("val" :: Text)]
let actual = toJSON (WithField (TestObj "key") (TestObj "val")
:: WithField "a" TestObj TestObj)
expected @=? actual
, testCase "Wrapper mode: atomic" $ do
let expected = object ["a" .= (0 :: Int), "value" .= ("val" :: Text)]
let actual = toJSON (WithField 0 "val" :: WithField "a" Int String)
expected @=? actual
, testCase "Wrapper mode: array" $ do
let expected = object ["a" .= (0 :: Int), "value" .= (["val1", "val2"] :: [Text])]
let actual = toJSON (WithField 0 ["val1", "val2"] :: WithField "a" Int [String])
expected @=? actual
, testCase "Wrapper mode: complex field" $ do
let expected = object [
"a" .= object ["field" .= ("key" :: Text)]
, "value" .= ("val":: Text)]
let actual = toJSON (WithField (TestObj "key") "val" :: WithField "a" TestObj String)
expected @=? actual
, testCase "Overwrite mode" $ do
let expected = object ["a" .= (0 :: Int)]
let actual = toJSON (WithField 0 (OnlyField "val") :: WithField "a" Int (OnlyField "a" Text))
expected @=? actual
, testCase "Overwrite mode: wrapper" $ do
let expected = object ["value" .= (0 :: Int)]
let actual = toJSON (WithField 0 "val" :: WithField "value" Int Text)
expected @=? actual
]
testsFromJSON = testGroup "fromJSON" [
testCase "Inline mode: atomic field" $ do
let A.Success (expected :: WithField "a" Int TestObj) = fromJSON $ object [
"a" .= (0 :: Int)
, "field" .= ("val" :: Text)]
let actual = WithField 0 (TestObj "val") :: WithField "a" Int TestObj
expected @=? actual
, testCase "Inline mode: complex field" $ do
let A.Success (expected :: WithField "a" TestObj TestObj) = fromJSON $ object [
"a" .= object ["field" .= ("key" :: Text)]
, "field" .= ("val" :: Text)]
let actual = WithField (TestObj "key") (TestObj "val") :: WithField "a" TestObj TestObj
expected @=? actual
, testCase "Wrapper mode: atomic" $ do
let A.Success (expected :: WithField "a" Int String) = fromJSON $ object [
"a" .= (0 :: Int)
, "value" .= ("val" :: Text)]
let actual = WithField 0 "val" :: WithField "a" Int String
expected @=? actual
, testCase "Wrapper mode: array" $ do
let A.Success (expected :: WithField "a" Int [String]) = fromJSON $ object [
"a" .= (0 :: Int)
, "value" .= (["val1", "val2"] :: [Text]) ]
let actual = WithField 0 ["val1", "val2"] :: WithField "a" Int [String]
expected @=? actual
, testCase "Wrapper mode: complex field" $ do
let A.Success (expected :: WithField "a" TestObj String) = fromJSON $ object [
"a" .= object ["field" .= ("key" :: Text)]
, "value" .= ("val":: Text)]
let actual = WithField (TestObj "key") "val" :: WithField "a" TestObj String
expected @=? actual
, testCase "Overwrite mode" $ do
let A.Success (expected :: WithField "a" Int (OnlyField "a" Double)) = fromJSON $ object [
"a" .= (0 :: Int) ]
let actual = WithField 0 (OnlyField 0) :: WithField "a" Int (OnlyField "a" Double)
expected @=? actual
, testCase "Overwrite mode: wrapper" $ do
let A.Success (expected :: WithField "value" Int Double) = fromJSON $ object [
"value" .= (0 :: Int) ]
let actual = WithField 0 0 :: WithField "value" Int Double
expected @=? actual
]
testsToSchema = testGroup "toSchema" [
testCase "Inline mode: atomic field" $ do
let expected = [
("a", Inline $ toSchema (Proxy :: Proxy Int))
, ("field", Inline $ toSchema (Proxy :: Proxy String))]
let actual = toSchema (Proxy :: Proxy (WithField "a" Int TestObj))
expected @=? (actual ^. properties)
, testCase "Inline mode: complex field" $ do
let expected = [
("a", Inline $ toSchema (Proxy :: Proxy TestObj))
, ("field", Inline $ toSchema (Proxy :: Proxy String))]
let actual = toSchema (Proxy :: Proxy (WithField "a" TestObj TestObj))
expected @=? (actual ^. properties)
, testCase "Wrapper mode: atomic" $ do
let expected = [
("a", Inline $ toSchema (Proxy :: Proxy Int))
, ("value", Inline $ toSchema (Proxy :: Proxy String))]
let actual = toSchema (Proxy :: Proxy (WithField "a" Int String))
expected @=? (actual ^. properties)
, testCase "Wrapper mode: array" $ do
let expected = [
("a", Inline $ toSchema (Proxy :: Proxy Int))
, ("value", Inline $ toSchema (Proxy :: Proxy [String]))]
let actual = toSchema (Proxy :: Proxy (WithField "a" Int [String]))
expected @=? (actual ^. properties)
, testCase "Wrapper mode: complex field" $ do
let expected = [
("a", Inline $ toSchema (Proxy :: Proxy TestObj))
, ("value", Inline $ toSchema (Proxy :: Proxy String))]
let actual = toSchema (Proxy :: Proxy (WithField "a" TestObj String))
expected @=? (actual ^. properties)
, testCase "Overwrite mode" $ do
let expected = [("a", Inline $ toSchema (Proxy :: Proxy Int))]
let actual = toSchema (Proxy :: Proxy (WithField "a" Int (OnlyField "a" Text)))
expected @=? (actual ^. properties)
, testCase "Overwrite mode: wrapper" $ do
let expected = [("value", Inline $ toSchema (Proxy :: Proxy Int))]
let actual = toSchema (Proxy :: Proxy (WithField "value" Int Text))
expected @=? (actual ^. properties)
]
testsRoundtip = testGroup "roundtip" [
testCase "Map Bool Int" $ do -- Issue #1
let val = WithField 10 (Map.fromList [(True, 20), (False, 30)]) :: WithField "foo" Int (Map.Map Bool Int)
Right val @=? (parseEither parseJSON . toJSON $ val)
]
data TestObj1 a = TestObj1 !a
deriving (Eq, Show)
instance ToJSON a => ToJSON (TestObj1 a) where
toJSON (TestObj1 t) = object ["field1" .= t]
instance FromJSON a => FromJSON (TestObj1 a) where
parseJSON (Object o) = TestObj1 <$> o .: "field1"
parseJSON _ = mzero
instance (ToSchema a, Typeable a) => ToSchema (TestObj1 a) where
declareNamedSchema prx = do
t <- declareSchema (Proxy :: Proxy a)
let nm = pack . show $ typeRep (Proxy :: Proxy a)
return $ NamedSchema (Just $ "TestObj1'" <> nm) $ mempty
& type_ .~ Just SwaggerObject
& properties .~ [("field1", Inline t)]
& required .~ ["field1"]
instance Arbitrary a => Arbitrary (TestObj1 a) where
arbitrary = TestObj1 <$> arbitrary
data TestObj2 = TestObj2 !Text
deriving (Eq, Show)
instance ToJSON TestObj2 where
toJSON (TestObj2 t) = object ["field2" .= t]
instance FromJSON TestObj2 where
parseJSON (Object o) = TestObj2 <$> o .: "field2"
parseJSON _ = mzero
instance ToSchema TestObj2 where
declareNamedSchema prx = do
t <- declareSchema (Proxy :: Proxy Text)
return $ NamedSchema (Just "TestObj2") $ mempty
& type_ .~ Just SwaggerObject
& properties .~ [("field2", Inline t)]
& required .~ ["field2"]
instance Arbitrary TestObj2 where
arbitrary = TestObj2 <$> arbitrary
withFieldsTests :: TestTree
withFieldsTests = testGroup "WithFields tests" [
testsToJSON
, testsFromJSON
, testsToSchema
, testsRoundtip
]
where
testsToJSON = testGroup "toJSON" [
testCase "Inline mode" $ do
let expected = object [
"field1" .= ("val1" :: Text)
, "field2" .= ("val2" :: Text) ]
let actual = toJSON (WithFields (TestObj1 "val1" :: TestObj1 Text) (TestObj2 "val2"))
expected @=? actual
, testCase "Wrapper mode: first" $ do
let expected = object [
"injected" .= ("val1" :: Text)
, "field2" .= ("val2" :: Text) ]
let actual = toJSON (WithFields ("val1" :: Text) (TestObj2 "val2"))
expected @=? actual
, testCase "Wrapper mode: second" $ do
let expected = object [
"field1" .= ("val1" :: Text)
, "value" .= ("val2" :: Text) ]
let actual = toJSON (WithFields (TestObj1 "val1" :: TestObj1 Text) ("val2" :: Text))
expected @=? actual
, testCase "Wrapper mode: both" $ do
let expected = object [
"injected" .= ("val1" :: Text)
, "value" .= ("val2" :: Text) ]
let actual = toJSON (WithFields ("val1" :: Text) ("val2" :: Text))
expected @=? actual
, testCase "Overwrite mode" $ do
let expected = object ["field1" .= ("val1" :: Text) ]
let actual = toJSON (WithFields (TestObj1 "val1" :: TestObj1 Text) (TestObj1 "val2" :: TestObj1 Text))
expected @=? actual
, testCase "Overwrite mode: wrapper first" $ do
let expected = object ["injected" .= ("val1" :: Text) ]
let actual = toJSON (WithFields ("val1" :: Text) (OnlyField "val2" :: OnlyField "injected" Text))
expected @=? actual
, testCase "Overwrite mode: wrapper second" $ do
let expected = object ["value" .= ("val1" :: Text) ]
let actual = toJSON (WithFields (OnlyField "val1" :: OnlyField "value" Text) ("val2" :: Text))
expected @=? actual
]
testsFromJSON = testGroup "fromJSON" [
testCase "Inline mode" $ do
let A.Success (expected :: WithFields (TestObj1 Text) TestObj2) = fromJSON $ object [
"field1" .= ("val1" :: Text)
, "field2" .= ("val2" :: Text)]
let actual = WithFields (TestObj1 "val1") (TestObj2 "val2")
expected @=? actual
, testCase "Wrapper mode: first" $ do
let A.Success (expected :: WithFields Text TestObj2) = fromJSON $ object [
"injected" .= ("val1" :: Text)
, "field2" .= ("val2" :: Text)]
let actual = WithFields ("val1" :: Text) (TestObj2 "val2")
expected @=? actual
, testCase "Wrapper mode: second" $ do
let A.Success (expected :: WithFields (TestObj1 Text) Text) = fromJSON $ object [
"field1" .= ("val1" :: Text)
, "value" .= ("val2" :: Text)]
let actual = WithFields (TestObj1 "val1") ("val2" :: Text)
expected @=? actual
, testCase "Wrapper mode: both" $ do
let A.Success (expected :: WithFields Text Text) = fromJSON $ object [
"injected" .= ("val1" :: Text)
, "value" .= ("val2" :: Text)]
let actual = WithFields ("val1" :: Text) ("val2" :: Text)
expected @=? actual
, testCase "Overwrite mode" $ do
let A.Success (expected :: WithFields (TestObj1 Text) (TestObj1 Text)) = fromJSON $ object [
"field1" .= ("val1" :: Text) ]
let actual = WithFields (TestObj1 "val1") (TestObj1 "val1")
expected @=? actual
, testCase "Overwrite mode: wrapper first" $ do
let A.Success (expected :: WithFields Text (OnlyField "injected" Text)) = fromJSON $ object [
"injected" .= ("val1" :: Text) ]
let actual = WithFields "val1" (OnlyField "val1" :: OnlyField "injected" Text)
expected @=? actual
, testCase "Overwrite mode: wrapper second" $ do
let A.Success (expected :: WithFields (OnlyField "value" Text) Text) = fromJSON $ object [
"value" .= ("val1" :: Text) ]
let actual = WithFields (OnlyField "val1" :: OnlyField "value" Text) "val1"
expected @=? actual
]
testsToSchema = testGroup "toSchema" [
testCase "Inline mode" $ do
let expected = [
("field1", Inline $ toSchema (Proxy :: Proxy Text))
, ("field2", Inline $ toSchema (Proxy :: Proxy Text))]
let actual = toSchema (Proxy :: Proxy (WithFields (TestObj1 Text) TestObj2))
expected @=? (actual ^. properties)
, testCase "Wrapper mode: first" $ do
let expected = [
("injected", Inline $ toSchema (Proxy :: Proxy Text))
, ("field2", Inline $ toSchema (Proxy :: Proxy Text))]
let actual = toSchema (Proxy :: Proxy (WithFields Text TestObj2))
expected @=? (actual ^. properties)
, testCase "Wrapper mode: second" $ do
let expected = [
("field1", Inline $ toSchema (Proxy :: Proxy Text))
, ("value", Inline $ toSchema (Proxy :: Proxy Text))]
let actual = toSchema (Proxy :: Proxy (WithFields (TestObj1 Text) Text))
expected @=? (actual ^. properties)
, testCase "Wrapper mode: both" $ do
let expected = [
("injected", Inline $ toSchema (Proxy :: Proxy Text))
, ("value", Inline $ toSchema (Proxy :: Proxy Text))]
let actual = toSchema (Proxy :: Proxy (WithFields Text Text))
expected @=? (actual ^. properties)
, testCase "Overwrite mode" $ do
let expected = [("field1", Inline $ toSchema (Proxy :: Proxy Text))]
let actual = toSchema (Proxy :: Proxy (WithFields (TestObj1 Text) (TestObj1 Int)))
expected @=? (actual ^. properties)
, testCase "Overwrite mode: wrapper first" $ do
let expected = [("injected", Inline $ toSchema (Proxy :: Proxy Text))]
let actual = toSchema (Proxy :: Proxy (WithFields Text (OnlyField "injected" Int)))
expected @=? (actual ^. properties)
, testCase "Overwrite mode: wrapper second" $ do
let expected = [("value", Inline $ toSchema (Proxy :: Proxy Int))]
let actual = toSchema (Proxy :: Proxy (WithFields (OnlyField "value" Int) Text))
expected @=? (actual ^. properties)
]
testsRoundtip = testGroup "roundtip" [
testCase "Map Bool Int, one field" $ do -- Issue #1
let val = WithFields (TestObj1 10) (Map.fromList [(True, 20), (False, 30)]) :: WithFields (TestObj1 Int) (Map.Map Bool Int)
Right val @=? (parseEither parseJSON . toJSON $ val)
, testCase "Map Bool Int, two fields" $ do -- Issue #1
let val = WithFields (WithFields (TestObj1 10) (TestObj2 "Blah")) (Map.fromList [(True, 20), (False, 30)]) :: WithFields (WithFields (TestObj1 Int) TestObj2) (Map.Map Bool Int)
Right val @=? (parseEither parseJSON . toJSON $ val)
]
onlyFieldTests :: TestTree
onlyFieldTests = testGroup "OnlyField tests" [
testsToJSON
, testsFromJSON
, testsToSchema
]
where
testsToJSON = testGroup "toJSON" [
testCase "Normal mode" $ do
let expected = object [ "a" .= (42 :: Int) ]
let actual = toJSON (OnlyField 42 :: OnlyField "a" Int)
expected @=? actual
]
testsFromJSON = testGroup "FromJSON" [
testCase "Normal mode" $ do
let expected = OnlyField 42 :: OnlyField "a" Int
let (A.Success (actual :: OnlyField "a" Int)) = fromJSON $ object [
"a" .= (42 :: Int) ]
expected @=? actual
]
testsToSchema = testGroup "ToSchema" [
testCase "Normal mode" $ do
let expected = [
("a", Inline $ toSchema (Proxy :: Proxy Int)) ]
let actual = toSchema (Proxy :: Proxy (OnlyField "a" Int))
expected @=? (actual ^. properties)
, testCase "Distinct names" $ do
let idA = toNamedSchema (Proxy :: Proxy (OnlyField "idA" TestObj))
let idB = toNamedSchema (Proxy :: Proxy (OnlyField "idB" TestObj))
idA ^. name /= idB ^. name @? "Different OnlyField fields produce schemas with equal names"
]
unitDataTests :: TestTree
unitDataTests = testGroup "Unit tests" [
testsToJSON
, testsFromJSON
, testsToSchema
]
where
testsToJSON = testGroup "toJSON" [
testCase "Normal mode" $ do
let expected = object [ ]
let actual = toJSON Unit
expected @=? actual
]
testsFromJSON = testGroup "FromJSON" [
testCase "Normal mode" $ do
let expected = Unit
let (A.Success (actual :: Unit)) = fromJSON $ object [ ]
expected @=? actual
]
testsToSchema = testGroup "ToSchema" [
testCase "Normal mode" $ do
let expected = NamedSchema Nothing $ mempty & type_ .~ Just SwaggerObject
let actual = toNamedSchema (Proxy :: Proxy Unit)
expected @=? actual
]
withFieldProps :: TestTree
withFieldProps = testGroup "withField properties" [
functorProps
, bifunctorProps
]
where
functorProps = QC.testProperty "fmap id == id" $ \(wf :: WithField "id" Int TestObj) ->
fmap id wf == wf
bifunctorProps = QC.testProperty "bimap id id == id" $ \(wf :: WithField "id" Int TestObj) ->
bimap id id wf == wf
withFieldsProps :: TestTree
withFieldsProps = testGroup "withFields properties" [
functorProps
, bifunctorProps
]
where
functorProps = QC.testProperty "fmap id == id" $ \(wf :: WithFields (TestObj1 Text) TestObj2) ->
fmap id wf == wf
bifunctorProps = QC.testProperty "bimap id id == id" $ \(wf :: WithFields (TestObj1 Text) TestObj2) ->
bimap id id wf == wf
onlyFieldProps :: TestTree
onlyFieldProps = testGroup "onlyField properties" [
functorProps
]
where
functorProps = QC.testProperty "fmap id == id" $ \(wf :: OnlyField "id" TestObj) ->
fmap id wf == wf
unitDataProps :: TestTree
unitDataProps = testGroup "Unit properties" [
parseProps
]
where
parseProps = QC.testProperty "parseJSON Unit not fails" $ \(json :: Value) ->
case fromJSON json of
A.Success Unit -> True
_ -> False