Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(validation): enum values should conform to their schema's type #2079

Merged
merged 7 commits into from
Jan 11, 2020
39 changes: 39 additions & 0 deletions src/plugins/validate-semantic/validators/2and3/schemas.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,42 @@ export const validate2And3TypeArrayRequiresItems = () => (system) => {
}, [])
})
}

export const validate2And3TypesInDefaultValuesMatchesWithEnum = () => (system) => {
return system.validateSelectors
.allSchemas()
.then(nodes => {
return nodes.reduce((acc, node) => {
const schemaObj = node.node
const { type } = schemaObj || {}
const enumeration = schemaObj.enum
if (enumeration !== null && typeof enumeration !== "undefined") {
var enumIndex = 0
enumeration.forEach((element, index) => {
var isValidFormat = true
if (type === "array" && (!Array.isArray(element) || element === null)) {
isValidFormat = false
enumIndex = index
} else if ((type === "number" || type === "string" || type === "boolean") && !(typeof element === type)) {
isValidFormat = false
enumIndex = index
} else if (type === "integer" && !Number.isInteger(element)) {
isValidFormat = false
enumIndex = index
} else if (type === "object" && ((element === null) || !(typeof element === type))) {
isValidFormat = false
enumIndex = index
}
if (!isValidFormat) {
acc.push({
message: "enum value should conform to its schema's `type`",
path: [...node.path, "enum", enumIndex],
level: "warning",
})
}
})
}
return acc
}, [])
})
}
344 changes: 344 additions & 0 deletions test/unit/plugins/validate-semantic/2and3/schemas.js
Original file line number Diff line number Diff line change
Expand Up @@ -169,3 +169,347 @@ describe(`validation plugin - semantic - 2and3 schemas`, () => {
})
})
})

describe(`values in Enum must be instance of the defined type`, () => {
// Numbers tests
it("should return an error for a text value in a enum number type in OpenApi 3", () => {
const spec = {
openapi: "3.0.0",
"paths": {
"/pets": {
"get": {
"parameters": [
{
name: "number",
in: "query",
schema: {
type: "number",
enum: [1, "text", 3]
}
},
]
}
}
}
}

return validateHelper(spec)
.then(system => {
const allErrors = system.errSelectors.allErrors().toJS()
expect(allErrors.length).toEqual(1)
expect(allErrors[0]).toInclude({
level: "warning",
message: "enum value should conform to its schema's `type`",
path: ["paths", "/pets", "get", "parameters", "0", "schema", "enum", 1]
})
})
})

it("should return an error for a number value inside quotes in a enum number type in OpenApi 3", () => {
const spec = {
openapi: "3.0.0",
"paths": {
"/pets": {
"get": {
"parameters": [
{
name: "number",
in: "query",
schema: {
type: "number",
enum: [1, 2, "3"]
}
},
]
}
}
}
}

return validateHelper(spec)
.then(system => {
const allErrors = system.errSelectors.allErrors().toJS()
expect(allErrors.length).toEqual(1)
expect(allErrors[0]).toInclude({
level: "warning",
message: "enum value should conform to its schema's `type`",
path: ["paths", "/pets", "get", "parameters", "0", "schema", "enum", 2]
})
})
})

it("should not return an error when all items are number in a enum number type in OpenApi 3", () => {
const spec = {
openapi: "3.0.0",
"paths": {
"/pets": {
"get": {
"parameters": [
{
name: "number",
in: "query",
schema: {
type: "number",
enum: [1, 2, 3]
}
},
]
}
}
}
}

return validateHelper(spec)
.then(system => {
const allErrors = system.errSelectors.allErrors().toJS()
expect(allErrors.length).toEqual(0)
})
})

//Array Tests

it("should return an error for a non array value in a enum array type in OpenApi 3", () => {
const spec = {
openapi: "3.0.0",
"paths": {
"/pets": {
"get": {
"parameters": [
{
name: "arraySample",
in: "query",
schema: {
type: "array",
enum: [1, 2, 3],
items: {
type: "array",
items: {
type: "number"
}
}
}
},
]
}
}
}
}

return validateHelper(spec)
.then(system => {
const allErrors = system.errSelectors.allErrors().toJS().filter((err => err.source !== ""))
expect(allErrors.length).toEqual(3)
expect(allErrors[0]).toInclude({
level: "warning",
message: "enum value should conform to its schema's `type`",
path: ["paths", "/pets", "get", "parameters", "0", "schema", "enum", 0]
})
expect(allErrors[1]).toInclude({
level: "warning",
message: "enum value should conform to its schema's `type`",
path: ["paths", "/pets", "get", "parameters", "0", "schema", "enum", 1]
})
expect(allErrors[2]).toInclude({
level: "warning",
message: "enum value should conform to its schema's `type`",
path: ["paths", "/pets", "get", "parameters", "0", "schema", "enum", 2]
})
})
})

it("should not return a type error when all items are array in a enum array type in OpenApi 3", () => {
const spec = {
openapi: "3.0.0",
"paths": {
"/pets": {
"get": {
"parameters": [
{
name: "arraySample",
in: "query",
schema: {
type: "array",
enum: [[1,2],[3,4]],
items: {
type: "number",
items: {
type: "number"
}
}
}
},
]
}
}
}
}

return validateHelper(spec)
.then(system => {
const allErrors = system.errSelectors.allErrors().toJS()
expect(allErrors.length).toEqual(0)
})
})

//Object Tests

it("should return an error for a non object value (array) in a enum object type in OpenApi 3", () => {
const spec = {
openapi: "3.0.0",
"paths": {
"/pets": {
"get": {
"parameters": [
{
name: "objectSample",
in: "query",
schema: {
type: "object",
enum: [[1,3], 2, 3]
}
},
]
}
}
}
}

return validateHelper(spec)
.then(system => {
const allErrors = system.errSelectors.allErrors().toJS()
expect(allErrors.length).toEqual(2)
expect(allErrors[0]).toInclude({
level: "warning",
message: "enum value should conform to its schema's `type`",
path: ["paths", "/pets", "get", "parameters", "0", "schema", "enum", 1]
})
expect(allErrors[1]).toInclude({
level: "warning",
message: "enum value should conform to its schema's `type`",
path: ["paths", "/pets", "get", "parameters", "0", "schema", "enum", 2]
})
})
})

it("should return an error for a null value in a enum object type in OpenApi 3", () => {
const spec = {
openapi: "3.0.0",
"paths": {
"/pets": {
"get": {
"parameters": [
{
name: "objectSample",
in: "query",
schema: {
type: "object",
enum: [null]
}
},
]
}
}
}
}

return validateHelper(spec)
.then(system => {
const allErrors = system.errSelectors.allErrors().toJS()
expect(allErrors[0]).toInclude({
level: "warning",
message: "enum value should conform to its schema's `type`",
path: ["paths", "/pets", "get", "parameters", "0", "schema", "enum", 0]
})
})
})

it("should not return an error when all items are array in a enum array type in OpenApi 3", () => {
const spec = {
openapi: "3.0.0",
"paths": {
"/pets": {
"get": {
"parameters": [
{
name: "objectSample",
in: "query",
schema: {
type: "object",
enum: [{ok: "Sample"},{}]
}
},
]
}
}
}
}

return validateHelper(spec)
.then(system => {
const allErrors = system.errSelectors.allErrors().toJS()
expect(allErrors.length).toEqual(0)
})
})

//Boolean Tests

it("should return an error for a non boolean value in a boolean array type in OpenApi 3", () => {
const spec = {
openapi: "3.0.0",
"paths": {
"/pets": {
"get": {
"parameters": [
{
name: "booleanEnum",
in: "query",
schema: {
type: "boolean",
enum: [1, true, false]
}
},
]
}
}
}
}

return validateHelper(spec)
.then(system => {
const allErrors = system.errSelectors.allErrors().toJS()
expect(allErrors[0]).toInclude({
level: "warning",
message: "enum value should conform to its schema's `type`",
path: ["paths", "/pets", "get", "parameters", "0", "schema", "enum", 0]
})
})
})

it("should not return an error when all items are boolean in a boolean array type in OpenApi 3", () => {
const spec = {
openapi: "3.0.0",
"paths": {
"/pets": {
"get": {
"parameters": [
{
name: "booleanEnum",
in: "query",
schema: {
type: "boolean",
enum: [true, false]
}
},
]
}
}
}
}

return validateHelper(spec)
.then(system => {
const allErrors = system.errSelectors.allErrors().toJS()
expect(allErrors.length).toEqual(0)
})
})
})