UNPKG

@furystack/rest

Version:
294 lines 12.7 kB
import { describe, expect, it } from 'vitest'; import { resolveOpenApiRefs } from './openapi-resolve-refs.js'; describe('resolveOpenApiRefs', () => { describe('Schema $ref resolution', () => { it('Should resolve $ref to components/schemas', () => { const doc = { openapi: '3.1.0', info: { title: 'Test', version: '1.0.0' }, paths: { '/users': { get: { responses: { '200': { description: 'OK', content: { 'application/json': { schema: { $ref: '#/components/schemas/User' }, }, }, }, }, }, }, }, components: { schemas: { User: { type: 'object', properties: { id: { type: 'string' }, name: { type: 'string' } } }, }, }, }; const resolved = resolveOpenApiRefs(doc); const { schema } = (resolved.paths?.['/users']?.get?.responses?.['200']).content['application/json']; expect(schema).toEqual({ type: 'object', properties: { id: { type: 'string' }, name: { type: 'string' } } }); }); it('Should resolve nested $ref chains', () => { const doc = { openapi: '3.1.0', info: { title: 'Test', version: '1.0.0' }, paths: { '/items': { get: { responses: { '200': { description: 'OK', content: { 'application/json': { schema: { type: 'object', properties: { category: { $ref: '#/components/schemas/Category' }, }, }, }, }, }, }, }, }, }, components: { schemas: { Category: { type: 'object', properties: { id: { type: 'integer' }, name: { type: 'string' } } }, }, }, }; const resolved = resolveOpenApiRefs(doc); const schema = (resolved.paths?.['/items']?.get?.responses?.['200']).content['application/json'].schema; const props = schema.properties.category; expect(props.type).toBe('object'); expect(props.properties).toEqual({ id: { type: 'integer' }, name: { type: 'string' } }); }); }); describe('Parameter $ref resolution', () => { it('Should resolve $ref parameters', () => { const doc = { openapi: '3.1.0', info: { title: 'Test', version: '1.0.0' }, paths: { '/items': { get: { parameters: [{ $ref: '#/components/parameters/LimitParam' }], responses: { '200': { description: 'OK' } }, }, }, }, components: { parameters: { LimitParam: { name: 'limit', in: 'query', schema: { type: 'integer' } }, }, }, }; const resolved = resolveOpenApiRefs(doc); const params = resolved.paths?.['/items']?.get?.parameters; expect(params[0].name).toBe('limit'); expect(params[0].in).toBe('query'); }); }); describe('Response $ref resolution', () => { it('Should resolve $ref responses', () => { const doc = { openapi: '3.1.0', info: { title: 'Test', version: '1.0.0' }, paths: { '/items': { get: { responses: { '400': { $ref: '#/components/responses/BadRequest' }, }, }, }, }, components: { responses: { BadRequest: { description: 'Bad request', content: { 'application/json': { schema: { type: 'object', properties: { message: { type: 'string' } } }, }, }, }, }, }, }; const resolved = resolveOpenApiRefs(doc); const resp = resolved.paths?.['/items']?.get?.responses?.['400']; expect(resp.description).toBe('Bad request'); }); }); describe('RequestBody $ref resolution', () => { it('Should resolve $ref in requestBody schema', () => { const doc = { openapi: '3.1.0', info: { title: 'Test', version: '1.0.0' }, paths: { '/items': { post: { requestBody: { content: { 'application/json': { schema: { $ref: '#/components/schemas/Item' }, }, }, }, responses: { '201': { description: 'Created' } }, }, }, }, components: { schemas: { Item: { type: 'object', properties: { name: { type: 'string' } } }, }, }, }; const resolved = resolveOpenApiRefs(doc); const body = resolved.paths?.['/items']?.post?.requestBody; const schema = body.content['application/json'] .schema; expect(schema.type).toBe('object'); }); }); describe('Edge cases', () => { it('Should handle circular $ref by breaking the cycle with empty object', () => { const doc = { openapi: '3.1.0', info: { title: 'Test', version: '1.0.0' }, components: { schemas: { Node: { type: 'object', properties: { value: { type: 'string' }, child: { $ref: '#/components/schemas/Node' }, }, }, }, }, }; const resolved = resolveOpenApiRefs(doc); const nodeSchema = resolved.components?.schemas?.Node; expect(nodeSchema.type).toBe('object'); const props = nodeSchema.properties; expect(props.value).toEqual({ type: 'string' }); // The circular child ref is resolved, but the nested self-ref within it breaks the cycle expect(props.child.type).toBe('object'); const childProps = props.child.properties; expect(childProps.value).toEqual({ type: 'string' }); expect(childProps.child).toEqual({}); }); it('Should not modify the original document', () => { const doc = { openapi: '3.1.0', info: { title: 'Test', version: '1.0.0' }, paths: { '/items': { get: { responses: { '200': { description: 'OK', content: { 'application/json': { schema: { $ref: '#/components/schemas/Item' } } }, }, }, }, }, }, components: { schemas: { Item: { type: 'object' } } }, }; resolveOpenApiRefs(doc); const schema = doc.paths?.['/items']?.get?.responses?.['200']; expect(schema.content['application/json'].schema).toEqual({ $ref: '#/components/schemas/Item', }); }); it('Should leave external $ref as-is', () => { const doc = { openapi: '3.1.0', info: { title: 'Test', version: '1.0.0' }, paths: { '/items': { get: { responses: { '200': { description: 'OK', content: { 'application/json': { schema: { $ref: 'external.json#/Schema' } } }, }, }, }, }, }, }; const resolved = resolveOpenApiRefs(doc); const { schema } = (resolved.paths?.['/items']?.get?.responses?.['200']).content['application/json']; expect(schema).toEqual({ $ref: 'external.json#/Schema' }); }); it('Should leave unresolvable $ref as-is', () => { const doc = { openapi: '3.1.0', info: { title: 'Test', version: '1.0.0' }, paths: { '/items': { get: { responses: { '200': { description: 'OK', content: { 'application/json': { schema: { $ref: '#/components/schemas/Missing' } } }, }, }, }, }, }, }; const resolved = resolveOpenApiRefs(doc); const { schema } = (resolved.paths?.['/items']?.get?.responses?.['200']).content['application/json']; expect(schema).toEqual({ $ref: '#/components/schemas/Missing' }); }); it('Should handle documents with no $ref', () => { const doc = { openapi: '3.1.0', info: { title: 'Test', version: '1.0.0' }, paths: { '/health': { get: { responses: { '200': { description: 'OK' } } } }, }, }; const resolved = resolveOpenApiRefs(doc); expect(resolved.paths?.['/health']?.get?.responses?.['200']).toEqual({ description: 'OK' }); }); it('Should resolve arrays of items with $ref', () => { const doc = { openapi: '3.1.0', info: { title: 'Test', version: '1.0.0' }, paths: { '/items': { get: { parameters: [{ $ref: '#/components/parameters/A' }, { $ref: '#/components/parameters/B' }], responses: { '200': { description: 'OK' } }, }, }, }, components: { parameters: { A: { name: 'a', in: 'query', schema: { type: 'string' } }, B: { name: 'b', in: 'query', schema: { type: 'integer' } }, }, }, }; const resolved = resolveOpenApiRefs(doc); const params = resolved.paths?.['/items']?.get?.parameters; expect(params).toHaveLength(2); expect(params[0].name).toBe('a'); expect(params[1].name).toBe('b'); }); }); }); //# sourceMappingURL=openapi-resolve-refs.spec.js.map