mirror of
https://github.com/LukeHagar/prettier-plugin-openapi.git
synced 2025-12-06 12:47:47 +00:00
402 lines
14 KiB
TypeScript
402 lines
14 KiB
TypeScript
import { describe, expect, it } from 'bun:test';
|
|
import plugin from '../src/index';
|
|
|
|
describe('Custom Extensions Support', () => {
|
|
it('should handle custom extensions in top-level keys', () => {
|
|
const jsonParser = plugin.parsers?.['openapi-json-parser'];
|
|
expect(jsonParser).toBeDefined();
|
|
|
|
const testJson = {
|
|
'x-custom-field': 'value',
|
|
'openapi': '3.0.0',
|
|
'info': { 'title': 'Test API', 'version': '1.0.0' },
|
|
'paths': {},
|
|
'x-metadata': { 'custom': 'data' }
|
|
};
|
|
|
|
// @ts-expect-error We are mocking things here
|
|
const result = jsonParser?.parse(JSON.stringify(testJson), {});
|
|
expect(result).toBeDefined();
|
|
expect(result?.content).toBeDefined();
|
|
});
|
|
|
|
it('should handle custom extensions in info section', () => {
|
|
const jsonParser = plugin.parsers?.['openapi-json-parser'];
|
|
expect(jsonParser).toBeDefined();
|
|
|
|
const testJson = {
|
|
'openapi': '3.0.0',
|
|
'info': {
|
|
'title': 'Test API',
|
|
'x-api-id': 'api-123',
|
|
'version': '1.0.0',
|
|
'x-version-info': 'v1.0.0-beta',
|
|
'description': 'API Description'
|
|
}
|
|
};
|
|
|
|
// @ts-expect-error We are mocking things here
|
|
const result = jsonParser?.parse(JSON.stringify(testJson), {});
|
|
expect(result).toBeDefined();
|
|
expect(result?.content.info).toBeDefined();
|
|
expect(result?.content.info['x-api-id']).toBe('api-123');
|
|
});
|
|
|
|
it('should handle custom extensions in operation objects', () => {
|
|
const jsonParser = plugin.parsers?.['openapi-json-parser'];
|
|
expect(jsonParser).toBeDefined();
|
|
|
|
const testJson = {
|
|
'openapi': '3.0.0',
|
|
'info': { 'title': 'Test API', 'version': '1.0.0' },
|
|
'paths': {
|
|
'/test': {
|
|
'get': {
|
|
'summary': 'Test endpoint',
|
|
'x-rate-limit': 100,
|
|
'responses': { '200': { 'description': 'OK' } },
|
|
'x-custom-auth': 'bearer'
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
// @ts-expect-error We are mocking things here
|
|
const result = jsonParser?.parse(JSON.stringify(testJson), {});
|
|
expect(result).toBeDefined();
|
|
expect(result?.content.paths['/test'].get['x-rate-limit']).toBe(100);
|
|
});
|
|
|
|
it('should handle custom extensions in schema objects', () => {
|
|
const jsonParser = plugin.parsers?.['openapi-json-parser'];
|
|
expect(jsonParser).toBeDefined();
|
|
|
|
const testJson = {
|
|
'openapi': '3.0.0',
|
|
'info': { 'title': 'Test API', 'version': '1.0.0' },
|
|
'components': {
|
|
'schemas': {
|
|
'User': {
|
|
'type': 'object',
|
|
'x-custom-type': 'entity',
|
|
'properties': {
|
|
'id': { 'type': 'integer' }
|
|
},
|
|
'x-validation-rules': 'required'
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
// @ts-expect-error We are mocking things here
|
|
const result = jsonParser?.parse(JSON.stringify(testJson), {});
|
|
expect(result).toBeDefined();
|
|
expect(result?.content.components.schemas.User['x-custom-type']).toBe('entity');
|
|
});
|
|
|
|
it('should format JSON with custom extensions', () => {
|
|
const jsonPrinter = plugin.printers?.['openapi-json-ast'];
|
|
expect(jsonPrinter).toBeDefined();
|
|
|
|
const testData = {
|
|
content: {
|
|
'x-custom-field': 'value',
|
|
'openapi': '3.0.0',
|
|
'info': { 'title': 'Test API', 'version': '1.0.0' },
|
|
'paths': {},
|
|
'x-metadata': { 'custom': 'data' }
|
|
}
|
|
};
|
|
|
|
// @ts-expect-error We are mocking things here so we don't need to pass a print function
|
|
const result = jsonPrinter?.print({ getValue: () => testData }, { tabWidth: 2 }, () => '');
|
|
expect(result).toBeDefined();
|
|
expect(result).toContain('"x-custom-field"');
|
|
expect(result).toContain('"openapi"');
|
|
});
|
|
|
|
it('should format YAML with custom extensions', () => {
|
|
const yamlPrinter = plugin.printers?.['openapi-yaml-ast'];
|
|
expect(yamlPrinter).toBeDefined();
|
|
|
|
const testData = {
|
|
content: {
|
|
'x-custom-field': 'value',
|
|
'openapi': '3.0.0',
|
|
'info': { 'title': 'Test API', 'version': '1.0.0' },
|
|
'paths': {},
|
|
'x-metadata': { 'custom': 'data' }
|
|
}
|
|
};
|
|
|
|
// @ts-expect-error We are mocking things here so we don't need to pass a print function
|
|
const result = yamlPrinter?.print({ getValue: () => testData }, { tabWidth: 2 }, () => '');
|
|
expect(result).toBeDefined();
|
|
expect(result).toContain('x-custom-field:');
|
|
expect(result).toContain('openapi:');
|
|
});
|
|
|
|
it('should handle unknown keys alphabetically at the end', () => {
|
|
const jsonParser = plugin.parsers?.['openapi-json-parser'];
|
|
expect(jsonParser).toBeDefined();
|
|
|
|
const testJson = {
|
|
'openapi': '3.0.0',
|
|
'info': { 'title': 'Test API', 'version': '1.0.0' },
|
|
'unknown-field': 'value',
|
|
'paths': {},
|
|
'another-unknown': 'value'
|
|
};
|
|
|
|
// @ts-expect-error We are mocking things here
|
|
const result = jsonParser?.parse(JSON.stringify(testJson), {});
|
|
expect(result).toBeDefined();
|
|
expect(result?.content).toBeDefined();
|
|
});
|
|
|
|
describe('Custom extension positioning', () => {
|
|
it('should position custom extensions correctly in top-level', () => {
|
|
const jsonPrinter = plugin.printers?.['openapi-json-ast'];
|
|
expect(jsonPrinter).toBeDefined();
|
|
|
|
const testData = {
|
|
content: {
|
|
'x-custom-field': 'value',
|
|
'openapi': '3.0.0',
|
|
'info': { 'title': 'Test API', 'version': '1.0.0' },
|
|
'paths': {},
|
|
'x-metadata': { 'custom': 'data' }
|
|
}
|
|
};
|
|
|
|
// @ts-expect-error We are mocking things here
|
|
const result = jsonPrinter?.print({ getValue: () => testData }, { tabWidth: 2 }, () => '');
|
|
expect(result).toBeDefined();
|
|
|
|
if (!result) {
|
|
throw new Error('Result is undefined');
|
|
}
|
|
|
|
// Custom extensions should come after standard keys
|
|
const openapiIndex = result.toString().indexOf('"openapi"');
|
|
const infoIndex = result.toString().indexOf('"info"');
|
|
const pathsIndex = result.toString().indexOf('"paths"');
|
|
const xCustomFieldIndex = result.toString().indexOf('"x-custom-field"');
|
|
const xMetadataIndex = result.toString().indexOf('"x-metadata"');
|
|
|
|
expect(openapiIndex).toBeLessThan(infoIndex);
|
|
expect(infoIndex).toBeLessThan(pathsIndex);
|
|
expect(pathsIndex).toBeLessThan(xCustomFieldIndex);
|
|
expect(xCustomFieldIndex).toBeLessThan(xMetadataIndex);
|
|
});
|
|
|
|
it('should position custom extensions correctly in info section', () => {
|
|
const jsonPrinter = plugin.printers?.['openapi-json-ast'];
|
|
expect(jsonPrinter).toBeDefined();
|
|
|
|
const testData = {
|
|
content: {
|
|
'openapi': '3.0.0',
|
|
'info': {
|
|
'title': 'Test API',
|
|
'x-api-id': 'api-123',
|
|
'description': 'API Description',
|
|
'version': '1.0.0',
|
|
'x-version-info': 'v1.0.0-beta'
|
|
}
|
|
}
|
|
};
|
|
|
|
// @ts-expect-error We are mocking things here
|
|
const result = jsonPrinter?.print({ getValue: () => testData }, { tabWidth: 2 }, () => '');
|
|
expect(result).toBeDefined();
|
|
|
|
if (!result) {
|
|
throw new Error('Result is undefined');
|
|
}
|
|
|
|
// Custom extensions should come after standard keys
|
|
const titleIndex = result.toString().indexOf('"title"');
|
|
const descriptionIndex = result.toString().indexOf('"description"');
|
|
const versionIndex = result.toString().indexOf('"version"');
|
|
const xApiIdIndex = result.toString().indexOf('"x-api-id"');
|
|
const xVersionInfoIndex = result.toString().indexOf('"x-version-info"');
|
|
|
|
expect(titleIndex).toBeLessThan(versionIndex);
|
|
expect(versionIndex).toBeLessThan(descriptionIndex);
|
|
expect(descriptionIndex).toBeLessThan(xApiIdIndex);
|
|
expect(xApiIdIndex).toBeLessThan(xVersionInfoIndex);
|
|
});
|
|
|
|
it('should position custom extensions correctly in operation objects', () => {
|
|
const jsonPrinter = plugin.printers?.['openapi-json-ast'];
|
|
expect(jsonPrinter).toBeDefined();
|
|
|
|
const testData = {
|
|
content: {
|
|
'openapi': '3.0.0',
|
|
'info': { 'title': 'Test API', 'version': '1.0.0' },
|
|
'paths': {
|
|
'/test': {
|
|
'get': {
|
|
'summary': 'Test endpoint',
|
|
'x-rate-limit': 100,
|
|
'responses': { '200': { 'description': 'OK' } },
|
|
'x-custom-auth': 'bearer'
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
// @ts-expect-error We are mocking things here
|
|
const result = jsonPrinter?.print({ getValue: () => testData }, { tabWidth: 2 }, () => '');
|
|
expect(result).toBeDefined();
|
|
|
|
if (!result) {
|
|
throw new Error('Result is undefined');
|
|
}
|
|
|
|
// Custom extensions should come after standard keys
|
|
// Find the operation section specifically
|
|
const operationStart = result.toString().indexOf('"get": {');
|
|
// Find the end of the operation object
|
|
const operationEnd = result.toString().indexOf('}', result.toString().lastIndexOf('"x-custom-auth"'));
|
|
const operationSection = result.toString().substring(operationStart, operationEnd + 1);
|
|
|
|
const summaryIndex = operationSection.indexOf('"summary"');
|
|
const responsesIndex = operationSection.indexOf('"responses"');
|
|
const xRateLimitIndex = operationSection.indexOf('"x-rate-limit"');
|
|
const xCustomAuthIndex = operationSection.indexOf('"x-custom-auth"');
|
|
|
|
expect(summaryIndex).toBeLessThan(responsesIndex);
|
|
expect(responsesIndex).toBeLessThan(xCustomAuthIndex);
|
|
expect(xCustomAuthIndex).toBeLessThan(xRateLimitIndex);
|
|
});
|
|
|
|
it('should position custom extensions correctly in schema objects', () => {
|
|
const jsonPrinter = plugin.printers?.['openapi-json-ast'];
|
|
expect(jsonPrinter).toBeDefined();
|
|
|
|
const testData = {
|
|
content: {
|
|
'openapi': '3.0.0',
|
|
'info': { 'title': 'Test API', 'version': '1.0.0' },
|
|
'components': {
|
|
'schemas': {
|
|
'User': {
|
|
'type': 'object',
|
|
'x-custom-type': 'entity',
|
|
'properties': { 'id': { 'type': 'integer' } },
|
|
'x-validation-rules': 'required'
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
// @ts-expect-error We are mocking things here
|
|
const result = jsonPrinter?.print({ getValue: () => testData }, { tabWidth: 2 }, () => '');
|
|
expect(result).toBeDefined();
|
|
|
|
if (!result) {
|
|
throw new Error('Result is undefined');
|
|
}
|
|
|
|
// Custom extensions should come after standard keys
|
|
const typeIndex = result.toString().indexOf('"type"');
|
|
const propertiesIndex = result.toString().indexOf('"properties"');
|
|
const xCustomTypeIndex = result.toString().indexOf('"x-custom-type"');
|
|
const xValidationRulesIndex = result.toString().indexOf('"x-validation-rules"');
|
|
|
|
expect(typeIndex).toBeLessThan(propertiesIndex);
|
|
expect(propertiesIndex).toBeLessThan(xCustomTypeIndex);
|
|
expect(xCustomTypeIndex).toBeLessThan(xValidationRulesIndex);
|
|
});
|
|
});
|
|
|
|
describe('Unknown key handling', () => {
|
|
it('should sort unknown keys alphabetically at the end', () => {
|
|
const jsonPrinter = plugin.printers?.['openapi-json-ast'];
|
|
expect(jsonPrinter).toBeDefined();
|
|
|
|
const testData = {
|
|
content: {
|
|
'openapi': '3.0.0',
|
|
'info': { 'title': 'Test API', 'version': '1.0.0' },
|
|
'unknown-field': 'value',
|
|
'paths': {},
|
|
'another-unknown': 'value'
|
|
}
|
|
};
|
|
|
|
// @ts-expect-error We are mocking things here
|
|
const result = jsonPrinter?.print({ getValue: () => testData }, { tabWidth: 2 }, () => '');
|
|
expect(result).toBeDefined();
|
|
|
|
if (!result) {
|
|
throw new Error('Result is undefined');
|
|
}
|
|
|
|
// Unknown keys should come after standard keys and be sorted alphabetically
|
|
const openapiIndex = result.toString().indexOf('"openapi"');
|
|
const infoIndex = result.toString().indexOf('"info"');
|
|
const pathsIndex = result.toString().indexOf('"paths"');
|
|
const anotherUnknownIndex = result.toString().indexOf('"another-unknown"');
|
|
const unknownFieldIndex = result.toString().indexOf('"unknown-field"');
|
|
|
|
expect(openapiIndex).toBeLessThan(infoIndex);
|
|
expect(infoIndex).toBeLessThan(pathsIndex);
|
|
// Unknown keys should come after standard keys
|
|
expect(pathsIndex).toBeLessThan(anotherUnknownIndex);
|
|
expect(pathsIndex).toBeLessThan(unknownFieldIndex);
|
|
// Unknown keys should be sorted alphabetically
|
|
expect(anotherUnknownIndex).toBeLessThan(unknownFieldIndex);
|
|
});
|
|
|
|
it('should handle mixed custom extensions and unknown keys', () => {
|
|
const jsonPrinter = plugin.printers?.['openapi-json-ast'];
|
|
expect(jsonPrinter).toBeDefined();
|
|
|
|
const testData = {
|
|
content: {
|
|
'openapi': '3.0.0',
|
|
'info': { 'title': 'Test API', 'version': '1.0.0' },
|
|
'x-custom-field': 'value',
|
|
'unknown-field': 'value',
|
|
'paths': {},
|
|
'x-metadata': { 'custom': 'data' },
|
|
'another-unknown': 'value'
|
|
}
|
|
};
|
|
|
|
// @ts-expect-error We are mocking things here
|
|
const result = jsonPrinter?.print({ getValue: () => testData }, { tabWidth: 2 }, () => '');
|
|
expect(result).toBeDefined();
|
|
|
|
if (!result) {
|
|
throw new Error('Result is undefined');
|
|
}
|
|
|
|
// Standard keys first, then custom extensions, then unknown keys alphabetically
|
|
const openapiIndex = result.toString().indexOf('"openapi"');
|
|
const infoIndex = result.toString().indexOf('"info"');
|
|
const pathsIndex = result.toString().indexOf('"paths"');
|
|
const xCustomFieldIndex = result.toString().indexOf('"x-custom-field"');
|
|
const xMetadataIndex = result.toString().indexOf('"x-metadata"');
|
|
const anotherUnknownIndex = result.toString().indexOf('"another-unknown"');
|
|
const unknownFieldIndex = result.toString().indexOf('"unknown-field"');
|
|
|
|
expect(openapiIndex).toBeLessThan(infoIndex);
|
|
expect(infoIndex).toBeLessThan(pathsIndex);
|
|
// Standard keys should come first
|
|
expect(pathsIndex).toBeLessThan(xCustomFieldIndex);
|
|
expect(pathsIndex).toBeLessThan(xMetadataIndex);
|
|
expect(pathsIndex).toBeLessThan(anotherUnknownIndex);
|
|
expect(pathsIndex).toBeLessThan(unknownFieldIndex);
|
|
// Unknown keys should be sorted alphabetically
|
|
expect(anotherUnknownIndex).toBeLessThan(unknownFieldIndex);
|
|
});
|
|
});
|
|
});
|