1
- {-# LANGUAGE DataKinds #-}
2
- {-# LANGUAGE FlexibleContexts #-}
3
- {-# LANGUAGE RankNTypes #-}
1
+ {-# LANGUAGE ConstraintKinds #-}
2
+ {-# LANGUAGE DataKinds #-}
3
+ {-# LANGUAGE FlexibleContexts #-}
4
+ {-# LANGUAGE RankNTypes #-}
4
5
{-# LANGUAGE ScopedTypeVariables #-}
5
- {-# LANGUAGE TypeOperators #-}
6
- {-# LANGUAGE ConstraintKinds #-}
6
+ {-# LANGUAGE TypeOperators #-}
7
7
module Servant.Swagger.Internal.Test where
8
8
9
- import Data.Aeson (ToJSON )
10
- import Data.Swagger
11
- import Data.Text (Text )
12
- import Data.Typeable
13
- import Test.Hspec
14
- import Test.Hspec.QuickCheck
15
- import Test.QuickCheck (Arbitrary , Property , property , counterexample )
9
+ import Data.Aeson (ToJSON (.. ))
10
+ import Data.Aeson.Encode.Pretty (encodePretty )
11
+ import Data.Swagger (Pattern , ToSchema ,
12
+ toSchema )
13
+ import Data.Swagger.Schema.Validation
14
+ import Data.Text (Text )
15
+ import qualified Data.Text.Lazy as TL
16
+ import qualified Data.Text.Lazy.Encoding as TL
17
+ import Data.Typeable
18
+ import Test.Hspec
19
+ import Test.Hspec.QuickCheck
20
+ import Test.QuickCheck (Arbitrary , Property ,
21
+ counterexample , property )
16
22
17
- import Servant.API
18
- import Servant.Swagger.Internal.TypeLevel
23
+ import Servant.API
24
+ import Servant.Swagger.Internal.TypeLevel
19
25
20
26
-- $setup
21
27
-- >>> import Control.Applicative
@@ -76,7 +82,7 @@ validateEveryToJSON :: forall proxy api. TMap (Every [Typeable, Show, Arbitrary,
76
82
-> Spec
77
83
validateEveryToJSON _ = props
78
84
(Proxy :: Proxy [ToJSON , ToSchema ])
79
- (reportErrors . validateToJSON)
85
+ (maybeCounterExample . prettyValidateWith validateToJSON)
80
86
(Proxy :: Proxy (BodyTypes JSON api ))
81
87
82
88
-- | Verify that every type used with @'JSON'@ content type in a servant API
@@ -89,7 +95,7 @@ validateEveryToJSONWithPatternChecker :: forall proxy api. TMap (Every [Typeable
89
95
-> Spec
90
96
validateEveryToJSONWithPatternChecker checker _ = props
91
97
(Proxy :: Proxy [ToJSON , ToSchema ])
92
- (reportErrors . validateToJSONWithPatternChecker checker)
98
+ (maybeCounterExample . prettyValidateWith ( validateToJSONWithPatternChecker checker) )
93
99
(Proxy :: Proxy (BodyTypes JSON api ))
94
100
95
101
-- * QuickCheck-related stuff
@@ -128,11 +134,65 @@ props _ f px = sequence_ specs
128
134
aprop :: forall p' a . (EveryTF cs a , Typeable a , Show a , Arbitrary a ) => p' a -> Spec
129
135
aprop _ = prop (show (typeOf (undefined :: a ))) (f :: a -> Property )
130
136
131
- -- | A property that prints a nicely formatted list of errors if there are
132
- -- any.
133
- reportErrors :: [ValidationError ] -> Property
134
- reportErrors [] = property True
135
- reportErrors ex = counterexample errString (property False )
137
+ -- | Pretty print validation errors
138
+ -- together with actual JSON and Swagger Schema
139
+ -- (using 'encodePretty').
140
+ --
141
+ -- >>> import Data.Aeson
142
+ -- >>> data Person = Person { name :: String, phone :: Integer } deriving (Generic)
143
+ -- >>> instance ToJSON Person where toJSON p = object [ "name" .= name p ]
144
+ -- >>> instance ToSchema Person
145
+ -- >>> let person = Person { name = "John", phone = 123456 }
146
+ -- >>> mapM_ putStrLn $ prettyValidateWith validateToJSON person
147
+ -- Validation against the schema fails:
148
+ -- * property "phone" is required, but not found in "{\"name\":\"John\"}"
149
+ -- <BLANKLINE>
150
+ -- JSON value:
151
+ -- {
152
+ -- "name": "John"
153
+ -- }
154
+ -- <BLANKLINE>
155
+ -- Swagger Schema:
156
+ -- {
157
+ -- "required": [
158
+ -- "name",
159
+ -- "phone"
160
+ -- ],
161
+ -- "type": "object",
162
+ -- "properties": {
163
+ -- "phone": {
164
+ -- "type": "integer"
165
+ -- },
166
+ -- "name": {
167
+ -- "type": "string"
168
+ -- }
169
+ -- }
170
+ -- }
171
+ -- <BLANKLINE>
172
+ --
173
+ -- FIXME: this belongs in "Data.Swagger.Schema.Validation" (in @swagger2@).
174
+ prettyValidateWith
175
+ :: forall a . (ToJSON a , ToSchema a )
176
+ => (a -> [ValidationError ]) -> a -> Maybe String
177
+ prettyValidateWith f x =
178
+ case f x of
179
+ [] -> Nothing
180
+ errors -> Just $ unlines
181
+ [ " Validation against the schema fails:"
182
+ , unlines (map (" * " ++ ) errors)
183
+ , " JSON value:"
184
+ , ppJSONString json
185
+ , " "
186
+ , " Swagger Schema:"
187
+ , ppJSONString (toJSON schema)
188
+ ]
136
189
where
137
- errString = unlines $ " Validation against the schema fails:"
138
- : map (" * " ++ ) ex
190
+ ppJSONString = TL. unpack . TL. decodeUtf8 . encodePretty
191
+
192
+ json = toJSON x
193
+ schema = toSchema (Proxy :: Proxy a )
194
+
195
+ -- | Provide a counterexample if there is any.
196
+ maybeCounterExample :: Maybe String -> Property
197
+ maybeCounterExample Nothing = property True
198
+ maybeCounterExample (Just s) = counterexample s (property False )
0 commit comments