@@ -7,6 +7,7 @@ import Data.Argonaut.Core (Json)
77import Data.Argonaut.Core as A
88import Data.Argonaut.Gen as AGen
99import Data.Foldable (foldMap , traverse_ )
10+ import Data.Int as Int
1011import Data.List (List (..), (:))
1112import Data.List as List
1213import Data.Markdown (CodeBlockType (..), Document )
@@ -15,7 +16,10 @@ import Data.Maybe (Maybe(..))
1516import Data.Set (Set )
1617import Data.Set as Set
1718import Data.String as String
18- import JsonSchema (JsonSchema (..), JsonValueType (..))
19+ import Data.String.Gen as StringGen
20+ import Data.Tuple.Nested ((/\))
21+ import Foreign.Object as Object
22+ import JsonSchema (JsonSchema (..), JsonValueType (..), Keywords )
1923import JsonSchema as Schema
2024import JsonSchema.Codec.Printing as Printing
2125import JsonSchema.Gen as SchemaGen
@@ -24,6 +28,7 @@ import JsonSchema.SchemaPath (SchemaPathSegment(..))
2428import JsonSchema.Validation (Violation , ViolationReason (..))
2529import JsonSchema.Validation as Validation
2630import Test.QuickCheck (Result (..))
31+ import Test.QuickCheck.Gen (Gen )
2732import Test.Spec (describe )
2833import Test.Types (Example , TestLength (..), TestSpec )
2934import Test.Utils (exampleTestCase , failWithDetails , generativeTestCase )
@@ -262,6 +267,71 @@ spec = describe "Validation" do
262267
263268 traverse_ exampleTestCase examples
264269
270+ keywordAppliesOnlyToProperty
271+ { genNonApplicableJson: AGen .genJson `Gen.suchThat`
272+ (not A .isArray)
273+ , genValidApplicableJson: A .fromArray <$>
274+ (Gen .unfoldable $ pure A .jsonNull)
275+ , jsonDescription: " array"
276+ , keywordName: " items"
277+ , keywords:
278+ Schema .defaultKeywords
279+ { items = Just $ ObjectSchema $
280+ Schema .defaultKeywords
281+ { typeKeyword = Just $ Set .singleton JsonNull }
282+ }
283+ }
284+
285+ keywordAppliesOnlyToProperty
286+ { genNonApplicableJson: AGen .genJson `Gen.suchThat`
287+ (not A .isNumber)
288+ , genValidApplicableJson: A .fromNumber <$> do
289+ i ← Gen .chooseInt (-1000 ) 1000
290+ pure $ Int .toNumber $ 2 * i
291+ , jsonDescription: " numeric"
292+ , keywordName: " multipleOf"
293+ , keywords: Schema .defaultKeywords { multipleOf = Just 2.0 }
294+ }
295+
296+ keywordAppliesOnlyToProperty
297+ { genNonApplicableJson: AGen .genJson `Gen.suchThat`
298+ (not A .isObject)
299+ , genValidApplicableJson: do
300+ otherProperties ← Gen .unfoldable do
301+ propertyName ← StringGen .genAlphaString
302+ json ← AGen .genJson
303+ pure $ propertyName /\ json
304+
305+ requiredProperty ← do
306+ json ← AGen .genJson
307+ pure $ " requiredProperty" /\ json
308+
309+ pure
310+ $ A .fromObject
311+ $ Object .fromFoldable
312+ $ [ requiredProperty ] <> otherProperties
313+ , jsonDescription: " object"
314+ , keywordName: " required"
315+ , keywords:
316+ Schema .defaultKeywords
317+ { required = Set .singleton " requiredProperty"
318+ }
319+ }
320+
321+ keywordAppliesOnlyToProperty
322+ { genNonApplicableJson: AGen .genJson `Gen.suchThat`
323+ (not A .isArray)
324+ , genValidApplicableJson: pure $ A .fromArray [ A .jsonNull ]
325+ , jsonDescription: " array"
326+ , keywordName: " uniqueItems"
327+ , keywords:
328+ Schema .defaultKeywords
329+ { items = Just
330+ $ ObjectSchema
331+ $ Schema .defaultKeywords { uniqueItems = true }
332+ }
333+ }
334+
265335 generativeTestCase Short
266336 " null type accepts only null JSON values"
267337 do
@@ -346,3 +416,40 @@ spec = describe "Validation" do
346416 $ Printing .printSchema originalSchema
347417 , originalSchemaViolations
348418 }
419+
420+ keywordAppliesOnlyToProperty
421+ ∷ { genNonApplicableJson ∷ Gen Json
422+ , genValidApplicableJson ∷ Gen Json
423+ , jsonDescription ∷ String
424+ , keywordName ∷ String
425+ , keywords ∷ Keywords
426+ }
427+ → TestSpec
428+ keywordAppliesOnlyToProperty spec =
429+ generativeTestCase Long
430+ ( spec.keywordName
431+ <> " applies only to "
432+ <> spec.jsonDescription
433+ <>
434+ " JSON values"
435+ )
436+ do
437+ json ← Gen .choose
438+ spec.genNonApplicableJson
439+ spec.genValidApplicableJson
440+
441+ let
442+ schema = ObjectSchema spec.keywords
443+ violations = json `Validation.validateAgainst` schema
444+
445+ pure
446+ if Set .isEmpty violations then
447+ Success
448+ else
449+ failWithDetails
450+ ( " validation has failed even though the only keyword was constraining only "
451+ <> spec.jsonDescription
452+ )
453+ { json: A .stringify json
454+ , schema: A .stringify $ Printing .printSchema schema
455+ }
0 commit comments