wai-middleware-validation-0.1.0.0: test/Network/Wai/Middleware/ValidationSpec.hs
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
module Network.Wai.Middleware.ValidationSpec (spec) where
import Control.Monad (forM_)
import Control.Monad.IO.Class (liftIO)
import qualified Data.ByteString.Lazy as L
import Data.Maybe (fromMaybe, isJust)
import Data.String.Here (here)
import Network.HTTP.Types (StdMethod (GET, POST), methodPost, status201,
status400, status500)
import Network.Wai (requestMethod, responseLBS)
import Network.Wai.Test
import Test.Hspec
import Network.Wai.Middleware.Validation
openApiJson :: L.ByteString
openApiJson = [here|
{
"openapi": "3.0.0",
"info": { "title": "validator test", "version": "1.0.0" },
"paths": {
"/articles": {
"get": {
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Article"
}
}
}
}
}
}
},
"post": {
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Article"
}
}
}
},
"responses": {
"201": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Article"
}
}
}
}
}
}
},
"/articles/{articleId}": {
"parameters": [
{
"name": "articleId",
"in": "path",
"required": true,
"schema": { "type": "integer" }
}
],
"put": {
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Article"
}
}
}
},
"responses": { "default": { "description": "response example" } }
}
}
},
"components": {
"schemas": {
"Article": {
"type": "object",
"required": [ "cint", "ctxt" ],
"properties": {
"cint": { "type": "integer" },
"ctxt": { "type": "string" }
}
}
}
}
}
|]
spec :: Spec
spec = do
describe "Middleware" $ do
let
mkApp responseBody _ respond = respond $ responseLBS status201 responseHeaders responseBody
validResponseBody = [here| {"cint": 1, "ctxt": "RESPONSE"} |]
invalidResponseBody = [here| {"cint": 1} |]
mkSession requestBody = srequest $ SRequest (setPath (defaultRequest {requestMethod = methodPost}) "/articles") requestBody
validRequestBody = [here| {"cint": 1, "ctxt": "REQUEST"} |]
invalidRequestBody = [here| {"cint": 1, "ctxt": 0} |]
context "request and response validation" $ do
let validator = fromMaybe (error "Invalid OpenAPI document") (mkValidator' openApiJson)
it "do nothing if the request and response body is valid" $ do
sResponse <- liftIO $ runSession (mkSession validRequestBody) $ validator (mkApp validResponseBody)
(simpleStatus sResponse, simpleBody sResponse) `shouldBe` (status201, validResponseBody)
it "returns 400 if the request body is invalid" $ do
sResponse <- liftIO $ runSession (mkSession invalidRequestBody) $ validator (mkApp validResponseBody)
(simpleStatus sResponse, simpleBody sResponse) `shouldBe`
(status400, [here| {"title":"Validation failed","detail":"expected JSON value of type string\n"} |])
it "returns 500 if the response body is invalid" $ do
sResponse <- liftIO $ runSession (mkSession validRequestBody) $ validator (mkApp invalidResponseBody)
(simpleStatus sResponse, simpleBody sResponse) `shouldBe`
(status500, [here| {"title":"Validation failed","detail":"property \"ctxt\" is required, but not found in \"{\\\"cint\\\":1}\"\n"} |])
context "request validation" $ do
let reqValidator = fromMaybe (error "Invalid OpenAPI document") (mkRequestValidator' openApiJson)
it "do nothing if the request and response body is valid" $ do
sResponse <- liftIO $ runSession (mkSession validRequestBody) $ reqValidator (mkApp validResponseBody)
(simpleStatus sResponse, simpleBody sResponse) `shouldBe` (status201, validResponseBody)
it "returns 400 if the request body is invalid" $ do
sResponse <- liftIO $ runSession (mkSession invalidRequestBody) $ reqValidator (mkApp validResponseBody)
(simpleStatus sResponse, simpleBody sResponse) `shouldBe`
(status400, [here| {"title":"Validation failed","detail":"expected JSON value of type string\n"} |])
it "do nothing even if the response body is invalid" $ do
sResponse <- liftIO $ runSession (mkSession validRequestBody) $ reqValidator (mkApp invalidResponseBody)
(simpleStatus sResponse, simpleBody sResponse) `shouldBe` (status201, invalidResponseBody)
context "response validation" $ do
let resValidator = fromMaybe (error "Invalid OpenAPI document") (mkResponseValidator' openApiJson)
it "do nothing if the request and response body is valid" $ do
sResponse <- liftIO $ runSession (mkSession validRequestBody) $ resValidator (mkApp validResponseBody)
(simpleStatus sResponse, simpleBody sResponse) `shouldBe` (status201, validResponseBody)
it "do nothing even if the request body is invalid" $ do
sResponse <- liftIO $ runSession (mkSession invalidRequestBody) $ resValidator (mkApp validResponseBody)
(simpleStatus sResponse, simpleBody sResponse) `shouldBe` (status201, validResponseBody)
it "returns 500 if the response body is invalid" $ do
sResponse <- liftIO $ runSession (mkSession validRequestBody) $ resValidator (mkApp invalidResponseBody)
(simpleStatus sResponse, simpleBody sResponse) `shouldBe`
(status500, [here| {"title":"Validation failed","detail":"property \"ctxt\" is required, but not found in \"{\\\"cint\\\":1}\"\n"} |])
describe "mkValidator'" $ do
it "returns Just Middleware if the OpenAPI document is valid" $
isJust (mkValidator' openApiJson) `shouldBe` True
it "returns Nothing if the OpenAPI document is invalid" $
isJust (mkValidator' "") `shouldBe` False
it "returns Nothing if the OpenAPI document has no paths object" $ do
let json = [here|
{
"openapi": "3.0.0",
"info": { "title": "info example", "version": "1.0.0" },
"components": {
}
}
|]
isJust (mkValidator' json) `shouldBe` False
describe "mkRequestValidator'" $ do
it "returns Just Middleware if the OpenAPI document is valid" $
isJust (mkRequestValidator' openApiJson) `shouldBe` True
it "returns Nothing if the OpenAPI document is invalid" $
isJust (mkRequestValidator' "") `shouldBe` False
it "returns Nothing if the OpenAPI document has no paths object" $ do
let json = [here|
{
"openapi": "3.0.0",
"info": { "title": "info example", "version": "1.0.0" },
"components": {
}
}
|]
isJust (mkRequestValidator' json) `shouldBe` False
describe "mkResponseValidator'" $ do
it "returns Just Middleware if the OpenAPI document is valid" $
isJust (mkResponseValidator' openApiJson) `shouldBe` True
it "returns Nothing if the OpenAPI document is invalid" $
isJust (mkResponseValidator' "") `shouldBe` False
it "returns Nothing if the OpenAPI document has no paths object" $ do
let json = [here|
{
"openapi": "3.0.0",
"info": { "title": "info example", "version": "1.0.0" },
"components": {
}
}
|]
isJust (mkResponseValidator' json) `shouldBe` False
describe "validateRequestBody" $ do
let makeOpenApiJson schemaJson = [here|
{
"openapi": "3.0.0",
"info": { "title": "info example", "version": "1.0.0" },
"paths": {
"/examples": {
"post": {
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Example"
}
}
}
},
"responses": { "default": { "description": "response example" } }
}
}
},
"components": {
"schemas": {
"Example": {
"type": "object",
"properties": |] <> schemaJson <> [here|
}
}
}
}
|]
context "type: integer" $ do
let
tests =
[ ( "returns Right [] if the value is integer"
, [here| {"mint": {"type": "integer"}} |]
, [here| {"mint": 1} |]
, Right []
)
, ( "returns Right [] if the value is integer"
, [here| {"mint": {"type": "integer"}} |]
, [here| {"mint": 1} |]
, Right []
)
, ( "returns Right [] if the value is integer"
, [here| {"mint": {"type": "integer"}} |]
, [here| {"mint": 1} |]
, Right []
)
, ( "returns Right [error] if the value is a number contains a decimal part"
, [here| {"mint": {"type": "integer"}} |]
, [here| {"mint": 1.1} |]
, Right ["not an integer"]
)
, ( "returns Right [error] if the value is a string type"
, [here| {"mint": {"type": "integer"}} |]
, [here| {"mint": "text"} |]
, Right ["expected JSON value of type integer"]
)
, ( "returns Right [error] if the value is a boolean type"
, [here| {"mint": {"type": "integer"}} |]
, [here| {"mint": true} |]
, Right ["expected JSON value of type integer"]
)
, ( "returns Right [error] if the value is an array type"
, [here| {"mint": {"type": "integer"}} |]
, [here| {"mint": [0, 1, 2]} |]
, Right ["expected JSON value of type integer"]
)
, ( "returns Right [error] if the value is an object type"
, [here| {"mint": {"type": "integer"}} |]
, [here| {"mint": {"k": "v"}} |]
, Right ["expected JSON value of type integer"]
)
, ( "returns Right [] if the value is the same as the minimum"
, [here| {"mint": {"type": "integer", "minimum": 10}} |]
, [here| {"mint": 10} |]
, Right []
)
, ( "returns Right [error] if the value is less than the minimum"
, [here| {"mint": {"type": "integer", "minimum": 10}} |]
, [here| {"mint": 9} |]
, Right ["value 9.0 falls below minimum (should be >=10.0)"]
)
, ( "returns Right [] if the value is the same as the minimum and exclusiveMinimum is true"
, [here| {"mint": {"type": "integer", "minimum": 10, "exclusiveMinimum": true}} |]
, [here| {"mint": 10} |]
, Right ["value 10.0 falls below minimum (should be >10.0)"]
)
, ( "returns Right [] if the value is the same as the minimum and exclusiveMinimum is false"
, [here| {"mint": {"type": "integer", "minimum": 10, "exclusiveMinimum": false}} |]
, [here| {"mint": 10} |]
, Right []
)
, ( "returns Right [] if the value is the same as the maximum"
, [here| {"mint": {"type": "integer", "maximum": 10}} |]
, [here| {"mint": 10} |]
, Right []
)
, ( "returns Right [error] if the value is greater than the maximum"
, [here| {"mint": {"type": "integer", "maximum": 10}} |]
, [here| {"mint": 11} |]
, Right ["value 11.0 exceeds maximum (should be <=10.0)"]
)
, ( "returns Right [] if the value is the same as the maximum and exclusiveMaximum is true"
, [here| {"mint": {"type": "integer", "maximum": 10, "exclusiveMaximum": true}} |]
, [here| {"mint": 11} |]
, Right ["value 11.0 exceeds maximum (should be <10.0)"]
)
, ( "returns Right [] if the value is the same as the maximum and exclusiveMaximum is false"
, [here| {"mint": {"type": "integer", "maximum": 10, "exclusiveMaximum": false}} |]
, [here| {"mint": 10} |]
, Right []
)
, ( "returns Right [] if the value is the multiple of the value of multipleOf"
, [here| {"mint": {"type": "integer", "multipleOf": 5}} |]
, [here| {"mint": 10} |]
, Right []
)
, ( "returns Right [error] if the value is not the multiple of the value of multipleOf"
, [here| {"mint": {"type": "integer", "multipleOf": 4}} |]
, [here| {"mint": 10} |]
, Right ["expected a multiple of 4.0 but got 10.0"]
)
, ( "returns Right [] if the value is the maximum value of int32"
, [here| {"mint": {"type": "integer", "format": "int32"}} |]
, [here| {"mint": 2147483647} |]
, Right []
)
]
forM_ tests $ \(description, schemaJson, bodyJson, result) ->
it description $ do
let apiJson = makeOpenApiJson schemaJson
validateRequestBody POST "/examples" apiJson bodyJson `shouldBe` result
context "type: number" $ do
let
tests =
[ ( "returns Right [] if the value is a number contains a decimal part"
, [here| {"mnum": {"type": "number"}} |]
, [here| {"mnum": 1.1} |]
, Right []
)
, ( "returns Right [] if the value is an integer"
, [here| {"mnum": {"type": "number"}} |]
, [here| {"mnum": 1} |]
, Right []
)
, ( "returns Right [error] if the value is a string type"
, [here| {"mnum": {"type": "number"}} |]
, [here| {"mnum": "text"} |]
, Right ["expected JSON value of type number"]
)
, ( "returns Right [error] if the value is a boolean type"
, [here| {"mnum": {"type": "number"}} |]
, [here| {"mnum": true} |]
, Right ["expected JSON value of type number"]
)
, ( "returns Right [error] if the value is an array type"
, [here| {"mnum": {"type": "number"}} |]
, [here| {"mnum": [0, 1, 2 ]} |]
, Right ["expected JSON value of type number"]
)
, ( "returns Right [error] if the value is an object type"
, [here| {"mnum": {"type": "number"}} |]
, [here| {"mnum": {"k": "v"}} |]
, Right ["expected JSON value of type number"]
)
]
forM_ tests $ \(description, schemaJson, bodyJson, result) ->
it description $ do
let apiJson = makeOpenApiJson schemaJson
validateRequestBody POST "/examples" apiJson bodyJson `shouldBe` result
context "type: string" $ do
let
tests =
[ ( "returns Right [] if the value is a string type"
, [here| {"mstr": {"type": "string"}} |]
, [here| {"mstr": "text"} |]
, Right []
)
, ( "returns Right [error] if the value is an integer type"
, [here| {"mstr": {"type": "string"}} |]
, [here| {"mstr": 1} |]
, Right ["expected JSON value of type string"]
)
, ( "returns Right [error] if the value is a number type"
, [here| {"mstr": {"type": "string"}} |]
, [here| {"mstr": 1.1} |]
, Right ["expected JSON value of type string"]
)
, ( "returns Right [error] if the value is a boolean type"
, [here| {"mstr": {"type": "string"}} |]
, [here| {"mstr": true} |]
, Right ["expected JSON value of type string"]
)
, ( "returns Right [error] if the value is an array type"
, [here| {"mstr": {"type": "string"}} |]
, [here| {"mstr": [0, 1, 2]} |]
, Right ["expected JSON value of type string"]
)
, ( "returns Right [error] if the value is an object type"
, [here| {"mstr": {"type": "string"}} |]
, [here| {"mstr": {"k": "v"}} |]
, Right ["expected JSON value of type string"]
)
, ( "returns Right [] if the length of the value is the same as minLength"
, [here| {"mstr": {"type": "string", "minLength": 3}} |]
, [here| { "mstr": "abc" } |]
, Right []
)
, ( "returns Right [error] if the length of the value is less than minLength"
, [here| {"mstr": {"type": "string", "minLength": 4}} |]
, [here| { "mstr": "abc" } |]
, Right ["string is too short (length should be >=4)"]
)
, ( "returns Right [] if the length of the value is the same as maxLength"
, [here| {"mstr": {"type": "string", "maxLength": 3}} |]
, [here| { "mstr": "abc" } |]
, Right []
)
, ( "returns Right [error] if the length of the value is greater than maxLength"
, [here| {"mstr": {"type": "string", "maxLength": 2}} |]
, [here| { "mstr": "abc" } |]
, Right ["string is too long (length should be <=2)"]
)
, ( "returns Right [] if the value follows format:date"
, [here| {"mstr": {"type": "string", "format": "date"}} |]
, [here| { "mstr": "2017-07-21" } |]
, Right []
)
, ( "returns Right [] if the value follows format:date-time"
, [here| {"mstr": {"type": "string", "format": "date-time"}} |]
, [here| { "mstr": "2017-07-21T17:32:28Z" } |]
, Right []
)
, ( "returns Right [] if the value follows format:byte"
, [here| {"mstr": {"type": "string", "format": "byte"}} |]
, [here| {"mstr": "U3dhZ2dlciByb2Nrcw=="} |]
, Right []
)
, ( "returns Right [] if the value matches the pattern"
, [here| {"mstr": {"type": "string", "pattern": "^abc$"}} |]
, [here| {"mstr": "abc"} |]
, Right []
)
]
forM_ tests $ \(description, schemaJson, bodyJson, result) ->
it description $ do
let apiJson = makeOpenApiJson schemaJson
validateRequestBody POST "/examples" apiJson bodyJson `shouldBe` result
context "type: string (pending)" $ do
let
tests =
[ ( "returns Right [] if the value does not follows format:date"
, [here| {"mstr": {"type": "string", "format": "date"}} |]
, [here| { "mstr": "not date!" } |]
-- , Right ["expected JSON value of type string and format of date"]
, "Keyword `format` is not supported"
)
, ( "returns Right [] if the value does not follows format:date-time"
, [here| {"mstr": {"type": "string", "format": "date-time"}} |]
, [here| {"mstr": "not datetime!"} |]
-- , Right ["expected JSON value of type string and format of date-time"]
, "Keyword `format` is not supported"
)
, ( "returns Right [] if the value does not follows format:byte"
, [here| {"mstr": {"type": "string", "format": "byte"}} |]
, [here| {"mstr": "not byte!"} |]
-- , Right ["expected JSON value of type string and format of byte"]
, "Keyword `format` is not supported"
)
, ( "returns Right [error] if the value does not matches the pattern"
, [here| {"mstr": {"type": "string", "pattern": "^abc$"}} |]
, [here| {"mstr": "def"} |]
-- , Right ["expected JSON value of type string and of pattern"]
, "Keyword `pattern` is not supported"
)
] :: [(String, L.ByteString, L.ByteString, String)]
-- forM_ tests $ \(description, schemaJson, bodyJson, result) ->
-- it description $ do
-- let apiJson = makeOpenApiJson schemaJson
-- validateRequestBody POST "/examples" apiJson bodyJson `shouldNotBe` result
forM_ tests $ \(description, _, _, reason) ->
it description $
pendingWith reason
context "type: boolean" $ do
let
tests =
[ ( "returns Right [] if the value is an boolean value"
, [here| {"mbol": {"type": "boolean"}} |]
, [here| {"mbol": true} |]
, Right []
)
, ( "returns Right [error] if the value is an integer type"
, [here| {"mbol": {"type": "boolean"}} |]
, [here| {"mbol": 1} |]
, Right ["expected JSON value of type boolean"]
)
, ( "returns Right [error] if the value is a number type"
, [here| {"mbol": {"type": "boolean"}} |]
, [here| {"mbol": 1.1} |]
, Right ["expected JSON value of type boolean"]
)
, ( "returns Right [error] if the value is a string type"
, [here| {"mbol": {"type": "boolean"}} |]
, [here| {"mbol": "text"} |]
, Right ["expected JSON value of type boolean"]
)
, ( "returns Right [error] if the value is an array type"
, [here| {"mbol": {"type": "boolean"}} |]
, [here| {"mbol": [0, 1, 2]} |]
, Right ["expected JSON value of type boolean"]
)
, ( "returns Right [error] if the value is an object type"
, [here| {"mbol": {"type": "boolean"}} |]
, [here| {"mbol": {"k": "v"}} |]
, Right ["expected JSON value of type boolean"]
)
]
forM_ tests $ \(description, schemaJson, bodyJson, result) ->
it description $ do
let apiJson = makeOpenApiJson schemaJson
validateRequestBody POST "/examples" apiJson bodyJson `shouldBe` result
context "type: array" $ do
let
tests =
[ ( "returns Right [] if all values in the list are the correct type"
, [here| {"mlst": {"type": "array", "items": {"type": "integer"}}} |]
, [here| {"mlst": [1, 2]} |]
, Right []
)
, ( "returns Right [error] if there are different types of values in the list"
, [here| {"mlst": {"type": "array", "items": {"type": "integer"}}} |]
, [here| {"mlst": [1, "2"]} |]
, Right ["expected JSON value of type integer"]
)
, ( "returns Right [] if all values in the tuple are the correct type"
, [here| {"mtpl": {"type": "array", "items": [{"type": "integer"}, {"type": "string"}]}} |]
, [here| {"mtpl": [1, "text"]} |]
, Right []
)
, ( "returns Right [error] if there are different types of values in the tuple"
, [here| {"mtpl": {"type": "array", "items": [{"type": "integer"}, {"type": "string"}]}} |]
, [here| {"mtpl": [1, 2]} |]
, Right ["expected JSON value of type string"]
)
, ( "returns Right [error] if the number of elements in the array is less than minItems"
, [here| {"mlst": {"type": "array", "items": {"type": "integer"}, "minItems": 2}} |]
, [here| {"mlst": [1]} |]
, Right ["array is too short (size should be >=2)"]
)
, ( "returns Right [error] if the number of elements in the array is greater than maxItems"
, [here| {"mlst": {"type": "array", "items": {"type": "integer"}, "maxItems": 2}} |]
, [here| {"mlst": [1, 2, 3]} |]
, Right ["array exceeds maximum size (should be <=2)"]
)
, ( "returns Right [] if uniqueItems is not specified and the array has some duplicated values"
, [here| {"mlst": {"type": "array", "items": {"type": "integer"}}} |]
, [here| {"mlst": [1, 2, 1]} |]
, Right []
)
, ( "returns Right [error] if uniqueItems is true and the array has some duplicated values"
, [here| {"mlst": {"type": "array", "items": {"type": "integer"}, "uniqueItems": true}} |]
, [here| {"mlst": [1, 2, 1]} |]
, Right ["array is expected to contain unique items, but it does not"]
)
, ( "returns Right [error] if uniqueItems is false and the array has some duplicated values"
, [here| {"mlst": {"type": "array", "items": {"type": "integer"}, "uniqueItems": false}} |]
, [here| {"mlst": [1, 2, 1]} |]
, Right []
)
]
forM_ tests $ \(description, schemaJson, bodyJson, result) ->
it description $ do
let apiJson = makeOpenApiJson schemaJson
validateRequestBody POST "/examples" apiJson bodyJson `shouldBe` result
context "type: object" $ do
let
tests =
[ ( "returns Right [error] if the value contains undefined property"
, [here|
{
"mobj": {
"type": "object",
"properties": {
"mint": {"type": "integer"}
}
}
}
|]
, [here| {"mobj": {"mstr": "hello"}} |]
, Right ["property \"mstr\" is found in JSON value, but it is not mentioned in Swagger schema"]
)
, ( "returns Right [] if the value contains required property"
, [here|
{
"mobj": {
"type": "object",
"required": ["cint", "cstr"],
"properties": {
"cint": {"type": "integer"},
"cstr": {"type": "string"},
"mbol": {"type": "boolean"}
}
}
}
|]
, [here| {"mobj": {"cint": 1, "cstr": "hello"}} |]
, Right []
)
, ( "returns Right [error] if the value does not contains required property"
, [here|
{
"mobj": {
"type": "object",
"required": ["cint", "cstr"],
"properties": {
"cint": {"type": "integer"},
"cstr": {"type": "string"},
"mbol": {"type": "boolean"}
}
}
}
|]
, [here| {"mobj": {"cint": 1, "mbol": true}} |]
, Right ["property \"cstr\" is required, but not found in \"{\\\"mbol\\\":true,\\\"cint\\\":1}\""]
)
, ( "returns Right [] if the number of properties of the value is greater than or equal to than minProperties"
, [here|
{
"mobj": {
"type": "object",
"minProperties": 2,
"properties": {
"mint": {"type": "integer"},
"mstr": {"type": "string"},
"mbol": {"type": "boolean"}
}
}
}
|]
, [here| {"mobj": {"mint": 1, "mstr": "text"}} |]
, Right []
)
, ( "returns Right [error] if the number of properties of the value is less than minProperties"
, [here|
{
"mobj": {
"type": "object",
"minProperties": 2,
"properties": {
"mint": {"type": "integer"},
"mstr": {"type": "string"},
"mbol": {"type": "boolean"}
}
}
}
|]
, [here| {"mobj": {"mint": 1}} |]
, Right ["object size is too small (total number of properties should be >=2)"]
)
, ( "returns Right [] if the number of properties of the value is less than or equal to maxProperties"
, [here|
{
"mobj": {
"type": "object",
"maxProperties": 2,
"properties": {
"mint": {"type": "integer"},
"mstr": {"type": "string"},
"mbol": {"type": "boolean"}
}
}
}
|]
, [here| {"mobj": {"mint": 1, "mstr": "text"}} |]
, Right []
)
, ( "returns Right [error] if the number of properties of the value is greater than maxProperties"
, [here|
{
"mobj": {
"type": "object",
"maxProperties": 2,
"properties": {
"mint": {"type": "integer"},
"mstr": {"type": "string"},
"mbol": {"type": "boolean"}
}
}
}
|]
, [here| {"mobj": {"mint": 1, "mstr": "text", "mbol": true}} |]
, Right ["object size exceeds maximum (total number of properties should be <=2)"]
)
]
forM_ tests $ \(description, schemaJson, bodyJson, result) ->
it description $ do
let apiJson = makeOpenApiJson schemaJson
validateRequestBody POST "/examples" apiJson bodyJson `shouldBe` result
context "enum" $ do
let
tests =
[ ( "returns Right [] if the value is one of the enumerated values"
, [here| {"mint": {"type": "integer", "enum": [10, 20, 30]}} |]
, [here| {"mint": 10} |]
, Right []
)
, ( "returns Right [error] if the value is not one of the enumerated values"
, [here| {"mint": {"type": "integer", "enum": [20, 30]}} |]
, [here| {"mint": 10} |]
, Right ["expected one of \"[20,30]\" but got Number 10.0"]
)
]
forM_ tests $ \(description, schemaJson, bodyJson, result) ->
it description $ do
let apiJson = makeOpenApiJson schemaJson
validateRequestBody POST "/examples" apiJson bodyJson `shouldBe` result
context "description" $ do
let
tests =
[ ( "returns Right [] if the description is defined"
, [here| {"mint": {"type": "integer", "description": "Can be any integer value"}} |]
, [here| {"mint": 1} |]
, Right []
)
]
forM_ tests $ \(description, schemaJson, bodyJson, result) ->
it description $ do
let apiJson = makeOpenApiJson schemaJson
validateRequestBody POST "/examples" apiJson bodyJson `shouldBe` result
context "nullable" $ do
let
tests =
[ ( "returns Right [] if nullable is true and the value is null"
, [here| {"mint": {"type": "integer", "nullable": true}} |]
, [here| {"mint": null} |]
, Right []
)
]
forM_ tests $ \(description, schemaJson, bodyJson, result) ->
it description $ do
let apiJson = makeOpenApiJson schemaJson
validateRequestBody POST "/examples" apiJson bodyJson `shouldBe` result
context "nullable (pending)" $ do
let
tests =
[ ( "returns Right [error] if nullable is false and the value is null"
, [here| {"mint": {"type": "integer", "nullable": false}} |]
, [here| {"mint": null} |]
-- , Right ["expected JSON value of type not null"]
, "Keyword `nullable` is not supported"
)
, ( "returns Right [error] if nullable is not specified and the value is null"
, [here| {"mint": {"type": "integer"}} |]
, [here| {"mint": null} |]
-- , Right ["expected JSON value of type not null"]
, "Keyword `nullable` is not supported"
)
] :: [(String, L.ByteString, L.ByteString, String)]
-- forM_ tests $ \(description, schemaJson, bodyJson, result) ->
-- it description $ do
-- let apiJson = makeOpenApiJson schemaJson
-- validateRequestBody POST "/examples" apiJson bodyJson `shouldNotBe` result
forM_ tests $ \(description, _, _, reason) ->
it description $
pendingWith reason
describe "validateResponseBody" $ do
let makeOpenApiJson schemaJson = [here|
{
"openapi": "3.0.0",
"info": { "title": "info example", "version": "1.0.0" },
"paths": {
"/examples": {
"get": {
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Example"
}
}
}
}
}
}
}
}
},
"components": {
"schemas": {
"Example": {
"type": "object",
"properties": |] <> schemaJson <> [here|
}
}
}
}
|]
context "type: integer" $ do
let
tests =
[ ( "returns Right [] if the type of the value is valid"
, [here| {"mint": {"type": "integer"}} |]
, [here| [{"mint": 1}, {"mint": 2}] |]
, Right []
)
, ( "returns Right [] if the type of the value is invalid"
, [here| {"mint": {"type": "integer"}} |]
, [here| [{"mint": 1}, {"mint": "two"}] |]
, Right ["expected JSON value of type integer"]
)
]
forM_ tests $ \(description, schemaJson, bodyJson, result) ->
it description $ do
let apiJson = makeOpenApiJson schemaJson
validateResponseBody GET "/examples" 200 apiJson bodyJson `shouldBe` result