import expect from "expect" import SwaggerUi from "swagger-ui" import ValidateBasePlugin from "plugins/validate-base" import ValidateSemanticPlugin from "plugins/validate-semantic" import ASTPlugin from "plugins/ast" function getSystem(spec) { return new Promise((resolve) => { const system = SwaggerUi({ spec, domNode: null, presets: [ SwaggerUi.plugins.SpecIndex, SwaggerUi.plugins.ErrIndex, SwaggerUi.plugins.DownloadUrl, SwaggerUi.plugins.SwaggerJsIndex, ], initialState: { layout: undefined }, plugins: [ ASTPlugin, ValidateBasePlugin, ValidateSemanticPlugin, () => ({ statePlugins: { configs: { actions: { loaded: () => { return { type: "noop" } } } } } }) ] }) resolve(system) }) } describe("validation plugin - selectors", function() { this.timeout(10 * 1000) describe("allSchemas", function() { describe("OpenAPI 2.0", function() { it("should pick up parameter schemas", () => { const spec = { paths: { test: { parameters: [{ name: "common" }], get: { parameters: [{ name: "tags" }] } } } } return getSystem(spec) .then(system => system.validateSelectors.allSchemas()) .then(nodes => { expect(nodes.length).toEqual(2) expect(nodes[0].path).toEqual(["paths","test","parameters","0"]) expect(nodes[1].path).toEqual(["paths","test","get","parameters","0"]) }) }) it("should pick up response schemas", () => { const spec = { paths: { test: { get: { responses: { "200": { schema: { type: "string" } } } } } } } return getSystem(spec) .then(system => system.validateSelectors.allSchemas()) .then(nodes => { expect(nodes.length).toEqual(1) expect(nodes[0].path.join(".")).toEqual("paths.test.get.responses.200.schema") }) }) it("should pick up global definitions", () => { const spec = { definitions: { fooModel: { type: "object", properties: { } } } } return getSystem(spec) .then(system => system.validateSelectors.allSchemas()) .then(nodes => { expect(nodes.length).toEqual(1) expect(nodes[0].key).toEqual("fooModel") }) }) it("should pick up global definitions named 'x-' (i.e. not consider them extensions)", () => { const spec = { definitions: { "x-fooModel": { type: "string" } } } return getSystem(spec) .then(system => system.validateSelectors.allSchemas()) .then(nodes => { expect(nodes.length).toEqual(1) expect(nodes[0].key).toEqual("x-fooModel") }) }) it("should pick up response headers", () => { const spec = { paths: { test: { get: { responses: { "200": { headers: { foo: { "type": "integer" } } } } } } } } return getSystem(spec) .then(system => system.validateSelectors.allSchemas()) .then(nodes => { expect(nodes.length).toEqual(1) expect(nodes[0].path.join(".")).toEqual("paths.test.get.responses.200.headers.foo") }) }) it("should pick up subschemas in properties", () => { const spec = { definitions: { fooModel: { type: "object", properties: { foo: { type: "string" } } } } } return getSystem(spec) .then(system => system.validateSelectors.allSchemas()) .then(nodes => { expect(nodes.length).toEqual(2) expect(nodes[0].key).toEqual("fooModel") expect(nodes[1].key).toEqual("foo") }) }) it("should pick up subschemas in additionalProperties - simple", () => { const spec = { definitions: { fooModel: { type: "object", additionalProperties: { type: "string" } } } } return getSystem(spec) .then(system => system.validateSelectors.allSchemas()) .then(nodes => { expect(nodes.length).toEqual(2) expect(nodes[0].key).toEqual("fooModel") expect(nodes[1].key).toEqual("additionalProperties") }) }) it("should pick up subschemas in additionalProperties - complex", () => { const spec = { definitions: { fooModel: { type: "object", additionalProperties: { type: "object", properties: { foo: { type: "string" } } } } } } return getSystem(spec) .then(system => system.validateSelectors.allSchemas()) .then(nodes => { expect(nodes.length).toEqual(3) expect(nodes[0].key).toEqual("fooModel") expect(nodes[1].key).toEqual("additionalProperties") expect(nodes[2].key).toEqual("foo") }) }) it("should pick up subschemas in array", () => { const spec = { definitions: { fooModel: { type: "array", items: { type: "string" } } } } return getSystem(spec) .then(system => system.validateSelectors.allSchemas()) .then(nodes => { expect(nodes.length).toEqual(2) expect(nodes[0].key).toEqual("fooModel") expect(nodes[1].key).toEqual("items") }) }) it("should pick up subschemas in array of objects", () => { const spec = { definitions: { fooModel: { type: "array", items: { type: "object", properties: { foo: { type: "string" } } } } } } return getSystem(spec) .then(system => system.validateSelectors.allSchemas()) .then(nodes => { expect(nodes.length).toEqual(3) expect(nodes[0].key).toEqual("fooModel") expect(nodes[1].key).toEqual("items") expect(nodes[2].key).toEqual("foo") }) }) }) describe("OpenAPI 3.0", function() { it("should pick up schemas in 'components'", () => { const spec = { openapi: "3.0.0", components: { schemas: { fooModel: { type: "string" } } } } return getSystem(spec) .then(system => system.validateSelectors.allSchemas()) .then(nodes => { expect(nodes.length).toEqual(1) expect(nodes[0].key).toEqual("fooModel") }) }) it("should pick up schemas named 'x-' (i.e. not consider them extensions)", () => { const spec = { openapi: "3.0.0", components: { schemas: { "x-fooModel": { type: "string" } } } } return getSystem(spec) .then(system => system.validateSelectors.allSchemas()) .then(nodes => { expect(nodes.length).toEqual(1) expect(nodes[0].key).toEqual("x-fooModel") }) }) it("should pick up request body schemas and response schemas", () => { const spec = { "openapi": "3.0.0", "paths": { "/ping": { "post": { "requestBody": { "content": { "application/myRequestMediaType": { "schema": { "type": "array" } } } }, "responses": { "200": { "description": "OK", "content": { "application/myResponseMediaType": { "schema": { "type": "string" } } } } } } } } } return getSystem(spec) .then(system => system.validateSelectors.allSchemas()) .then(nodes => { expect(nodes.length).toEqual(2) expect(nodes[0].node).toNotBe(nodes[1].node) expect(nodes[0].key).toEqual("schema") expect(nodes[0].parent.key).toEqual("application/myRequestMediaType") expect(nodes[1].key).toEqual("schema") expect(nodes[1].parent.key).toEqual("application/myResponseMediaType") }) }) it("should pick up schemas in response components", () => { const spec = { openapi: "3.0.0", components: { responses: { MyResponse: { content: { "application/json": { schema: { type: "string" } } } } } } } return getSystem(spec) .then(system => system.validateSelectors.allSchemas()) .then(nodes => { expect(nodes.length).toEqual(1) expect(nodes[0].path).toEqual(["components","responses","MyResponse","content","application/json","schema"]) }) }) it("should pick up subschemas in properties", () => { const spec = { openapi: "3.0.0", components: { schemas: { fooModel: { type: "object", properties: { foo: { type: "string" } } } } } } return getSystem(spec) .then(system => system.validateSelectors.allSchemas()) .then(nodes => { expect(nodes.length).toEqual(2) expect(nodes[0].key).toEqual("fooModel") expect(nodes[1].key).toEqual("foo") }) }) it("should pick up subschemas in additionalProperties - simple", () => { const spec = { openapi: "3.0.0", components: { schemas: { fooModel: { type: "object", additionalProperties: { type: "string" } } } } } return getSystem(spec) .then(system => system.validateSelectors.allSchemas()) .then(nodes => { expect(nodes.length).toEqual(2) expect(nodes[0].key).toEqual("fooModel") expect(nodes[1].key).toEqual("additionalProperties") }) }) it("should pick up subschemas in additionalProperties - complex", () => { const spec = { openapi: "3.0.0", components: { schemas: { fooModel: { type: "object", additionalProperties: { type: "object", properties: { foo: { type: "string" } } } } } } } return getSystem(spec) .then(system => system.validateSelectors.allSchemas()) .then(nodes => { expect(nodes.length).toEqual(3) expect(nodes[0].key).toEqual("fooModel") expect(nodes[1].key).toEqual("additionalProperties") expect(nodes[2].key).toEqual("foo") }) }) it("should pick up subschemas in array", () => { const spec = { openapi: "3.0.0", components: { schemas: { fooModel: { type: "array", items: { type: "string" } } } } } return getSystem(spec) .then(system => system.validateSelectors.allSchemas()) .then(nodes => { expect(nodes.length).toEqual(2) expect(nodes[0].key).toEqual("fooModel") expect(nodes[1].key).toEqual("items") }) }) it("should pick up subschemas in array of objects", () => { const spec = { openapi: "3.0.0", components: { schemas: { fooModel: { type: "array", items: { type: "object", properties: { foo: { type: "string" } } } } } } } return getSystem(spec) .then(system => system.validateSelectors.allSchemas()) .then(nodes => { expect(nodes.length).toEqual(3) expect(nodes[0].key).toEqual("fooModel") expect(nodes[1].key).toEqual("items") expect(nodes[2].key).toEqual("foo") }) }) }) }) describe("allResponses", function() { it("should pick up operation responses with specific codes like 200", () => { const spec = { paths: { "/foo": { get: { responses: { "200": { description: "ok" } } } } } } return getSystem(spec) .then(system => system.validateSelectors.allResponses()) .then(nodes => { expect(nodes.length).toEqual(1) expect(nodes[0].path).toEqual(["paths","/foo","get","responses","200"]) }) }) it("should pick up the 'default' response", () => { const spec = { paths: { "/foo": { get: { responses: { default: { description: "ok" } } } } } } return getSystem(spec) .then(system => system.validateSelectors.allResponses()) .then(nodes => { expect(nodes.length).toEqual(1) expect(nodes[0].path).toEqual(["paths","/foo","get","responses","default"]) }) }) it("should pick up OAS3 response code ranges like 2XX", () => { const spec = { openapi: "3.0.0", paths: { "/foo": { get: { responses: { "2XX": { description: "ok" } } } } } } return getSystem(spec) .then(system => system.validateSelectors.allResponses()) .then(nodes => { expect(nodes.length).toEqual(1) expect(nodes[0].path).toEqual(["paths","/foo","get","responses","2XX"]) }) }) it("should ignore x- extension keys in an operation's `responses` section", () => { const spec = { paths: { "/foo": { get: { responses: { "x-ext": { foo: "bar" } } } } } } return getSystem(spec) .then(system => system.validateSelectors.allResponses()) .then(nodes => { expect(nodes.length).toEqual(0) }) }) it("should pick up OpenAPI 2 global response definitions", () => { const spec = { swagger: "2.0", responses: { OkResponse: { description: "ok" } } } return getSystem(spec) .then(system => system.validateSelectors.allResponses()) .then(nodes => { expect(nodes.length).toEqual(1) expect(nodes[0].path).toEqual(["responses","OkResponse"]) }) }) it("should pick up OpenAPI 2 response definitions named 'x-'", () => { const spec = { swagger: "2.0", responses: { "x-MyResponse": { description: "ok" } } } return getSystem(spec) .then(system => system.validateSelectors.allResponses()) .then(nodes => { expect(nodes.length).toEqual(1) expect(nodes[0].path).toEqual(["responses","x-MyResponse"]) }) }) it("should pick up OpenAPI 3 response components", () => { const spec = { openapi: "3.0.0", components : { responses: { OkResponse: { description: "ok" } } } } return getSystem(spec) .then(system => system.validateSelectors.allResponses()) .then(nodes => { expect(nodes.length).toEqual(1) expect(nodes[0].path).toEqual(["components","responses","OkResponse"]) }) }) it("should pick up OpenAPI 3 response components named 'x-'", () => { const spec = { openapi: "3.0.0", components : { responses: { "x-MyResponse": { description: "ok" } } } } return getSystem(spec) .then(system => system.validateSelectors.allResponses()) .then(nodes => { expect(nodes.length).toEqual(1) expect(nodes[0].path).toEqual(["components","responses","x-MyResponse"]) }) }) }) describe("allSecurityDefinitions", () => { it("should pick up OAS2 security schemes", () => { const spec = { swagger: "2.0", securityDefinitions: { basicAuth: { type: "basic" }, apiKeyAuth: { type: "apiKey" } } } return getSystem(spec) .then(system => system.validateSelectors.allSecurityDefinitions()) .then(nodes => { expect(nodes.length).toEqual(2) expect(nodes[0].node).toNotBe(nodes[1].node) expect(nodes[0].key).toEqual("basicAuth") expect(nodes[1].key).toEqual("apiKeyAuth") }) }) it("should pick up OAS3 security schemes", () => { const spec = { openapi: "3.0.0", components: { securitySchemes: { basicAuth: { type: "http" }, apiKeyAuth: { type: "apiKey" } } } } return getSystem(spec) .then(system => system.validateSelectors.allSecurityDefinitions()) .then(nodes => { expect(nodes.length).toEqual(2) expect(nodes[0].node).toNotBe(nodes[1].node) expect(nodes[0].key).toEqual("basicAuth") expect(nodes[1].key).toEqual("apiKeyAuth") }) }) it("should pick up OAS2 security schemes named x- (i.e. not consider them extensions)", () => { const spec = { swagger: "2.0", securityDefinitions: { "x-auth": { type: "basic" } } } return getSystem(spec) .then(system => system.validateSelectors.allSecurityDefinitions()) .then(nodes => { expect(nodes.length).toEqual(1) expect(nodes[0].key).toEqual("x-auth") }) }) it("should pick up OAS3 security schemes named x- (i.e. not consider them extensions)", () => { const spec = { openapi: "3.0.0", components: { securitySchemes: { "x-auth": { type: "basic" } } } } return getSystem(spec) .then(system => system.validateSelectors.allSecurityDefinitions()) .then(nodes => { expect(nodes.length).toEqual(1) expect(nodes[0].key).toEqual("x-auth") }) }) it("should not pick up arbitrary OAS2 nodes named `securityDefinitions`", () => { const spec = { swagger: "2.0", definitions: { securityDefinitions: { type: "object" } } } return getSystem(spec) .then(system => system.validateSelectors.allSecurityDefinitions()) .then(nodes => { expect(nodes.length).toEqual(0) }) }) it("should not pick up arbitrary OAS3 nodes named `securitySchemes`", () => { const spec = { openapi: "3.0.0", components: { schemas: { securitySchemes: { type: "object" } } } } return getSystem(spec) .then(system => system.validateSelectors.allSecurityDefinitions()) .then(nodes => { expect(nodes.length).toEqual(0) }) }) }) describe("allSecurityRequirements", () => { it("should pick up global security requirements", () => { const spec = { security: [ { auth1: [] }, { auth2: [], auth3: [] } ] } return getSystem(spec) .then(system => system.validateSelectors.allSecurityRequirements()) .then(nodes => { expect(nodes.length).toEqual(2) expect(nodes[0].node).toNotBe(nodes[1].node) expect(nodes[0].node).toEqual( { "auth1": [] } ) expect(nodes[1].node).toEqual( { "auth2": [], "auth3": [] } ) }) }) it("should pick up operation-level security requirements", () => { const spec = { paths: { "/": { get: { security: [ { auth1: [] }, { auth2: [], auth3: [] } ] } } } } return getSystem(spec) .then(system => system.validateSelectors.allSecurityRequirements()) .then(nodes => { expect(nodes.length).toEqual(2) expect(nodes[0].node).toNotBe(nodes[1].node) expect(nodes[0].node).toEqual( { "auth1": [] } ) expect(nodes[1].node).toEqual( { "auth2": [], "auth3": [] } ) }) }) it("should pick up empty security requirements", () => { const spec = { security: [ {} ], paths: { "/": { get: { security: [ {} ] } } } } return getSystem(spec) .then(system => system.validateSelectors.allSecurityRequirements()) .then(nodes => { expect(nodes.length).toEqual(2) expect(nodes[0].node).toNotBe(nodes[1].node) expect(nodes[0].node).toEqual({}) expect(nodes[1].node).toEqual({}) }) }) it("should not pick up the `security` key inside extensions", () => { const spec = { paths: { "/": { "x-ext1": { security: [ { foo: [] } ] } }, "x-ext2": { get: { security: [ { bar: [] } ] } } } } return getSystem(spec) .then(system => system.validateSelectors.allSecurityRequirements()) .then(nodes => { expect(nodes.length).toEqual(0) }) }) }) })