Files
redocly-cli/packages/core/__tests__/resolve.test.ts
2020-11-13 21:13:47 +02:00

368 lines
9.8 KiB
TypeScript

import { outdent } from 'outdent';
import * as path from 'path';
import { resolveDocument, BaseResolver, Document } from '../src/resolve';
import { parseYamlToDocument } from './utils';
import { Oas3Types } from '../src/types/oas3';
import { normalizeTypes } from '../src/types';
describe('collect refs', () => {
it('should resolve local refs', async () => {
const rootDocument = parseYamlToDocument(
outdent`
openapi: 3.0.0
info:
$ref: "#/defs/info"
defs:
info:
contact: {}
license: {}
`,
'foobar.yaml',
);
const resolvedRefs = await resolveDocument({
rootDocument,
externalRefResolver: new BaseResolver(),
rootType: normalizeTypes(Oas3Types).DefinitionRoot,
});
expect(resolvedRefs).toBeDefined();
expect(resolvedRefs.size).toEqual(1);
expect(Array.from(resolvedRefs.keys())).toMatchInlineSnapshot(
[`foobar.yaml::#/defs/info`],
`
Array [
"foobar.yaml::#/defs/info",
]
`,
);
expect(Array.from(resolvedRefs.values()).map((info) => info.node)).toEqual([
{ contact: {}, license: {} },
]);
});
// Or using async/await.
it('should throw on self-circular refs', async () => {
expect.assertions(1);
const rootDocument = parseYamlToDocument(
outdent`
openapi: 3.0.0
info:
$ref: "#/info"
defs:
info:
contact: {}
license: {}
`,
'',
);
try {
await resolveDocument({
rootDocument,
externalRefResolver: new BaseResolver(),
rootType: normalizeTypes(Oas3Types).DefinitionRoot,
});
} catch (e) {
expect(e.message).toEqual('Self-referencing circular pointer');
}
});
it('should resolve local transitive refs', async () => {
const rootDocument = parseYamlToDocument(
outdent`
openapi: 3.0.0
info:
$ref: "#/tmp/info"
tmp:
$ref: '#/defs'
prop:
$ref: '#/propTrans'
propTrans:
$ref: '#/propDest'
propDest:
type: string
defs:
info:
contact: {}
license: {}
`,
'foobar.yaml',
);
const resolvedRefs = await resolveDocument({
rootDocument,
externalRefResolver: new BaseResolver(),
rootType: normalizeTypes(Oas3Types).DefinitionRoot,
});
expect(resolvedRefs).toBeDefined();
expect(resolvedRefs.size).toEqual(4);
expect(Array.from(resolvedRefs.keys())).toEqual([
'foobar.yaml::#/defs',
'foobar.yaml::#/propDest',
'foobar.yaml::#/tmp/info',
'foobar.yaml::#/propTrans',
]);
expect(Array.from(resolvedRefs.values()).map((info) => info.node)).toEqual([
{ info: { contact: {}, license: {} } },
{ type: 'string' },
{ contact: {}, license: {} },
{ type: 'string' },
]);
});
it('should throw on ref loop', async () => {
const rootDocument = parseYamlToDocument(
outdent`
openapi: 3.0.0
info:
$ref: "#/loop"
loop:
$ref: '#/loop2'
loop2:
$ref: '#/info'
`,
'foobar.yaml',
);
try {
await resolveDocument({
rootDocument,
externalRefResolver: new BaseResolver(),
rootType: normalizeTypes(Oas3Types).DefinitionRoot,
});
} catch (e) {
expect(e.message).toEqual('Self-referencing circular pointer');
}
});
it('should resolve external ref', async () => {
const cwd = path.join(__dirname, 'fixtures/resolve');
const rootDocument = parseYamlToDocument(
outdent`
openapi: 3.0.0
info:
$ref: "./externalInfo.yaml#/info"
`,
path.join(cwd, 'foobar.yaml'),
);
const resolvedRefs = await resolveDocument({
rootDocument,
externalRefResolver: new BaseResolver(),
rootType: normalizeTypes(Oas3Types).DefinitionRoot,
});
expect(resolvedRefs).toBeDefined();
// expect(resolvedRefs.size).toEqual(2);
expect(Array.from(resolvedRefs.keys()).map((ref) => ref.substring(cwd.length + 1))).toEqual([
'foobar.yaml::./externalInfo.yaml#/info',
'externalInfo.yaml::./externalLicense.yaml',
]);
expect(Array.from(resolvedRefs.values()).map((info) => info.node)).toEqual([
{
contact: {},
license: {
$ref: './externalLicense.yaml',
},
},
{
name: 'MIT',
},
]);
});
it('should resolve back references', async () => {
const cwd = path.join(__dirname, 'fixtures/resolve');
const externalRefResolver = new BaseResolver();
const rootDocument = await externalRefResolver.resolveDocument(
null,
`${cwd}/openapi-with-back.yaml`,
);
const resolvedRefs = await resolveDocument({
rootDocument: rootDocument as Document,
externalRefResolver: externalRefResolver,
rootType: normalizeTypes(Oas3Types).DefinitionRoot,
});
expect(resolvedRefs).toBeDefined();
expect(Array.from(resolvedRefs.keys()).map((ref) => ref.substring(cwd.length + 1)))
.toMatchInlineSnapshot(`
Array [
"openapi-with-back.yaml::./schemas/type-a.yaml#/",
"openapi-with-back.yaml::./schemas/type-b.yaml#/",
"schemas/type-a.yaml::../openapi-with-back.yaml#/components/schemas/TypeB",
]
`);
expect(Array.from(resolvedRefs.values()).map((val) => val.node)).toMatchInlineSnapshot(`
Array [
Object {
"allOf": Array [
Object {
"properties": Object {
"integration_type": Object {
"$ref": "../openapi-with-back.yaml#/components/schemas/TypeB",
},
"name": Object {
"type": "string",
},
},
"required": Array [
"name",
"integration_type",
],
"type": "object",
},
],
},
Object {
"enum": Array [
"webhook",
"api_key",
"sftp",
"netsuite",
],
"type": "string",
},
Object {
"enum": Array [
"webhook",
"api_key",
"sftp",
"netsuite",
],
"type": "string",
},
]
`);
});
it('should resolve external refs with circular', async () => {
const cwd = path.join(__dirname, 'fixtures/resolve');
const externalRefResolver = new BaseResolver();
const rootDocument = await externalRefResolver.resolveDocument(null, `${cwd}/openapi.yaml`);
const resolvedRefs = await resolveDocument({
rootDocument: rootDocument as Document,
externalRefResolver: externalRefResolver,
rootType: normalizeTypes(Oas3Types).DefinitionRoot,
});
expect(resolvedRefs).toBeDefined();
expect(Array.from(resolvedRefs.keys()).map((ref) => ref.substring(cwd.length + 1)))
.toMatchInlineSnapshot(`
Array [
"openapi.yaml::#/components/schemas/Local",
"openapi.yaml::#/components/schemas/Local/properties/string",
"openapi.yaml::./External.yaml#/properties/string",
"openapi.yaml::./External.yaml",
"External.yaml::./External2.yaml",
"External2.yaml::./External.yaml#/properties",
]
`);
expect(Array.from(resolvedRefs.values()).map((val) => val.node)).toMatchInlineSnapshot(`
Array [
Object {
"properties": Object {
"localCircular": Object {
"$ref": "#/components/schemas/Local",
},
"number": Object {
"type": "number",
},
"string": Object {
"type": "string",
},
},
},
Object {
"type": "string",
},
Object {
"type": "string",
},
Object {
"properties": Object {
"external": Object {
"$ref": "./External2.yaml",
},
"number": Object {
"type": "number",
},
"string": Object {
"type": "string",
},
"unknown": Object {
"type": "string",
},
},
"type": "object",
},
Object {
"properties": Object {
"circularParent": Object {
"$ref": "./External.yaml#/properties",
},
},
"type": "object",
},
Object {
"external": Object {
"$ref": "./External2.yaml",
},
"number": Object {
"type": "number",
},
"string": Object {
"type": "string",
},
"unknown": Object {
"type": "string",
},
},
]
`);
});
it('should resolve referenceable scalars', async () => {
const cwd = path.join(__dirname, 'fixtures/resolve');
const externalRefResolver = new BaseResolver();
const rootDocument = await externalRefResolver.resolveDocument(
null,
`${cwd}/openapi-with-md-description.yaml`,
);
expect(rootDocument).toBeDefined();
const resolvedRefs = await resolveDocument({
rootDocument: rootDocument as Document,
externalRefResolver: externalRefResolver,
rootType: normalizeTypes(Oas3Types).DefinitionRoot,
});
expect(resolvedRefs).toBeDefined();
// expect(resolvedRefs.size).toEqual(2);
expect(Array.from(resolvedRefs.keys()).map((ref) => ref.substring(cwd.length + 1)))
.toMatchInlineSnapshot(`
Array [
"openapi-with-md-description.yaml::./description.md",
]
`);
expect(Array.from(resolvedRefs.values()).map((val) => val.node)).toMatchInlineSnapshot(`
Array [
"# Hello World
Lorem ipsum",
]
`);
});
});