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();
});
});
});