UNPKG

@redocly/openapi-core

Version:

See https://github.com/Redocly/redocly-cli

479 lines (427 loc) 13.1 kB
import outdent from 'outdent'; import * as path from 'path'; import { bundleDocument, bundle, bundleFromString } from '../bundle'; import { parseYamlToDocument, yamlSerializer, makeConfig } from '../../__tests__/utils'; import { StyleguideConfig, Config, ResolvedConfig, createConfig, loadConfig } from '../config'; import { BaseResolver } from '../resolve'; const stringDocument = outdent` openapi: 3.0.0 paths: /pet: get: operationId: get parameters: - $ref: '#/components/parameters/shared_a' - name: get_b post: operationId: post parameters: - $ref: '#/components/parameters/shared_a' components: parameters: shared_a: name: shared-a `; const testDocument = parseYamlToDocument(stringDocument, ''); describe('bundle', () => { const fetchMock = jest.fn(() => Promise.resolve({ ok: true, text: () => 'External schema content', headers: { get: () => '', }, }) ); expect.addSnapshotSerializer(yamlSerializer); it('change nothing with only internal refs', async () => { const { bundle, problems } = await bundleDocument({ document: testDocument, externalRefResolver: new BaseResolver(), config: new StyleguideConfig({}), }); const origCopy = JSON.parse(JSON.stringify(testDocument.parsed)); expect(problems).toHaveLength(0); expect(bundle.parsed).toEqual(origCopy); }); it('should bundle external refs', async () => { const { bundle: res, problems } = await bundle({ config: new Config({} as ResolvedConfig), ref: path.join(__dirname, 'fixtures/refs/openapi-with-external-refs.yaml'), }); expect(problems).toHaveLength(0); expect(res.parsed).toMatchSnapshot(); }); it('should bundle external refs and warn for conflicting names', async () => { const { bundle: res, problems } = await bundle({ config: new Config({} as ResolvedConfig), ref: path.join(__dirname, 'fixtures/refs/openapi-with-external-refs-conflicting-names.yaml'), }); expect(problems).toHaveLength(1); expect(problems[0].message).toEqual( `Two schemas are referenced with the same name but different content. Renamed param-b to param-b-2.` ); expect(res.parsed).toMatchSnapshot(); }); it('should dereferenced correctly when used with dereference', async () => { const { bundle: res, problems } = await bundleDocument({ externalRefResolver: new BaseResolver(), config: new StyleguideConfig({}), document: testDocument, dereference: true, }); expect(problems).toHaveLength(0); expect(res.parsed).toMatchSnapshot(); }); it('should place referenced schema inline when referenced schema name resolves to original schema name', async () => { const { bundle: res, problems } = await bundle({ config: new Config({} as ResolvedConfig), ref: path.join(__dirname, 'fixtures/refs/externalref.yaml'), }); expect(problems).toHaveLength(0); expect(res.parsed).toMatchSnapshot(); }); it('should not place referenced schema inline when component in question is not of type "schemas"', async () => { const { bundle: res, problems } = await bundle({ config: new Config({} as ResolvedConfig), ref: path.join(__dirname, 'fixtures/refs/external-request-body.yaml'), }); expect(problems).toHaveLength(0); expect(res.parsed).toMatchSnapshot(); }); it('should pull hosted schema', async () => { const { bundle: res, problems } = await bundle({ config: new Config({} as ResolvedConfig), externalRefResolver: new BaseResolver({ http: { customFetch: fetchMock, headers: [], }, }), ref: path.join(__dirname, 'fixtures/refs/hosted.yaml'), }); expect(problems).toHaveLength(0); expect(fetchMock).toHaveBeenCalledWith('https://someexternal.schema', { headers: {}, }); expect(res.parsed).toMatchSnapshot(); }); it('should not bundle url refs if used with keepUrlRefs', async () => { const { bundle: res, problems } = await bundle({ config: new Config({} as ResolvedConfig), externalRefResolver: new BaseResolver({ http: { customFetch: fetchMock, headers: [], }, }), ref: path.join(__dirname, 'fixtures/refs/openapi-with-url-refs.yaml'), keepUrlRefs: true, }); expect(problems).toHaveLength(0); expect(res.parsed).toMatchSnapshot(); }); it('should add to meta ref from redocly registry', async () => { const testDocument = parseYamlToDocument( outdent` openapi: 3.0.0 paths: /pet: get: operationId: get parameters: - $ref: '#/components/parameters/shared_a' - name: get_b post: operationId: post parameters: - $ref: 'https://api.redocly.com/registry/params' components: parameters: shared_a: name: shared-a `, '' ); const config = await makeConfig({ rules: {}, decorators: { 'registry-dependencies': 'on' } }); const { bundle: result, problems, ...meta } = await bundleDocument({ document: testDocument, config: config, externalRefResolver: new BaseResolver({ http: { customFetch: fetchMock, headers: [], }, }), }); const parsedMeta = JSON.parse(JSON.stringify(meta)); expect(problems).toHaveLength(0); expect(parsedMeta).toMatchSnapshot(); }); it('should bundle refs using $anchors', async () => { const testDocument = parseYamlToDocument( outdent` openapi: 3.1.0 components: schemas: User: type: object properties: profile: $ref: '#user-profile' UserProfile: $anchor: user-profile type: string `, '' ); const config = await makeConfig({ rules: {} }); const { bundle: { parsed }, problems, } = await bundleDocument({ document: testDocument, config: config, externalRefResolver: new BaseResolver(), }); expect(problems).toHaveLength(0); expect(parsed).toMatchInlineSnapshot(` openapi: 3.1.0 components: schemas: User: type: object properties: profile: $ref: '#user-profile' UserProfile: $anchor: user-profile type: string `); }); it('should throw an error when there is no document to bundle', () => { const wrapper = () => bundle({ config: new Config({} as ResolvedConfig), }); expect(wrapper()).rejects.toThrowError('Document or reference is required.\n'); }); it('should bundle with a doc provided', async () => { const { bundle: { parsed }, problems, } = await bundle({ config: await loadConfig({ configPath: path.join(__dirname, 'fixtures/redocly.yaml') }), doc: testDocument, }); const origCopy = JSON.parse(JSON.stringify(testDocument.parsed)); expect(problems).toHaveLength(0); expect(parsed).toEqual(origCopy); }); it('should bundle schemas with properties named $ref and externalValues correctly', async () => { const { bundle: res, problems } = await bundle({ config: new Config({} as ResolvedConfig), ref: path.join(__dirname, 'fixtures/refs/openapi-with-special-names-in-props.yaml'), }); expect(problems).toHaveLength(0); expect(res.parsed).toMatchSnapshot(); }); it('should not fail when bundling openapi with nulls', async () => { const testDocument = parseYamlToDocument( outdent` openapi: 3.1.0 paths: /: get: responses: 200: content: application/json: schema: type: object properties: examples: Foo: `, '' ); const config = await makeConfig({ rules: {} }); const { bundle: { parsed }, problems, } = await bundleDocument({ document: testDocument, config: config, externalRefResolver: new BaseResolver(), }); expect(problems).toHaveLength(0); expect(parsed).toMatchInlineSnapshot(` openapi: 3.1.0 paths: /: get: responses: '200': content: application/json: schema: type: object properties: null examples: Foo: null components: {} `); }); }); describe('bundleFromString', () => { it('should bundle from string using bundleFromString', async () => { const { bundle: { parsed, ...rest }, problems, } = await bundleFromString({ config: await createConfig(` extends: - recommended `), source: testDocument.source.body, }); expect(problems).toHaveLength(0); expect(rest.source.body).toEqual(stringDocument); }); }); describe('bundle async', () => { it('should bundle async of version 2.x', async () => { const testDocument = parseYamlToDocument( outdent` asyncapi: '2.6.0' info: title: Account Service version: 1.0.0 description: This service is in charge of processing user signups channels: user/signedup: subscribe: message: $ref: '#/components/messages/UserSignedUp' components: schemas: UserSignedUp: type: object properties: displayName: type: string description: Name of the user messages: UserSignedUp: payload: $ref: '#/components/schemas/UserSignedUp' `, '' ); const config = await makeConfig({ rules: {} }); const { bundle: { parsed }, problems, } = await bundleDocument({ document: testDocument, config: config, externalRefResolver: new BaseResolver(), dereference: true, }); expect(problems).toHaveLength(0); expect(parsed).toMatchInlineSnapshot(` asyncapi: 2.6.0 info: title: Account Service version: 1.0.0 description: This service is in charge of processing user signups channels: user/signedup: subscribe: message: payload: &ref_1 type: object properties: &ref_0 displayName: type: string description: Name of the user components: schemas: UserSignedUp: type: object properties: *ref_0 messages: UserSignedUp: payload: *ref_1 `); }); it('should bundle async of version 3.0', async () => { const testDocument = parseYamlToDocument( outdent` asyncapi: 3.0.0 info: title: Account Service version: 1.0.0 description: This service is in charge of processing user signups operations: sendUserSignedup: action: send messages: - $ref: '#/components/messages/UserSignedUp' components: schemas: UserSignedUp: type: object properties: displayName: type: string description: Name of the user messages: UserSignedUp: payload: $ref: '#/components/schemas/UserSignedUp' `, '' ); const config = await makeConfig({ rules: {} }); const { bundle: { parsed }, problems, } = await bundleDocument({ document: testDocument, config: config, externalRefResolver: new BaseResolver(), dereference: true, }); expect(problems).toHaveLength(0); expect(parsed).toMatchInlineSnapshot(` asyncapi: 3.0.0 info: title: Account Service version: 1.0.0 description: This service is in charge of processing user signups operations: sendUserSignedup: action: send messages: - payload: &ref_1 type: object properties: &ref_0 displayName: type: string description: Name of the user components: schemas: UserSignedUp: type: object properties: *ref_0 messages: UserSignedUp: payload: *ref_1 `); }); });