545 lines
15 KiB
JavaScript
545 lines
15 KiB
JavaScript
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)
|
|
})
|
|
})
|
|
})
|
|
})
|