@@ -1,6 +1,8 @@
1{-# LANGUAGE CPP #-}
1{-# LANGUAGE CPP #-}
2{-# LANGUAGE DeriveGeneric #-}
2{-# LANGUAGE DeriveGeneric #-}
3{-# LANGUAGE PackageImports #-}
3{-# LANGUAGE PackageImports #-}
4+{-# LANGUAGE RecordWildCards #-}
5+{-# LANGUAGE ScopedTypeVariables #-}
4{-# OPTIONS_GHC -fno-warn-orphans #-}
6{-# OPTIONS_GHC -fno-warn-orphans #-}
5module Data.Swagger.Schema.ValidationSpec where
7module Data.Swagger.Schema.ValidationSpec where
@@ -15,6 +17,7 @@ import qualified "unordered-containers" Data.HashSet as HashSet
15import Data.HashMap.Strict (HashMap)
17import Data.HashMap.Strict (HashMap)
16import qualified Data.HashMap.Strict as HashMap
18import qualified Data.HashMap.Strict as HashMap
17import Data.Map (Map)
19import Data.Map (Map)
20+import Data.Monoid (mempty)
18import Data.Proxy
21import Data.Proxy
19import Data.Time
22import Data.Time
20import qualified Data.Text as T
23import qualified Data.Text as T
@@ -24,6 +27,7 @@ import Data.Word
24import GHC.Generics
27import GHC.Generics
26import Data.Swagger
29import Data.Swagger
30+import Data.Swagger.Declare
28import Test.Hspec
32import Test.Hspec
29import Test.Hspec.QuickCheck
33import Test.Hspec.QuickCheck
@@ -32,6 +36,11 @@ import Test.QuickCheck
32shouldValidate :: (ToJSON a, ToSchema a) => Proxy a -> a -> Bool
36shouldValidate :: (ToJSON a, ToSchema a) => Proxy a -> a -> Bool
33shouldValidate _ x = validateToJSON x == []
37shouldValidate _ x = validateToJSON x == []
39+shouldNotValidate :: forall a. ToSchema a => (a -> Value) -> a -> Bool
40+shouldNotValidate f = not . null . validateJSON defs sch . f
42+ (defs, sch) = runDeclare (declareSchema (Proxy :: Proxy a)) mempty
35spec :: Spec
44spec :: Spec
37 describe "Validation" $ do
46 describe "Validation" $ do
@@ -77,6 +86,14 @@ spec = do
77 prop "Paint" $ shouldValidate (Proxy :: Proxy Paint)
86 prop "Paint" $ shouldValidate (Proxy :: Proxy Paint)
78 prop "MyRoseTree" $ shouldValidate (Proxy :: Proxy MyRoseTree)
87 prop "MyRoseTree" $ shouldValidate (Proxy :: Proxy MyRoseTree)
79 prop "Light" $ shouldValidate (Proxy :: Proxy Light)
88 prop "Light" $ shouldValidate (Proxy :: Proxy Light)
89+ prop "ButtonImages" $ shouldValidate (Proxy :: Proxy ButtonImages)
91+ describe "invalid cases" $ do
92+ prop "invalidPersonToJSON" $ shouldNotValidate invalidPersonToJSON
93+ prop "invalidColorToJSON" $ shouldNotValidate invalidColorToJSON
94+ prop "invalidPaintToJSON" $ shouldNotValidate invalidPaintToJSON
95+ prop "invalidLightToJSON" $ shouldNotValidate invalidLightToJSON
96+ prop "invalidButtonImagesToJSON" $ shouldNotValidate invalidButtonImagesToJSON
81main :: IO ()
98main :: IO ()
82main = hspec spec
99main = hspec spec
@@ -96,6 +113,13 @@ instance ToSchema Person
96instance Arbitrary Person where
113instance Arbitrary Person where
97 arbitrary = Person <$> arbitrary <*> arbitrary <*> arbitrary
114 arbitrary = Person <$> arbitrary <*> arbitrary <*> arbitrary
116+invalidPersonToJSON :: Person -> Value
117+invalidPersonToJSON Person{..} = object
118+ [ T.pack "personName" .= toJSON name
119+ , T.pack "personPhone" .= toJSON phone
120+ , T.pack "personEmail" .= toJSON email
99-- ========================================================================
123-- ========================================================================
100-- Color (enum)
124-- Color (enum)
101-- ========================================================================
125-- ========================================================================
@@ -107,6 +131,11 @@ instance ToSchema Color
107instance Arbitrary Color where
131instance Arbitrary Color where
108 arbitrary = arbitraryBoundedEnum
132 arbitrary = arbitraryBoundedEnum
134+invalidColorToJSON :: Color -> Value
135+invalidColorToJSON Red = toJSON "red"
136+invalidColorToJSON Green = toJSON "green"
137+invalidColorToJSON Blue = toJSON "blue"
110-- ========================================================================
139-- ========================================================================
111-- Paint (record with bounded enum property)
140-- Paint (record with bounded enum property)
112-- ========================================================================
141-- ========================================================================
@@ -120,6 +149,9 @@ instance ToSchema Paint
120instance Arbitrary Paint where
149instance Arbitrary Paint where
121 arbitrary = Paint <$> arbitrary
150 arbitrary = Paint <$> arbitrary
152+invalidPaintToJSON :: Paint -> Value
153+invalidPaintToJSON = toJSON . color
123-- ========================================================================
155-- ========================================================================
124-- MyRoseTree (custom datatypeNameModifier)
156-- MyRoseTree (custom datatypeNameModifier)
125-- ========================================================================
157-- ========================================================================
@@ -161,6 +193,41 @@ instance Arbitrary Light where
161 , LightColor <$> arbitrary
193 , LightColor <$> arbitrary
196+invalidLightToJSON :: Light -> Value
197+invalidLightToJSON = genericToJSON defaultOptions
199+-- ========================================================================
200+-- ButtonImages (bounded enum key mapping)
201+-- ========================================================================
203+data ButtonState = Neutral | Focus | Active | Hover | Disabled
204+ deriving (Show, Eq, Ord, Bounded, Enum, Generic)
206+instance ToJSON ButtonState
207+instance ToSchema ButtonState
208+instance ToJSONKey ButtonState where toJSONKey = toJSONKeyText (T.pack . show)
210+instance Arbitrary ButtonState where
211+ arbitrary = arbitraryBoundedEnum
213+type ImageUrl = T.Text
215+newtype ButtonImages = ButtonImages { getButtonImages :: Map ButtonState ImageUrl }
216+ deriving (Show, Generic)
218+instance ToJSON ButtonImages where
219+ toJSON = toJSON . getButtonImages
221+instance ToSchema ButtonImages where
222+ declareNamedSchema = genericDeclareNamedSchemaNewtype defaultSchemaOptions
223+ declareSchemaBoundedEnumKeyMapping
225+invalidButtonImagesToJSON :: ButtonImages -> Value
226+invalidButtonImagesToJSON = genericToJSON defaultOptions
228+instance Arbitrary ButtonImages where
229+ arbitrary = ButtonImages <$> arbitrary
164-- Arbitrary instances for common types
231-- Arbitrary instances for common types
166instance (Eq k, Hashable k, Arbitrary k, Arbitrary v) => Arbitrary (HashMap k v) where
233instance (Eq k, Hashable k, Arbitrary k, Arbitrary v) => Arbitrary (HashMap k v) where
@@ -193,4 +260,3 @@ instance Arbitrary ZonedTime where
194instance Arbitrary UTCTime where
261instance Arbitrary UTCTime where
195 arbitrary = UTCTime <$> arbitrary <*> fmap fromInteger (choose (0, 86400))
262 arbitrary = UTCTime <$> arbitrary <*> fmap fromInteger (choose (0, 86400))