699 lines
20 KiB
JavaScript
699 lines
20 KiB
JavaScript
import expect from "expect"
|
|
import validateHelper, { expectNoErrorsOrWarnings } from "../validate-helper.js"
|
|
|
|
describe("validation plugin - semantic - 2and3 refs", function() {
|
|
this.timeout(10 * 1000)
|
|
describe("Ref siblings", () => {
|
|
|
|
it("should return a warning when another property is a sibling of a $ref in OpenAPI 3", () => {
|
|
const spec = {
|
|
openapi: "3.0.0",
|
|
paths: {
|
|
"/CoolPath": {
|
|
get: {
|
|
$ref: "#/components/schemas/abc",
|
|
description: "My very cool get"
|
|
}
|
|
}
|
|
},
|
|
components: {
|
|
schemas: {
|
|
abc: {}
|
|
}
|
|
}
|
|
}
|
|
|
|
return validateHelper(spec)
|
|
.then(system => {
|
|
const allErrors = system.errSelectors.allErrors().toJS()
|
|
expect(allErrors.length).toEqual(1)
|
|
const firstError = allErrors[0]
|
|
expect(firstError.message).toMatch("Sibling values alongside $refs are ignored.\nTo add properties to a $ref, wrap the $ref into allOf, or move the extra properties into the referenced definition (if applicable).")
|
|
expect(firstError.level).toEqual("warning")
|
|
expect(firstError.path).toEqual(["paths", "/CoolPath", "get", "description"])
|
|
})
|
|
})
|
|
it("should return a warning when another property is a sibling of a $ref in Swagger 2", () => {
|
|
const spec = {
|
|
swagger: "2.0",
|
|
paths: {
|
|
"/CoolPath": {
|
|
get: {
|
|
$ref: "#/definitions/abc",
|
|
description: "My very cool get"
|
|
}
|
|
}
|
|
},
|
|
definitions: {
|
|
abc: {}
|
|
}
|
|
}
|
|
|
|
return validateHelper(spec)
|
|
.then(system => {
|
|
const allErrors = system.errSelectors.allErrors().toJS()
|
|
expect(allErrors.length).toEqual(1)
|
|
const firstError = allErrors[0]
|
|
expect(firstError.message).toMatch("Sibling values alongside $refs are ignored.\nTo add properties to a $ref, wrap the $ref into allOf, or move the extra properties into the referenced definition (if applicable).")
|
|
expect(firstError.level).toEqual("warning")
|
|
expect(firstError.path).toEqual(["paths", "/CoolPath", "get", "description"])
|
|
})
|
|
})
|
|
|
|
it("should return no warnings when a $ref has no siblings in OpenAPI 3", () => {
|
|
const spec = {
|
|
openapi: "3.0.0",
|
|
paths: {
|
|
"/CoolPath": {
|
|
get: {
|
|
$ref: "#/components/schemas/abc"
|
|
}
|
|
}
|
|
},
|
|
components: {
|
|
schemas: {
|
|
abc: {}
|
|
}
|
|
}
|
|
}
|
|
|
|
return expectNoErrorsOrWarnings(spec)
|
|
})
|
|
it("should return no warnings when a $ref has no siblings in Swagger 2", () => {
|
|
const spec = {
|
|
swagger: "2.0",
|
|
paths: {
|
|
"/CoolPath": {
|
|
get: {
|
|
$ref: "#/definitions/abc"
|
|
}
|
|
}
|
|
},
|
|
definitions: {
|
|
abc: {}
|
|
}
|
|
}
|
|
|
|
return expectNoErrorsOrWarnings(spec)
|
|
})
|
|
|
|
it("should return no warnings when a path item $ref has siblings in OpenAPI 3", () => {
|
|
const spec = {
|
|
openapi: "3.0.0",
|
|
paths: {
|
|
$ref: "#/components/schemas/abc",
|
|
"/CoolPath": {
|
|
get: {
|
|
$ref: "#/components/schemas/abc"
|
|
}
|
|
}
|
|
},
|
|
components: {
|
|
schemas: {
|
|
abc: {}
|
|
}
|
|
}
|
|
}
|
|
|
|
return expectNoErrorsOrWarnings(spec)
|
|
})
|
|
it("should return no warnings when a path item $ref has siblings in Swagger 2", () => {
|
|
const spec = {
|
|
swagger: "2.0",
|
|
paths: {
|
|
$ref: "#/definitions/abc",
|
|
"/CoolPath": {
|
|
get: {
|
|
$ref: "#/definitions/abc"
|
|
}
|
|
}
|
|
},
|
|
definitions: {
|
|
abc: {}
|
|
}
|
|
}
|
|
|
|
return expectNoErrorsOrWarnings(spec)
|
|
})
|
|
|
|
})
|
|
describe("Unused definitions", () => {
|
|
it("should return a warning when a definition is declared but not used in OpenAPI 3", () => {
|
|
const spec = {
|
|
openapi: "3.0.0",
|
|
components: {
|
|
schemas: {
|
|
"x-Foo": {
|
|
type: "object"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return validateHelper(spec)
|
|
.then(system => {
|
|
const allErrors = system.errSelectors.allErrors().toJS()
|
|
expect(allErrors.length).toEqual(1)
|
|
const firstError = allErrors[0]
|
|
expect(firstError.message).toMatch("Definition was declared but never used in document")
|
|
expect(firstError.level).toEqual("warning")
|
|
expect(firstError.path).toEqual(["components", "schemas", "x-Foo"])
|
|
})
|
|
})
|
|
|
|
it("should not return a warning when a definition with special characters is properly referenced in OpenAPI 3", () => {
|
|
const spec = {
|
|
openapi: "3.0.0",
|
|
paths: {
|
|
"/CoolPath": {
|
|
get: {
|
|
responses: {
|
|
200: {
|
|
content: {
|
|
"application/json": {
|
|
schema: {
|
|
$ref: "#/components/schemas/x~1Foo"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
400: {
|
|
content: {
|
|
"application/json": {
|
|
schema: {
|
|
$ref: "#/components/schemas/x~0Bar"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
components: {
|
|
schemas: {
|
|
"x/Foo": {
|
|
type: "object"
|
|
},
|
|
"x~Bar": {
|
|
type: "object"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return validateHelper(spec)
|
|
.then(system => {
|
|
// We want warnings only, without errors about invalid component names
|
|
const allWarnings = system.errSelectors.allErrors().toJS()
|
|
.filter(err => err.level === "warning")
|
|
expect(allWarnings.length).toEqual(0)
|
|
})
|
|
})
|
|
|
|
it("should return a warning when a definition is declared but not used in Swagger 2", () => {
|
|
const spec = {
|
|
swagger: "2.0",
|
|
paths: {
|
|
"/CoolPath": {}
|
|
},
|
|
definitions: {
|
|
abc: {
|
|
type: "string"
|
|
}
|
|
}
|
|
}
|
|
|
|
return validateHelper(spec)
|
|
.then(system => {
|
|
const allErrors = system.errSelectors.allErrors().toJS()
|
|
expect(allErrors.length).toEqual(1)
|
|
const firstError = allErrors[0]
|
|
expect(firstError.message).toMatch("Definition was declared but never used in document")
|
|
expect(firstError.level).toEqual("warning")
|
|
expect(firstError.path).toEqual(["definitions", "abc"])
|
|
})
|
|
})
|
|
|
|
it("should not return a warning when a definition with special character is declared and used in Swagger 2", () => {
|
|
const spec = {
|
|
swagger: "2.0",
|
|
paths: {
|
|
"/CoolPath": {
|
|
get: {
|
|
responses: {
|
|
200: {
|
|
schema: {
|
|
$ref: "#/definitions/x~1Foo"
|
|
}
|
|
},
|
|
400: {
|
|
schema: {
|
|
$ref: "#/definitions/x~0Bar"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
definitions: {
|
|
"x/Foo": {
|
|
type: "object"
|
|
},
|
|
"x~Bar": {
|
|
type: "object"
|
|
}
|
|
}
|
|
}
|
|
|
|
return validateHelper(spec)
|
|
.then(system => {
|
|
const allErrors = system.errSelectors.allErrors().toJS()
|
|
expect(allErrors.length).toEqual(0)
|
|
})
|
|
})
|
|
|
|
})
|
|
describe("Malformed $ref values", () => {
|
|
it("should return an error when a JSON pointer lacks a leading `#/` in Swagger 2", () => {
|
|
const spec = {
|
|
swagger: "2.0",
|
|
paths: {
|
|
"/CoolPath": {
|
|
$ref: "#myObj/abc"
|
|
}
|
|
},
|
|
myObj: {
|
|
abc: {
|
|
type: "string"
|
|
}
|
|
}
|
|
}
|
|
|
|
return validateHelper(spec)
|
|
.then(system => {
|
|
const allErrors = system.errSelectors.allErrors().toJS()
|
|
expect(allErrors.length).toEqual(1)
|
|
const firstError = allErrors[0]
|
|
expect(firstError.message).toMatch("$ref paths must begin with `#/`")
|
|
expect(firstError.level).toEqual("error")
|
|
expect(firstError.path).toEqual(["paths", "/CoolPath", "$ref"])
|
|
})
|
|
})
|
|
it("should return an error when a JSON pointer lacks a leading `#/` in OpenAPI 3", () => {
|
|
const spec = {
|
|
openapi: "3.0.0",
|
|
paths: {
|
|
"/CoolPath": {
|
|
$ref: "#myObj/abc"
|
|
}
|
|
},
|
|
myObj: {
|
|
abc: {
|
|
type: "string"
|
|
}
|
|
}
|
|
}
|
|
|
|
return validateHelper(spec)
|
|
.then(system => {
|
|
const allErrors = system.errSelectors.allErrors().toJS()
|
|
expect(allErrors.length).toEqual(1)
|
|
const firstError = allErrors[0]
|
|
expect(firstError.message).toMatch("$ref paths must begin with `#/`")
|
|
expect(firstError.level).toEqual("error")
|
|
expect(firstError.path).toEqual(["paths", "/CoolPath", "$ref"])
|
|
})
|
|
})
|
|
it("should return no errors when a JSON pointer is a well-formed remote reference in Swagger 2", () => {
|
|
const spec = {
|
|
swagger: "2.0",
|
|
paths: {
|
|
"/CoolPath": {
|
|
$ref: "http://google.com#/myObj/abc"
|
|
},
|
|
},
|
|
myObj: {
|
|
abc: {
|
|
type: "string",
|
|
properties: {
|
|
$ref: "http://google.com/MyRegularURLReference"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return validateHelper(spec)
|
|
.then(system => {
|
|
const allSemanticErrors = system.errSelectors.allErrors().toJS()
|
|
.filter(err => err.source !== "resolver")
|
|
expect(allSemanticErrors).toEqual([])
|
|
})
|
|
})
|
|
it("should return no errors when a JSON pointer is a well-formed remote reference in OpenAPI 3", () => {
|
|
const spec = {
|
|
openapi: "3.0.0",
|
|
paths: {
|
|
"/CoolPath": {
|
|
$ref: "http://google.com#/myObj/abc"
|
|
},
|
|
},
|
|
myObj: {
|
|
abc: {
|
|
type: "string",
|
|
properties: {
|
|
$ref: "http://google.com/MyRegularURLReference"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return validateHelper(spec)
|
|
.then(system => {
|
|
const allSemanticErrors = system.errSelectors.allErrors().toJS()
|
|
.filter(err => err.source !== "resolver")
|
|
expect(allSemanticErrors).toEqual([])
|
|
})
|
|
})
|
|
it("should return an error when a JSON pointer uses incorrect percent-encoding in Swagger 2", () => {
|
|
const spec = {
|
|
"swagger": "2.0",
|
|
"paths": {
|
|
"/foo": {
|
|
"get": {
|
|
"responses": {
|
|
"200": {
|
|
"description": "Success",
|
|
"schema": {
|
|
"$ref": "#/definitions/foo bar"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"definitions": {
|
|
"foo bar": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
}
|
|
|
|
return validateHelper(spec)
|
|
.then(system => {
|
|
const allSemanticErrors = system.errSelectors.allErrors().toJS()
|
|
.filter(err => err.source !== "resolver")
|
|
expect(allSemanticErrors[0]).toInclude({
|
|
level: "warning",
|
|
message: "Definition was declared but never used in document",
|
|
path: ["definitions", "foo bar"]
|
|
})
|
|
expect(allSemanticErrors.length).toEqual(2)
|
|
expect(allSemanticErrors[1]).toInclude({
|
|
level: "error",
|
|
message: "$ref values must be RFC3986-compliant percent-encoded URIs",
|
|
path: ["paths", "/foo", "get", "responses", "200", "schema", "$ref"]
|
|
})
|
|
})
|
|
})
|
|
it("should return an error when a JSON pointer uses incorrect percent-encoding in OpenAPI 3", () => {
|
|
const spec = {
|
|
"openapi": "3.0.0",
|
|
"paths": {
|
|
"/foo": {
|
|
"get": {
|
|
"responses": {
|
|
"200": {
|
|
"description": "Success",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/foo bar"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
components: {
|
|
schemas: {
|
|
"foo bar": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return validateHelper(spec)
|
|
.then(system => {
|
|
const allSemanticErrors = system.errSelectors.allErrors().toJS()
|
|
.filter(err => err.source !== "resolver")
|
|
expect(allSemanticErrors.length).toEqual(3)
|
|
expect(allSemanticErrors[0]).toInclude({
|
|
level: "warning",
|
|
message: "Definition was declared but never used in document",
|
|
path: ["components", "schemas", "foo bar"]
|
|
})
|
|
expect(allSemanticErrors[1]).toInclude({
|
|
level: "error",
|
|
message: "$ref values must be RFC3986-compliant percent-encoded URIs",
|
|
path: ["paths", "/foo", "get", "responses", "200", "content", "application/json", "schema", "$ref"]
|
|
}),
|
|
expect(allSemanticErrors[2]).toInclude({
|
|
level: "error",
|
|
message: "Component names can only contain the characters A-Z a-z 0-9 - . _",
|
|
path: ["components", "schemas", "foo bar"]
|
|
})
|
|
})
|
|
})
|
|
it("should return no errors when a JSON pointer uses correct percent-encoding in Swagger 2", () => {
|
|
const spec = {
|
|
"swagger": "2.0",
|
|
"paths": {
|
|
"/foo": {
|
|
"get": {
|
|
"responses": {
|
|
"200": {
|
|
"description": "Success",
|
|
"schema": {
|
|
"$ref": "#/definitions/foo%20bar"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"definitions": {
|
|
"foo bar": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
}
|
|
|
|
return validateHelper(spec)
|
|
.then(system => {
|
|
const allSemanticErrors = system.errSelectors.allErrors().toJS()
|
|
.filter(err => err.source !== "resolver")
|
|
expect(allSemanticErrors).toEqual([])
|
|
})
|
|
})
|
|
it("should return no errors when a JSON pointer uses correct percent-encoding in OpenAPI 3", () => {
|
|
const spec = {
|
|
"openapi": "3.0.0",
|
|
"paths": {
|
|
"/foo": {
|
|
"get": {
|
|
"responses": {
|
|
"200": {
|
|
"description": "Success",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/foo%20bar"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
components: {
|
|
schemas: {
|
|
"foo bar": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return validateHelper(spec)
|
|
.then(system => {
|
|
const allSemanticErrors = system.errSelectors.allErrors().toJS()
|
|
.filter(err => err.source !== "resolver")
|
|
expect(allSemanticErrors.length).toEqual(1)
|
|
expect(allSemanticErrors[0]).toInclude({
|
|
level: "error",
|
|
message: "Component names can only contain the characters A-Z a-z 0-9 - . _",
|
|
path: ["components", "schemas", "foo bar"]
|
|
})
|
|
})
|
|
})
|
|
|
|
})
|
|
describe("Nonexistent $ref pointers", () => {
|
|
it("should return an error when a local JSON pointer does not exist in Swagger 2", () => {
|
|
const spec = {
|
|
swagger: "2.0",
|
|
paths: {
|
|
"/CoolPath": {
|
|
$ref: "#/myObj/DoesNotExist"
|
|
}
|
|
},
|
|
myObj: {
|
|
abc: {
|
|
type: "string",
|
|
properties: {
|
|
$ref: "http://google.com/MyRegularURLReference"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return validateHelper(spec)
|
|
.then(system => {
|
|
const allErrors = system.errSelectors.allErrors().toJS()
|
|
.filter(err => err.source !== "resolver")
|
|
expect(allErrors.length).toEqual(1)
|
|
const firstError = allErrors[0]
|
|
expect(firstError.message).toMatch("$refs must reference a valid location in the document")
|
|
expect(firstError.level).toEqual("error")
|
|
expect(firstError.path).toEqual(["paths", "/CoolPath", "$ref"])
|
|
})
|
|
})
|
|
it("should return an error when a local JSON pointer does not exist in OpenAPI 3", () => {
|
|
const spec = {
|
|
openapi: "3.0.0",
|
|
paths: {
|
|
"/CoolPath": {
|
|
$ref: "#/myObj/DoesNotExist"
|
|
}
|
|
},
|
|
myObj: {
|
|
abc: {
|
|
type: "string",
|
|
properties: {
|
|
$ref: "http://google.com/MyRegularURLReference"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return validateHelper(spec)
|
|
.then(system => {
|
|
const allErrors = system.errSelectors.allErrors().toJS()
|
|
.filter(err => err.source !== "resolver")
|
|
.filter((el, i, arr) => arr.indexOf(el) === i)
|
|
expect(allErrors.length).toEqual(1)
|
|
const firstError = allErrors[0]
|
|
expect(firstError.message).toMatch("$refs must reference a valid location in the document")
|
|
expect(firstError.level).toEqual("error")
|
|
expect(firstError.path).toEqual(["paths", "/CoolPath", "$ref"])
|
|
})
|
|
})
|
|
it("should return no errors when a JSON pointer exists in Swagger 2", () => {
|
|
const spec = {
|
|
swagger: "2.0",
|
|
paths: {
|
|
"/CoolPath": {
|
|
$ref: "#/myObj/abc"
|
|
},
|
|
},
|
|
myObj: {
|
|
abc: {
|
|
type: "string"
|
|
}
|
|
}
|
|
}
|
|
|
|
return validateHelper(spec)
|
|
.then(system => {
|
|
const allSemanticErrors = system.errSelectors.allErrors().toJS()
|
|
.filter(err => err.source !== "resolver")
|
|
expect(allSemanticErrors).toEqual([])
|
|
})
|
|
})
|
|
it("should return no errors when a JSON pointer exists in OpenAPI 3", () => {
|
|
const spec = {
|
|
openapi: "3.0.0",
|
|
paths: {
|
|
"/CoolPath": {
|
|
$ref: "#/myObj/abc"
|
|
},
|
|
},
|
|
myObj: {
|
|
abc: {
|
|
type: "string",
|
|
properties: {
|
|
$ref: "http://google.com/MyRegularURLReference"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return validateHelper(spec)
|
|
.then(system => {
|
|
const allSemanticErrors = system.errSelectors.allErrors().toJS()
|
|
.filter(err => err.source !== "resolver")
|
|
expect(allSemanticErrors).toEqual([])
|
|
})
|
|
})
|
|
it("should return no errors when a JSON pointer is a remote reference in Swagger 2", () => {
|
|
const spec = {
|
|
swagger: "2.0",
|
|
paths: {
|
|
"/CoolPath": {
|
|
$ref: "http://google.com#/myObj/abc"
|
|
},
|
|
},
|
|
myObj: {
|
|
abc: {
|
|
type: "string"
|
|
}
|
|
}
|
|
}
|
|
|
|
return validateHelper(spec)
|
|
.then(system => {
|
|
const allSemanticErrors = system.errSelectors.allErrors().toJS()
|
|
.filter(err => err.source !== "resolver")
|
|
expect(allSemanticErrors).toEqual([])
|
|
})
|
|
})
|
|
it("should return no errors when a JSON pointer is a remote reference in OpenAPI 3", () => {
|
|
const spec = {
|
|
openapi: "3.0.0",
|
|
paths: {
|
|
"/CoolPath": {
|
|
$ref: "http://google.com#/myObj/abc"
|
|
},
|
|
},
|
|
myObj: {
|
|
abc: {
|
|
type: "string"
|
|
}
|
|
}
|
|
}
|
|
|
|
return validateHelper(spec)
|
|
.then(system => {
|
|
const allSemanticErrors = system.errSelectors.allErrors().toJS()
|
|
.filter(err => err.source !== "resolver")
|
|
expect(allSemanticErrors).toEqual([])
|
|
})
|
|
})
|
|
})
|
|
})
|