packages feed

aeson-schema-0.3.0.2: test/Data/Aeson/Schema/Examples.hs

{-# LANGUAGE QuasiQuotes #-}

module Data.Aeson.Schema.Examples
  ( examples
  ) where

import           Data.Aeson        (Value)
import           Data.Aeson.LitQQ  (aesonLitQQ)
import qualified Data.Map          as M
import           Data.Text         (Text)

import           Data.Aeson.Schema

examples :: (String -> IO () -> a)
         -> (Graph Schema Text -> Schema Text -> Value -> IO ())
         -> (Graph Schema Text -> Schema Text -> Value -> IO ())
         -> [a]
examples testCase assertValid' assertInvalid' =
  [ testCase "patternProperties" $ do
      let schema = [schemaQQ| {
        "type": "object",
        "properties": {
          "positiveNumber": {
            "type": "number",
            "minimum": 0,
            "exclusiveMinimum": true
          }
        },
        "patternProperties": {
          ".+Number$": { "type": "integer" },
          ".+String$": { "type": "string" }
        }
      } |]
      assertValid schema [aesonLitQQ| { "positiveNumber": 13 } |]
      assertInvalid schema [aesonLitQQ| { "positiveNumber": -13 } |]
      assertInvalid schema [aesonLitQQ| { "positiveNumber": 13.5 } |]
      assertValid schema [aesonLitQQ| { "fooString": "foo", "barString": "bar" } |]
      assertInvalid schema [aesonLitQQ| { "fooString": null, "barString": "bar" } |]
  , testCase "additionalProperties" $ do
      let additionalNumbers = [schemaQQ| {
        "type": "object",
        "properties": { "null": { "type": "null" } },
        "patternProperties": {
          ".+String$": { "type": "string" }
        },
        "additionalProperties": { "type": "number" }
      } |]
      assertValid additionalNumbers [aesonLitQQ| {
        "null": null,
        "emptyString": "",
        "oneMoreThing": 23,
        "theLastThing": 999
      } |]
      assertInvalid additionalNumbers [aesonLitQQ| { "null": null, "notANumber": true } |]
      let noAdditionalProperties = [schemaQQ| {
        "type": "object",
        "properties": { "null": { "type": "null" } },
        "patternProperties": {
          ".+String$": { "type": "string" }
        },
        "additionalProperties": false
      } |]
      assertValid noAdditionalProperties [aesonLitQQ| { "null": null, "emptyString": "" } |]
      assertInvalid noAdditionalProperties [aesonLitQQ| {
        "null": null,
        "emptyString": "",
        "oneMoreThing": 23,
        "theLastThing": 999
      } |]
  , testCase "disallow" $ do
      let onlyFloats = [schemaQQ| { "type": "number", "disallow": "integer" } |]
      assertInvalid onlyFloats [aesonLitQQ| 9 |]
      assertValid onlyFloats [aesonLitQQ| 9.75 |]
      let notLengthThree = [schemaQQ| {
        "type": "array",
        "disallow": [{
          "type": "array",
          "minItems": 3,
          "maxItems": 3
        }]
      } |]
      assertValid notLengthThree [aesonLitQQ| [] |]
      assertValid notLengthThree [aesonLitQQ| [1] |]
      assertValid notLengthThree [aesonLitQQ| [1, 2] |]
      assertInvalid notLengthThree [aesonLitQQ| [1, 2, 3] |]
      assertValid notLengthThree [aesonLitQQ| [1, 2, 3, 4] |]
      let everythingExceptNumbers = [schemaQQ| { "disallow": "number" } |]
      assertInvalid everythingExceptNumbers [aesonLitQQ| 3 |]
      assertInvalid everythingExceptNumbers [aesonLitQQ| 3.5 |]
      assertValid everythingExceptNumbers [aesonLitQQ| true |]
      assertValid everythingExceptNumbers [aesonLitQQ| "nobody expects the ..." |]
      assertValid everythingExceptNumbers [aesonLitQQ| { "eins": 1, "zwei": 2 } |]
      assertValid everythingExceptNumbers [aesonLitQQ| ["eins", "zwei"] |]
      assertValid everythingExceptNumbers [aesonLitQQ| null |]
  , testCase "type: subschema" $ do
      let schema = [schemaQQ| {
        "type": [
          {
            "type": "object",
            "properties": { "insert": { "type": "string", "minLength": 1 } },
            "additionalProperties": false
          },
          {
            "type": "object",
            "properties": { "delete": { "type": "number", "minimum": 1 } },
            "additionalProperties": false
          },
          {
            "type": "object",
            "properties": { "retain": { "type": "number", "minimum": 1 } },
            "additionalProperties": false
          }
        ]
      } |]
      assertValid schema [aesonLitQQ| { "insert": "lorem" } |]
      assertInvalid schema [aesonLitQQ| { "insert": "lorem", "delete": 5 } |]
      assertValid schema [aesonLitQQ| { "delete": 5 } |]
      assertInvalid schema [aesonLitQQ| { "delete": 5, "retain": 76 } |]
      assertValid schema [aesonLitQQ| { "retain": 76 } |]
  , testCase "dependencies" $ do
      let aRequiresB = [schemaQQ| {
        "type": "object",
        "dependencies": { "a": "b" }
      } |]
      assertValid aRequiresB [aesonLitQQ| {} |]
      assertValid aRequiresB [aesonLitQQ| { "b": false } |]
      assertValid aRequiresB [aesonLitQQ| { "a": true, "b": false } |]
      assertInvalid aRequiresB [aesonLitQQ| { "a": 3 } |]
      let aRequiresBToBeANumber = [schemaQQ| {
        "type": "object",
        "dependencies": { "a": { "properties": { "b": { "type": "number" } } } }
      } |]
      assertValid aRequiresBToBeANumber [aesonLitQQ| {} |]
      assertValid aRequiresBToBeANumber [aesonLitQQ| { "b": "lorem" } |]
      assertValid aRequiresBToBeANumber [aesonLitQQ| { "a": "yes, we can" } |]
      assertInvalid aRequiresBToBeANumber [aesonLitQQ| { "a": "yes, we can", "b": "lorem" } |]
      assertValid aRequiresBToBeANumber [aesonLitQQ| { "a": "hi there", "b": 42 } |]
      let aDisallowsB = [schemaQQ| {
        "type": "object",
        "dependencies": {
          "a": {
            "disallow": [{
              "properties": {
                "b": { "type": "any", "required": true }
              }
            }]
          }
        }
      } |]
      assertValid aDisallowsB [aesonLitQQ| { "a": "lorem" } |]
      assertValid aDisallowsB [aesonLitQQ| { "b": 42 } |]
      assertInvalid aDisallowsB [aesonLitQQ| { "a": "lorem", "b": 42 } |]
  , testCase "extends" $ do
      let schema = [schemaQQ| {
        "type": "object",
        "properties": {
          "a": { "type": "number" }
        },
        "extends": [
          {
            "properties": {
              "a": { "required": true }
            }
          },
          {
            "patternProperties": {
              "^[a-z]$": { "minimum": -3 }
            }
          }
        ]
      } |]
      assertValid schema [aesonLitQQ| { "a": 2 } |]
      assertInvalid schema [aesonLitQQ| {} |]
      assertInvalid schema [aesonLitQQ| { "a": -4 } |]
      assertInvalid schema [aesonLitQQ| { "a": "foo" } |]
      assertInvalid schema [aesonLitQQ| { "a": -1, "b": -10 } |]
      assertValid schema [aesonLitQQ| { "a": -1, "ba": -10 } |]
  , testCase "$ref" $ do
      let
        a = [schemaQQ| { "$ref": "b", "minimum": 3 } |]
        b = [schemaQQ| { "type": "number", "maximum": 2 } |]
        graph = M.fromList [("a", a), ("b", b)]
      assertValid' graph a [aesonLitQQ| 1 |]
      assertInvalid' graph a [aesonLitQQ| 4 |]
  ]
  where
    assertValid = assertValid' M.empty
    assertInvalid = assertInvalid' M.empty