From 5a8108b00a4f07b5a34c89586d6c4650112f98eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Urba=C5=84czyk?= Date: Thu, 22 Sep 2022 12:46:12 +0200 Subject: [PATCH] refactor: add tests for old api (#622) --- src/custom-operations/anonymous-naming.ts | 2 +- src/iterator.ts | 4 +- src/models/v2/asyncapi.ts | 2 +- src/old-api/asyncapi.ts | 43 +- src/old-api/iterator.ts | 4 +- src/old-api/mixins.ts | 10 +- src/old-api/schema.ts | 11 +- src/spec-types/v2.ts | 128 +-- src/stringify.ts | 4 +- .../anonymous-naming.spec.ts | 64 ++ test/mocks/circular-refs.yaml | 30 + test/mocks/nested-schemas.yaml | 189 +++ test/mocks/refs-1.yaml | 4 + test/mocks/refs-2.yaml | 1 + test/mocks/simple-with-refs.yaml | 26 + test/mocks/simple.yaml | 7 +- test/old-api/asyncapi.spec.ts | 1013 +++++++++++++++++ test/old-api/base.spec.ts | 25 + test/old-api/channel-parameter.spec.ts | 27 + test/old-api/channel.spec.ts | 132 +++ test/old-api/components.spec.ts | 526 +++++++++ test/old-api/contact.spec.ts | 29 + test/old-api/correlation-id.spec.ts | 16 + test/old-api/external-docs.spec.ts | 16 + test/old-api/info.spec.ts | 58 + test/old-api/license.spec.ts | 22 + test/old-api/message-trait.spec.ts | 107 ++ test/old-api/message.spec.ts | 155 +++ test/old-api/mixins.spec.ts | 92 ++ test/old-api/mixins.ts | 310 +++++ test/old-api/oauth-flow.spec.ts | 37 + test/old-api/operation-trait.spec.ts | 40 + test/old-api/operation.spec.ts | 133 +++ test/old-api/schema.spec.ts | 777 +++++++++++++ test/old-api/security-scheme.spec.ts | 63 + test/old-api/server-variable.spec.ts | 66 ++ test/old-api/server.spec.ts | 74 ++ test/old-api/tag.spec.ts | 17 + 38 files changed, 4172 insertions(+), 92 deletions(-) create mode 100644 test/mocks/circular-refs.yaml create mode 100644 test/mocks/nested-schemas.yaml create mode 100644 test/mocks/refs-1.yaml create mode 100644 test/mocks/refs-2.yaml create mode 100644 test/mocks/simple-with-refs.yaml create mode 100644 test/old-api/asyncapi.spec.ts create mode 100644 test/old-api/base.spec.ts create mode 100644 test/old-api/channel-parameter.spec.ts create mode 100644 test/old-api/channel.spec.ts create mode 100644 test/old-api/components.spec.ts create mode 100644 test/old-api/contact.spec.ts create mode 100644 test/old-api/correlation-id.spec.ts create mode 100644 test/old-api/external-docs.spec.ts create mode 100644 test/old-api/info.spec.ts create mode 100644 test/old-api/license.spec.ts create mode 100644 test/old-api/message-trait.spec.ts create mode 100644 test/old-api/message.spec.ts create mode 100644 test/old-api/mixins.spec.ts create mode 100644 test/old-api/mixins.ts create mode 100644 test/old-api/oauth-flow.spec.ts create mode 100644 test/old-api/operation-trait.spec.ts create mode 100644 test/old-api/operation.spec.ts create mode 100644 test/old-api/schema.spec.ts create mode 100644 test/old-api/security-scheme.spec.ts create mode 100644 test/old-api/server-variable.spec.ts create mode 100644 test/old-api/server.spec.ts create mode 100644 test/old-api/tag.spec.ts diff --git a/src/custom-operations/anonymous-naming.ts b/src/custom-operations/anonymous-naming.ts index 53fd00053..42c1b3655 100644 --- a/src/custom-operations/anonymous-naming.ts +++ b/src/custom-operations/anonymous-naming.ts @@ -28,7 +28,7 @@ function assignNameToComponentMessages(document: AsyncAPIDocumentInterface) { function assignNameToAnonymousMessages(document: AsyncAPIDocumentInterface) { let anonymousMessageCounter = 0; document.messages().forEach(message => { - if (message.name() === undefined && message.extensions().get(xParserMessageName) === undefined) { + if (message.name() === undefined && message.extensions().get(xParserMessageName)?.value() === undefined) { setExtension(xParserMessageName, ``, message); } }); diff --git a/src/iterator.ts b/src/iterator.ts index cf8e65e61..b6b2eb1f3 100644 --- a/src/iterator.ts +++ b/src/iterator.ts @@ -39,7 +39,7 @@ export enum SchemaTypesToIterate { export type TraverseOptions = { callback: TraverseCallback - schemaTypesToIterate: SchemaTypesToIterate[] + schemaTypesToIterate: Array<`${SchemaTypesToIterate}`>; seenSchemas: Set } @@ -48,7 +48,7 @@ export type TraverseCallback = (schema: SchemaInterface, propOrIndex: string | n /** * Go through each channel and for each parameter, and message payload and headers recursively call the callback for each schema. */ -export function traverseAsyncApiDocument(doc: AsyncAPIDocumentInterface, callback: TraverseCallback, schemaTypesToIterate: SchemaTypesToIterate[] = []) { +export function traverseAsyncApiDocument(doc: AsyncAPIDocumentInterface, callback: TraverseCallback, schemaTypesToIterate: Array<`${SchemaTypesToIterate}`> = []) { if (schemaTypesToIterate.length === 0) { schemaTypesToIterate = Object.values(SchemaTypesToIterate); } diff --git a/src/models/v2/asyncapi.ts b/src/models/v2/asyncapi.ts index 1e53b4894..fc14f4689 100644 --- a/src/models/v2/asyncapi.ts +++ b/src/models/v2/asyncapi.ts @@ -88,7 +88,7 @@ export class AsyncAPIDocument extends BaseModel implements As } components(): ComponentsInterface { - return new Components(this._json.components || {}); + return this.createModel(Components, this._json.components || {}, { pointer: '/components' }); } extensions(): ExtensionsInterface { diff --git a/src/old-api/asyncapi.ts b/src/old-api/asyncapi.ts index 89c543302..5ebea4c98 100644 --- a/src/old-api/asyncapi.ts +++ b/src/old-api/asyncapi.ts @@ -7,8 +7,8 @@ import { Message } from './message'; import { Schema } from './schema'; import { traverseAsyncApiDocument } from './iterator'; -import { xParserCircular } from '../constants'; -import { stringify, unstringify } from '../stringify'; +import { xParserCircular, xParserSpecStringified, xParserSpecParsed } from '../constants'; +import { refReplacer, traverseStringifiedData } from '../stringify'; import type { v2 } from '../spec-types'; import type { Operation } from './operation'; @@ -154,16 +154,41 @@ export class AsyncAPIDocument extends SpecificationExtensionsModel = []) { traverseAsyncApiDocument(this, callback, schemaTypesToIterate); } - static stringify(doc: AsyncAPIDocument, space: number): string | undefined { - return stringify(doc, { space }); + static stringify(doc: AsyncAPIDocument, space?: number): string | undefined { + const rawDoc = doc.json(); + const copiedDoc = { ...rawDoc }; + copiedDoc[xParserSpecStringified] = true; + return JSON.stringify(copiedDoc, refReplacer(), space); } - static parse(doc: string): AsyncAPIDocument | undefined { - const possibleDocument = unstringify(doc); - return possibleDocument ? new AsyncAPIDocument(possibleDocument.json()) : undefined; + static parse(doc: string | Record): AsyncAPIDocument | undefined { + let parsedJSON = doc; + if (typeof doc === 'string') { + parsedJSON = JSON.parse(doc); + } else if (typeof doc === 'object') { + // shall copy + parsedJSON = { ...(parsedJSON as Record) }; + } + + // the `doc` must be an AsyncAPI parsed document + if (typeof parsedJSON !== 'object' || !parsedJSON[xParserSpecParsed]) { + throw new Error('Cannot parse invalid AsyncAPI document'); + } + // if the `doc` is not stringified via the `stringify` static method then immediately return a model. + if (!parsedJSON[xParserSpecStringified]) { + return new AsyncAPIDocument(parsedJSON as v2.AsyncAPIObject); + } + // remove `x-parser-spec-stringified` extension + delete parsedJSON[String(xParserSpecStringified)]; + + const objToPath = new Map(); + const pathToObj = new Map(); + traverseStringifiedData(parsedJSON, undefined, parsedJSON, objToPath, pathToObj); + + return new AsyncAPIDocument(parsedJSON as v2.AsyncAPIObject); } -} \ No newline at end of file +} diff --git a/src/old-api/iterator.ts b/src/old-api/iterator.ts index 519ea0c2d..0d4f78ad7 100644 --- a/src/old-api/iterator.ts +++ b/src/old-api/iterator.ts @@ -38,7 +38,7 @@ export enum SchemaTypesToIterate { export type TraverseOptions = { callback: TraverseCallback - schemaTypesToIterate: SchemaTypesToIterate[] + schemaTypesToIterate: Array<`${SchemaTypesToIterate}`>; seenSchemas: Set } @@ -47,7 +47,7 @@ export type TraverseCallback = (schema: Schema, propOrIndex: string | number | n /** * Go through each channel and for each parameter, and message payload and headers recursively call the callback for each schema. */ -export function traverseAsyncApiDocument(doc: AsyncAPIDocument, callback: TraverseCallback, schemaTypesToIterate: SchemaTypesToIterate[] = []) { +export function traverseAsyncApiDocument(doc: AsyncAPIDocument, callback: TraverseCallback, schemaTypesToIterate: Array<`${SchemaTypesToIterate}`> = []) { if (schemaTypesToIterate.length === 0) { schemaTypesToIterate = Object.values(SchemaTypesToIterate); } diff --git a/src/old-api/mixins.ts b/src/old-api/mixins.ts index 7b0debbdd..7f8734ed8 100644 --- a/src/old-api/mixins.ts +++ b/src/old-api/mixins.ts @@ -44,18 +44,20 @@ export function hasDescription(model: Base<{ description?: string }>) { return Boolean(model.json('description')); } -export function description(model: Base<{ description?: string }>): string | undefined { - return model.json('description'); +export function description(model: Base<{ description?: string }>): string | null { + const description = model.json('description'); + return typeof description === 'string' ? description : null; } export function hasExternalDocs(model: Base<{ externalDocs?: v2.ExternalDocumentationObject }>): boolean { return Object.keys(model.json('externalDocs') || {}).length > 0; } -export function externalDocs(model: Base<{ externalDocs?: v2.ExternalDocumentationObject }>): ExternalDocs | undefined { - if (hasExternalDocs(model)) { +export function externalDocs(model: Base<{ externalDocs?: v2.ExternalDocumentationObject }>): ExternalDocs | null { + if (typeof model.json('externalDocs') === 'object') { return new ExternalDocs(model.json('externalDocs') as v2.ExternalDocumentationObject); } + return null; } export const extensionsMixins = { diff --git a/src/old-api/schema.ts b/src/old-api/schema.ts index cfc55fc25..329311591 100644 --- a/src/old-api/schema.ts +++ b/src/old-api/schema.ts @@ -1,5 +1,6 @@ import { SpecificationExtensionsModel, createMapOfType, getMapValue, description, hasDescription, hasExternalDocs, externalDocs } from './mixins'; import { xParserCircular, xParserCircularProps } from '../constants'; +import { hasRef } from '../utils'; import type { Base } from './base'; import type { v2 } from '../spec-types'; @@ -117,15 +118,19 @@ export class Schema extends SpecificationExtensionsModel; @@ -86,22 +86,22 @@ export interface ChannelObject extends SpecificationExtensions { } export interface ChannelBindingsObject extends SpecificationExtensions { - http: Binding; - ws: Binding; - kafka: Binding; - anypointmq: Binding; - amqp: Binding; - amqp1: Binding; - mqtt: Binding; - mqtt5: Binding; - nats: Binding; - jms: Binding; - sns: Binding; - sqs: Binding; - stomp: Binding; - redis: Binding; - mercure: Binding; - ibmmq: Binding; + http?: Binding; + ws?: Binding; + kafka?: Binding; + anypointmq?: Binding; + amqp?: Binding; + amqp1?: Binding; + mqtt?: Binding; + mqtt5?: Binding; + nats?: Binding; + jms?: Binding; + sns?: Binding; + sqs?: Binding; + stomp?: Binding; + redis?: Binding; + mercure?: Binding; + ibmmq?: Binding; } export interface OperationObject extends OperationTraitObject, SpecificationExtensions { @@ -120,22 +120,22 @@ export interface OperationTraitObject extends SpecificationExtensions { } export interface OperationBindingsObject extends SpecificationExtensions { - http: Binding; - ws: Binding; - kafka: Binding; - anypointmq: Binding; - amqp: Binding; - amqp1: Binding; - mqtt: Binding; - mqtt5: Binding; - nats: Binding; - jms: Binding; - sns: Binding; - sqs: Binding; - stomp: Binding; - redis: Binding; - mercure: Binding; - ibmmq: Binding; + http?: Binding; + ws?: Binding; + kafka?: Binding; + anypointmq?: Binding; + amqp?: Binding; + amqp1?: Binding; + mqtt?: Binding; + mqtt5?: Binding; + nats?: Binding; + jms?: Binding; + sns?: Binding; + sqs?: Binding; + stomp?: Binding; + redis?: Binding; + mercure?: Binding; + ibmmq?: Binding; } export type ParametersObject = Record; @@ -175,22 +175,22 @@ export interface MessageExampleObject extends SpecificationExtensions { } export interface MessageBindingsObject extends SpecificationExtensions { - http: Binding; - ws: Binding; - kafka: Binding; - anypointmq: Binding; - amqp: Binding; - amqp1: Binding; - mqtt: Binding; - mqtt5: Binding; - nats: Binding; - jms: Binding; - sns: Binding; - sqs: Binding; - stomp: Binding; - redis: Binding; - mercure: Binding; - ibmmq: Binding; + http?: Binding; + ws?: Binding; + kafka?: Binding; + anypointmq?: Binding; + amqp?: Binding; + amqp1?: Binding; + mqtt?: Binding; + mqtt5?: Binding; + nats?: Binding; + jms?: Binding; + sns?: Binding; + sqs?: Binding; + stomp?: Binding; + redis?: Binding; + mercure?: Binding; + ibmmq?: Binding; } export type TagsObject = Array; diff --git a/src/stringify.ts b/src/stringify.ts index 95d3c253b..b1b9e2783 100644 --- a/src/stringify.ts +++ b/src/stringify.ts @@ -57,7 +57,7 @@ export function copy(data: Record) { return unstringifiedData; } -function refReplacer() { +export function refReplacer() { const modelPaths = new Map(); const paths = new Map(); let init: unknown = null; @@ -90,7 +90,7 @@ function refReplacer() { } const refRoot = '$ref:$'; -function traverseStringifiedData(parent: any, field: string | undefined, root: any, objToPath: Map, pathToObj: Map) { +export function traverseStringifiedData(parent: any, field: string | undefined, root: any, objToPath: Map, pathToObj: Map) { let objOrPath = parent; let path = refRoot; diff --git a/test/custom-operations/anonymous-naming.spec.ts b/test/custom-operations/anonymous-naming.spec.ts index a5a57a76c..cbd92d923 100644 --- a/test/custom-operations/anonymous-naming.spec.ts +++ b/test/custom-operations/anonymous-naming.spec.ts @@ -174,4 +174,68 @@ describe('custom operations - anonymous naming', function() { expect(document?.messages()[0].payload()?.extensions().get(xParserSchemaId)?.value()).toEqual('schema'); }); + + it('should apply anonymous ids across whole document', async function() { + const { document, diagnostics } = await parser.parse({ + asyncapi: '2.0.0', + info: { + title: 'Valid AsyncApi document', + version: '1.0', + }, + channels: { + 'channel/{streetlightId}': { + parameters: { + streetlightId: { + schema: { + type: 'string', + } + } + }, + subscribe: { + message: { + $ref: '#/components/messages/someMessage', + }, + }, + publish: { + message: { + payload: {} + } + } + } + }, + components: { + parameters: { + someParameter: { + schema: { + type: 'string', + } + } + }, + messages: { + someMessage: { + payload: { + $ref: '#/components/schemas/someSchema', + }, + }, + }, + schemas: { + someSchema: {}, + } + } + }); + + expect(document?.json()?.channels?.['channel/{streetlightId}']?.subscribe?.message?.[xParserMessageName]).toEqual('someMessage'); + expect((document?.json()?.channels?.['channel/{streetlightId}']?.subscribe?.message as any)?.payload?.[xParserSchemaId]).toEqual('someSchema'); + + expect(document?.json()?.channels?.['channel/{streetlightId}']?.publish?.message?.[xParserMessageName]).toEqual(''); + expect((document?.json()?.channels?.['channel/{streetlightId}']?.publish?.message as any)?.payload?.[xParserSchemaId]).toEqual(''); + + expect((document?.json()?.channels?.['channel/{streetlightId}']?.parameters?.streetlightId as any)?.schema?.[xParserSchemaId]).toEqual('streetlightId'); + + expect(document?.json()?.components?.messages?.someMessage?.[xParserMessageName]).toEqual('someMessage'); + expect((document?.json()?.channels?.['channel/{streetlightId}']?.subscribe?.message as any)?.payload?.[xParserSchemaId]).toEqual('someSchema'); + expect((document?.json()?.components?.parameters?.someParameter as any)?.schema?.[xParserSchemaId]).toEqual('someParameter'); + + expect(document?.json()?.components?.schemas?.someSchema?.[xParserSchemaId]).toEqual('someSchema'); + }); }); diff --git a/test/mocks/circular-refs.yaml b/test/mocks/circular-refs.yaml new file mode 100644 index 000000000..e990f293f --- /dev/null +++ b/test/mocks/circular-refs.yaml @@ -0,0 +1,30 @@ +asyncapi: 2.0.0 +info: + title: Test API + version: 1.0.0 +channels: + mychannel: + publish: + message: + payload: + type: object + properties: + name: + type: string +components: + messages: + testMessage: + payload: + $ref: "#/components/schemas/testSchema" + schemas: + testSchema: + type: object + properties: + name: + type: string + deep: + type: object + properties: + circular: + $ref: '#/components/schemas/testSchema' + diff --git a/test/mocks/nested-schemas.yaml b/test/mocks/nested-schemas.yaml new file mode 100644 index 000000000..f176aaf3a --- /dev/null +++ b/test/mocks/nested-schemas.yaml @@ -0,0 +1,189 @@ +asyncapi: '2.0.0' +info: + title: AsyncAPi Document with Nested schemas + version: 0.1.0 +channels: + test: + parameters: + testParam1: + schema: + $id: testParamSchema + type: object + test: true + properties: + testParamNestedSchemaProp: + $id: testParamNestedSchemaProp + type: object + test: true + properties: + testParamNestedNestedSchemaProp2: + $id: testParamNestedNestedSchemaProp2 + test: true + type: string + publish: + message: + headers: + $id: testHeaderSchema + type: object + test: true + properties: + testHeaderNestedSchemaProp: + $id: testHeaderNestedSchemaProp + type: object + test: true + properties: + testprop2: + $id: testHeaderNestedNestedSchemaProp1 + test: true + type: string + testHeaderNestedSchemaPropArray: + $id: testHeaderNestedSchemaPropArray + type: array + test: true + items: + - $id: testHeaderNestedSchemaPropArrayProp1 + test: true + type: string + payload: + $id: testPayloadSchema + type: object + test: true + properties: + testPayloadNestedSchemaProp: + $id: testPayloadNestedSchemaProp + type: object + test: true + properties: + testPayloadNestedNestedSchemaProp1: + $id: testPayloadNestedNestedSchemaProp1 + test: true + type: string + testPayloadNestedSchemaPropArray: + $id: testPayloadNestedSchemaPropArray + type: array + test: true + items: + - $id: testPayloadNestedSchemaPropArrayProp1 + test: true + type: string + test2: + subscribe: + message: + payload: + $id: testPayload + test: true + k: 2 +components: + schemas: + testSchema: + $id: testComponentSchemaSchema + type: object + test: true + properties: + testprop: + $id: testComponentSchemaNestedSchemaPropAllOf + test: true + allOf: + - $id: testComponentSchemaNestedSchemaPropAllOfSchema1 + type: object + test: true + properties: + testprop1: + $id: testComponentSchemaNestedSchemaPropAllOfSchema1Prop1 + test: true + type: string + - $id: testComponentSchemaNestedSchemaPropAllOfSchema2 + type: object + test: true + properties: + testprop2: + $id: testComponentSchemaNestedSchemaPropAllOfSchema2Prop1 + test: true + type: string + testArray: + $id: testComponentSchemaNestedSchemaPropArray + type: array + test: true + items: + - $id: testComponentSchemaNestedSchemaPropArrayProp1 + test: true + type: string + - $id: testComponentSchemaNestedSchemaPropArrayProp2 + test: true + type: string + testPatternProperties: + $id: testComponentSchemaNestedSchemaPropPatternProperties + type: object + test: true + patternProperties: + "^S_": + $id: testComponentSchemaNestedSchemaPropPatternPropertiesProp1 + test: true + type: string + "^N_": + $id: testComponentSchemaNestedSchemaPropPatternPropertiesProp2 + test: true + type: number + testConditional: + $id: testComponentSchemaNestedSchemaPropConditional + type: string + test: true + if: + $id: testComponentSchemaNestedSchemaPropConditionalIf + test: true + type: string + then: + $id: testComponentSchemaNestedSchemaPropConditionalThen + test: true + type: number + else: + $id: testComponentSchemaNestedSchemaPropConditionalElse + test: true + type: boolean + testDependencies: + $id: testComponentSchemaNestedSchemaPropDependencies + type: string + test: true + dependencies: + dep1: + $id: testComponentSchemaNestedSchemaPropDependenciesDep1 + test: true + type: string + dep2: + - test1 + - test2 + dep3: + $id: testComponentSchemaNestedSchemaPropDependenciesDep3 + test: true + type: number + testDefinitions: + $id: testComponentSchemaNestedSchemaPropDefinitions + type: string + test: true + definitions: + def1: + $id: testComponentSchemaNestedSchemaPropDefinitionsDef1 + test: true + type: string + def2: + $id: testComponentSchemaNestedSchemaPropDefinitionsDef2 + test: true + type: number + testMisc: + $id: testComponentSchemaNestedSchemaPropMisc + type: + - object + - array + test: true + not: + $id: testComponentSchemaNestedSchemaPropMiscNot + test: true + type: string + propertyNames: + $id: testComponentSchemaNestedSchemaPropMiscPropertyNames + test: true + type: string + contains: + $id: testComponentSchemaNestedSchemaPropMiscContains + test: true + type: string diff --git a/test/mocks/refs-1.yaml b/test/mocks/refs-1.yaml new file mode 100644 index 000000000..e798a0ffe --- /dev/null +++ b/test/mocks/refs-1.yaml @@ -0,0 +1,4 @@ +type: object +properties: + testing: + $ref: './refs-2.yaml' \ No newline at end of file diff --git a/test/mocks/refs-2.yaml b/test/mocks/refs-2.yaml new file mode 100644 index 000000000..2d8bb0e0f --- /dev/null +++ b/test/mocks/refs-2.yaml @@ -0,0 +1 @@ +type: string \ No newline at end of file diff --git a/test/mocks/simple-with-refs.yaml b/test/mocks/simple-with-refs.yaml new file mode 100644 index 000000000..37d5000af --- /dev/null +++ b/test/mocks/simple-with-refs.yaml @@ -0,0 +1,26 @@ +asyncapi: 2.0.0 +info: + title: Test API + version: 1.0.0 +channels: + mychannel: + publish: + message: + payload: + type: object + properties: + name: + type: string +components: + messages: + testMessage: + payload: + $ref: "#/components/schemas/testSchema" + schemas: + testSchema: + type: object + properties: + name: + type: string + test: + $ref: ./refs-1.yaml diff --git a/test/mocks/simple.yaml b/test/mocks/simple.yaml index 050c78afa..67e0fb378 100644 --- a/test/mocks/simple.yaml +++ b/test/mocks/simple.yaml @@ -1,8 +1,7 @@ -asyncapi: '2.4.0' +asyncapi: '2.0.0' info: - title: Account Service - version: 1.0.0 - description: This service is in charge of processing user signups + title: Simple AsyncAPI Document + version: 0.1.0 channels: user/signedup: subscribe: diff --git a/test/old-api/asyncapi.spec.ts b/test/old-api/asyncapi.spec.ts new file mode 100644 index 000000000..6741e5758 --- /dev/null +++ b/test/old-api/asyncapi.spec.ts @@ -0,0 +1,1013 @@ +import fs from 'fs'; +import path from 'path'; + +import { Parser, convertToOldAPI } from '../../src'; +import { xParserSpecParsed, xParserSpecStringified } from '../../src/constants'; +import { AsyncAPIDocument } from '../../src/old-api/asyncapi'; +import { Info } from '../../src/old-api/info'; +import { Server } from '../../src/old-api/server'; +import { Channel } from '../../src/old-api/channel'; +import { Components } from '../../src/old-api/components'; +import { Message } from '../../src/old-api/message'; +import { Schema } from '../../src/old-api/schema'; +import { assertExternalDocumentationMixin, assertExtensionsMixin, assertTagsMixin } from './mixins'; + +/* +const simpleInputJSON = fs.readFileSync(path.resolve(__dirname, '../good/asyncapi.json'), 'utf8'); +const simpleOutputJSON = '{"asyncapi":"2.0.0","info":{"title":"My API","version":"1.0.0"},"channels":{"mychannel":{"publish":{"message":{"payload":{"type":"object","properties":{"name":{"type":"string","x-parser-schema-id":""}},"x-parser-schema-id":""},"x-parser-original-schema-format":"application/vnd.aai.asyncapi;version=2.0.0","x-parser-original-payload":{"type":"object","properties":{"name":{"type":"string"}}},"schemaFormat":"application/vnd.aai.asyncapi;version=2.0.0","x-parser-message-parsed":true,"x-parser-message-name":""}}}},"components":{"messages":{"testMessage":{"payload":{"type":"object","properties":{"name":{"type":"string","x-parser-schema-id":""},"test":{"type":"object","properties":{"testing":{"type":"string","x-parser-schema-id":""}},"x-parser-schema-id":""}},"x-parser-schema-id":"testSchema"},"x-parser-original-schema-format":"application/vnd.aai.asyncapi;version=2.0.0","x-parser-original-payload":{"type":"object","properties":{"name":{"type":"string"},"test":{"type":"object","properties":{"testing":{"type":"string"}}}}},"schemaFormat":"application/vnd.aai.asyncapi;version=2.0.0","x-parser-message-parsed":true,"x-parser-message-name":"testMessage"}},"schemas":{"testSchema":"$ref:$.components.messages.testMessage.payload"}},"x-parser-spec-parsed":true,"x-parser-spec-stringified":true}'; +const circularYAML = fs.readFileSync(path.resolve(__dirname, '../good/circular-refs.yaml'), 'utf8'); +const circularOutputYAML = '{"asyncapi":"2.0.0","info":{"title":"My Circular API","version":"1.0.0"},"channels":{"recursive":{"subscribe":{"message":{"payload":{"type":"object","properties":{"selfChildren":{"type":"array","items":"$ref:$.channels.recursive.subscribe.message.payload","x-parser-schema-id":""},"selfObjectChildren":{"type":"object","properties":{"test":"$ref:$.channels.recursive.subscribe.message.payload","nonRecursive":{"type":"string","x-parser-schema-id":""}},"x-parser-schema-id":""},"selfSomething":{"type":"object","properties":{"test":{"type":"object","properties":{"ancestorChildren":{"type":"array","items":"$ref:$.channels.recursive.subscribe.message.payload","x-parser-schema-id":""},"ancestorSomething":{"type":"string","x-parser-schema-id":""}},"x-parser-schema-id":"RecursiveAncestor"}},"x-parser-schema-id":""}},"x-parser-schema-id":"RecursiveSelf"},"x-parser-original-schema-format":"application/vnd.aai.asyncapi;version=2.0.0","x-parser-original-payload":"$ref:$.channels.recursive.subscribe.message.payload","schemaFormat":"application/vnd.aai.asyncapi;version=2.0.0","x-parser-message-parsed":true,"x-parser-message-name":""}}},"external/file":{"publish":{"message":{"payload":{"type":"object","properties":{"testExt":{"type":"object","properties":{"children":{"type":"array","items":"$ref:$.channels.external/file.publish.message.payload","x-parser-schema-id":""}},"x-parser-schema-id":""}},"x-parser-schema-id":""},"x-parser-original-schema-format":"application/vnd.aai.asyncapi;version=2.0.0","x-parser-original-payload":"$ref:$.channels.external/file.publish.message.payload","schemaFormat":"application/vnd.aai.asyncapi;version=2.0.0","x-parser-message-parsed":true,"x-parser-message-name":""}}},"nonRecursive":{"subscribe":{"message":{"payload":{"type":"object","properties":{"child":{"type":"object","properties":{"value":{"type":"string","x-parser-schema-id":""}},"x-parser-schema-id":"NonRecursiveChild"}},"x-parser-schema-id":"NonRecursive"},"x-parser-original-schema-format":"application/vnd.aai.asyncapi;version=2.0.0","x-parser-original-payload":{"type":"object","properties":{"child":{"type":"object","properties":{"value":{"type":"string"}}}}},"schemaFormat":"application/vnd.aai.asyncapi;version=2.0.0","x-parser-message-parsed":true,"x-parser-message-name":""}}},"testChannel":{"subscribe":{"message":{"oneOf":[{"contentType":"application/json","payload":{"type":"object","properties":{"schemaBReference":{"type":"string","enum":["ENUM_A","ENUM_B","ENUM_C","ENUM_D"],"x-parser-schema-id":"NormalSchemaB"},"schemaCReference":{"allOf":["$ref:$.channels.testChannel.subscribe.message.oneOf[0].payload.properties.schemaBReference",{"type":"string","enum":["ENUM_E"],"x-parser-schema-id":""}],"x-parser-schema-id":"NormalSchemaC"},"commonEnumName":{"type":"string","enum":["ENUM_1","ENUM_2"],"x-parser-schema-id":""}},"x-parser-schema-id":"NormalSchemaA"},"x-parser-original-schema-format":"application/vnd.aai.asyncapi;version=2.0.0","x-parser-original-payload":{"type":"object","properties":{"schemaBReference":{"type":"string","enum":["ENUM_A","ENUM_B","ENUM_C","ENUM_D"]},"schemaCReference":{"allOf":["$ref:$.channels.testChannel.subscribe.message.oneOf[0].x-parser-original-payload.properties.schemaBReference",{"type":"string","enum":["ENUM_E"]}]},"commonEnumName":{"type":"string","enum":["ENUM_1","ENUM_2"]}}},"schemaFormat":"application/vnd.aai.asyncapi;version=2.0.0","x-parser-message-parsed":true,"x-parser-message-name":"testMessage"}]}}}},"components":{"messages":{"testMessage":"$ref:$.channels.testChannel.subscribe.message.oneOf[0]"},"schemas":{"NonRecursive":"$ref:$.channels.nonRecursive.subscribe.message.payload","NonRecursiveChild":"$ref:$.channels.nonRecursive.subscribe.message.payload.properties.child","RecursiveSelf":"$ref:$.channels.recursive.subscribe.message.payload","RecursiveAncestor":"$ref:$.channels.recursive.subscribe.message.payload.properties.selfSomething.properties.test","NormalSchemaA":"$ref:$.channels.testChannel.subscribe.message.oneOf[0].payload","NormalSchemaB":"$ref:$.channels.testChannel.subscribe.message.oneOf[0].payload.properties.schemaBReference","NormalSchemaC":"$ref:$.channels.testChannel.subscribe.message.oneOf[0].payload.properties.schemaCReference","NestedAllOfSchema":{"allOf":["$ref:$.channels.testChannel.subscribe.message.oneOf[0].payload",{"type":"object","properties":{"parent":{"allOf":["$ref:$.components.schemas.NestedAllOfSchema","$ref:$.channels.testChannel.subscribe.message.oneOf[0].payload"],"x-parser-schema-id":""},"name":{"type":"string","x-parser-schema-id":""}},"required":["name"],"x-parser-schema-id":""}],"x-parser-schema-id":"NestedAllOfSchema"},"OneOf":{"type":"object","properties":{"kind":{"oneOf":["$ref:$.components.schemas.OneOf",{"type":"string","x-parser-schema-id":""},{"enum":["boolean","string"],"x-parser-schema-id":""}],"x-parser-schema-id":""}},"x-parser-schema-id":"OneOf"},"AnyOf":{"anyOf":[{"type":"integer","x-parser-schema-id":""},{"type":"number","x-parser-schema-id":""},{"type":"string","x-parser-schema-id":""},{"type":"boolean","x-parser-schema-id":""},{"type":"object","x-parser-schema-id":""},{"type":"array","items":"$ref:$.components.schemas.AnyOf","x-parser-schema-id":""}],"x-parser-schema-id":"AnyOf"},"RecursiveComplex":{"type":["object","array"],"patternProperties":{"^foo":"$ref:$.channels.recursive.subscribe.message.payload","^bar":{"type":"string","x-parser-schema-id":""}},"contains":"$ref:$.components.schemas.RecursiveComplex","items":[{"type":"string","x-parser-schema-id":""},"$ref:$.components.schemas.RecursiveComplex"],"if":"$ref:$.channels.recursive.subscribe.message.payload.properties.selfSomething.properties.test","then":"$ref:$.components.schemas.RecursiveComplex","x-parser-schema-id":"RecursiveComplex"}}},"x-parser-circular":true,"x-parser-spec-parsed":true,"x-parser-spec-stringified":true}'; +*/ + +describe('AsyncAPIDocument', function() { + const parser = new Parser(); + + describe('info()', function() { + it('should return an info object', function() { + const doc: any = { info: { title: 'Test', version: '1.2.3', license: { name: 'Apache 2.0', url: 'https://www.apache.org/licenses/LICENSE-2.0' } } }; + const d = new AsyncAPIDocument(doc); + expect(d.info()).toBeInstanceOf(Info); + expect(d.info().json()).toEqual(doc.info); + }); + }); + + describe('id()', function() { + it('should return the id string', function() { + const doc: any = { id: 'urn:test' }; + const d = new AsyncAPIDocument(doc); + expect(d.id()).toEqual(doc.id); + }); + }); + + describe('hasServers()', function() { + it('should return a boolean indicating if the AsyncAPI document has servers', function() { + const doc: any = { servers: { test1: { url: 'test1' }, test2: { url: 'test2' } } }; + const docNoServers: any = { test: 'testing' }; + const d = new AsyncAPIDocument(doc); + const d2 = new AsyncAPIDocument(docNoServers); + expect(d.hasServers()).toEqual(true); + expect(d2.hasServers()).toEqual(false); + }); + }); + + describe('servers()', function() { + it('should return a map of server objects', function() { + const doc: any = { servers: { test1: { url: 'test1' }, test2: { url: 'test2' } } }; + const d = new AsyncAPIDocument(doc); + expect(typeof d.servers()).toEqual('object'); + expect(d.servers().test1).toBeInstanceOf(Server); + expect(d.servers().test1.json()).toEqual(doc.servers.test1); + expect(d.servers().test2).toBeInstanceOf(Server); + expect(d.servers().test2.json()).toEqual(doc.servers.test2); + }); + + it('should return an empty object if the AsyncAPI document has no defined servers', function() { + const doc: any = {}; + const d = new AsyncAPIDocument(doc); + expect(typeof d.servers()).toEqual('object'); + expect(d.servers()).toEqual({}); + }); + }); + + describe('serverNames()', function() { + it('should return an array of strings', function() { + const doc: any = { servers: { test1: { url: 'test1' }, test2: { url: 'test2' } } }; + const d = new AsyncAPIDocument(doc); + expect(Array.isArray(d.serverNames())).toEqual(true); + expect(d.serverNames()).toEqual(['test1', 'test2']); + }); + + it('should return an empty array if the AsyncAPI document has no defined servers', function() { + const doc: any = {}; + const d = new AsyncAPIDocument(doc); + expect(Array.isArray(d.serverNames())).toEqual(true); + expect(d.serverNames()).toEqual([]); + }); + }); + + describe('server()', function() { + it('should return a specific server object', function() { + const doc: any = { servers: { test1: { url: 'test1' }, test2: { url: 'test2' } } }; + const d = new AsyncAPIDocument(doc); + expect(d.server('test1')).toBeInstanceOf(Server); + expect(d.server('test1')?.json()).toEqual(doc.servers.test1); + }); + + it('should return null if a server name is not provided', function() { + const doc: any = { servers: { test1: { url: 'test1' }, test2: { url: 'test2' } } }; + const d = new AsyncAPIDocument(doc); + expect(d.server(undefined as any)).toEqual(null); + }); + + it('should return null if a server name is not found', function() { + const doc: any = { servers: { test1: { url: 'test1' }, test2: { url: 'test2' } } }; + const d = new AsyncAPIDocument(doc); + expect(d.server('not found')).toEqual(null); + }); + }); + + describe('hasDefaultContentType()', function() { + it('should return true if field exists', function() { + const doc: any = { defaultContentType: 'application/json' }; + const d = new AsyncAPIDocument(doc); + expect(d.hasDefaultContentType()).toEqual(true); + }); + + it('should return false if field does not exist', function() { + const doc: any = {}; + const d = new AsyncAPIDocument(doc); + expect(d.hasDefaultContentType()).toEqual(false); + }); + }); + + describe('defaultContentType()', function() { + it('should return string if field exists', function() { + const doc: any = { defaultContentType: 'application/json' }; + const d = new AsyncAPIDocument(doc); + expect(d.defaultContentType()).toEqual('application/json'); + }); + + it('should return null if field does not exist', function() { + const doc: any = {}; + const d = new AsyncAPIDocument(doc); + expect(d.defaultContentType()).toEqual(null); + }); + }); + + describe('hasChannels()', function() { + it('should return a boolean indicating if the AsyncAPI document has channels', function() { + const doc: any = { channels: { test1: { description: 'test1' }, test2: { description: 'test2' } } }; + const docNoChannels: any = { test: 'testing' }; + const d = new AsyncAPIDocument(doc); + const d2 = new AsyncAPIDocument(docNoChannels); + expect(d.hasChannels()).toEqual(true); + expect(d2.hasChannels()).toEqual(false); + }); + }); + + describe('channels()', function() { + it('should return a map of channel objects', function() { + const doc: any = { channels: { test1: { description: 'test1' }, test2: { description: 'test2' } } }; + const d = new AsyncAPIDocument(doc); + expect(typeof d.channels()).toEqual('object'); + expect(d.channels().test1).toBeInstanceOf(Channel); + expect(d.channels().test1.json()).toEqual(doc.channels.test1); + expect(d.channels().test2).toBeInstanceOf(Channel); + expect(d.channels().test2.json()).toEqual(doc.channels.test2); + }); + + it('should return an empty object if the AsyncAPI document has no defined channels', function() { + const doc: any = {}; + const d = new AsyncAPIDocument(doc); + expect(typeof d.channels()).toEqual('object'); + expect(d.servers()).toEqual({}); + }); + }); + + describe('channelNames()', function() { + it('should return an array of strings', function() { + const doc: any = { channels: { test1: { description: 'test1' }, test2: { description: 'test2' } } }; + const d = new AsyncAPIDocument(doc); + expect(Array.isArray(d.channelNames())).toEqual(true); + expect(d.channelNames()).toEqual(['test1', 'test2']); + }); + + it('should return an empty array if the AsyncAPI document has no defined channels', function() { + const doc: any = {}; + const d = new AsyncAPIDocument(doc); + expect(Array.isArray(d.channelNames())).toEqual(true); + expect(d.channelNames()).toEqual([]); + }); + }); + + describe('channel()', function() { + it('should return a specific channel object', function() { + const doc: any = { channels: { test1: { description: 'test1' }, test2: { description: 'test2' } } }; + const d = new AsyncAPIDocument(doc); + expect(d.channel('test1')).toBeInstanceOf(Channel); + expect(d.channel('test1')?.json()).toEqual(doc.channels.test1); + }); + + it('should return null if a channel name is not provided', function() { + const doc: any = { channels: { test1: { description: 'test1' }, test2: { description: 'test2' } } }; + const d = new AsyncAPIDocument(doc); + expect(d.channel(undefined as any)).toEqual(null); + }); + + it('should return null if a channel name is not found', function() { + const doc: any = { channels: { test1: { description: 'test1' }, test2: { description: 'test2' } } }; + const d = new AsyncAPIDocument(doc); + expect(d.channel('not found')).toEqual(null); + }); + }); + + describe('hasComponents()', function() { + it('should return a boolean indicating if the AsyncAPI document has components', function() { + const doc: any = { components: { test1: { description: 'test1' }, test2: { description: 'test2' } } }; + const docNoComponents: any = { test: 'testing' }; + const d = new AsyncAPIDocument(doc); + const d2 = new AsyncAPIDocument(docNoComponents); + expect(d.hasComponents()).toEqual(true); + expect(d2.hasComponents()).toEqual(false); + }); + }); + + describe('components()', function() { + it('should return the components object', function() { + const doc: any = { components: { test: 'testing' } }; + const d = new AsyncAPIDocument(doc); + expect(d.components()).toBeInstanceOf(Components); + expect(d.components()?.json()).toEqual(doc.components); + }); + }); + + describe('hasMessages()', function() { + it('should return true if there is a message in components but not in channels', function() { + const doc: any = { components: { messages: { test: { test: true, k: 3 } } } }; + const d = new AsyncAPIDocument(doc); + expect(d.hasMessages()).toEqual(true); + }); + it('should return true if there is a message in channels operations but not in components', function() { + const doc: any = { channels: { test: { publish: { message: { name: 'test', test: false, k: 1 } } } } }; + const d = new AsyncAPIDocument(doc); + expect(d.hasMessages()).toEqual(true); + }); + it('should return false if there are no messages neither in components nor in channels operations', function() { + const doc: any = { channels: { test: { publish: { } } }, components: { } }; + const d = new AsyncAPIDocument(doc); + expect(d.hasMessages()).toEqual(false); + }); + }); + + describe('allMessages()', function() { + it('should return an array with all the messages used in the document and overwrite the message from channel', async function() { + const doc: any = { asyncapi: '2.0.0', info: { title: 'AsyncAPI Test', version: '0.1.0' }, channels: { test: { publish: { message: { name: 'test' } } } }, components: { messages: { test: {} } } }; + const { document } = await parser.parse(doc); + const d = convertToOldAPI(document!); + const allMessages = d.allMessages(); + expect(allMessages.size).toEqual(1); + expect(allMessages.get('test')).toBeInstanceOf(Message); + }); + + it('should return an array with all the messages used in the document', async function() { + const doc: any = { asyncapi: '2.0.0', info: { title: 'AsyncAPI Test', version: '0.1.0' }, channels: { test: { publish: { message: {} } }, test2: { subscribe: { message: { name: 'test' } } } }, components: { messages: { test: {} } } }; + const { document } = await parser.parse(doc); + const d = convertToOldAPI(document!); + expect(d.allMessages().size).toEqual(2); + d.allMessages().forEach(t => { + expect(t).toBeInstanceOf(Message); + }); + }); + }); + + describe('allSchemas()', function() { + it('should return additional items schemas when no items specified', async function() { + const doc: any = { + asyncapi: '2.0.0', + info: { + title: 'AsyncAPI Test', + version: '0.1.0', + }, + channels: { + some_channel: { + subscribe: { + message: { + name: 'some_map', + payload: { + type: 'array', + $id: 'payloadSchema', + test: true, + additionalItems: { + type: 'string', + $id: 'additionalItemSchema', + test: true + } + } + } + } + } + } + }; + const { document } = await parser.parse(doc); + const d = convertToOldAPI(document!); + const schemas = d.allSchemas(); + expect(schemas.size).toEqual(2); + //Ensure the actual keys are as expected + const schemaKeys = Array.from(schemas.keys()); + expect(schemaKeys).toEqual([ + 'payloadSchema', + 'additionalItemSchema' + ]); + for (const t of schemas.values()) { + expect(t).toBeInstanceOf(Schema); + expect((t.json() as any).test).toEqual(true); + } + }); + + it('should return additional property schemas when no properties are specified', async function() { + const doc: any = { + asyncapi: '2.0.0', + info: { + title: 'AsyncAPI Test', + version: '0.1.0', + }, + channels: { + some_channel: { + subscribe: { + message: { + name: 'some_map', + payload: { + type: 'object', + $id: 'payloadSchema', + test: true, + additionalProperties: { + type: 'string', + $id: 'additionalPropSchema', + test: true + } + } + } + } + } + } + }; + const { document } = await parser.parse(doc); + const d = convertToOldAPI(document!); + const schemas = d.allSchemas(); + expect(schemas.size).toEqual(2); + //Ensure the actual keys are as expected + const schemaKeys = Array.from(schemas.keys()); + expect(schemaKeys).toEqual([ + 'payloadSchema', + 'additionalPropSchema' + ]); + for (const t of schemas.values()) { + expect(t).toBeInstanceOf(Schema); + expect((t.json() as any).test).toEqual(true); + } + }); + + it('should return a map with all the schemas used in the document', async function() { + const doc: any = { asyncapi: '2.0.0', info: { title: 'AsyncAPI Test', version: '0.1.0' }, channels: { test: { parameters: { testParam1: { schema: { $id: 'testParamSchema', test: true, k: 0 } } }, publish: { message: { headers: { type: 'object', test: true, k: 1 }, payload: { test: true, k: 2 } } } }, test2: { subscribe: { message: { payload: { $id: 'testPayload', test: true, k: 2 } } } } }, components: { schemas: { testSchema: { test: true, k: 3 } } } }; + const { document } = await parser.parse(doc); + const d = convertToOldAPI(document!); + const schemas = d.allSchemas(); + expect(schemas.size).toEqual(5); + + //Ensure the actual keys are as expected + const schemaKeys = Array.from(schemas.keys()); + expect(schemaKeys).toEqual([ + 'testParamSchema', + '', + '', + 'testPayload', + 'testSchema' + ]); + for (const t of schemas.values()) { + expect(t).toBeInstanceOf(Schema); + expect((t.json() as any).test).toEqual(true); + } + }); + + it('should return a map with all the nested schemas', async function() { + const nestedSchemas = fs.readFileSync(path.resolve(__dirname, '../mocks/nested-schemas.yaml'), 'utf8'); + const { document } = await parser.parse(nestedSchemas); + const d = convertToOldAPI(document!); + const schemas = d.allSchemas(); + + // const doc = JSON.parse(fs.readFileSync(path.resolve(__dirname, '../mocks/nested-schemas.json'), 'utf8')); + // const d = new AsyncAPIDocument(doc); + + //Ensure the actual keys are as expected + const schemaKeys = Array.from(schemas.keys()); + expect(schemaKeys).toEqual([ + 'testParamSchema', + 'testParamNestedSchemaProp', + 'testParamNestedNestedSchemaProp2', + 'testHeaderSchema', + 'testHeaderNestedSchemaProp', + 'testHeaderNestedNestedSchemaProp1', + 'testHeaderNestedSchemaPropArray', + 'testHeaderNestedSchemaPropArrayProp1', + 'testPayloadSchema', + 'testPayloadNestedSchemaProp', + 'testPayloadNestedNestedSchemaProp1', + 'testPayloadNestedSchemaPropArray', + 'testPayloadNestedSchemaPropArrayProp1', + 'testPayload', + 'testComponentSchemaSchema', + 'testComponentSchemaNestedSchemaPropAllOf', + 'testComponentSchemaNestedSchemaPropAllOfSchema1', + 'testComponentSchemaNestedSchemaPropAllOfSchema1Prop1', + 'testComponentSchemaNestedSchemaPropAllOfSchema2', + 'testComponentSchemaNestedSchemaPropAllOfSchema2Prop1', + 'testComponentSchemaNestedSchemaPropArray', + 'testComponentSchemaNestedSchemaPropArrayProp1', + 'testComponentSchemaNestedSchemaPropArrayProp2', + 'testComponentSchemaNestedSchemaPropPatternProperties', + 'testComponentSchemaNestedSchemaPropPatternPropertiesProp1', + 'testComponentSchemaNestedSchemaPropPatternPropertiesProp2', + 'testComponentSchemaNestedSchemaPropConditional', + 'testComponentSchemaNestedSchemaPropConditionalIf', + 'testComponentSchemaNestedSchemaPropConditionalThen', + 'testComponentSchemaNestedSchemaPropConditionalElse', + 'testComponentSchemaNestedSchemaPropDependencies', + 'testComponentSchemaNestedSchemaPropDependenciesDep1', + 'testComponentSchemaNestedSchemaPropDependenciesDep3', + 'testComponentSchemaNestedSchemaPropDefinitions', + 'testComponentSchemaNestedSchemaPropDefinitionsDef1', + 'testComponentSchemaNestedSchemaPropDefinitionsDef2', + 'testComponentSchemaNestedSchemaPropMisc', + 'testComponentSchemaNestedSchemaPropMiscPropertyNames', + 'testComponentSchemaNestedSchemaPropMiscContains', + 'testComponentSchemaNestedSchemaPropMiscNot', + ]); + for (const t of schemas.values()) { + expect(t).toBeInstanceOf(Schema); + expect((t.json() as any).test).toEqual(true); + } + }); + }); + + /* eslint-disable sonarjs/cognitive-complexity */ + describe('traverseSchemas()', function() { // NOSONAR + let d: AsyncAPIDocument; + beforeEach(async () => { + const nestedSchemas = fs.readFileSync(path.resolve(__dirname, '../mocks/nested-schemas.yaml'), 'utf8'); + const { document } = await parser.parse(nestedSchemas); + d = convertToOldAPI(document!); + }); + + const parameterSchemas = [ + 'testParamSchema', + 'testParamNestedSchemaProp', + 'testParamNestedNestedSchemaProp2' + ]; + const headerObjectSchemas = [ + 'testHeaderSchema', + 'testHeaderNestedSchemaProp', + 'testHeaderNestedNestedSchemaProp1', + ]; + const headerArraySchemas = [ + 'testHeaderNestedSchemaPropArray', + 'testHeaderNestedSchemaPropArrayProp1' + ]; + const payloadObjectSchemas = [ + 'testPayloadSchema', + 'testPayloadNestedSchemaProp', + 'testPayloadNestedNestedSchemaProp1' + ]; + const payloadArraySchemas = [ + 'testPayloadNestedSchemaPropArray', + 'testPayloadNestedSchemaPropArrayProp1' + ]; + const payloadSchemas = [ + 'testPayload' + ]; + const componentObjectAllOfSchema = [ + 'testComponentSchemaNestedSchemaPropAllOf', + ]; + const componentObjectAllOfSchemas = [ + 'testComponentSchemaNestedSchemaPropAllOf', + 'testComponentSchemaNestedSchemaPropAllOfSchema1', + 'testComponentSchemaNestedSchemaPropAllOfSchema1Prop1', + 'testComponentSchemaNestedSchemaPropAllOfSchema2', + 'testComponentSchemaNestedSchemaPropAllOfSchema2Prop1', + ]; + const componentObjectSchemas = [ + 'testComponentSchemaSchema' + ]; + const componentArraySchemas = [ + 'testComponentSchemaNestedSchemaPropArray', + 'testComponentSchemaNestedSchemaPropArrayProp1', + 'testComponentSchemaNestedSchemaPropArrayProp2' + ]; + const componentPatternPropertiesSchema = [ + 'testComponentSchemaNestedSchemaPropPatternProperties', + ]; + const componentPatternPropertiesSchemas = [ + ...componentPatternPropertiesSchema, + 'testComponentSchemaNestedSchemaPropPatternPropertiesProp1', + 'testComponentSchemaNestedSchemaPropPatternPropertiesProp2', + ]; + const componentConditionalSchema = [ + 'testComponentSchemaNestedSchemaPropConditional', + ]; + const componentConditionalSchemas = [ + ...componentConditionalSchema, + 'testComponentSchemaNestedSchemaPropConditionalIf', + 'testComponentSchemaNestedSchemaPropConditionalThen', + 'testComponentSchemaNestedSchemaPropConditionalElse', + ]; + const componentDependenciesSchema = [ + 'testComponentSchemaNestedSchemaPropDependencies', + ]; + const componentDependenciesSchemas = [ + ...componentDependenciesSchema, + 'testComponentSchemaNestedSchemaPropDependenciesDep1', + 'testComponentSchemaNestedSchemaPropDependenciesDep3', + ]; + const componentDefinitionsSchema = [ + 'testComponentSchemaNestedSchemaPropDefinitions', + ]; + const componentDefinitionsSchemas = [ + ...componentDefinitionsSchema, + 'testComponentSchemaNestedSchemaPropDefinitionsDef1', + 'testComponentSchemaNestedSchemaPropDefinitionsDef2', + ]; + const componentMiscSchema = [ + 'testComponentSchemaNestedSchemaPropMisc', + ]; + const componentMiscSchemas = [ + ...componentMiscSchema, + 'testComponentSchemaNestedSchemaPropMiscPropertyNames', + 'testComponentSchemaNestedSchemaPropMiscContains', + 'testComponentSchemaNestedSchemaPropMiscNot', + ]; + + it('should not include parameter schemas if defined', function() { + const schemas = new Map(); + const cb = (schema) => { + schemas.set(schema.uid(), schema); + }; + d.traverseSchemas(cb, [ + 'objects', + 'arrays', + 'components', + 'oneOfs', + 'allOfs', + 'anyOfs', + 'payloads', + 'headers', + 'patternProperties', + 'ifs', + 'thenes', + 'elses', + 'dependencies', + 'definitions', + ]); + + //Ensure the actual keys are as expected + const schemaKeys = Array.from(schemas.keys()); + expect(schemaKeys).toEqual([ + ...headerObjectSchemas, + ...headerArraySchemas, + ...payloadObjectSchemas, + ...payloadArraySchemas, + ...payloadSchemas, + ...componentObjectSchemas, + ...componentObjectAllOfSchemas, + ...componentArraySchemas, + ...componentPatternPropertiesSchemas, + ...componentConditionalSchemas, + ...componentDependenciesSchemas, + ...componentDefinitionsSchemas, + ...componentMiscSchema, + ]); + for (const t of schemas.values()) { + expect(t).toBeInstanceOf(Schema); + expect(t.json().test).toEqual(true); + } + }); + + it('should not include payload schemas if defined', function() { + const schemas = new Map(); + const cb = (schema) => { + schemas.set(schema.uid(), schema); + }; + d.traverseSchemas(cb, [ + 'objects', + 'arrays', + 'components', + 'oneOfs', + 'allOfs', + 'anyOfs', + 'parameters', + 'headers', + 'patternProperties', + 'ifs', + 'thenes', + 'elses', + 'dependencies', + 'definitions', + ]); + + //Ensure the actual keys are as expected + const schemaKeys = Array.from(schemas.keys()); + expect(schemaKeys).toEqual([ + ...parameterSchemas, + ...headerObjectSchemas, + ...headerArraySchemas, + ...componentObjectSchemas, + ...componentObjectAllOfSchemas, + ...componentArraySchemas, + ...componentPatternPropertiesSchemas, + ...componentConditionalSchemas, + ...componentDependenciesSchemas, + ...componentDefinitionsSchemas, + ...componentMiscSchema, + ]); + for (const t of schemas.values()) { + expect(t).toBeInstanceOf(Schema); + expect(t.json().test).toEqual(true); + } + }); + + it('should not include header schemas if defined', function() { + const schemas = new Map(); + const cb = (schema) => { + schemas.set(schema.uid(), schema); + }; + d.traverseSchemas(cb, [ + 'objects', + 'arrays', + 'components', + 'oneOfs', + 'allOfs', + 'anyOfs', + 'parameters', + 'payloads', + 'patternProperties', + 'ifs', + 'thenes', + 'elses', + 'dependencies', + 'definitions', + ]); + + //Ensure the actual keys are as expected + const schemaKeys = Array.from(schemas.keys()); + expect(schemaKeys).toEqual([ + ...parameterSchemas, + ...payloadObjectSchemas, + ...payloadArraySchemas, + ...payloadSchemas, + ...componentObjectSchemas, + ...componentObjectAllOfSchemas, + ...componentArraySchemas, + ...componentPatternPropertiesSchemas, + ...componentConditionalSchemas, + ...componentDependenciesSchemas, + ...componentDefinitionsSchemas, + ...componentMiscSchema, + ]); + for (const t of schemas.values()) { + expect(t).toBeInstanceOf(Schema); + expect(t.json().test).toEqual(true); + } + }); + + it('should not include arrays if defined', function() { + const schemas = new Map(); + const cb = (schema) => { + schemas.set(schema.uid(), schema); + }; + d.traverseSchemas(cb, [ + 'objects', + 'components', + 'oneOfs', + 'allOfs', + 'anyOfs', + 'parameters', + 'payloads', + 'headers', + 'patternProperties', + 'ifs', + 'thenes', + 'elses', + 'dependencies', + 'definitions', + ]); + + //Ensure the actual keys are as expected + const schemaKeys = Array.from(schemas.keys()); + expect(schemaKeys).toEqual([ + ...parameterSchemas, + ...headerObjectSchemas, + ...payloadObjectSchemas, + ...payloadSchemas, + ...componentObjectSchemas, + ...componentObjectAllOfSchemas, + ...componentPatternPropertiesSchemas, + ...componentConditionalSchemas, + ...componentDependenciesSchemas, + ...componentDefinitionsSchemas, + ]); + for (const t of schemas.values()) { + expect(t).toBeInstanceOf(Schema); + expect(t.json().test).toEqual(true); + } + }); + + it('should not include components if defined', function() { + const schemas = new Map(); + const cb = (schema) => { + schemas.set(schema.uid(), schema); + }; + d.traverseSchemas(cb, [ + 'objects', + 'arrays', + 'oneOfs', + 'allOfs', + 'anyOfs', + 'parameters', + 'payloads', + 'headers', + ]); + + //Ensure the actual keys are as expected + const schemaKeys = Array.from(schemas.keys()); + expect(schemaKeys).toEqual([ + ...parameterSchemas, + ...headerObjectSchemas, + ...headerArraySchemas, + ...payloadObjectSchemas, + ...payloadArraySchemas, + ...payloadSchemas, + ]); + for (const t of schemas.values()) { + expect(t).toBeInstanceOf(Schema); + expect(t.json().test).toEqual(true); + } + }); + + it('should not include combined schemas if defined', function() { + const schemas = new Map(); + const cb = (schema) => { + schemas.set(schema.uid(), schema); + }; + d.traverseSchemas(cb, [ + 'objects', + 'arrays', + 'parameters', + 'payloads', + 'headers', + 'components', + 'patternProperties', + 'ifs', + 'thenes', + 'elses', + 'dependencies', + 'definitions', + ]); + + //Ensure the actual keys are as expected + const schemaKeys = Array.from(schemas.keys()); + expect(schemaKeys).toEqual([ + ...parameterSchemas, + ...headerObjectSchemas, + ...headerArraySchemas, + ...payloadObjectSchemas, + ...payloadArraySchemas, + ...payloadSchemas, + ...componentObjectSchemas, + ...componentObjectAllOfSchema, + ...componentArraySchemas, + ...componentPatternPropertiesSchemas, + ...componentConditionalSchemas, + ...componentDependenciesSchemas, + ...componentDefinitionsSchemas, + ...componentMiscSchema, + ]); + for (const t of schemas.values()) { + expect(t).toBeInstanceOf(Schema); + expect(t.json().test).toEqual(true); + } + }); + + it('should not include conditional schemas if defined', function() { + const schemas = new Map(); + const cb = (schema) => { + schemas.set(schema.uid(), schema); + }; + d.traverseSchemas(cb, [ + 'objects', + 'arrays', + 'parameters', + 'payloads', + 'headers', + 'components', + 'patternProperties', + 'dependencies', + 'definitions', + ]); + + //Ensure the actual keys are as expected + const schemaKeys = Array.from(schemas.keys()); + expect(schemaKeys).toEqual([ + ...parameterSchemas, + ...headerObjectSchemas, + ...headerArraySchemas, + ...payloadObjectSchemas, + ...payloadArraySchemas, + ...payloadSchemas, + ...componentObjectSchemas, + ...componentObjectAllOfSchema, + ...componentArraySchemas, + ...componentPatternPropertiesSchemas, + ...componentConditionalSchema, + ...componentDependenciesSchemas, + ...componentDefinitionsSchemas, + ...componentMiscSchema, + ]); + for (const t of schemas.values()) { + expect(t).toBeInstanceOf(Schema); + expect(t.json().test).toEqual(true); + } + }); + + it('should not include dependencies schemas if defined', function() { + const schemas = new Map(); + const cb = (schema) => { + schemas.set(schema.uid(), schema); + }; + d.traverseSchemas(cb, [ + 'objects', + 'arrays', + 'parameters', + 'payloads', + 'headers', + 'components', + 'patternProperties', + 'ifs', + 'thenes', + 'elses', + 'definitions', + ]); + + //Ensure the actual keys are as expected + const schemaKeys = Array.from(schemas.keys()); + expect(schemaKeys).toEqual([ + ...parameterSchemas, + ...headerObjectSchemas, + ...headerArraySchemas, + ...payloadObjectSchemas, + ...payloadArraySchemas, + ...payloadSchemas, + ...componentObjectSchemas, + ...componentObjectAllOfSchema, + ...componentArraySchemas, + ...componentPatternPropertiesSchemas, + ...componentConditionalSchemas, + ...componentDependenciesSchema, + ...componentDefinitionsSchemas, + ...componentMiscSchema, + ]); + for (const t of schemas.values()) { + expect(t).toBeInstanceOf(Schema); + expect(t.json().test).toEqual(true); + } + }); + + it('should not include definitions schemas if defined', function() { + const schemas = new Map(); + const cb = (schema) => { + schemas.set(schema.uid(), schema); + }; + d.traverseSchemas(cb, [ + 'objects', + 'arrays', + 'parameters', + 'payloads', + 'headers', + 'components', + 'patternProperties', + 'ifs', + 'thenes', + 'elses', + 'dependencies', + ]); + + //Ensure the actual keys are as expected + const schemaKeys = Array.from(schemas.keys()); + expect(schemaKeys).toEqual([ + ...parameterSchemas, + ...headerObjectSchemas, + ...headerArraySchemas, + ...payloadObjectSchemas, + ...payloadArraySchemas, + ...payloadSchemas, + ...componentObjectSchemas, + ...componentObjectAllOfSchema, + ...componentArraySchemas, + ...componentPatternPropertiesSchemas, + ...componentConditionalSchemas, + ...componentDependenciesSchemas, + ...componentDefinitionsSchema, + ...componentMiscSchema, + ]); + for (const t of schemas.values()) { + expect(t).toBeInstanceOf(Schema); + expect(t.json().test).toEqual(true); + } + }); + it('should include all schemas', function() { + const schemas = new Map(); + const cb = (schema) => { + schemas.set(schema.uid(), schema); + }; + d.traverseSchemas(cb); + + //Ensure the actual keys are as expected + const schemaKeys = Array.from(schemas.keys()); + expect(schemaKeys).toEqual([ + ...parameterSchemas, + ...headerObjectSchemas, + ...headerArraySchemas, + ...payloadObjectSchemas, + ...payloadArraySchemas, + ...payloadSchemas, + ...componentObjectSchemas, + ...componentObjectAllOfSchemas, + ...componentArraySchemas, + ...componentPatternPropertiesSchemas, + ...componentConditionalSchemas, + ...componentDependenciesSchemas, + ...componentDefinitionsSchemas, + ...componentMiscSchemas, + ]); + for (const t of schemas.values()) { + expect(t).toBeInstanceOf(Schema); + expect(t.json().test).toEqual(true); + } + }); + }); + /* eslint-enable sonarjs/cognitive-complexity */ + + describe('stringify()', function() { + it('should stringify simple document', async function() { + const source = path.resolve(__dirname, '../mocks/simple-with-refs.yaml'); + const nestedSchemas = fs.readFileSync(source, 'utf8'); + const { document } = await parser.parse(nestedSchemas, { source }); + const doc = convertToOldAPI(document!); + const output = {asyncapi: '2.0.0',info: {title: 'Test API',version: '1.0.0'},channels: {mychannel: {publish: {message: {payload: {type: 'object',properties: {name: {type: 'string','x-parser-schema-id': ''}},'x-parser-schema-id': ''},'x-parser-message-name': '','x-parser-original-schema-format': 'application/vnd.aai.asyncapi;version=2.0.0',schemaFormat: 'application/vnd.aai.asyncapi;version=2.0.0','x-parser-original-payload': '$ref:$.channels.mychannel.publish.message.payload','x-parser-message-parsed': true}}}},components: {messages: {testMessage: {payload: {type: 'object',properties: {name: {type: 'string','x-parser-schema-id': ''},test: {type: 'object',properties: {testing: {type: 'string','x-parser-schema-id': ''}},'x-parser-schema-id': ''}},'x-parser-schema-id': 'testSchema'},'x-parser-message-name': 'testMessage','x-parser-original-schema-format': 'application/vnd.aai.asyncapi;version=2.0.0',schemaFormat: 'application/vnd.aai.asyncapi;version=2.0.0','x-parser-original-payload': '$ref:$.components.messages.testMessage.payload','x-parser-message-parsed': true}},schemas: {testSchema: '$ref:$.components.messages.testMessage.payload'}},'x-parser-spec-parsed': true,'x-parser-spec-stringified': true}; + + const stringified = AsyncAPIDocument.stringify(doc); + expect(stringified).toEqual(JSON.stringify(output)); + }); + + it('should stringify document with circular references', async function() { + const source = path.resolve(__dirname, '../mocks/circular-refs.yaml'); + const nestedSchemas = fs.readFileSync(source, 'utf8'); + const { document } = await parser.parse(nestedSchemas, { source }); + const doc = convertToOldAPI(document!); + const output = {asyncapi: '2.0.0',info: {title: 'Test API',version: '1.0.0'},channels: {mychannel: {publish: {message: {payload: {type: 'object',properties: {name: {type: 'string','x-parser-schema-id': ''}},'x-parser-schema-id': ''},'x-parser-message-name': '','x-parser-original-schema-format': 'application/vnd.aai.asyncapi;version=2.0.0',schemaFormat: 'application/vnd.aai.asyncapi;version=2.0.0','x-parser-original-payload': '$ref:$.channels.mychannel.publish.message.payload','x-parser-message-parsed': true}}}},components: {messages: {testMessage: {payload: {type: 'object',properties: {name: {type: 'string','x-parser-schema-id': ''},deep: {type: 'object',properties: {circular: {$ref: '#/components/schemas/testSchema','x-parser-schema-id': ''}},'x-parser-schema-id': ''}},'x-parser-schema-id': 'testSchema'},'x-parser-message-name': 'testMessage','x-parser-original-schema-format': 'application/vnd.aai.asyncapi;version=2.0.0',schemaFormat: 'application/vnd.aai.asyncapi;version=2.0.0','x-parser-original-payload': '$ref:$.components.messages.testMessage.payload','x-parser-message-parsed': true}},schemas: {testSchema: '$ref:$.components.messages.testMessage.payload'}},'x-parser-spec-parsed': true,'x-parser-circular': true,'x-parser-spec-stringified': true}; + + const stringified = AsyncAPIDocument.stringify(doc); + expect(stringified).toEqual(JSON.stringify(output)); + }); + + it('should copy object', async function() { + const source = path.resolve(__dirname, '../mocks/simple-with-refs.yaml'); + const nestedSchemas = fs.readFileSync(source, 'utf8'); + const { document } = await parser.parse(nestedSchemas, { source }); + const doc = convertToOldAPI(document!); + const stringified = AsyncAPIDocument.stringify(doc) as string; + expect(doc.json()[xParserSpecStringified]).toEqual(undefined); + expect(JSON.parse(stringified)[xParserSpecStringified]).toEqual(true); + }); + }); + + describe('parse()', function() { + it('should parse stringified simple document', async function() { + const source = path.resolve(__dirname, '../mocks/simple-with-refs.yaml'); + const nestedSchemas = fs.readFileSync(source, 'utf8'); + const { document } = await parser.parse(nestedSchemas, { source }); + const oldDoc = convertToOldAPI(document!); + const output = {asyncapi: '2.0.0',info: {title: 'Test API',version: '1.0.0'},channels: {mychannel: {publish: {message: {payload: {type: 'object',properties: {name: {type: 'string','x-parser-schema-id': ''}},'x-parser-schema-id': ''},'x-parser-message-name': '','x-parser-original-schema-format': 'application/vnd.aai.asyncapi;version=2.0.0',schemaFormat: 'application/vnd.aai.asyncapi;version=2.0.0','x-parser-original-payload': '$ref:$.channels.mychannel.publish.message.payload','x-parser-message-parsed': true}}}},components: {messages: {testMessage: {payload: {type: 'object',properties: {name: {type: 'string','x-parser-schema-id': ''},test: {type: 'object',properties: {testing: {type: 'string','x-parser-schema-id': ''}},'x-parser-schema-id': ''}},'x-parser-schema-id': 'testSchema'},'x-parser-message-name': 'testMessage','x-parser-original-schema-format': 'application/vnd.aai.asyncapi;version=2.0.0',schemaFormat: 'application/vnd.aai.asyncapi;version=2.0.0','x-parser-original-payload': '$ref:$.components.messages.testMessage.payload','x-parser-message-parsed': true}},schemas: {testSchema: '$ref:$.components.messages.testMessage.payload'}},'x-parser-spec-parsed': true,'x-parser-spec-stringified': true}; + const doc = AsyncAPIDocument.parse(output) as AsyncAPIDocument; + expect(JSON.stringify(doc.json())).toEqual(JSON.stringify(oldDoc.json())); + }); + + it('should not parse invalid document', async function() { + const source = path.resolve(__dirname, '../mocks/simple-with-refs.yaml'); + const nestedSchemas = fs.readFileSync(source, 'utf8'); + const { document } = await parser.parse(nestedSchemas, { source }); + const doc = convertToOldAPI(document!); + delete doc.json()[xParserSpecParsed]; + + let error; + try { + AsyncAPIDocument.parse(doc); + } catch (err) { + error = err; + } + expect(error.message).toEqual('Cannot parse invalid AsyncAPI document'); + }); + + it('should parse stringified document with circular references', async function() { + const circularOutput = {asyncapi: '2.0.0',info: {title: 'Test API',version: '1.0.0'},channels: {mychannel: {publish: {message: {payload: {type: 'object',properties: {name: {type: 'string','x-parser-schema-id': ''}},'x-parser-schema-id': ''},'x-parser-message-name': '','x-parser-original-schema-format': 'application/vnd.aai.asyncapi;version=2.0.0',schemaFormat: 'application/vnd.aai.asyncapi;version=2.0.0','x-parser-original-payload': '$ref:$.channels.mychannel.publish.message.payload','x-parser-message-parsed': true}}}},components: {messages: {testMessage: {payload: {type: 'object',properties: {name: {type: 'string','x-parser-schema-id': ''},deep: {type: 'object',properties: {circular: {$ref: '#/components/schemas/testSchema','x-parser-schema-id': ''}},'x-parser-schema-id': ''}},'x-parser-schema-id': 'testSchema'},'x-parser-message-name': 'testMessage','x-parser-original-schema-format': 'application/vnd.aai.asyncapi;version=2.0.0',schemaFormat: 'application/vnd.aai.asyncapi;version=2.0.0','x-parser-original-payload': '$ref:$.components.messages.testMessage.payload','x-parser-message-parsed': true}},schemas: {testSchema: '$ref:$.components.messages.testMessage.payload'}},'x-parser-spec-parsed': true,'x-parser-circular': true,'x-parser-spec-stringified': true}; + const result = AsyncAPIDocument.parse(circularOutput) as AsyncAPIDocument; + + expect(result.hasCircular()).toEqual(true); + expect(result.components()?.schema('testSchema')?.isCircular()).toEqual(false); + expect(result.components()?.schema('testSchema')?.properties()['name']?.isCircular()).toEqual(false); + expect(result.components()?.schema('testSchema')?.properties()['deep']?.isCircular()).toEqual(false); + expect(result.components()?.schema('testSchema')?.properties()['deep']?.properties()['circular']?.isCircular()).toEqual(true); + }); + }); + + assertTagsMixin(AsyncAPIDocument); + assertExtensionsMixin(AsyncAPIDocument); + assertExternalDocumentationMixin(AsyncAPIDocument); +}); \ No newline at end of file diff --git a/test/old-api/base.spec.ts b/test/old-api/base.spec.ts new file mode 100644 index 000000000..a0b706d46 --- /dev/null +++ b/test/old-api/base.spec.ts @@ -0,0 +1,25 @@ +import { Base } from '../../src/old-api/base'; + +describe('Base', function() { + class Model extends Base {} + + describe('json()', function() { + it('should return the whole JSON object', function() { + const doc = { test: 'testing' }; + const d = new Model(doc); + expect(d.json()).toEqual(doc); + }); + + it('should return the value of a given key', function() { + const doc = { test: 'testing' }; + const d = new Model(doc); + expect(d.json('test')).toEqual(doc.test); + }); + + it('should return the value of a given key, even when this is falsy', function() { + const doc = { 0: 'testing' }; + const d = new Model(doc); + expect(d.json(0)).toEqual(doc[0]); + }); + }); +}); \ No newline at end of file diff --git a/test/old-api/channel-parameter.spec.ts b/test/old-api/channel-parameter.spec.ts new file mode 100644 index 000000000..c4978ba62 --- /dev/null +++ b/test/old-api/channel-parameter.spec.ts @@ -0,0 +1,27 @@ +import { ChannelParameter } from '../../src/old-api/channel-parameter'; +import { Schema } from '../../src/old-api/schema'; +import { assertDescriptionMixin, assertExtensionsMixin } from './mixins'; + +import type { v2 } from '../../src/spec-types'; + +describe('ChannelParameter', function() { + const json: v2.ParameterObject = { description: 'param1', location: '$message.headers#/x-param1', schema: { type: 'string' }, 'x-test': 'testing' }; + + describe('location()', function() { + it('should return a string', function() { + const d = new ChannelParameter(json); + expect(d.location()).toEqual(json.location); + }); + }); + + describe('schema()', function() { + it('should return a Schema object', function() { + const d = new ChannelParameter(json); + expect(d.schema()).toBeInstanceOf(Schema); + expect(d.schema()?.json()).toEqual(json.schema); + }); + }); + + assertDescriptionMixin(ChannelParameter); + assertExtensionsMixin(ChannelParameter); +}); \ No newline at end of file diff --git a/test/old-api/channel.spec.ts b/test/old-api/channel.spec.ts new file mode 100644 index 000000000..64253c219 --- /dev/null +++ b/test/old-api/channel.spec.ts @@ -0,0 +1,132 @@ +import { Channel } from '../../src/old-api/channel'; +import { ChannelParameter } from '../../src/old-api/channel-parameter'; +import { Operation } from '../../src/old-api/operation'; +import { assertDescriptionMixin, assertExtensionsMixin, assertBindingsMixin } from './mixins'; + +describe('Channel', function() { + const json: any = { description: 'test', parameters: { param1: { description: 'param1', location: '$message.headers#/x-param1', schema: { type: 'string' } } }, bindings: { amqp: 'test' }, 'x-test': 'testing' }; + const jsonWithServers = { description: 'channel with servers', servers: ['server1', 'server2'] }; + const jsonWithoutServers = { description: 'channel without servers' }; + + describe('hasParameters()', function() { + it('should return a boolean indicating if the AsyncAPI document has channel parameters', function() { + const doc = { parameters: { test1param: { description: 'test1param' } } }; + const docNoChannelParams = { description: 'test' }; + const d = new Channel(doc); + const d2 = new Channel(docNoChannelParams); + expect(d.hasParameters()).toEqual(true); + expect(d2.hasParameters()).toEqual(false); + }); + }); + + describe('parameters()', function() { + it('should return a map of ChannelParameter objects', function() { + const d = new Channel(json); + expect(typeof d.parameters()).toEqual('object'); + expect(d.parameters().param1).toBeInstanceOf(ChannelParameter); + expect(d.parameters().param1.json()).toEqual(json.parameters.param1); + }); + }); + + describe('parameter()', function() { + it('should return a specific ChannelParameter object', function() { + const d = new Channel(json); + expect(d.parameter('param1')).toBeInstanceOf(ChannelParameter); + expect(d.parameter('param1')?.json()).toEqual(json.parameters.param1); + }); + }); + + describe('hasServers()', function() { + it('should return a boolean indicating if the channel has a servers list', function() { + const d1 = new Channel(jsonWithServers); + const d2 = new Channel(jsonWithoutServers); + expect(d1.hasServers()).toEqual(true); + expect(d2.hasServers()).toEqual(false); + }); + }); + + describe('servers()', function() { + it('should return an array of String server names if the channel has a servers list', function() { + const d = new Channel(jsonWithServers); + expect(Array.isArray(d.servers())).toEqual(true); + d.servers().forEach((s, i) => { + expect(typeof s === 'string').toEqual(true); + expect(s).toEqual(jsonWithServers.servers[i]); + }); + }); + + it('should return an empty array if the channel doesn\'t have servers', function() { + const d = new Channel(jsonWithoutServers); + expect(Array.isArray(d.servers())).toEqual(true); + expect(d.servers().length).toEqual(0); + }); + }); + + describe('server()', function() { + it('should return null if the channel doesn\'t have servers', function() { + const d = new Channel(jsonWithoutServers); + expect(d.server(undefined as any)).toEqual(null); + }); + + it('should return a specific server String name', function() { + const d = new Channel(jsonWithServers); + jsonWithServers.servers.forEach((s, i) => { + expect(d.server(i)).toEqual(s); + }); + }); + + it('should return null when index is out of bounds', function() { + const d1 = new Channel(jsonWithServers); + const d2 = new Channel(jsonWithoutServers); + expect(d1.server(100)).toEqual(null); + expect(d2.server(1)).toEqual(null); + }); + + it('should return null if index is not a number', function() { + const d = new Channel(jsonWithServers); + expect(d.server('0')).toEqual(null); + }); + }); + + describe('publish()', function() { + it('should return a publish Operation object', function() { + const jsWithPub = { publish: { description: 'pub' } }; + const d = new Channel(jsWithPub); + expect(d.publish()).toBeInstanceOf(Operation); + expect(d.publish()?.kind()).toEqual('publish'); + expect(d.publish()?.json()).toEqual(jsWithPub.publish); + }); + }); + + describe('subscribe()', function() { + it('should return a subscribe Operation object', function() { + const jsWithSub = { subscribe: { description: 'sub' } }; + const d = new Channel(jsWithSub); + expect(d.subscribe()).toBeInstanceOf(Operation); + expect(d.subscribe()?.kind()).toEqual('subscribe'); + expect(d.subscribe()?.json()).toEqual(jsWithSub.subscribe); + }); + }); + + describe('hasPublish()', function() { + it('should return true if the channel contains the publish operation', function() { + const d = new Channel({ publish: { description: 'pub' } }); + expect(d.hasPublish()).toEqual(true); + const d2 = new Channel({ subscribe: { description: 'sub' } }); + expect(d2.hasPublish()).toEqual(false); + }); + }); + + describe('hasSubscribe()', function() { + it('should return true if the channel contains the publish operation', function() { + const d = new Channel({ publish: { description: 'pub' } }); + expect(d.hasSubscribe()).toEqual(false); + const d2 = new Channel({ subscribe: { description: 'sub' } }); + expect(d2.hasSubscribe()).toEqual(true); + }); + }); + + assertDescriptionMixin(Channel); + assertExtensionsMixin(Channel); + assertBindingsMixin(Channel); +}); \ No newline at end of file diff --git a/test/old-api/components.spec.ts b/test/old-api/components.spec.ts new file mode 100644 index 000000000..bf04213dd --- /dev/null +++ b/test/old-api/components.spec.ts @@ -0,0 +1,526 @@ +import { Components } from '../../src/old-api/components'; +import { Channel } from '../../src/old-api/channel'; +import { Message } from '../../src/old-api/message'; +import { Schema } from '../../src/old-api/schema'; +import { SecurityScheme } from '../../src/old-api/security-scheme'; +import { Server } from '../../src/old-api/server'; +import { ChannelParameter } from '../../src/old-api/channel-parameter'; +import { CorrelationId } from '../../src/old-api/correlation-id'; +import { OperationTrait } from '../../src/old-api/operation-trait'; +import { MessageTrait } from '../../src/old-api/message-trait'; +import { ServerVariable } from '../../src/old-api/server-variable'; +import { assertExtensionsMixin } from './mixins'; + +describe('Components', function() { + describe('channels()', function() { + it('should return a map of Channel objects', function() { + const doc = { channels: { test1: { description: 'test1' }, test2: { description: 'test2' } } }; + const d = new Components(doc); + expect(typeof d.channels()).toEqual('object'); + expect(d.channels().test1).toBeInstanceOf(Channel); + expect(d.channels().test1.json()).toEqual(doc.channels.test1); + expect(d.channels().test2).toBeInstanceOf(Channel); + expect(d.channels().test2.json()).toEqual(doc.channels.test2); + }); + + it('should return an empty object if the components field has no defined channels', function() { + const doc = {}; + const d = new Components(doc); + expect(typeof d.channels()).toEqual('object'); + expect(d.channels()).toEqual({}); + }); + }); + + describe('hasChannels()', function() { + it('should return a boolean indicating if the components field has channels', function() { + const doc = { channels: { test1: { description: 'test1' }, test2: { description: 'test2' } } }; + const docNoChannels = { schemas: {} }; + const d = new Components(doc); + const d2 = new Components(docNoChannels); + expect(d.hasChannels()).toEqual(true); + expect(d2.hasChannels()).toEqual(false); + }); + }); + + describe('channel()', function() { + it('should return a specific Channel object', function() { + const doc = { channels: { test1: { description: 'test1' }, test2: { description: 'test2' } } }; + const d = new Components(doc); + expect(d.channel('test1')).toBeInstanceOf(Channel); + expect(d.channel('test1')?.json()).toEqual(doc.channels.test1); + }); + + it('should return null if a channel name is not provided', function() { + const doc = { channels: { test1: { description: 'test1' }, test2: { description: 'test2' } } }; + const d = new Components(doc); + expect(d.channel(undefined as any)).toEqual(null); + }); + + it('should return null if a channel name is not found', function() { + const doc = { channels: { test1: { description: 'test1' }, test2: { description: 'test2' } } }; + const d = new Components(doc); + expect(d.channel('not found')).toEqual(null); + }); + }); + + describe('messages()', function() { + it('should return a map of Message objects', function() { + const doc = { messages: { test1: { test: 'test1' }, test2: { test: 'test2' } } }; + const d = new Components(doc); + expect(typeof d.messages()).toEqual('object'); + expect(d.messages().test1).toBeInstanceOf(Message); + expect(d.messages().test1.json()).toEqual(doc.messages.test1); + expect(d.messages().test2).toBeInstanceOf(Message); + expect(d.messages().test2.json()).toEqual(doc.messages.test2); + }); + + it('should return an empty object if the components field has no defined messages', function() { + const doc = {}; + const d = new Components(doc); + expect(typeof d.messages()).toEqual('object'); + expect(d.messages()).toEqual({}); + }); + }); + + describe('hasMessages()', function() { + it('should return a boolean indicating if the components field has messages', function() { + const doc = { messages: { test1: { test: 'test1' }, test2: { test: 'test2' } } }; + const docNoMessages = { schemas: {} }; + const d = new Components(doc); + const d2 = new Components(docNoMessages); + expect(d.hasMessages()).toEqual(true); + expect(d2.hasMessages()).toEqual(false); + }); + }); + + describe('message()', function() { + it('should return a specific Message object', function() { + const doc = { messages: { test1: { test: 'test1' }, test2: { test: 'test2' } } }; + const d = new Components(doc); + expect(d.message('test1')).toBeInstanceOf(Message); + expect(d.message('test1')?.json()).toEqual(doc.messages.test1); + }); + + it('should return null if a message name is not provided', function() { + const doc = { messages: { test1: { test: 'test1' }, test2: { test: 'test2' } } }; + const d = new Components(doc); + expect(d.message(undefined as any)).toEqual(null); + }); + + it('should return null if a message name is not found', function() { + const doc = { messages: { test1: { test: 'test1' }, test2: { test: 'test2' } } }; + const d = new Components(doc); + expect(d.message('not found')).toEqual(null); + }); + }); + + describe('schemas()', function() { + it('should return a map of Schema objects', function() { + const doc = { schemas: { test1: { test: 'test1' }, test2: { test: 'test2' } } }; + const d = new Components(doc); + expect(typeof d.schemas()).toEqual('object'); + expect(d.schemas().test1).toBeInstanceOf(Schema); + expect(d.schemas().test1.json()).toEqual(doc.schemas.test1); + expect(d.schemas().test2).toBeInstanceOf(Schema); + expect(d.schemas().test2.json()).toEqual(doc.schemas.test2); + }); + + it('should return an empty object if the components field has no defined schemas', function() { + const doc = {}; + const d = new Components(doc); + expect(typeof d.schemas()).toEqual('object'); + expect(d.schemas()).toEqual({}); + }); + }); + + describe('hasSchemas()', function() { + it('should return a boolean indicating if the components field has schemas', function() { + const doc = { schemas: { test1: { test: 'test1' }, test2: { test: 'test2' } } }; + const docNoSchemas = { messages: {} }; + const d = new Components(doc); + const d2 = new Components(docNoSchemas); + expect(d.hasSchemas()).toEqual(true); + expect(d2.hasSchemas()).toEqual(false); + }); + }); + + describe('schema()', function() { + it('should return a specific Schema object', function() { + const doc = { schemas: { test1: { test: 'test1' }, test2: { test: 'test2' } } }; + const d = new Components(doc); + expect(d.schema('test1')).toBeInstanceOf(Schema); + expect(d.schema('test1')?.json()).toEqual(doc.schemas.test1); + }); + + it('should return null if a schema name is not provided', function() { + const doc = { schemas: { test1: { test: 'test1' }, test2: { test: 'test2' } } }; + const d = new Components(doc); + expect(d.schema(undefined as any)).toEqual(null); + }); + + it('should return null if a schema name is not found', function() { + const doc = { schemas: { test1: { test: 'test1' }, test2: { test: 'test2' } } }; + const d = new Components(doc); + expect(d.schema('not found')).toEqual(null); + }); + }); + + describe('securitySchemes()', function() { + it('should return a map of Security Scheme objects', function() { + const doc: any = { securitySchemes: { test1: { test: 'test1' }, test2: { test: 'test2' } } }; + const d = new Components(doc); + expect(typeof d.securitySchemes()).toEqual('object'); + expect(d.securitySchemes().test1).toBeInstanceOf(SecurityScheme); + expect(d.securitySchemes().test1.json()).toEqual(doc.securitySchemes.test1); + expect(d.securitySchemes().test2).toBeInstanceOf(SecurityScheme); + expect(d.securitySchemes().test2.json()).toEqual(doc.securitySchemes.test2); + }); + + it('should return an empty object if the components field has no defined securitySchemes', function() { + const doc = {}; + const d = new Components(doc); + expect(typeof d.securitySchemes()).toEqual('object'); + expect(d.securitySchemes()).toEqual({}); + }); + }); + + describe('hasSecuritySchemes()', function() { + it('should return a boolean indicating if the components field has securitySchemes', function() { + const doc: any = { securitySchemes: { test1: { test: 'test1' }, test2: { test: 'test2' } } }; + const docNoSchemas = { messages: {} }; + const d = new Components(doc); + const d2 = new Components(docNoSchemas); + expect(d.hasSecuritySchemes()).toEqual(true); + expect(d2.hasSecuritySchemes()).toEqual(false); + }); + }); + + describe('securityScheme()', function() { + it('should return a specific securityScheme object', function() { + const doc: any = { securitySchemes: { test1: { test: 'test1' }, test2: { test: 'test2' } } }; + const d = new Components(doc); + expect(d.securityScheme('test1')).toBeInstanceOf(SecurityScheme); + expect(d.securityScheme('test1')?.json()).toEqual(doc.securitySchemes.test1); + }); + + it('should return null if a securityScheme name is not provided', function() { + const doc: any = { securitySchemes: { test1: { test: 'test1' }, test2: { test: 'test2' } } }; + const d = new Components(doc); + expect(d.securityScheme(undefined as any)).toEqual(null); + }); + + it('should return null if a securityScheme name is not found', function() { + const doc: any = { securitySchemes: { test1: { test: 'test1' }, test2: { test: 'test2' } } }; + const d = new Components(doc); + expect(d.securityScheme('not found')).toEqual(null); + }); + }); + + describe('servers()', function() { + it('should return a map of Server objects', function() { + const doc: any = { servers: { test1: { url: 'test1' }, test2: { url: 'test2' } } }; + const d = new Components(doc); + expect(typeof d.servers()).toEqual('object'); + expect(d.servers().test1).toBeInstanceOf(Server); + expect(d.servers().test1.json()).toEqual(doc.servers.test1); + expect(d.servers().test2).toBeInstanceOf(Server); + expect(d.servers().test2.json()).toEqual(doc.servers.test2); + }); + + it('should return an empty object if the components field has no defined servers', function() { + const doc = {}; + const d = new Components(doc); + expect(typeof d.servers()).toEqual('object'); + expect(d.servers()).toEqual({}); + }); + }); + + describe('hasServers()', function() { + it('should return a boolean indicating if the components field has servers', function() { + const doc: any = { servers: { test1: { url: 'test1' }, test2: { url: 'test2' } } }; + const docNoServers = { schemas: {} }; + const d = new Components(doc); + const d2 = new Components(docNoServers); + expect(d.hasServers()).toEqual(true); + expect(d2.hasServers()).toEqual(false); + }); + }); + + describe('server()', function() { + it('should return a specific Server object', function() { + const doc: any = { servers: { test1: { url: 'test1' }, test2: { url: 'test2' } } }; + const d = new Components(doc); + expect(d.server('test1')).toBeInstanceOf(Server); + expect(d.server('test1')?.json()).toEqual(doc.servers.test1); + }); + + it('should return null if a message name is not provided', function() { + const doc: any = { servers: { test1: { url: 'test1' }, test2: { url: 'test2' } } }; + const d = new Components(doc); + expect(d.server(undefined as any)).toEqual(null); + }); + + it('should return null if a message name is not found', function() { + const doc: any = { servers: { test1: { url: 'test1' }, test2: { url: 'test2' } } }; + const d = new Components(doc); + expect(d.server('not found')).toEqual(null); + }); + }); + + describe('parameters()', function() { + it('should return a map of ChannelParameter objects', function() { + const doc = { parameters: { test1: { test: 'test1' }, test2: { test: 'test2' } } }; + const d = new Components(doc); + expect(typeof d.parameters()).toEqual('object'); + expect(d.parameters().test1).toBeInstanceOf(ChannelParameter); + expect(d.parameters().test1.json()).toEqual(doc.parameters.test1); + expect(d.parameters().test2).toBeInstanceOf(ChannelParameter); + expect(d.parameters().test2.json()).toEqual(doc.parameters.test2); + }); + + it('should return an empty object if the components field has no defined parameters', function() { + const doc = {}; + const d = new Components(doc); + expect(typeof d.parameters()).toEqual('object'); + expect(d.parameters()).toEqual({}); + }); + }); + + describe('hasParameters()', function() { + it('should return a boolean indicating if the components field has parameters', function() { + const doc = { parameters: { test1: { test: 'test1' }, test2: { test: 'test2' } } }; + const docNoSchemas = { messages: {} }; + const d = new Components(doc); + const d2 = new Components(docNoSchemas); + expect(d.hasParameters()).toEqual(true); + expect(d2.hasParameters()).toEqual(false); + }); + }); + + describe('parameter()', function() { + it('should return a specific parameter object', function() { + const doc = { parameters: { test1: { test: 'test1' }, test2: { test: 'test2' } } }; + const d = new Components(doc); + expect(d.parameter('test1')).toBeInstanceOf(ChannelParameter); + expect(d.parameter('test1')?.json()).toEqual(doc.parameters.test1); + }); + + it('should return null if a parameter name is not provided', function() { + const doc = { parameters: { test1: { test: 'test1' }, test2: { test: 'test2' } } }; + const d = new Components(doc); + expect(d.parameter(undefined as any)).toEqual(null); + }); + + it('should return null if a parameter name is not found', function() { + const doc = { parameters: { test1: { test: 'test1' }, test2: { test: 'test2' } } }; + const d = new Components(doc); + expect(d.parameter('not found')).toEqual(null); + }); + }); + + describe('correlationIds()', function() { + it('should return a map of CorrelationId objects', function() { + const doc: any = { correlationIds: { test1: { test: 'test1' }, test2: { test: 'test2' } } }; + const d = new Components(doc); + expect(typeof d.correlationIds()).toEqual('object'); + expect(d.correlationIds().test1).toBeInstanceOf(CorrelationId); + expect(d.correlationIds().test1.json()).toEqual(doc.correlationIds.test1); + expect(d.correlationIds().test2).toBeInstanceOf(CorrelationId); + expect(d.correlationIds().test2.json()).toEqual(doc.correlationIds.test2); + }); + + it('should return an empty object if the components field has no defined correlationIds', function() { + const doc = {}; + const d = new Components(doc); + expect(typeof d.correlationIds()).toEqual('object'); + expect(d.correlationIds()).toEqual({}); + }); + }); + + describe('hasCorrelationIds()', function() { + it('should return a boolean indicating if the components field has correlationIds', function() { + const doc: any = { correlationIds: { test1: { test: 'test1' }, test2: { test: 'test2' } } }; + const docNoSchemas = { messages: {} }; + const d = new Components(doc); + const d2 = new Components(docNoSchemas); + expect(d.hasCorrelationIds()).toEqual(true); + expect(d2.hasCorrelationIds()).toEqual(false); + }); + }); + + describe('correlationId()', function() { + it('should return a specific correlationId object', function() { + const doc: any = { correlationIds: { test1: { test: 'test1' }, test2: { test: 'test2' } } }; + const d = new Components(doc); + expect(d.correlationId('test1')).toBeInstanceOf(CorrelationId); + expect(d.correlationId('test1')?.json()).toEqual(doc.correlationIds.test1); + }); + + it('should return null if a correlationId name is not provided', function() { + const doc: any = { correlationIds: { test1: { test: 'test1' }, test2: { test: 'test2' } } }; + const d = new Components(doc); + expect(d.correlationId(undefined as any)).toEqual(null); + }); + + it('should return null if a correlationId name is not found', function() { + const doc: any = { correlationIds: { test1: { test: 'test1' }, test2: { test: 'test2' } } }; + const d = new Components(doc); + expect(d.correlationId('not found')).toEqual(null); + }); + }); + + describe('operationTraits()', function() { + it('should return a map of OperationTrait objects', function() { + const doc = { operationTraits: { test1: { test: 'test1' }, test2: { test: 'test2' } } }; + const d = new Components(doc); + expect(typeof d.operationTraits()).toEqual('object'); + expect(d.operationTraits().test1).toBeInstanceOf(OperationTrait); + expect(d.operationTraits().test1.json()).toEqual(doc.operationTraits.test1); + expect(d.operationTraits().test2).toBeInstanceOf(OperationTrait); + expect(d.operationTraits().test2.json()).toEqual(doc.operationTraits.test2); + }); + + it('should return an empty object if the components field has no defined operationTraits', function() { + const doc = {}; + const d = new Components(doc); + expect(typeof d.operationTraits()).toEqual('object'); + expect(d.operationTraits()).toEqual({}); + }); + }); + + describe('hasOperationTraits()', function() { + it('should return a boolean indicating if the components field has operationTraits', function() { + const doc = { operationTraits: { test1: { test: 'test1' }, test2: { test: 'test2' } } }; + const docNoSchemas = { messages: {} }; + const d = new Components(doc); + const d2 = new Components(docNoSchemas); + expect(d.hasOperationTraits()).toEqual(true); + expect(d2.hasOperationTraits()).toEqual(false); + }); + }); + + describe('operationTrait()', function() { + it('should return a specific operationTrait object', function() { + const doc = { operationTraits: { test1: { test: 'test1' }, test2: { test: 'test2' } } }; + const d = new Components(doc); + expect(d.operationTrait('test1')).toBeInstanceOf(OperationTrait); + expect(d.operationTrait('test1')?.json()).toEqual(doc.operationTraits.test1); + }); + + it('should return null if a operationTrait name is not provided', function() { + const doc = { operationTraits: { test1: { test: 'test1' }, test2: { test: 'test2' } } }; + const d = new Components(doc); + expect(d.operationTrait(undefined as any)).toEqual(null); + }); + + it('should return null if a operationTrait name is not found', function() { + const doc = { operationTraits: { test1: { test: 'test1' }, test2: { test: 'test2' } } }; + const d = new Components(doc); + expect(d.operationTrait('not found')).toEqual(null); + }); + }); + + describe('messageTraits()', function() { + it('should return a map of MessageTrait objects', function() { + const doc = { messageTraits: { test1: { test: 'test1' }, test2: { test: 'test2' } } }; + const d = new Components(doc); + expect(typeof d.messageTraits()).toEqual('object'); + expect(d.messageTraits().test1).toBeInstanceOf(MessageTrait); + expect(d.messageTraits().test1.json()).toEqual(doc.messageTraits.test1); + expect(d.messageTraits().test2).toBeInstanceOf(MessageTrait); + expect(d.messageTraits().test2.json()).toEqual(doc.messageTraits.test2); + }); + + it('should return an empty object if the components field has no defined messageTraits', function() { + const doc = {}; + const d = new Components(doc); + expect(typeof d.messageTraits()).toEqual('object'); + expect(d.messageTraits()).toEqual({}); + }); + }); + + describe('hasMessageTraits()', function() { + it('should return a boolean indicating if the components field has messageTraits', function() { + const doc = { messageTraits: { test1: { test: 'test1' }, test2: { test: 'test2' } } }; + const docNoSchemas = { messages: {} }; + const d = new Components(doc); + const d2 = new Components(docNoSchemas); + expect(d.hasMessageTraits()).toEqual(true); + expect(d2.hasMessageTraits()).toEqual(false); + }); + }); + + describe('messageTrait()', function() { + it('should return a specific messageTrait object', function() { + const doc = { messageTraits: { test1: { test: 'test1' }, test2: { test: 'test2' } } }; + const d = new Components(doc); + expect(d.messageTrait('test1')).toBeInstanceOf(MessageTrait); + expect(d.messageTrait('test1').json()).toEqual(doc.messageTraits.test1); + }); + + it('should return null if a messageTrait name is not provided', function() { + const doc = { messageTraits: { test1: { test: 'test1' }, test2: { test: 'test2' } } }; + const d = new Components(doc); + expect(d.messageTrait(undefined as any)).toEqual(null); + }); + + it('should return null if a messageTrait name is not found', function() { + const doc = { messageTraits: { test1: { test: 'test1' }, test2: { test: 'test2' } } }; + const d = new Components(doc); + expect(d.messageTrait('not found')).toEqual(null); + }); + }); + + describe('serverVariables()', function() { + it('should return a map of ServerVariable objects', function() { + const doc = { serverVariables: { test1: {test: 'test1'}, test2: {test: 'test2'} } }; + const d = new Components(doc); + expect(typeof d.serverVariables()).toEqual('object'); + expect(d.serverVariables().test1).toBeInstanceOf(ServerVariable); + expect(d.serverVariables().test1.json()).toEqual(doc.serverVariables.test1); + expect(d.serverVariables().test2).toBeInstanceOf(ServerVariable); + expect(d.serverVariables().test2.json()).toEqual(doc.serverVariables.test2); + }); + + it('should return an empty object if the components field has no defined serverVariables', function() { + const doc = {}; + const d = new Components(doc); + expect(typeof d.serverVariables()).toEqual('object'); + expect(d.serverVariables()).toEqual({}); + }); + }); + + describe('hasServerVariables()', function() { + it('should return a boolean indicating if the components field has serverVariables', function() { + const doc = { serverVariables: { test1: {test: 'test1'}, test2: {test: 'test2'} } }; + const docNoSchemas = { servers: {} }; + const d = new Components(doc); + const d2 = new Components(docNoSchemas); + expect(d.hasServerVariables()).toEqual(true); + expect(d2.hasServerVariables()).toEqual(false); + }); + }); + + describe('serverVariable()', function() { + it('should return a specific serverVariable object', function() { + const doc = { serverVariables: { test1: {test: 'test1'}, test2: {test: 'test2'} } }; + const d = new Components(doc); + expect(d.serverVariable('test1')).toBeInstanceOf(ServerVariable); + expect(d.serverVariable('test1')?.json()).toEqual(doc.serverVariables.test1); + }); + + it('should return null if a serverVariable name is not provided', function() { + const doc = { serverVariables: { test1: {test: 'test1'}, test2: {test: 'test2'} } }; + const d = new Components(doc); + expect(d.serverVariable(undefined as any)).toEqual(null); + }); + + it('should return null if a serverVariable name is not found', function() { + const doc = { serverVariables: { test1: {test: 'test1'}, test2: {test: 'test2'} } }; + const d = new Components(doc); + expect(d.serverVariable('not found')).toEqual(null); + }); + }); + + assertExtensionsMixin(Components); +}); diff --git a/test/old-api/contact.spec.ts b/test/old-api/contact.spec.ts new file mode 100644 index 000000000..1e8a00bde --- /dev/null +++ b/test/old-api/contact.spec.ts @@ -0,0 +1,29 @@ +import { Contact } from '../../src/old-api/contact'; +import { assertExtensionsMixin } from './mixins'; + +describe('Contact', function() { + const json = { name: 'Fran', url: 'https://www.asyncapi.com', email: 'fmvilas@gmail.com', 'x-test': 'testing' }; + + describe('name()', function() { + it('should return a string', function() { + const d = new Contact(json); + expect(d.name()).toEqual(json.name); + }); + }); + + describe('url()', function() { + it('should return a string', function() { + const d = new Contact(json); + expect(d.url()).toEqual(json.url); + }); + }); + + describe('email()', function() { + it('should return a string', function() { + const d = new Contact(json); + expect(d.email()).toEqual(json.email); + }); + }); + + assertExtensionsMixin(Contact); +}); \ No newline at end of file diff --git a/test/old-api/correlation-id.spec.ts b/test/old-api/correlation-id.spec.ts new file mode 100644 index 000000000..ba6ddfe6a --- /dev/null +++ b/test/old-api/correlation-id.spec.ts @@ -0,0 +1,16 @@ +import { CorrelationId } from '../../src/old-api/correlation-id'; +import { assertDescriptionMixin, assertExtensionsMixin } from './mixins'; + +describe('CorrelationId', function() { + const json = { location: '$message.header#/correlationId' }; + + describe('location()', function() { + it('should return a string', function() { + const c = new CorrelationId(json); + expect(c.location()).toEqual(json.location); + }); + }); + + assertDescriptionMixin(CorrelationId); + assertExtensionsMixin(CorrelationId); +}); \ No newline at end of file diff --git a/test/old-api/external-docs.spec.ts b/test/old-api/external-docs.spec.ts new file mode 100644 index 000000000..b5d91acfc --- /dev/null +++ b/test/old-api/external-docs.spec.ts @@ -0,0 +1,16 @@ +import { ExternalDocs } from '../../src/old-api/external-docs'; +import { assertDescriptionMixin, assertExtensionsMixin } from './mixins'; + +describe('ExternalDocs', function() { + const json = { url: 'somewhere' }; + + describe('url()', function() { + it('should return a string', function() { + const d = new ExternalDocs(json); + expect(d.url()).toEqual(json.url); + }); + }); + + assertDescriptionMixin(ExternalDocs); + assertExtensionsMixin(ExternalDocs); +}); \ No newline at end of file diff --git a/test/old-api/info.spec.ts b/test/old-api/info.spec.ts new file mode 100644 index 000000000..595dc0ba3 --- /dev/null +++ b/test/old-api/info.spec.ts @@ -0,0 +1,58 @@ +import { Info } from '../../src/old-api/info'; +import { Contact } from '../../src/old-api/contact'; +import { License } from '../../src/old-api/license'; +import { assertDescriptionMixin, assertExtensionsMixin } from './mixins'; + +describe('Info', function() { + const json = { title: 'Test', version: '1.2.3', termsOfService: '...', license: { name: 'Apache 2.0', url: 'https://www.apache.org/licenses/LICENSE-2.0' }, contact: { name: 'Fran', url: 'https://www.asyncapi.com', email: 'fmvilas@gmail.com' }, 'x-test': 'testing' }; + + describe('title()', function() { + it('should return a string', function() { + const d = new Info(json); + expect(d.title()).toEqual(json.title); + }); + }); + + describe('version()', function() { + it('should return a string', function() { + const d = new Info(json); + expect(d.version()).toEqual(json.version); + }); + }); + + describe('termsOfService()', function() { + it('should return a string', function() { + const d = new Info(json); + expect(d.termsOfService()).toEqual(json.termsOfService); + }); + }); + + describe('license()', function() { + it('should return a license object', function() { + const d = new Info(json); + expect(d.license()).toBeInstanceOf(License); + expect(d.license()?.json()).toEqual(json.license); + }); + + it('should return null if a license object is not given', function() { + const d = new Info({ ...json, license: undefined }); + expect(d.license()).toEqual(null); + }); + }); + + describe('contact()', function() { + it('should return a license object', function() { + const d = new Info(json); + expect(d.contact()).toBeInstanceOf(Contact); + expect(d.contact()?.json()).toEqual(json.contact); + }); + + it('should return null if a contact object is not given', function() { + const d = new Info({ ...json, contact: undefined }); + expect(d.contact()).toEqual(null); + }); + }); + + assertDescriptionMixin(Info); + assertExtensionsMixin(Info); +}); \ No newline at end of file diff --git a/test/old-api/license.spec.ts b/test/old-api/license.spec.ts new file mode 100644 index 000000000..0e143a530 --- /dev/null +++ b/test/old-api/license.spec.ts @@ -0,0 +1,22 @@ +import { License } from '../../src/old-api/license'; +import { assertExtensionsMixin } from './mixins'; + +describe('License', function() { + const json = { name: 'Apache 2.0', url: 'https://www.apache.org/licenses/LICENSE-2.0', 'x-test': 'testing' }; + + describe('name()', function() { + it('should return a string', function() { + const d = new License(json); + expect(d.name()).toEqual(json.name); + }); + }); + + describe('url()', function() { + it('should return a string', function() { + const d = new License(json); + expect(d.url()).toEqual(json.url); + }); + }); + + assertExtensionsMixin(License); +}); \ No newline at end of file diff --git a/test/old-api/message-trait.spec.ts b/test/old-api/message-trait.spec.ts new file mode 100644 index 000000000..874ae06d9 --- /dev/null +++ b/test/old-api/message-trait.spec.ts @@ -0,0 +1,107 @@ +import { MessageTrait } from '../../src/old-api/message-trait'; +import { Schema } from '../../src/old-api/schema'; +import { assertDescriptionMixin, assertExternalDocumentationMixin, assertExtensionsMixin, assertBindingsMixin, assertTagsMixin } from './mixins'; + +describe('MessageTrait', function() { + const json: any = { messageId: 'someId', schemaFormat: 'mySchema', headers: { properties: { test1: { type: 'string' }, test2: { type: 'number' } } }, correlationId: { test: true }, contentType: 'application/json', name: 'test', title: 'Test', summary: 'test', description: 'testing', externalDocs: { test: true }, tags: [{ name: 'tag1' }], bindings: { amqp: { test: true } }, examples: [{name: 'test', summary: 'test summary', payload: {test: true}}], 'x-test': 'testing' }; + + describe('headers()', function() { + it('should return a map of Schema objects', function() { + const d = new MessageTrait(json); + expect(d.headers()).toBeInstanceOf(Schema); + expect(d.headers()?.json()).toEqual(json.headers); + }); + }); + + describe('header()', function() { + it('should return a specific Schema object', function() { + const d = new MessageTrait(json); + expect(d.header('test1')).toBeInstanceOf(Schema); + expect(d.header('test1')?.json()).toEqual(json.headers.properties.test1); + }); + }); + + describe('payload()', function() { + it('should NOT have a payload method', function() { + const d = new MessageTrait(json); + expect((d as any).payload).toEqual(undefined); + }); + }); + + describe('originalPayload()', function() { + it('should NOT have an originalPayload method', function() { + const d = new MessageTrait(json); + expect((d as any).originalPayload).toEqual(undefined); + }); + }); + + describe('id()', function() { + it('should return a string', function() { + const d = new MessageTrait(json); + expect(d.id()).toEqual(json.messageId); + }); + }); + + describe('correlationId()', function() { + it('should return a CorrelationId object', function() { + const d = new MessageTrait(json); + expect(d.correlationId()?.json()).toEqual(json.correlationId); + }); + }); + + describe('schemaFormat()', function() { + it('should return a string', function() { + const d = new MessageTrait(json); + expect(d.schemaFormat()).toEqual('mySchema'); + }); + }); + + describe('originalSchemaFormat()', function() { + it('should NOT have an originalSchemaFormat method', function() { + const d = new MessageTrait(json); + expect((d as any).originalSchemaFormat).toEqual(undefined); + }); + }); + + describe('contentType()', function() { + it('should return a string', function() { + const d = new MessageTrait(json); + expect(d.contentType()).toEqual(json.contentType); + }); + }); + + describe('name()', function() { + it('should return a string', function() { + const d = new MessageTrait(json); + expect(d.name()).toEqual(json.name); + }); + }); + + describe('title()', function() { + it('should return a string', function() { + const d = new MessageTrait(json); + expect(d.title()).toEqual(json.title); + }); + }); + + describe('summary()', function() { + it('should return a string', function() { + const d = new MessageTrait(json); + expect(d.summary()).toEqual(json.summary); + }); + }); + + describe('examples()', function() { + it('should return an array of examples', function() { + const d = new MessageTrait(json); + expect(Array.isArray(d.examples())).toEqual(true); + expect(d.examples()).toEqual(json.examples); + }); + }); + + assertDescriptionMixin(MessageTrait); + assertExternalDocumentationMixin(MessageTrait); + assertBindingsMixin(MessageTrait); + assertTagsMixin(MessageTrait); + assertExtensionsMixin(MessageTrait); +}); \ No newline at end of file diff --git a/test/old-api/message.spec.ts b/test/old-api/message.spec.ts new file mode 100644 index 000000000..53b654e10 --- /dev/null +++ b/test/old-api/message.spec.ts @@ -0,0 +1,155 @@ +import { Message } from '../../src/old-api/message'; +import { Schema } from '../../src/old-api/schema'; +import { assertDescriptionMixin, assertExternalDocumentationMixin, assertExtensionsMixin, assertBindingsMixin, assertTagsMixin } from './mixins'; + +describe('Message', function() { + const json: any = { schemaFormat: 'mySchema', traits: [{headers: {type: 'object',properties: {'my-app-header': {type: 'integer',minimum: 0,maximum: 100}}}}], headers: { properties: { test1: { type: 'string' }, test2: { type: 'number' } } }, payload: { test: true }, 'x-parser-original-payload': { testing: true }, correlationId: { test: true }, 'x-parser-original-schema-format': 'application/vnd.apache.avro;version=1.9.0', contentType: 'application/json', messageId: 'test', title: 'Test', summary: 'test', description: 'testing', externalDocs: { test: true }, tags: [{ name: 'tag1' }], bindings: { amqp: { test: true } }, examples: [{name: 'test', summary: 'test summary', payload: {test: true}}], 'x-test': 'testing' }; + + describe('uid()', function() { + it('should return a string with the messageId', function() { + const d = new Message(json); + expect(d.uid()).toEqual('test'); + }); + + it('should return a string with the name when messageId is not available', function() { + const msg = { ...json, ...{ name: 'test', messageId: undefined } }; + const d = new Message(msg); + expect(d.uid()).toEqual('test'); + }); + + it('should return a string with the x-parser-message-name extension when name and messageId are not available', function() { + const msg = { ...json, ...{ 'x-parser-message-name': 'test', messageId: undefined } }; + const d = new Message(msg); + expect(d.uid()).toEqual('test'); + }); + }); + + describe('headers()', function() { + it('should return a map of Schema objects', function() { + const d = new Message(json); + expect(d.headers()).toBeInstanceOf(Schema); + expect(d.headers()?.json()).toEqual(json.headers); + }); + }); + + describe('header()', function() { + it('should return a specific Schema object', function() { + const d = new Message(json); + expect(d.header('test1')).toBeInstanceOf(Schema); + expect(d.header('test1')?.json()).toEqual(json.headers.properties.test1); + }); + }); + + describe('payload()', function() { + it('should return payload as a Schema object', function() { + const d = new Message(json); + expect(d.payload()).toBeInstanceOf(Schema); + expect(d.payload()?.json()).toEqual(json.payload); + }); + }); + + describe('originalPayload()', function() { + it('should return the original payload', function() { + const d = new Message(json); + expect(d.originalPayload()).toEqual(json['x-parser-original-payload']); + }); + }); + + describe('correlationId()', function() { + it('should return a CorrelationId object', function() { + const d = new Message(json); + expect(d.correlationId()?.json()).toEqual(json.correlationId); + }); + }); + + describe('schemaFormat()', function() { + it('should return a string', function() { + const d = new Message(json); + expect(d.schemaFormat()).toEqual('mySchema'); + }); + }); + + describe('originalSchemaFormat()', function() { + it('should return a string', function() { + const d = new Message(json); + expect(d.originalSchemaFormat()).toEqual(json['x-parser-original-schema-format']); + }); + }); + + describe('contentType()', function() { + it('should return a string', function() { + const d = new Message(json); + expect(d.contentType()).toEqual(json.contentType); + }); + }); + + describe('name()', function() { + it('should return a string', function() { + const d = new Message(json); + expect(d.name()).toEqual(json.name); + }); + }); + + describe('title()', function() { + it('should return a string', function() { + const d = new Message(json); + expect(d.title()).toEqual(json.title); + }); + }); + + describe('summary()', function() { + it('should return a string', function() { + const d = new Message(json); + expect(d.summary()).toEqual(json.summary); + }); + }); + + describe('traits()', function() { + it('should return a list of traits from traits', function() { + const d = new Message(json); + expect(d.traits()[0].json()).toEqual(json.traits[0]); + }); + + it('should return a list of traits from x-parser-original-traits', function() { + const { traits, ...newJs } = json; + newJs['x-parser-original-traits'] = traits; + const d = new Message(newJs); + expect(d.traits()[0].json()).toEqual(newJs['x-parser-original-traits'][0]); + }); + }); + + describe('hasTraits()', function() { + it('should return true if in traits', function() { + const d = new Message(json); + expect(d.hasTraits()).toEqual(true); + }); + + it('should return true if in x-parser-original-traits', function() { + const { traits, ...newJs } = json; + newJs['x-parser-original-traits'] = traits; + const d = new Message(newJs); + expect(d.hasTraits()).toEqual(true); + }); + + it('should return false', function() { + // eslint-disable-next-line no-unused-vars + const { traits, ...newJs } = json; + const d = new Message(newJs); + expect(d.hasTraits()).toEqual(false); + }); + }); + + describe('examples()', function() { + it('should return an array of examples', function() { + const d = new Message(json); + expect(Array.isArray(d.examples())).toEqual(true); + expect(d.examples()).toEqual(json.examples); + }); + }); + + assertDescriptionMixin(Message); + assertExternalDocumentationMixin(Message); + assertBindingsMixin(Message); + assertTagsMixin(Message); + assertExtensionsMixin(Message); +}); \ No newline at end of file diff --git a/test/old-api/mixins.spec.ts b/test/old-api/mixins.spec.ts new file mode 100644 index 000000000..016e0532c --- /dev/null +++ b/test/old-api/mixins.spec.ts @@ -0,0 +1,92 @@ +import { Base } from '../../src/old-api/base'; +import { SpecificationExtensionsModel, hasDescription, description, hasExternalDocs, externalDocs, bindingsMixins, tagsMixins } from '../../src/old-api/mixins'; + +import { assertDescriptionMixin, assertExternalDocumentationMixin, assertBindingsMixin, assertExtensionsMixin, assertTagsMixin } from './mixins'; + +describe('mixins', function() { + describe('description', function() { + class Model extends Base { + hasDescription() { + return hasDescription(this); + } + + description() { + return description(this); + } + } + + assertDescriptionMixin(Model); + }); + + describe('externalDocs', function() { + class Model extends Base { + hasExternalDocs() { + return hasExternalDocs(this); + } + + externalDocs() { + return externalDocs(this); + } + } + + assertExternalDocumentationMixin(Model); + }); + + describe('bindings', function() { + class Model extends Base { + hasBindings() { + return bindingsMixins.hasBindings(this as any); + } + + bindings() { + return bindingsMixins.bindings(this as any); + } + + bindingProtocols() { + return bindingsMixins.bindingProtocols(this as any); + } + + hasBinding(name: string): boolean { + return bindingsMixins.hasBinding(this as any, name); + } + + binding(name: string) { + return bindingsMixins.binding(this as any, name); + } + } + + assertBindingsMixin(Model); + }); + + describe('extensions', function() { + class Model extends SpecificationExtensionsModel {} + + assertExtensionsMixin(Model); + }); + + describe('tags', function() { + class Model extends Base { + hasTags() { + return tagsMixins.hasTags(this); + } + + tags() { + return tagsMixins.tags(this); + } + + tagNames() { + return tagsMixins.tagNames(this); + } + + hasTag(name: string) { + return tagsMixins.hasTag(this, name); + } + + tag(name: string) { + return tagsMixins.tag(this, name); + } + } + + assertTagsMixin(Model); + }); +}); diff --git a/test/old-api/mixins.ts b/test/old-api/mixins.ts new file mode 100644 index 000000000..9621b70ca --- /dev/null +++ b/test/old-api/mixins.ts @@ -0,0 +1,310 @@ +import { ExternalDocs } from '../../src/old-api/external-docs'; +import { Tag } from '../../src/old-api/tag'; + +export function assertDescriptionMixin(Model: any) { + describe('description', function() { + const doc1 = { description: 'Testing' }; + const doc2 = { description: '' }; + const doc3 = {}; + const d1 = new Model(doc1); + const d2 = new Model(doc2); + const d3 = new Model(doc3); + + describe('hasDescription()', function() { + it('should return a boolean indicating if the object has description', function() { + expect(d1.hasDescription()).toEqual(true); + expect(d2.hasDescription()).toEqual(false); + expect(d3.hasDescription()).toEqual(false); + }); + }); + + describe('description()', function() { + it('should return a value', function() { + expect(d1.description()).toEqual(doc1.description); + expect(d2.description()).toEqual(''); + }); + + it('should return a null', function() { + expect(d3.description()).toEqual(null); + }); + }); + }); +} + +export function assertExternalDocumentationMixin(Model: any) { + describe('externalDocs', function() { + const doc1 = { externalDocs: { url: 'test.com' } }; + const doc2 = { externalDocs: {} }; + const doc3 = {}; + const d1 = new Model(doc1); + const d2 = new Model(doc2); + const d3 = new Model(doc3); + + describe('hasExternalDocs()', function() { + it('should return a boolean indicating if the object has externalDocs', function() { + expect(d1.hasExternalDocs()).toEqual(true); + expect(d2.hasExternalDocs()).toEqual(false); + expect(d3.hasExternalDocs()).toEqual(false); + }); + }); + + describe('externalDocs()', function() { + it('should return a externalDocs object', function() { + expect(d1.externalDocs() instanceof ExternalDocs).toEqual(true); + expect(d1.externalDocs()?.json()).toEqual(doc1.externalDocs); + + expect(d2.externalDocs() instanceof ExternalDocs).toEqual(true); + expect(d2.externalDocs()?.json()).toEqual(doc2.externalDocs); + }); + + it('should return a null', function() { + expect(d3.externalDocs()).toEqual(null); + }); + }); + }); +} + +export function assertBindingsMixin(Model: any) { + describe('bindings', function() { + const doc1 = { bindings: { amqp: { test: 'test1' } } }; + const doc2 = { bindings: {} }; + const doc3 = {}; + const d1 = new Model(doc1); + const d2 = new Model(doc2); + const d3 = new Model(doc3); + + describe('hasBindings()', function() { + it('should return a boolean indicating if the object has bindings', function() { + expect(d1.hasBindings()).toEqual(true); + expect(d2.hasBindings()).toEqual(false); + expect(d3.hasBindings()).toEqual(false); + }); + }); + + describe('bindings()', function() { + it('should return a map of bindings', function() { + expect(d1.bindings()).toEqual(doc1.bindings); + }); + + it('should return an empty object', function() { + expect(d2.bindings()).toEqual({}); + expect(d3.bindings()).toEqual({}); + }); + }); + + describe('bindingProtocols()', function() { + it('should return an array of protocol names', function() { + expect(d1.bindingProtocols()).toEqual(['amqp']); + }); + + it('should return an empty array', function() { + expect(d2.bindingProtocols()).toEqual([]); + expect(d3.bindingProtocols()).toEqual([]); + }); + }); + + describe('hasBinding()', function() { + it('should return a boolean indicating if the bindings object has appropriate binding by name', function() { + expect(d1.hasBinding('amqp')).toEqual(true); + expect(d1.hasBinding('http')).toEqual(false); + expect(d2.hasBinding('amqp')).toEqual(false); + expect(d3.hasBinding('amqp')).toEqual(false); + }); + }); + + describe('binding()', function() { + it('should return a binding object', function() { + expect(d1.binding('amqp')).toEqual(doc1.bindings.amqp); + }); + + it('should return a null', function() { + expect(d1.binding('http')).toEqual(null); + expect(d2.binding('amqp')).toEqual(null); + expect(d3.binding('amqp')).toEqual(null); + }); + }); + }); +} + +export function assertExtensionsMixin(Model: any) { + describe('extensions', function() { + const doc1 = { 'x-test': 'testing', test: 'testing' }; + const doc2 = { test: 'testing' }; + const doc3 = {}; + const d1 = new Model(doc1); + const d2 = new Model(doc2); + const d3 = new Model(doc3); + + describe('hasExtensions()', function() { + it('should return a boolean indicating if the object has extensions', function() { + expect(d1.hasExtensions()).toEqual(true); + expect(d2.hasExtensions()).toEqual(false); + expect(d3.hasExtensions()).toEqual(false); + }); + }); + + describe('extensions()', function() { + it('should return a object with extensions', function() { + expect(d1.extensions()).toEqual({ 'x-test': 'testing'}); + }); + + it('should return a empty object', function() { + expect(d2.extensions()).toEqual({}); + expect(d3.extensions()).toEqual({}); + }); + }); + + describe('extensionKeys()', function() { + it('should return an array of extension keys', function() { + expect(d1.extensionKeys()).toEqual(['x-test']); + }); + + it('should return an empty array', function() { + expect(d2.extensionKeys()).toEqual([]); + expect(d3.extensionKeys()).toEqual([]); + }); + }); + + describe('extKeys()', function() { + it('should return an array of extension keys', function() { + expect(d1.extKeys()).toEqual(['x-test']); + }); + + it('should return a null', function() { + expect(d2.extKeys()).toEqual([]); + expect(d3.extKeys()).toEqual([]); + }); + }); + + describe('hasExtension()', function() { + it('should return a boolean indicating if the object has appropriate extension by key', function() { + expect(d1.hasExtension('x-test')).toEqual(true); + expect(d1.hasExtension('x-test2')).toEqual(false); + expect(d2.hasExtension('x-test')).toEqual(false); + expect(d3.hasExtension('x-test')).toEqual(false); + }); + + it('should return false key is not prefixed by `x-`', function() { + expect(d1.hasExtension('test')).toEqual(false); + }); + }); + + describe('extension()', function() { + it('should return a value', function() { + expect(d1.extension('x-test')).toEqual('testing'); + }); + + it('should return an undefined', function() { + expect(d1.extension('x-test2')).toEqual(undefined); + expect(d2.extension('x-test')).toEqual(undefined); + expect(d3.extension('x-test')).toEqual(undefined); + }); + + it('should return null if key is not prefixed by `x-`', function() { + expect(d1.extension('test')).toEqual(null); + }); + }); + + describe('hasExt()', function() { + it('should return a boolean indicating if the object has appropriate extension by key', function() { + expect(d1.hasExt('x-test')).toEqual(true); + expect(d1.hasExt('x-test2')).toEqual(false); + expect(d2.hasExt('x-test')).toEqual(false); + expect(d3.hasExt('x-test')).toEqual(false); + }); + + it('should return false key is not prefixed by `x-`', function() { + expect(d1.hasExt('test')).toEqual(false); + }); + }); + + describe('ext()', function() { + it('should return a value', function() { + expect(d1.ext('x-test')).toEqual('testing'); + }); + + it('should return an undefined', function() { + expect(d1.ext('x-test2')).toEqual(undefined); + expect(d2.ext('x-test')).toEqual(undefined); + expect(d3.ext('x-test')).toEqual(undefined); + }); + + it('should return null if key is not prefixed by `x-`', function() { + expect(d1.ext('test')).toEqual(null); + }); + }); + }); +} + +export function assertTagsMixin(Model: any) { + describe('tags', function() { + const doc1 = { tags: [{ name: 'test1' }, { name: 'test2' }] }; + const doc2 = { tags: [] }; + const doc3 = {}; + const d1 = new Model(doc1); + const d2 = new Model(doc2); + const d3 = new Model(doc3); + + describe('hasTags()', function() { + it('should return a boolean indicating if the object has tags', function() { + expect(d1.hasTags()).toEqual(true); + expect(d2.hasTags()).toEqual(false); + expect(d3.hasTags()).toEqual(false); + }); + }); + + describe('tags()', function() { + it('should return an array of tag objects', function() { + expect(Array.isArray(d1.tags())).toEqual(true); + d1.tags().forEach((tag, i) => { + expect(tag instanceof Tag).toEqual(true); + expect(tag.json()).toEqual(doc1.tags[i]); + }); + }); + + it('should return an empty array', function() { + expect(d2.tags()).toEqual([]); + expect(d3.tags()).toEqual([]); + }); + }); + + describe('tagNames()', function() { + it('should return an array of tag names', function() { + expect(d1.tagNames()).toEqual(['test1', 'test2']); + }); + + it('should return an empty array', function() { + expect(d2.tagNames()).toEqual([]); + expect(d3.tagNames()).toEqual([]); + }); + }); + + describe('hasTag()', function() { + it('should return a boolean indicating if the tags object has appropriate tag by name', function() { + expect(d1.hasTag('test1')).toEqual(true); + expect(d1.hasTag('test2')).toEqual(true); + expect(d1.hasTag('test3')).toEqual(false); + expect(d2.hasTag('test1')).toEqual(false); + expect(d3.hasTag('test1')).toEqual(false); + }); + }); + + describe('tag()', function() { + it('should return a tag object', function() { + expect(d1.tag('test1')).not.toEqual(null); + expect(d1.tag('test1') instanceof Tag).toEqual(true); + expect(d1.tag('test2')).not.toEqual(null); + expect(d1.tag('test2') instanceof Tag).toEqual(true); + }); + + it('should return a null', function() { + expect(d1.tag('test3')).toEqual(null); + expect(d1.tag('test3') instanceof Tag).not.toEqual(true); + expect(d2.tag('test1')).toEqual(null); + expect(d2.tag('test1') instanceof Tag).not.toEqual(true); + expect(d3.tag('test1')).toEqual(null); + expect(d3.tag('test1') instanceof Tag).not.toEqual(true); + }); + }); + }); +} \ No newline at end of file diff --git a/test/old-api/oauth-flow.spec.ts b/test/old-api/oauth-flow.spec.ts new file mode 100644 index 000000000..8b7ab0647 --- /dev/null +++ b/test/old-api/oauth-flow.spec.ts @@ -0,0 +1,37 @@ +import { OAuthFlow } from '../../src/old-api/oauth-flow'; +import { assertExtensionsMixin } from './mixins'; + +describe('OAuthFlow', function() { + const json = { authorizationUrl: 'testing', refreshUrl: 'testing', tokenUrl: 'testing', scopes: { test: 'testing' }, 'x-test': 'testing' }; + + describe('authorizationUrl()', function() { + it('should return a string', function() { + const d = new OAuthFlow(json); + expect(d.authorizationUrl()).toEqual(json.authorizationUrl); + }); + }); + + describe('tokenUrl()', function() { + it('should return a string', function() { + const d = new OAuthFlow(json); + expect(d.tokenUrl()).toEqual(json.tokenUrl); + }); + }); + + describe('refreshUrl()', function() { + it('should return a string', function() { + const d = new OAuthFlow(json); + expect(d.refreshUrl()).toEqual(json.refreshUrl); + }); + }); + + describe('scopes()', function() { + it('should return a Map of strings', function() { + const d = new OAuthFlow(json); + expect(typeof d.scopes()).toEqual('object'); + expect(d.scopes()).toEqual(json.scopes); + }); + }); + + assertExtensionsMixin(OAuthFlow); +}); \ No newline at end of file diff --git a/test/old-api/operation-trait.spec.ts b/test/old-api/operation-trait.spec.ts new file mode 100644 index 000000000..e9b6b21b1 --- /dev/null +++ b/test/old-api/operation-trait.spec.ts @@ -0,0 +1,40 @@ +import { OperationTrait } from '../../src/old-api/operation-trait'; +import { assertDescriptionMixin, assertExternalDocumentationMixin, assertExtensionsMixin, assertBindingsMixin, assertTagsMixin } from './mixins'; + +describe('OperationTrait', function() { + const json: any = { summary: 't', description: 'test', operationId: 'test', tags: [{name: 'tag1'}], externalDocs: { url: 'somewhere' }, bindings: { amqp: { test: true } }, 'x-test': 'testing' }; + + describe('summary()', function() { + it('should return a string', function() { + const d = new OperationTrait(json); + expect(d.summary()).toEqual(json.summary); + }); + }); + + describe('id()', function() { + it('should return a string', function() { + const d = new OperationTrait(json); + expect(d.id()).toEqual(json.operationId); + }); + }); + + describe('messages()', function() { + it('should NOT have a messages method', function() { + const d = new OperationTrait(json); + expect((d as any).messages).toEqual(undefined); + }); + }); + + describe('message()', function() { + it('should NOT have a message method', function() { + const d = new OperationTrait(json); + expect((d as any).message).toEqual(undefined); + }); + }); + + assertDescriptionMixin(OperationTrait); + assertExternalDocumentationMixin(OperationTrait); + assertBindingsMixin(OperationTrait); + assertTagsMixin(OperationTrait); + assertExtensionsMixin(OperationTrait); +}); \ No newline at end of file diff --git a/test/old-api/operation.spec.ts b/test/old-api/operation.spec.ts new file mode 100644 index 000000000..75c900db2 --- /dev/null +++ b/test/old-api/operation.spec.ts @@ -0,0 +1,133 @@ +import { Operation } from '../../src/old-api/operation'; +import { SecurityRequirement } from '../../src/old-api/security-requirement'; +import { assertDescriptionMixin, assertExternalDocumentationMixin, assertExtensionsMixin, assertBindingsMixin, assertTagsMixin } from './mixins'; + +describe('Operation', function() { + const json: any = { summary: 't', description: 'test', traits: [{bindings: {kafka: {clientId: 'my-app-id'}}}], operationId: 'test', tags: [{name: 'tag1'}], externalDocs: { url: 'somewhere' }, bindings: { amqp: { test: true } }, message: { test: true }, 'x-test': 'testing', security: [{ oauth2: ['user:read'] }]}; + + describe('summary()', function() { + it('should return a string', function() { + const d = new Operation(json); + expect(d.summary()).toEqual(json.summary); + }); + }); + + describe('traits()', function() { + it('should return a list of traits', function() { + const d = new Operation(json); + expect(d.traits()[0].json()).toEqual(json.traits[0]); + }); + + it('should return a list of traits from x-parser-original-traits', function() { + const { traits, ...newJs } = json; + newJs['x-parser-original-traits'] = traits; + const d = new Operation(newJs); + expect(d.traits()[0].json()).toEqual(newJs['x-parser-original-traits'][0]); + }); + }); + + describe('hasTraits()', function() { + it('should return true', function() { + const d = new Operation(json); + expect(d.hasTraits()).toEqual(true); + }); + + it('should return true if in x-parser-original-traits', function() { + const { traits, ...newJs } = json; + newJs['x-parser-original-traits'] = traits; + const d = new Operation(newJs); + expect(d.hasTraits()).toEqual(true); + }); + + it('should return false', function() { + const { traits, ...newJs } = json; + const d = new Operation(newJs); + expect(d.hasTraits()).toEqual(false); + }); + }); + + describe('id()', function() { + it('should return a string', function() { + const d = new Operation(json); + expect(d.id()).toEqual(json.operationId); + }); + }); + + describe('messages()', function() { + it('should return an array of Message objects', function() { + const d = new Operation(json); + expect(Array.isArray(d.messages())).toEqual(true); + d.messages().forEach(m => { + expect(m.constructor.name).toEqual('Message'); + expect(m.json()).toEqual(json.message); + }); + }); + + it('should return an array of Message objects when using oneOf', function() { + const doc = { message: { oneOf: [{test: true }, {test: false}] } }; + const d = new Operation(doc); + expect(Array.isArray(d.messages())).toEqual(true); + d.messages().forEach((m, i) => { + expect(m.constructor.name).toEqual('Message'); + expect(m.json()).toEqual(doc.message.oneOf[i]); + }); + }); + }); + + describe('message()', function() { + it('should return null if channel doesn\'t have a message', function() { + const doc = { }; + const d = new Operation(doc); + expect(d.message()).toEqual(null); + }); + + it('should return a specific Message object', function() { + const doc = { message: { oneOf: [{ test: true }, { test: false }] } }; + const d = new Operation(doc); + expect(d.message(0)?.json()).toEqual(doc.message.oneOf[0]); + expect(d.message(1)?.json()).toEqual(doc.message.oneOf[1]); + }); + + it('should return a Message object if no index is provided and message is oneOf from one element', function() { + const doc = { message: { oneOf: [{ test: true }] } }; + const d = new Operation(doc); + expect(d.message()?.json()).toEqual(doc.message.oneOf[0]); + }); + + it('should return null when index is out of bounds', function() { + const doc = { message: { oneOf: [{ test: true }, { test: false }] } }; + const d = new Operation(doc); + expect(d.message(100)).toEqual(null); + }); + + it('should return null if index is not a number', function() { + const doc = { message: { oneOf: [{ test: true }, { test: false }] } }; + const d = new Operation(doc); + expect(d.message('0')).toEqual(null); + }); + + it('should return message object if no index is provided and message is not oneOf', function() { + const doc = { message: { test: true } }; + const d = new Operation(doc); + expect(d.message()?.json()).toEqual(doc.message); + }); + }); + + describe('security()', function() { + it('should return an array of security requirements objects', function() { + const d = new Operation(json); + expect(Array.isArray(d.security())).toEqual(true); + expect(d.security()).toHaveLength(1); + d.security()?.forEach((s, i) => { + expect(s).toBeInstanceOf(SecurityRequirement); + expect(s.json()).toEqual(json.security[i]); + }); + }); + }); + + assertDescriptionMixin(Operation); + assertExternalDocumentationMixin(Operation); + assertBindingsMixin(Operation); + assertTagsMixin(Operation); + assertExtensionsMixin(Operation); +}); \ No newline at end of file diff --git a/test/old-api/schema.spec.ts b/test/old-api/schema.spec.ts new file mode 100644 index 000000000..0aa828187 --- /dev/null +++ b/test/old-api/schema.spec.ts @@ -0,0 +1,777 @@ +import { Schema } from '../../src/old-api/schema'; +import { assertDescriptionMixin, assertExtensionsMixin, assertExternalDocumentationMixin } from './mixins'; + +describe('Schema', function() { + describe('uid()', function() { + it('should return a string', function() { + const doc: any = { $id: 'test' }; + const d = new Schema(doc); + expect(typeof d.uid()).toEqual('string'); + expect(d.uid()).toEqual(doc.$id); + }); + + it('should return a string with the value of x-parser-schema-id when $id is not available', function() { + const doc: any = { 'x-parser-schema-id': 'test' }; + const d = new Schema(doc); + expect(typeof d.uid()).toEqual('string'); + expect(d.uid()).toEqual(doc['x-parser-schema-id']); + }); + }); + + describe('$id()', function() { + it('should return a string', function() { + const doc: any = { $id: 'test' }; + const d = new Schema(doc); + expect(typeof d.$id()).toEqual('string'); + expect(d.$id()).toEqual(doc.$id); + }); + }); + + describe('maximum()', function() { + it('should return a number', function() { + const doc: any = { type: 'number', maximum: 10 }; + const d = new Schema(doc); + expect(typeof d.maximum()).toEqual('number'); + expect(d.maximum()).toEqual(doc.maximum); + }); + }); + + describe('exclusiveMaximum()', function() { + it('should return a number', function() { + const doc: any = { type: 'number', exclusiveMaximum: 10 }; + const d = new Schema(doc); + expect(typeof d.exclusiveMaximum()).toEqual('number'); + expect(d.exclusiveMaximum()).toEqual(doc.exclusiveMaximum); + }); + }); + + describe('minimum()', function() { + it('should return a number', function() { + const doc: any = { type: 'number', minimum: 10 }; + const d = new Schema(doc); + expect(typeof d.minimum()).toEqual('number'); + expect(d.minimum()).toEqual(doc.minimum); + }); + }); + + describe('exclusiveMinimum()', function() { + it('should return a number', function() { + const doc: any = { type: 'number', exclusiveMinimum: 10 }; + const d = new Schema(doc); + expect(typeof d.exclusiveMinimum()).toEqual('number'); + expect(d.exclusiveMinimum()).toEqual(doc.exclusiveMinimum); + }); + }); + + describe('multipleOf()', function() { + it('should return a number', function() { + const doc: any = { type: 'number', multipleOf: 1.0 }; + const d = new Schema(doc); + expect(typeof d.multipleOf()).toEqual('number'); + expect(d.multipleOf()).toEqual(doc.multipleOf); + }); + }); + + describe('maxLength()', function() { + it('should return a number', function() { + const doc: any = { type: 'string', maxLength: 10 }; + const d = new Schema(doc); + expect(typeof d.maxLength()).toEqual('number'); + expect(d.maxLength()).toEqual(doc.maxLength); + }); + }); + + describe('minLength()', function() { + it('should return a number', function() { + const doc: any = { type: 'string', minLength: 10 }; + const d = new Schema(doc); + expect(typeof d.minLength()).toEqual('number'); + expect(d.minLength()).toEqual(doc.minLength); + }); + }); + + describe('pattern()', function() { + it('should return a string', function() { + const doc: any = { type: 'string', pattern: '^test' }; + const d = new Schema(doc); + expect(typeof d.pattern()).toEqual('string'); + expect(d.pattern()).toEqual(doc.pattern); + }); + }); + + describe('maxItems()', function() { + it('should return a number', function() { + const doc: any = { type: 'array', maxItems: 10 }; + const d = new Schema(doc); + expect(typeof d.maxItems()).toEqual('number'); + expect(d.maxItems()).toEqual(doc.maxItems); + }); + }); + + describe('minItems()', function() { + it('should return a number', function() { + const doc: any = { type: 'array', minItems: 10 }; + const d = new Schema(doc); + expect(typeof d.minItems()).toEqual('number'); + expect(d.minItems()).toEqual(doc.minItems); + }); + }); + + describe('uniqueItems()', function() { + it('should return a boolean', function() { + const doc: any = { type: 'array', uniqueItems: true }; + const d = new Schema(doc); + expect(typeof d.uniqueItems()).toEqual('boolean'); + expect(d.uniqueItems()).toEqual(doc.uniqueItems); + }); + }); + + describe('maxProperties()', function() { + it('should return a number', function() { + const doc: any = { type: 'object', maxProperties: 10 }; + const d = new Schema(doc); + expect(typeof d.maxProperties()).toEqual('number'); + expect(d.maxProperties()).toEqual(doc.maxProperties); + }); + }); + + describe('minProperties()', function() { + it('should return a number', function() { + const doc: any = { type: 'object', minProperties: 10 }; + const d = new Schema(doc); + expect(typeof d.minProperties()).toEqual('number'); + expect(d.minProperties()).toEqual(doc.minProperties); + }); + }); + + describe('required()', function() { + it('should return a number', function() { + const doc: any = { type: 'object', required: ['test'] }; + const d = new Schema(doc); + expect(Array.isArray(d.required())).toEqual(true); + expect(d.required()).toEqual(doc.required); + }); + }); + + describe('enum()', function() { + it('should return a number', function() { + const doc: any = { type: 'string', enum: ['test'] }; + const d = new Schema(doc); + expect(Array.isArray(d.enum())).toEqual(true); + expect(d.enum()).toEqual(doc.enum); + }); + }); + + describe('type()', function() { + it('should return a string', function() { + const doc: any = { type: 'string' }; + const d = new Schema(doc); + expect(typeof d.type()).toEqual('string'); + expect(d.type()).toEqual(doc.type); + }); + + it('should return an array of strings', function() { + const doc: any = { type: ['number', 'string'] }; + const d = new Schema(doc); + expect(Array.isArray(d.type())).toEqual(true); + expect(d.type()).toEqual(doc.type); + }); + }); + + describe('allOf()', function() { + it('should return an array of Schema objects', function() { + const doc: any = { allOf: [{ type: 'string' }, { type: 'number' }] }; + const d = new Schema(doc); + expect(Array.isArray(d.allOf())).toEqual(true); + d.allOf()?.forEach((s, i) => { + expect(s).toBeInstanceOf(Schema); + expect(s.json()).toEqual(doc.allOf[i]); + }); + }); + }); + + describe('oneOf()', function() { + it('should return an array of Schema objects', function() { + const doc: any = { oneOf: [{ type: 'string' }, { type: 'number' }] }; + const d = new Schema(doc); + expect(Array.isArray(d.oneOf())).toEqual(true); + d.oneOf()?.forEach((s, i) => { + expect(s).toBeInstanceOf(Schema); + expect(s.json()).toEqual(doc.oneOf[i]); + }); + }); + }); + + describe('anyOf()', function() { + it('should return an array of Schema objects', function() { + const doc: any = { anyOf: [{ type: 'string' }, { type: 'number' }] }; + const d = new Schema(doc); + expect(Array.isArray(d.anyOf())).toEqual(true); + d.anyOf()?.forEach((s, i) => { + expect(s).toBeInstanceOf(Schema); + expect(s.json()).toEqual(doc.anyOf[i]); + }); + }); + }); + + describe('not()', function() { + it('should return a Schema object', function() { + const doc: any = { not: { type: 'string' } }; + const d = new Schema(doc); + expect(d.not()).toBeInstanceOf(Schema); + expect(d.not()?.json()).toEqual(doc.not); + }); + + it('should return null when not is omitted from the json document', function() { + const doc: any = {}; + const d = new Schema(doc); + expect(d.not()).toEqual(null); + }); + }); + + describe('items()', function() { + it('should return a Schema object', function() { + const doc: any = { items: { type: 'string' } }; + const d = new Schema(doc); + expect(d.items()).toBeInstanceOf(Schema); + expect((d.items() as Schema).json()).toEqual(doc.items); + }); + + it('should return an array of Schema objects', function() { + const doc: any = { items: [{ type: 'string' }, { type: 'number' }] }; + const d = new Schema(doc); + expect(Array.isArray(d.items())).toEqual(true); + (d.items() as Schema[]).forEach((s, i) => { + expect(s).toBeInstanceOf(Schema); + expect(s.json()).toEqual(doc.items[i]); + }); + }); + }); + + describe('properties()', function() { + it('should return a map of Schema objects', function() { + const doc: any = { properties: { test: { type: 'string' } } }; + const d = new Schema(doc); + expect(typeof d.properties()).toEqual('object'); + Object.keys(d.properties()).forEach(key => { + const s = d.properties()[key]; + expect(s).toBeInstanceOf(Schema); + expect(s.json()).toEqual(doc.properties[key]); + }); + }); + }); + + describe('property()', function() { + it('should return a specific Schema object', function() { + const doc: any = { properties: { test: { type: 'string' } } }; + const d = new Schema(doc); + expect(d.property('test')).toBeInstanceOf(Schema); + expect(d.property('test')?.json()).toEqual(doc.properties.test); + }); + }); + + describe('additionalProperties()', function() { + it('should return a Schema object', function() { + const doc: any = { additionalProperties: { type: 'string' } }; + const d = new Schema(doc); + expect(d.additionalProperties()).toBeInstanceOf(Schema); + expect((d.additionalProperties() as Schema).json()).toEqual(doc.additionalProperties); + }); + + it('should return a boolean', function() { + const doc: any = { additionalProperties: true }; + const d = new Schema(doc); + expect(typeof d.additionalProperties()).toEqual('boolean'); + expect(d.additionalProperties()).toEqual(doc.additionalProperties); + }); + + it('should return true when not defined', function() { + const doc: any = {}; + const d = new Schema(doc); + expect(d.additionalProperties()).toEqual(true); + }); + + it('should return false when null', function() { + const doc: any = { additionalProperties: null }; + const d = new Schema(doc); + expect(d.additionalProperties()).toEqual(false); + }); + }); + + describe('additionalItems()', function() { + it('should return a Schema object', function() { + const doc: any = { additionalItems: { type: 'string' } }; + const d = new Schema(doc); + expect(d.additionalItems()).toBeInstanceOf(Schema); + expect((d.additionalItems() as Schema).json()).toEqual(doc.additionalItems); + }); + + it('should return a boolean', function() { + const doc: any = { additionalItems: true }; + const d = new Schema(doc); + expect(typeof d.additionalItems()).toEqual('boolean'); + expect(d.additionalItems()).toEqual(doc.additionalItems); + }); + + it('should return true when not defined', function() { + const doc: any = {}; + const d = new Schema(doc); + expect(d.additionalItems()).toEqual(true); + }); + + it('should return false when null', function() { + const doc: any = { additionalItems: null }; + const d = new Schema(doc); + expect(d.additionalItems()).toEqual(false); + }); + }); + + describe('patternProperties()', function() { + it('should return a map of Schema objects', function() { + const doc: any = { patternProperties: { test: { type: 'string' } } }; + const d = new Schema(doc); + expect(typeof d.patternProperties()).toEqual('object'); + Object.keys(d.patternProperties()).forEach(key => { + const s = d.patternProperties()[key]; + expect(s).toBeInstanceOf(Schema); + expect(s.json()).toEqual(doc.patternProperties[key]); + }); + }); + }); + + describe('const()', function() { + it('should return a number', function() { + const doc: any = { type: 'object', const: 10 }; + const d = new Schema(doc); + expect(typeof d.const()).toEqual('number'); + expect(d.const()).toEqual(doc.const); + }); + + it('should return null', function() { + const doc: any = { type: 'object', const: null }; + const d = new Schema(doc); + expect(d.const()).toEqual(doc.const); + }); + + it('should return an object', function() { + const doc: any = { type: 'object', const: { test: true } }; + const d = new Schema(doc); + expect(typeof d.const()).toEqual('object'); + expect(d.const()).toEqual(doc.const); + }); + + it('should return an array', function() { + const doc: any = { type: 'object', const: ['test'] }; + const d = new Schema(doc); + expect(Array.isArray(d.const())).toEqual(true); + expect(d.const()).toEqual(doc.const); + }); + }); + + describe('contains()', function() { + it('should return a Schema object', function() { + const doc: any = { contains: { type: 'string' } }; + const d = new Schema(doc); + expect(d.contains()).toBeInstanceOf(Schema); + expect(d.contains()?.json()).toEqual(doc.contains); + }); + + it('should return null when contains is omitted from the json document', function() { + const doc: any = {}; + const d = new Schema(doc); + expect(d.contains()).toEqual(null); + }); + }); + + describe('dependencies()', function() { + it('should return a map with an array value', function() { + const doc: any = { properties: { test: { type: 'string' }, test2: { type: 'number' } }, dependencies: { test: ['test2'] } }; + const d = new Schema(doc); + expect(typeof d.dependencies()).toEqual('object'); + Object.keys(d.dependencies() || {}).forEach(key => { + const v = d.dependencies()![key]; + expect(Array.isArray(v)).toEqual(true); + expect(v).toEqual(doc.dependencies[key]); + }); + }); + + it('should return a map with a Schema value', function() { + const doc: any = { properties: { test: { type: 'string' } }, dependencies: { test: { properties: { test2: { type: 'number' } } } } }; + const d = new Schema(doc); + expect(typeof d.dependencies()).toEqual('object'); + Object.keys(d.dependencies() || {}).forEach(key => { + const s = d.dependencies()![key]; + expect(s).toBeInstanceOf(Schema); + expect((s as Schema).json()).toEqual(doc.dependencies[key]); + }); + }); + + it('should return null when dependencies are omitted from the json document', function() { + const doc: any = {}; + const d = new Schema(doc); + expect(d.dependencies()).toEqual(null); + }); + }); + + describe('propertyNames()', function() { + it('should return a Schema object', function() { + const doc: any = { propertyNames: { type: 'string' } }; + const d = new Schema(doc); + expect(d.propertyNames()).toBeInstanceOf(Schema); + expect(d.propertyNames()?.json()).toEqual(doc.propertyNames); + }); + + it('should return null when propertyNames are omitted from the json document', function() { + const doc: any = {}; + const d = new Schema(doc); + expect(d.propertyNames()).toEqual(null); + }); + }); + + describe('if()', function() { + it('should return a Schema object', function() { + const doc: any = { if: { type: 'string' } }; + const d = new Schema(doc); + expect(d.if()).toBeInstanceOf(Schema); + expect(d.if()?.json()).toEqual(doc.if); + }); + + it('should return null when if is omitted from the json document', function() { + const doc: any = {}; + const d = new Schema(doc); + expect(d.if()).toEqual(null); + }); + }); + + describe('then()', function() { + it('should return a Schema object', function() { + const doc: any = { then: { type: 'string' } }; + const d = new Schema(doc); + expect(d.then()).toBeInstanceOf(Schema); + expect(d.then()?.json()).toEqual(doc.then); + }); + + it('should return null when then is omitted from the json document', function() { + const doc: any = {}; + const d = new Schema(doc); + expect(d.then()).toEqual(null); + }); + }); + + describe('else()', function() { + it('should return a Schema object', function() { + const doc: any = { else: { type: 'string' } }; + const d = new Schema(doc); + expect(d.else()).toBeInstanceOf(Schema); + expect(d.else()?.json()).toEqual(doc.else); + }); + + it('should return null when else is omitted from the json document', function() { + const doc: any = {}; + const d = new Schema(doc); + expect(d.else()).toEqual(null); + }); + }); + + describe('format()', function() { + it('should return a string', function() { + const doc: any = { type: 'string', format: 'email' }; + const d = new Schema(doc); + expect(typeof d.format()).toEqual('string'); + expect(d.format()).toEqual(doc.format); + }); + }); + + describe('contentEncoding()', function() { + it('should return a string', function() { + const doc: any = { type: 'string', contentEncoding: 'base64' }; + const d = new Schema(doc); + expect(typeof d.contentEncoding()).toEqual('string'); + expect(d.contentEncoding()).toEqual(doc.contentEncoding); + }); + }); + + describe('contentMediaType()', function() { + it('should return a string', function() { + const doc: any = { type: 'string', contentMediaType: 'text/html' }; + const d = new Schema(doc); + expect(typeof d.contentMediaType()).toEqual('string'); + expect(d.contentMediaType()).toEqual(doc.contentMediaType); + }); + }); + + describe('definitions()', function() { + it('should return a map of Schema objects', function() { + const doc: any = { definitions: { test: { type: 'string' } } }; + const d = new Schema(doc); + expect(typeof d.definitions()).toEqual('object'); + Object.keys(d.definitions()).forEach(key => { + const s = d.definitions()[key]; + expect(s).toBeInstanceOf(Schema); + expect(s.json()).toEqual(doc.definitions[key]); + }); + }); + }); + + describe('title()', function() { + it('should return a string', function() { + const doc: any = { type: 'string', title: 'test' }; + const d = new Schema(doc); + expect(typeof d.title()).toEqual('string'); + expect(d.title()).toEqual(doc.title); + }); + }); + + describe('default()', function() { + it('should return a value', function() { + const doc: any = { type: 'string', default: 'test' }; + const d = new Schema(doc); + expect(d.default()).toEqual('test'); + }); + }); + + describe('deprecated()', function() { + it('should return a boolean', function() { + const doc: any = { type: 'string', deprecated: true }; + const d = new Schema(doc); + expect(typeof d.deprecated()).toEqual('boolean'); + expect(d.deprecated()).toEqual(doc.deprecated); + }); + }); + + describe('discriminator()', function() { + it('should return a string', function() { + const doc: any = { type: 'string', discriminator: 'someType' }; + const d = new Schema(doc); + expect(typeof d.discriminator()).toEqual('string'); + expect(d.discriminator()).toEqual(doc.discriminator); + }); + }); + + describe('readOnly()', function() { + it('should return a boolean', function() { + const doc: any = { type: 'string', readOnly: true }; + const d = new Schema(doc); + expect(typeof d.readOnly()).toEqual('boolean'); + expect(d.readOnly()).toEqual(doc.readOnly); + }); + }); + + describe('writeOnly()', function() { + it('should return a boolean', function() { + const doc: any = { type: 'string', writeOnly: true }; + const d = new Schema(doc); + expect(typeof d.writeOnly()).toEqual('boolean'); + expect(d.writeOnly()).toEqual(doc.writeOnly); + }); + }); + + describe('examples()', function() { + it('should return an array', function() { + const doc: any = { type: 'string', examples: ['test'] }; + const d = new Schema(doc); + expect(Array.isArray(d.examples())).toEqual(true); + expect(d.examples()).toEqual(doc.examples); + }); + }); + + describe('isBooleanSchema()', function() { + it('should return a true when schema is true', function() { + const d = new Schema(true as any); + expect(d.isBooleanSchema()).toEqual(true); + }); + + it('_json property should equal to true when schema is true', function() { + const d = new Schema(true as any); + expect(d.json()).toEqual(true); + }); + + it('should return a true when schema is false', function() { + const d = new Schema(false as any); + expect(d.isBooleanSchema()).toEqual(true); + }); + + it('_json property should equal to false when schema is false', function() { + const d = new Schema(false as any); + expect(d.json()).toEqual(false); + }); + }); + + describe('isCircular()', function() { + it('should return a true when appropriate extension is injected', function() { + const doc: any = { 'x-parser-circular': true }; + const d = new Schema(doc); + expect(d.isCircular()).toEqual(true); + }); + + it('should return a true when schema has circular reference', function() { + const doc: any = { + properties: { + nonCircular: { + type: 'string', + }, + circular: {}, + } + }; + doc.properties.circular = doc; + const d = new Schema(doc); + expect(d.isCircular()).toEqual(false); + expect(d.properties()['nonCircular'].isCircular()).toEqual(false); + expect(d.properties()['circular'].isCircular()).toEqual(true); + }); + + it('should return a true when schema has unresolved $ref', function() { + const doc: any = { + properties: { + nonCircular: { + type: 'string', + }, + circular: { + $ref: '../../', + }, + } + }; + doc.properties.circular = doc; + const d = new Schema(doc); + expect(d.isCircular()).toEqual(false); + expect(d.properties()['nonCircular'].isCircular()).toEqual(false); + expect(d.properties()['circular'].isCircular()).toEqual(true); + }); + }); + + describe('circularSchema()', function() { + it('should return a circular schema', function() { + const doc: any = { + properties: { + nonCircular: { + type: 'string', + }, + circular: {}, + } + }; + doc.properties.circular = doc; + const d = new Schema(doc); + expect(d.isCircular()).toEqual(false); + expect(d.properties()['nonCircular'].circularSchema()).toEqual(undefined); + expect(d.properties()['circular'].circularSchema()).toEqual(d); + }); + }); + + describe('circularProps()', function() { + it('should return values from appropriate extenion', function() { + const doc: any = { + properties: { + nonCircular: { + type: 'string', + }, + circular1: {}, + circular2: {}, + }, + 'x-parser-circular-props': [ + 'circular1', + 'circular2', + ] + }; + const d = new Schema(doc); + expect(d.circularProps()).toEqual([ + 'circular1', + 'circular2', + ]); + }); + + it('should return empty array if circular properties do not exist', function() { + const doc: any = { + properties: { + nonCircular1: { + type: 'string', + }, + nonCircular2: { + type: 'number', + }, + nonCircular3: { + type: 'integer', + }, + } + }; + const d = new Schema(doc); + expect(d.circularProps()).toEqual([]); + }); + + it('should return names of circular properties', function() { + const doc: any = { + properties: { + nonCircular: { + type: 'string', + }, + circular1: {}, + circular2: {}, + } + }; + doc.properties.circular1 = doc; + doc.properties.circular2 = doc; + const d = new Schema(doc); + expect(d.circularProps()).toEqual([ + 'circular1', + 'circular2', + ]); + }); + }); + + describe('hasCircularProps()', function() { + it('should return true when appropriate extenion is injected', function() { + const doc: any = { + properties: { + nonCircular: { + type: 'string', + }, + circular1: {}, + circular2: {}, + }, + 'x-parser-circular-props': [ + 'circular1', + 'circular2', + ] + }; + const d = new Schema(doc); + expect(d.hasCircularProps()).toEqual(true); + }); + + it('should return false when circular properties do not exist', function() { + const doc: any = { + properties: { + nonCircular1: { + type: 'string', + }, + nonCircular2: { + type: 'number', + }, + nonCircular3: { + type: 'integer', + }, + } + }; + const d = new Schema(doc); + expect(d.hasCircularProps()).toEqual(false); + }); + + it('should return true when circular properties exist', function() { + const doc: any = { + properties: { + nonCircular: { + type: 'string', + }, + circular1: {}, + circular2: {}, + } + }; + doc.properties.circular1 = doc; + doc.properties.circular2 = doc; + const d = new Schema(doc); + expect(d.hasCircularProps()).toEqual(true); + }); + }); + + assertDescriptionMixin(Schema); + assertExtensionsMixin(Schema); + assertExternalDocumentationMixin(Schema); +}); \ No newline at end of file diff --git a/test/old-api/security-scheme.spec.ts b/test/old-api/security-scheme.spec.ts new file mode 100644 index 000000000..7a71a63df --- /dev/null +++ b/test/old-api/security-scheme.spec.ts @@ -0,0 +1,63 @@ +import { SecurityScheme } from '../../src/old-api/security-scheme'; +import { OAuthFlow } from '../../src/old-api/oauth-flow'; +import { assertDescriptionMixin, assertExtensionsMixin } from './mixins'; + +import type { v2 } from '../../src/spec-types'; + +describe('SecurityScheme', function() { + const json: v2.SecuritySchemeObject = { type: 'apiKey', description: 'testing', name: 'testing', in: 'cookie', scheme: 'testing', bearerFormat: 'testing', openIdConnectUrl: 'testing', flows: { authorizationCode: { authorizationUrl: '', tokenUrl: '', scopes: {}, } }, 'x-test': 'testing' }; + + describe('type()', function() { + it('should return a string', function() { + const d = new SecurityScheme(json); + expect(d.type()).toEqual(json.type); + }); + }); + + describe('name()', function() { + it('should return a string', function() { + const d = new SecurityScheme(json); + expect(d.name()).toEqual(json.name); + }); + }); + + describe('in()', function() { + it('should return a string', function() { + const d = new SecurityScheme(json); + expect(d.in()).toEqual(json.in); + }); + }); + + describe('scheme()', function() { + it('should return a string', function() { + const d = new SecurityScheme(json); + expect(d.scheme()).toEqual(json.scheme); + }); + }); + + describe('bearerFormat()', function() { + it('should return a string', function() { + const d = new SecurityScheme(json); + expect(d.bearerFormat()).toEqual(json.bearerFormat); + }); + }); + + describe('openIdConnectUrl()', function() { + it('should return a string', function() { + const d = new SecurityScheme(json); + expect(d.openIdConnectUrl()).toEqual(json.openIdConnectUrl); + }); + }); + + describe('flows()', function() { + it('should return a map of OAuthFlow objects', function() { + const d = new SecurityScheme(json); + expect(typeof d.flows()).toEqual('object'); + expect(d.flows().authorizationCode).toBeInstanceOf(OAuthFlow); + expect(d.flows().authorizationCode.json()).toEqual(json.flows?.authorizationCode); + }); + }); + + assertDescriptionMixin(SecurityScheme); + assertExtensionsMixin(SecurityScheme); +}); \ No newline at end of file diff --git a/test/old-api/server-variable.spec.ts b/test/old-api/server-variable.spec.ts new file mode 100644 index 000000000..99c544a4c --- /dev/null +++ b/test/old-api/server-variable.spec.ts @@ -0,0 +1,66 @@ +import { ServerVariable } from '../../src/old-api/server-variable'; +import { assertDescriptionMixin, assertExtensionsMixin } from './mixins'; + +describe('ServerVariable', function() { + const json = { enum: ['value1', 'value2'], default: 'value1', description: 'test1', examples: ['value2'], 'x-test': 'testing' }; + + describe('allowedValues()', function() { + it('should return an array of strings', function() { + const d = new ServerVariable(json); + expect(d.allowedValues()).toEqual(json.enum); + }); + }); + + describe('hasAllowedValues()', function() { + it('should return a true when enum is present', function() { + const d = new ServerVariable(json); + expect(d.hasAllowedValues()).toEqual(true); + }); + + it('should return a false when enum is not present', function() { + const d = new ServerVariable({}); + expect(d.hasAllowedValues()).toEqual(false); + }); + }); + + describe('allows()', function() { + it('should return true if the value is in the enum', function() { + const d = new ServerVariable(json); + expect(d.allows('value1')).toEqual(true); + }); + + it('should return false if the value is not in the enum', function() { + const d = new ServerVariable(json); + expect(d.allows('not found')).toEqual(false); + }); + }); + + describe('defaultValue()', function() { + it('should return a string', function() { + const d = new ServerVariable(json); + expect(d.defaultValue()).toEqual(json.default); + }); + }); + + describe('hasDefaultValue()', function() { + it('should return true if default is present', function() { + const d = new ServerVariable(json); + expect(d.hasDefaultValue()).toEqual(true); + }); + + it('should return false if the value is not in the enum', function() { + const d = new ServerVariable({}); + expect(d.hasDefaultValue()).toEqual(false); + }); + }); + + describe('examples()', function() { + it('should return an array of strings', function() { + const d = new ServerVariable(json); + expect(d.examples()).toEqual(json.examples); + }); + }); + + assertDescriptionMixin(ServerVariable); + assertExtensionsMixin(ServerVariable); +}); \ No newline at end of file diff --git a/test/old-api/server.spec.ts b/test/old-api/server.spec.ts new file mode 100644 index 000000000..d9d569405 --- /dev/null +++ b/test/old-api/server.spec.ts @@ -0,0 +1,74 @@ +import { Server } from '../../src/old-api/server'; +import { ServerVariable } from '../../src/old-api/server-variable'; +import { SecurityRequirement } from '../../src/old-api/security-requirement'; +import { assertDescriptionMixin, assertExtensionsMixin, assertBindingsMixin } from './mixins'; + +import type { v2 } from '../../src/spec-types'; + +describe('Server', function() { + const json: v2.ServerObject = { url: 'test.com', protocol: 'amqp', protocolVersion: '0-9-1', description: 'test', variables: { test1: { enum: ['value1', 'value2'], default: 'value1', description: 'test1', examples: ['value2'] } }, security: [{ oauth2: ['user:read'] }], bindings: { amqp: {} }, 'x-test': 'testing' }; + + describe('url()', function() { + it('should return a string', function() { + const d = new Server(json); + expect(d.url()).toEqual(json.url); + }); + }); + + describe('protocol()', function() { + it('should return a string', function() { + const d = new Server(json); + expect(d.protocol()).toEqual(json.protocol); + }); + }); + + describe('protocolVersion()', function() { + it('should return a string', function() { + const d = new Server(json); + expect(d.protocolVersion()).toEqual(json.protocolVersion); + }); + }); + + describe('hasVariables()', function() { + it('should return a boolean indicating if a server URL has variables', function() { + const doc = { url: 'test1:{port}', variables: { port: { desc: 'test1' } } } as any; + const docNoServerVariables = { url: 'test' } as any; + const d = new Server(doc); + const d2 = new Server(docNoServerVariables); + expect(d.hasVariables()).toEqual(true); + expect(d2.hasVariables()).toEqual(false); + }); + }); + + describe('variables()', function() { + it('should return a map of ServerVariable objects', function() { + const d = new Server(json); + expect(typeof d.variables()).toEqual('object'); + expect(d.variables().test1).toBeInstanceOf(ServerVariable); + expect(d.variables().test1.json()).toEqual(json?.variables?.test1); + }); + }); + + describe('variable()', function() { + it('should return a specific ServerVariable object', function() { + const d = new Server(json); + expect(d.variable('test1')).toBeInstanceOf(ServerVariable); + expect(d.variable('test1')?.json()).toEqual(json?.variables?.test1); + }); + }); + + describe('security()', function() { + it('should return an array of security requirements objects', function() { + const d = new Server(json); + expect(Array.isArray(d.security())).toEqual(true); + d.security()?.forEach((s, i) => { + expect(s).toBeInstanceOf(SecurityRequirement); + expect(s.json()).toEqual(json?.security?.[i]); + }); + }); + }); + + assertDescriptionMixin(Server); + assertExtensionsMixin(Server); + assertBindingsMixin(Server); +}); \ No newline at end of file diff --git a/test/old-api/tag.spec.ts b/test/old-api/tag.spec.ts new file mode 100644 index 000000000..96567d159 --- /dev/null +++ b/test/old-api/tag.spec.ts @@ -0,0 +1,17 @@ +import { Tag } from '../../src/old-api/tag'; +import { assertDescriptionMixin, assertExtensionsMixin, assertExternalDocumentationMixin } from './mixins'; + +describe('Tag', function() { + const json = { name: 'test', description: 'Testing', externalDocs: { url: 'somewhere' }, 'x-test': 'testing' }; + + describe('name()', function() { + it('should return a string', function() { + const d = new Tag(json); + expect(d.name()).toEqual(json.name); + }); + }); + + assertDescriptionMixin(Tag); + assertExtensionsMixin(Tag); + assertExternalDocumentationMixin(Tag); +}); \ No newline at end of file