Skip to main content
Glama
kadykov

OpenAPI Schema Explorer

path-item.test.ts7.95 kB
import { OpenAPIV3 } from 'openapi-types'; import { RenderablePathItem } from '../../../../src/rendering/path-item'; import { RenderContext } from '../../../../src/rendering/types'; import { IFormatter, JsonFormatter } from '../../../../src/services/formatters'; // Mock Formatter & Context const mockFormatter: IFormatter = new JsonFormatter(); const mockContext: RenderContext = { formatter: mockFormatter, baseUri: 'openapi://', }; // Sample PathItem Object Fixture const samplePathItem: OpenAPIV3.PathItemObject = { get: { summary: 'Get Item', responses: { '200': { description: 'OK' } }, }, post: { summary: 'Create Item', responses: { '201': { description: 'Created' } }, }, delete: { // No summary responses: { '204': { description: 'No Content' } }, }, parameters: [ // Example path-level parameter { name: 'commonParam', in: 'query', schema: { type: 'string' } }, ], }; // Define both the raw path and the expected suffix (built using the builder logic) const rawPath = '/items'; const pathUriSuffix = 'paths/items'; // Builder removes leading '/' and encodes, but '/items' has no special chars describe('RenderablePathItem', () => { describe('renderList (List Methods)', () => { it('should render a list of methods correctly', () => { // Provide all 3 arguments to constructor const renderable = new RenderablePathItem(samplePathItem, rawPath, pathUriSuffix); const result = renderable.renderList(mockContext); expect(result).toHaveLength(1); expect(result[0].uriSuffix).toBe(pathUriSuffix); expect(result[0].renderAsList).toBe(true); expect(result[0].isError).toBeUndefined(); // Define expected output lines based on the new format and builder logic // generateListHint uses buildOperationUriSuffix which encodes the path // Since rawPath is '/items', encoded is 'items'. // The first sorted method is 'delete'. const expectedHint = "Hint: Use 'openapi://paths/items/{method}' to view details for a specific operation. (e.g., openapi://paths/items/delete)"; const expectedLineDelete = 'DELETE'; // No summary/opId const expectedLineGet = 'GET: Get Item'; // Summary exists const expectedLinePost = 'POST: Create Item'; // Summary exists const expectedOutput = `${expectedHint}\n\n${expectedLineDelete}\n${expectedLineGet}\n${expectedLinePost}`; // Check the full output string expect(result[0].data).toBe(expectedOutput); }); it('should handle path item with no standard methods', () => { const noMethodsPathItem: OpenAPIV3.PathItemObject = { parameters: samplePathItem.parameters, }; // Provide all 3 arguments to constructor const renderable = new RenderablePathItem(noMethodsPathItem, rawPath, pathUriSuffix); const result = renderable.renderList(mockContext); expect(result).toHaveLength(1); expect(result[0]).toEqual({ uriSuffix: pathUriSuffix, data: 'No standard HTTP methods found for path: items', renderAsList: true, }); }); it('should return error if path item is undefined', () => { // Provide all 3 arguments to constructor const renderable = new RenderablePathItem(undefined, rawPath, pathUriSuffix); const result = renderable.renderList(mockContext); expect(result).toHaveLength(1); expect(result[0]).toMatchObject({ uriSuffix: pathUriSuffix, isError: true, errorText: 'Path item not found.', renderAsList: true, }); }); }); describe('renderOperationDetail (Get Operation Detail)', () => { it('should return detail for a single valid method', () => { // Provide all 3 arguments to constructor const renderable = new RenderablePathItem(samplePathItem, rawPath, pathUriSuffix); const result = renderable.renderOperationDetail(mockContext, ['get']); expect(result).toHaveLength(1); expect(result[0]).toEqual({ uriSuffix: `${pathUriSuffix}/get`, data: samplePathItem.get, // Expect raw operation object }); }); it('should return details for multiple valid methods', () => { // Provide all 3 arguments to constructor const renderable = new RenderablePathItem(samplePathItem, rawPath, pathUriSuffix); const result = renderable.renderOperationDetail(mockContext, ['post', 'delete']); expect(result).toHaveLength(2); expect(result).toContainEqual({ uriSuffix: `${pathUriSuffix}/post`, data: samplePathItem.post, }); expect(result).toContainEqual({ uriSuffix: `${pathUriSuffix}/delete`, data: samplePathItem.delete, }); }); it('should return error for non-existent method', () => { // Provide all 3 arguments to constructor const renderable = new RenderablePathItem(samplePathItem, rawPath, pathUriSuffix); const result = renderable.renderOperationDetail(mockContext, ['put']); expect(result).toHaveLength(1); expect(result[0]).toEqual({ uriSuffix: `${pathUriSuffix}/put`, data: null, isError: true, errorText: 'Method "PUT" not found for path.', renderAsList: true, }); }); it('should handle mix of valid and invalid methods', () => { // Provide all 3 arguments to constructor const renderable = new RenderablePathItem(samplePathItem, rawPath, pathUriSuffix); const result = renderable.renderOperationDetail(mockContext, ['get', 'patch']); expect(result).toHaveLength(2); expect(result).toContainEqual({ uriSuffix: `${pathUriSuffix}/get`, data: samplePathItem.get, }); expect(result).toContainEqual({ uriSuffix: `${pathUriSuffix}/patch`, data: null, isError: true, errorText: 'Method "PATCH" not found for path.', renderAsList: true, }); }); it('should return error if path item is undefined', () => { // Provide all 3 arguments to constructor const renderable = new RenderablePathItem(undefined, rawPath, pathUriSuffix); const result = renderable.renderOperationDetail(mockContext, ['get']); expect(result).toHaveLength(1); expect(result[0]).toEqual({ uriSuffix: `${pathUriSuffix}/get`, data: null, isError: true, errorText: 'Path item not found.', renderAsList: true, }); }); }); describe('renderDetail (Interface Method)', () => { it('should delegate to renderList', () => { // Provide all 3 arguments to constructor const renderable = new RenderablePathItem(samplePathItem, rawPath, pathUriSuffix); const listResult = renderable.renderList(mockContext); const detailResult = renderable.renderDetail(mockContext); expect(detailResult).toEqual(listResult); }); }); describe('getOperation', () => { it('should return correct operation object (case-insensitive)', () => { // Provide all 3 arguments to constructor const renderable = new RenderablePathItem(samplePathItem, rawPath, pathUriSuffix); expect(renderable.getOperation('get')).toBe(samplePathItem.get); expect(renderable.getOperation('POST')).toBe(samplePathItem.post); expect(renderable.getOperation('Delete')).toBe(samplePathItem.delete); }); it('should return undefined for non-existent method', () => { // Provide all 3 arguments to constructor const renderable = new RenderablePathItem(samplePathItem, rawPath, pathUriSuffix); expect(renderable.getOperation('put')).toBeUndefined(); }); it('should return undefined if path item is undefined', () => { // Provide all 3 arguments to constructor const renderable = new RenderablePathItem(undefined, rawPath, pathUriSuffix); expect(renderable.getOperation('get')).toBeUndefined(); }); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/kadykov/mcp-openapi-schema-explorer'

If you have feedback or need assistance with the MCP directory API, please join our Discord server