Ekapp/swagger/test/unit/plugins/validate-semantic/schema.js

545 lines
15 KiB
JavaScript
Raw Normal View History

2020-06-10 06:25:21 +00:00
import expect from "expect"
import validateHelper, {
expectNoErrors,
expectNoErrorsOrWarnings
} from "./validate-helper.js"
describe("validation plugin - semantic - schema", function() {
this.timeout(10 * 1000)
it("should return an error when a definition's property is readOnly and required by the schema", () => {
const spec = {
swagger: "2.0",
blah: {
$ref: "#/definitions/CoolModel"
},
definitions: {
CoolModel: {
required: ["BadProperty"],
properties: {
BadProperty: {
readOnly: true
}
}
}
}
}
return validateHelper(spec)
.then( system => {
const allErrors = system.errSelectors.allErrors().toJS()
expect(allErrors.length).toEqual(1)
const firstError = allErrors[0]
expect(firstError.message).toEqual("Read only properties cannot be marked as required by a schema.")
expect(firstError.path).toEqual(["definitions", "CoolModel", "required", "0"])
})
})
it("should not return an error when a definition's property is not readOnly and required by the schema", () => {
const spec = {
swagger: "2.0",
definitions: {
CoolModel: {
required: ["BadProperty"],
properties: {
BadProperty: {
type: "string"
}
}
}
}
}
return validateHelper(spec)
.then( system => {
const allErrors = system.errSelectors
.allErrors()
.filter(a => a.get("level") === "error") // We have an incidental "warning"
.toJS()
expect(allErrors).toEqual(0)
})
})
it("should return an error when a response schema's property is readOnly and required by the schema", () => {
const spec = {
swagger: "2.0",
paths: {
"/CoolPath": {
get: {
responses: {
200: {
schema: {
required: ["BadProperty"],
properties: {
BadProperty: {
readOnly: true
}
}
}
}
}
}
}
}
}
return validateHelper(spec)
.then( system => {
const allErrors = system.errSelectors
.allErrors()
.toJS()
expect(allErrors.length).toEqual(1)
const firstError = allErrors[0]
expect(firstError.message).toEqual("Read only properties cannot be marked as required by a schema.")
expect(firstError.path).toEqual(["paths", "/CoolPath", "get", "responses", "200", "schema", "required", "0"])
})
})
it("should not return an error when a response schema's property is not readOnly and required by the schema", () => {
const spec = {
swagger: "2.0",
paths: {
"/CoolPath": {
get: {
responses: {
200: {
schema: {
required: ["BadProperty"],
properties: {
BadProperty: {
type: "string"
}
}
}
}
}
}
}
}
}
return expectNoErrorsOrWarnings(spec)
})
it("should return an error when a parameter schema's property is readOnly and required by the schema", () => {
const spec = {
swagger: "2.0",
paths: {
"/CoolPath": {
get: {
parameters: [{
name: "BadParameter",
in: "body",
schema: {
required: ["BadProperty"],
properties: {
BadProperty: {
readOnly: true
}
}
}
}]
}
}
}
}
return validateHelper(spec)
.then( system => {
const allErrors = system.errSelectors
.allErrors()
.toJS()
expect(allErrors.length).toEqual(1)
const firstError = allErrors[0]
expect(firstError.message).toEqual("Read only properties cannot be marked as required by a schema.")
expect(firstError.path).toEqual(["paths", "/CoolPath", "get", "parameters", "0", "schema", "required", "0"])
})
})
it("should not return an error when a parameter schema's property is not readOnly and required by the schema", () => {
const spec = {
swagger: "2.0",
paths: {
"/CoolPath": {
get: {
parameters: [{
name: "BadParameter",
in: "body",
schema: {
required: ["BadProperty"],
properties: {
BadProperty: {
type: "string"
}
}
}
}]
}
}
}
}
return expectNoErrorsOrWarnings(spec)
})
describe("Type key", () => {
it("should return an error when \"type\" is an array", () => {
const spec = {
swagger: "2.0",
paths: {
"/CoolPath": {
get: {
responses: {
"200": {
schema: {
type: ["number", "string"]
}
}
}
}
}
}
}
return validateHelper(spec)
.then( system => {
const allErrors = system.errSelectors
.allErrors()
.toJS()
expect(allErrors.length).toEqual(1)
const firstError = allErrors[0]
expect(firstError.message).toEqual(`Schema "type" key must be a string`)
expect(firstError.path).toEqual(["paths", "/CoolPath", "get", "responses", "200", "schema", "type"])
})
})
it("should not return an error when \"type\" is a property name", () => {
const spec = {
"swagger": "2.0",
"definitions": {
"ApiResponse": {
"type": "object",
"properties": {
"code": {
"type": "integer",
"format": "int32"
},
"type": {
"type": "string"
},
"message": {
"type": "string"
}
}
}
}
}
return validateHelper(spec)
.then(system => {
let allErrors = system.errSelectors.allErrors().toJS()
allErrors = allErrors.filter(a => a.level != "warning") // ignore warnings
expect(allErrors.length).toEqual(0)
})
})
it("should not return an error when \"type\" is a property name inside additionalProperties", () => {
const spec = {
"swagger": "2.0",
"definitions": {
"ApiResponse": {
"type": "object",
"additionalProperties": {
"type": "object",
"properties": {
"code": {
"type": "integer",
"format": "int32"
},
"type": {
"type": "string"
},
"message": {
"type": "string"
}
}
}
}
}
}
return validateHelper(spec)
.then(system => {
let allErrors = system.errSelectors.allErrors().toJS()
allErrors = allErrors.filter(a => a.level != "warning") // ignore warnings
expect(allErrors.length).toEqual(0)
})
})
it("should not return an error when \"type\" is a model name", () => {
const spec = {
"swagger": "2.0",
"definitions": {
"type": {
"type": "object",
"properties": {
"code": {
"type": "integer",
"format": "int32"
},
"message": {
"type": "string"
}
}
}
}
}
return expectNoErrors(spec)
})
})
describe(`"type: array" requires "items"`, () => {
describe("header objects", function() {
// It takes a while to start up swagger-ui, for some reason
it("should return an error when an array header object omits an `items` property", () => {
// Given
const spec = {
"swagger": "2.0",
"info": {
"version": "1.0.0",
"title": "Swagger Petstore"
},
"paths": {
"/pets": {
"get": {
"description": "Returns all pets from the system that the user has access to",
"responses": {
"200": {
"description": "pet response",
"headers": {
"X-MyHeader": {
"type": "array"
}
}
},
"default": {
"description": "unexpected error"
}
}
}
}
}
}
// When
return validateHelper(spec)
.then(system => {
// Then
expect(system.errSelectors.allErrors().count()).toEqual(1)
const firstError = system.errSelectors.allErrors().first().toJS()
expect(firstError.message).toMatch(/.*type.*array.*require.*items/)
expect(firstError.path).toEqual(["paths", "/pets", "get", "responses", "200", "headers", "X-MyHeader"])
})
})
it("should not return an error when an array header object has an `items` property", () => {
const spec = {
"swagger": "2.0",
"info": {
"version": "1.0.0",
"title": "Swagger Petstore"
},
"paths": {
"/pets": {
"get": {
"description": "Returns all pets from the system that the user has access to",
"responses": {
"200": {
"description": "pet response",
"headers": {
"X-MyHeader": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"default": {
"description": "unexpected error"
}
}
}
}
}
}
return expectNoErrorsOrWarnings(spec)
})
})
describe("definitions", function() {
// It takes a while to start up swagger-ui, for some reason
it("should return an error when an array definition omits an `items` property", () => {
// Given
const spec = {
swagger: "2.0",
paths: {
$ref: "#/definitions/asdf"
},
"definitions": {
"asdf": {
type: "array"
}
}
}
// When
return validateHelper(spec)
.then(system => {
// Then
expect(system.errSelectors.allErrors().count()).toEqual(1)
const firstError = system.errSelectors.allErrors().first().toJS()
expect(firstError.message).toMatch(/.*type.*array.*require.*items/)
expect(firstError.path).toEqual(["definitions", "asdf"])
})
})
it("should not return an error when an array definition has an `items` property", () => {
const spec = {
"swagger": "2.0",
"info": {
"version": "1.0.0",
"title": "Swagger Petstore"
},
"paths": {
"/pets": {
"get": {
"description": "Returns all pets from the system that the user has access to",
"responses": {
"200": {
"description": "pet response",
"headers": {
"X-MyHeader": {
$ref: "#/definitions/array"
}
}
},
"default": {
"description": "unexpected error"
}
}
}
}
},
"definitions": {
"array": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
return expectNoErrorsOrWarnings(spec)
})
})
describe("'pattern' Z anchors", function() {
it("should return an error when a schema has a Z anchor in its pattern", () => {
// Given
const spec = {
swagger: "2.0",
paths: {
$ref: "#/definitions/asdf"
},
"definitions": {
"asdf": {
type: "string",
pattern: "^[-a-zA-Z0-9_]+\\Z"
}
}
}
// When
return validateHelper(spec)
.then(system => {
// Then
expect(system.errSelectors.allErrors().count()).toEqual(1)
const firstError = system.errSelectors.allErrors().first().toJS()
expect(firstError.message).toEqual(`"\\Z" anchors are not allowed in regular expression patterns`)
expect(firstError.path).toEqual(["definitions", "asdf", "pattern"])
})
})
it("should return an error when a subschema has a Z anchor in its pattern", () => {
// Given
const spec = {
swagger: "2.0",
paths: {
"/": {
get: {
responses: {
"200": {
schema: {
type: "object",
properties: {
slug: {
type: "string",
pattern: "^[-a-zA-Z0-9_]+\\Z"
}
}
}
}
}
}
}
},
}
// When
return validateHelper(spec)
.then(system => {
// Then
expect(system.errSelectors.allErrors().count()).toEqual(1)
const firstError = system.errSelectors.allErrors().first().toJS()
expect(firstError.message).toEqual(`"\\Z" anchors are not allowed in regular expression patterns`)
expect(firstError.path).toEqual(["paths", "/", "get", "responses", "200", "schema", "properties", "slug", "pattern"])
})
})
it("should not return an error when a regex pattern doesn't use a Z anchor", () => {
const spec = {
swagger: "2.0",
paths: {
$ref: "#/definitions/asdf"
},
"definitions": {
"asdf": {
type: "string",
pattern: "^[-a-zA-Z0-9_]+"
}
}
}
return expectNoErrorsOrWarnings(spec)
})
})
})
})