import { describe, expect, it } from 'bun:test'; import { parseOpenAPI, traverse } from '@gitbook/openapi-parser'; import serverPrecedenceSpec from './fixtures/spec-server-precedence.json'; import { resolveOpenAPIOperation } from './resolveOpenAPIOperation'; async function loadFixture(spec: object) { const { filesystem } = await parseOpenAPI({ value: JSON.stringify(spec), rootURL: 'memory://spec.json', }); return filesystem; } async function fetchFilesystem(url: string) { const response = await fetch(url); const text = await response.text(); const { filesystem } = await parseOpenAPI({ value: text, rootURL: url }); const transformedFs = await traverse(filesystem, async (node) => { if ('description' in node && typeof node.description === 'string' && node.description) { node['x-gitbook-description-html'] = node.description; } return node; }); return transformedFs; } describe('#resolveOpenAPIOperation', () => { it('should resolve refs', async () => { const filesystem = await fetchFilesystem( 'https://petstore3.swagger.io/api/v3/openapi.json' ); const resolved = await resolveOpenAPIOperation(filesystem, { method: 'put', path: '/pet' }); expect(resolved).toMatchObject({ servers: [ { url: '/api/v3', }, ], operation: { tags: ['pet'], summary: 'Update an existing pet.', description: 'Update an existing pet by Id.', requestBody: { content: { 'application/json': { schema: { type: 'object', required: ['name', 'photoUrls'], }, }, }, }, }, }); }); it('should support yaml', async () => { const filesystem = await fetchFilesystem( 'https://petstore3.swagger.io/api/v3/openapi.yaml' ); const resolved = await resolveOpenAPIOperation(filesystem, { method: 'put', path: '/pet' }); expect(resolved).toMatchObject({ servers: [ { url: '/api/v3', }, ], operation: { tags: ['pet'], summary: 'Update an existing pet.', description: 'Update an existing pet by Id.', requestBody: { content: { 'application/json': { schema: { type: 'object', required: ['name', 'photoUrls'], }, }, }, }, }, }); }); it('should resolve circular refs', async () => { const filesystem = await fetchFilesystem('https://api.gitbook.com/openapi.json'); const resolved = await resolveOpenAPIOperation(filesystem, { method: 'get', path: '/spaces/{spaceId}/content/page/{pageId}', }); expect(resolved).toMatchObject({ servers: [ { url: '{host}/v1', }, ], operation: { operationId: 'getPageById', }, }); }); it('should resolve to null if the method is not supported', async () => { const filesystem = await fetchFilesystem( 'https://petstore3.swagger.io/api/v3/openapi.json' ); const resolved = await resolveOpenAPIOperation(filesystem, { method: 'dontexist', path: '/pet', }); expect(resolved).toBe(null); }); it('should parse Swagger 2.0', async () => { const filesystem = await fetchFilesystem('https://petstore.swagger.io/v2/swagger.json'); const resolved = await resolveOpenAPIOperation(filesystem, { method: 'put', path: '/pet', }); expect(resolved).toMatchObject({ servers: [ { url: 'https://petstore.swagger.io/v2', }, { url: 'http://petstore.swagger.io/v2', }, ], operation: { tags: ['pet'], summary: 'Update an existing pet', description: '', requestBody: { content: { 'application/json': { schema: { type: 'object', required: ['name', 'photoUrls'], }, }, }, }, }, }); }); it('should resolve a ref with whitespace', async () => { const filesystem = await fetchFilesystem( ' https://petstore3.swagger.io/api/v3/openapi.json' ); const resolved = await resolveOpenAPIOperation(filesystem, { method: 'put', path: '/pet', }); expect(resolved).toMatchObject({ servers: [ { url: '/api/v3', }, ], operation: { tags: ['pet'], summary: 'Update an existing pet.', description: 'Update an existing pet by Id.', requestBody: { content: { 'application/json': { schema: { type: 'object', required: ['name', 'photoUrls'], }, }, }, }, }, }); }); describe('x-enable-proxy', () => { it('should extract x-enable-proxy when set to true', async () => { const filesystem = await loadFixture({ openapi: '3.1.0', info: { title: 'Test', version: '1.0' }, 'x-enable-proxy': true, paths: { '/test': { get: { responses: { '200': { description: 'OK' } } }, }, }, }); const resolved = await resolveOpenAPIOperation(filesystem, { method: 'get', path: '/test', }); expect(resolved?.['x-enable-proxy']).toBe(true); }); it('should extract x-enable-proxy when set to false', async () => { const filesystem = await loadFixture({ openapi: '3.1.0', info: { title: 'Test', version: '1.0' }, 'x-enable-proxy': false, paths: { '/test': { get: { responses: { '200': { description: 'OK' } } }, }, }, }); const resolved = await resolveOpenAPIOperation(filesystem, { method: 'get', path: '/test', }); expect(resolved?.['x-enable-proxy']).toBe(false); }); it('should return undefined when x-enable-proxy is not set', async () => { const filesystem = await loadFixture({ openapi: '3.1.0', info: { title: 'Test', version: '1.0' }, paths: { '/test': { get: { responses: { '200': { description: 'OK' } } }, }, }, }); const resolved = await resolveOpenAPIOperation(filesystem, { method: 'get', path: '/test', }); expect(resolved?.['x-enable-proxy']).toBeUndefined(); }); it('should ignore x-enable-proxy when not a boolean', async () => { const filesystem = await loadFixture({ openapi: '3.1.0', info: { title: 'Test', version: '1.0' }, 'x-enable-proxy': 'yes', paths: { '/test': { get: { responses: { '200': { description: 'OK' } } }, }, }, }); const resolved = await resolveOpenAPIOperation(filesystem, { method: 'get', path: '/test', }); expect(resolved?.['x-enable-proxy']).toBeUndefined(); }); it('should be available at the operation level', async () => { const filesystem = await loadFixture({ openapi: '3.1.0', info: { title: 'Test', version: '1.0' }, paths: { '/test': { get: { 'x-enable-proxy': true, responses: { '200': { description: 'OK' } }, }, }, }, }); const resolved = await resolveOpenAPIOperation(filesystem, { method: 'get', path: '/test', }); expect(resolved?.operation['x-enable-proxy']).toBe(true); }); it('should allow operation-level to override spec-level', async () => { const filesystem = await loadFixture({ openapi: '3.1.0', info: { title: 'Test', version: '1.0' }, 'x-enable-proxy': true, paths: { '/test': { get: { 'x-enable-proxy': false, responses: { '200': { description: 'OK' } }, }, }, }, }); const resolved = await resolveOpenAPIOperation(filesystem, { method: 'get', path: '/test', }); expect(resolved?.['x-enable-proxy']).toBe(true); expect(resolved?.operation['x-enable-proxy']).toBe(false); }); it('should allow operation-level true to override spec-level false', async () => { const filesystem = await loadFixture({ openapi: '3.1.0', info: { title: 'Test', version: '1.0' }, 'x-enable-proxy': false, paths: { '/test': { get: { 'x-enable-proxy': true, responses: { '200': { description: 'OK' } }, }, }, }, }); const resolved = await resolveOpenAPIOperation(filesystem, { method: 'get', path: '/test', }); expect(resolved?.['x-enable-proxy']).toBe(false); expect(resolved?.operation['x-enable-proxy']).toBe(true); }); }); describe('server precedence', () => { it('should use root-level servers when no path or operation servers are defined', async () => { const filesystem = await loadFixture(serverPrecedenceSpec); const resolved = await resolveOpenAPIOperation(filesystem, { method: 'get', path: '/root-only', }); expect(resolved?.servers).toEqual([{ url: 'https://root.example.com' }]); }); it('should use path-level servers over root-level servers', async () => { const filesystem = await loadFixture(serverPrecedenceSpec); const resolved = await resolveOpenAPIOperation(filesystem, { method: 'get', path: '/path-override', }); expect(resolved?.servers).toEqual([{ url: 'https://path.example.com' }]); }); it('should use operation-level servers over path and root-level servers', async () => { const filesystem = await loadFixture(serverPrecedenceSpec); const resolved = await resolveOpenAPIOperation(filesystem, { method: 'get', path: '/operation-override', }); expect(resolved?.servers).toEqual([{ url: 'https://operation.example.com' }]); }); it('should use operation-level servers over root-level when no path servers exist', async () => { const filesystem = await loadFixture(serverPrecedenceSpec); const resolved = await resolveOpenAPIOperation(filesystem, { method: 'get', path: '/operation-skip-path', }); expect(resolved?.servers).toEqual([{ url: 'https://operation.example.com' }]); }); it('should fallback to root-level servers for endpoints without overrides', async () => { const filesystem = await loadFixture(serverPrecedenceSpec); const resolved = await resolveOpenAPIOperation(filesystem, { method: 'get', path: '/no-servers', }); expect(resolved?.servers).toEqual([{ url: 'https://root.example.com' }]); }); }); });