mirror of
https://github.com/LukeHagar/prettier-plugin-openapi.git
synced 2025-12-06 12:47:47 +00:00
Merge pull request #1 from LukeHagar/fixing-tests
Correcting tests and atomic file support
This commit is contained in:
93
src/index.ts
93
src/index.ts
@@ -182,6 +182,21 @@ function isOpenAPIFile(content: any, filePath?: string): boolean {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for header-like structures (OpenAPI 3.0+)
|
||||||
|
if (isHeaderObject(content)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for link-like structures (OpenAPI 3.0+)
|
||||||
|
if (isLinkObject(content)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for request body-like structures (OpenAPI 3.0+)
|
||||||
|
if (isRequestBodyObject(content)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Check for security scheme-like structures
|
// Check for security scheme-like structures
|
||||||
if (isSecuritySchemeObject(content)) {
|
if (isSecuritySchemeObject(content)) {
|
||||||
return true;
|
return true;
|
||||||
@@ -293,9 +308,38 @@ function formatOpenAPI(content: any, format: 'json' | 'yaml', options?: OpenAPIP
|
|||||||
}
|
}
|
||||||
|
|
||||||
function sortOpenAPIKeys(obj: any): any {
|
function sortOpenAPIKeys(obj: any): any {
|
||||||
|
// Special handling: if root object is a referenced OpenAPI object (for referenced files)
|
||||||
|
// Check for ref-able object types before checking for root-level keys
|
||||||
|
let contextKey = 'top-level';
|
||||||
|
|
||||||
// Determine what class of OpebAPI schema this is
|
// Skip detection if it's a full OpenAPI spec (has openapi/swagger)
|
||||||
const contextKey = getContextKey("", obj);
|
if (!('openapi' in obj) && !('swagger' in obj)) {
|
||||||
|
// Check for all ref-able object types in priority order
|
||||||
|
// Check more specific types first to avoid false positives
|
||||||
|
if (isLinkObject(obj)) {
|
||||||
|
contextKey = 'link';
|
||||||
|
} else if (isOperationObject(obj)) {
|
||||||
|
contextKey = 'operation';
|
||||||
|
} else if (isSchemaObject(obj)) {
|
||||||
|
contextKey = 'schema';
|
||||||
|
} else if (isParameterObject(obj)) {
|
||||||
|
contextKey = 'parameter';
|
||||||
|
} else if (isResponseObject(obj)) {
|
||||||
|
contextKey = 'response';
|
||||||
|
} else if (isHeaderObject(obj)) {
|
||||||
|
contextKey = 'header';
|
||||||
|
} else if (isPathItemObject(obj)) {
|
||||||
|
contextKey = 'pathItem';
|
||||||
|
} else if (isRequestBodyObject(obj)) {
|
||||||
|
contextKey = 'requestBody';
|
||||||
|
} else {
|
||||||
|
// Fall back to standard context detection
|
||||||
|
contextKey = getContextKey("", obj);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Determine what class of OpenAPI schema this is for full specs
|
||||||
|
contextKey = getContextKey("", obj);
|
||||||
|
}
|
||||||
|
|
||||||
const standardKeys = getStandardKeysForContext(contextKey);
|
const standardKeys = getStandardKeysForContext(contextKey);
|
||||||
const customExtensions = vendorExtensions[contextKey] || {};
|
const customExtensions = vendorExtensions[contextKey] || {};
|
||||||
@@ -331,6 +375,19 @@ function sortOpenAPIKeysEnhanced(obj: any, path: string = ''): any {
|
|||||||
return sortedObjs.sort((a, b) => sortTags(a, b));
|
return sortedObjs.sort((a, b) => sortTags(a, b));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sort parameter arrays so $ref items come first
|
||||||
|
// Check if this array is a parameters array (path ends with '.parameters' or is 'parameters')
|
||||||
|
if (path.endsWith('.parameters') || path === 'parameters') {
|
||||||
|
return sortedObjs.sort((a, b) => {
|
||||||
|
const aHasRef = a && typeof a === 'object' && '$ref' in a;
|
||||||
|
const bHasRef = b && typeof b === 'object' && '$ref' in b;
|
||||||
|
|
||||||
|
if (aHasRef && !bHasRef) return -1; // $ref comes first
|
||||||
|
if (!aHasRef && bHasRef) return 1; // $ref comes first
|
||||||
|
return 0; // Keep original order for items of same type
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return sortedObjs;
|
return sortedObjs;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -394,8 +451,25 @@ function sortResponseCodes(a: string, b: string): number {
|
|||||||
//#region Object type detection functions
|
//#region Object type detection functions
|
||||||
|
|
||||||
function isOperationObject(obj: any): boolean {
|
function isOperationObject(obj: any): boolean {
|
||||||
const httpMethods = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace'];
|
if (!obj || typeof obj !== 'object') {
|
||||||
return Object.keys(obj).some(key => httpMethods.includes(key.toLowerCase()));
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// An operation object must have strong operation indicators
|
||||||
|
// HTTP methods indicate a path item, not an operation
|
||||||
|
// Strong indicators: operationId or responses (required fields in operations)
|
||||||
|
// Secondary indicators: requestBody, callbacks (operation-specific)
|
||||||
|
if ('operationId' in obj || 'responses' in obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it has both requestBody or callbacks (operation-specific) AND other operation keys
|
||||||
|
if (('requestBody' in obj || 'callbacks' in obj) &&
|
||||||
|
('parameters' in obj || 'security' in obj || 'servers' in obj)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isParameterObject(obj: any): boolean {
|
function isParameterObject(obj: any): boolean {
|
||||||
@@ -468,7 +542,11 @@ function isHeaderObject(obj: any): boolean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function isLinkObject(obj: any): boolean {
|
function isLinkObject(obj: any): boolean {
|
||||||
return obj && typeof obj === 'object' && ('operationRef' in obj || 'operationId' in obj);
|
if (!obj || typeof obj !== 'object') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Link objects have operationRef OR operationId, but NOT responses (which indicates an operation)
|
||||||
|
return ('operationRef' in obj || ('operationId' in obj && !('responses' in obj)));
|
||||||
}
|
}
|
||||||
|
|
||||||
function isExampleObject(obj: any): boolean {
|
function isExampleObject(obj: any): boolean {
|
||||||
@@ -622,7 +700,10 @@ function getContextKey(path: string, obj: any): string {
|
|||||||
if (path.includes('.variables.')) return 'serverVariable';
|
if (path.includes('.variables.')) return 'serverVariable';
|
||||||
|
|
||||||
// Check object types as fallback
|
// Check object types as fallback
|
||||||
if (isOperationObject(obj)) return 'operation';
|
// Only check for operation if path is not empty (not at root level)
|
||||||
|
// Root-level objects should not be detected as operations unless they're truly operations
|
||||||
|
// (handled separately in sortOpenAPIKeys for referenced operation files)
|
||||||
|
if (path && isOperationObject(obj)) return 'operation';
|
||||||
if (isParameterObject(obj)) return 'parameter';
|
if (isParameterObject(obj)) return 'parameter';
|
||||||
if (isSchemaObject(obj)) return 'schema';
|
if (isSchemaObject(obj)) return 'schema';
|
||||||
if (isResponseObject(obj)) return 'response';
|
if (isResponseObject(obj)) return 'response';
|
||||||
|
|||||||
15
src/keys.ts
15
src/keys.ts
@@ -156,6 +156,9 @@ export const OperationKeys = [
|
|||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
export const ParameterKeys = [
|
export const ParameterKeys = [
|
||||||
|
// $ref should always be at the top, because when its included there are at most 2 other keys that are present.
|
||||||
|
'$ref',
|
||||||
|
|
||||||
// Important short info at a glance.
|
// Important short info at a glance.
|
||||||
'name',
|
'name',
|
||||||
'description',
|
'description',
|
||||||
@@ -375,6 +378,9 @@ export const SchemaKeys = [
|
|||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
export const ResponseKeys = [
|
export const ResponseKeys = [
|
||||||
|
// $ref should always be at the top, because when its included there are at most 2 other keys that are present.
|
||||||
|
'$ref',
|
||||||
|
|
||||||
// Description is a good at a glance key, and stays at the top.
|
// Description is a good at a glance key, and stays at the top.
|
||||||
'description',
|
'description',
|
||||||
|
|
||||||
@@ -509,6 +515,9 @@ export const PathItemKeys = [
|
|||||||
|
|
||||||
// Simple/short first
|
// Simple/short first
|
||||||
export const RequestBodyKeys = [
|
export const RequestBodyKeys = [
|
||||||
|
// $ref should always be at the top, because when its included there are at most 2 other keys that are present.
|
||||||
|
'$ref',
|
||||||
|
|
||||||
'description',
|
'description',
|
||||||
'required',
|
'required',
|
||||||
'content',
|
'content',
|
||||||
@@ -537,6 +546,9 @@ export const EncodingKeys = [
|
|||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
export const HeaderKeys = [
|
export const HeaderKeys = [
|
||||||
|
// $ref should always be at the top, because when its included there are at most 2 other keys that are present.
|
||||||
|
'$ref',
|
||||||
|
|
||||||
// Description is a good at a glance key, and stays at the top.
|
// Description is a good at a glance key, and stays at the top.
|
||||||
'description',
|
'description',
|
||||||
'required',
|
'required',
|
||||||
@@ -575,6 +587,9 @@ export const HeaderKeys = [
|
|||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
export const LinkKeys = [
|
export const LinkKeys = [
|
||||||
|
// $ref should always be at the top, because when its included there are at most 2 other keys that are present.
|
||||||
|
'$ref',
|
||||||
|
|
||||||
'operationId',
|
'operationId',
|
||||||
'description',
|
'description',
|
||||||
'server',
|
'server',
|
||||||
|
|||||||
384
test/all-ref-types.test.ts
Normal file
384
test/all-ref-types.test.ts
Normal file
@@ -0,0 +1,384 @@
|
|||||||
|
import { describe, expect, it } from "bun:test";
|
||||||
|
import { parsers, printers } from "../src/index.js";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Comprehensive tests for all OpenAPI object types that can be referenced via $ref
|
||||||
|
* This covers all versions: Swagger 2.0, OpenAPI 3.0.x, 3.1.x, and 3.2.0
|
||||||
|
*/
|
||||||
|
describe("All $ref Reference Types", () => {
|
||||||
|
const printer = printers?.["openapi-ast"];
|
||||||
|
const parser = parsers?.["openapi-parser"];
|
||||||
|
|
||||||
|
describe("Schema Object (all versions)", () => {
|
||||||
|
it("should format schema object with $ref at root correctly", () => {
|
||||||
|
expect(printer).toBeDefined();
|
||||||
|
|
||||||
|
const testData = {
|
||||||
|
isOpenAPI: true,
|
||||||
|
format: "yaml",
|
||||||
|
content: {
|
||||||
|
type: "object",
|
||||||
|
description: "User schema",
|
||||||
|
title: "User",
|
||||||
|
properties: {
|
||||||
|
id: { type: "integer" },
|
||||||
|
name: { type: "string" },
|
||||||
|
},
|
||||||
|
$ref: "#/components/schemas/User",
|
||||||
|
required: ["id", "name"],
|
||||||
|
},
|
||||||
|
originalText: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
// @ts-expect-error We are mocking things here
|
||||||
|
const result = printer?.print({ getNode: () => testData }, { tabWidth: 2 });
|
||||||
|
expect(result).toBeDefined();
|
||||||
|
|
||||||
|
if (!result) throw new Error("Result is undefined");
|
||||||
|
|
||||||
|
const resultString = result.toString();
|
||||||
|
|
||||||
|
// $ref should be first
|
||||||
|
const refIndex = resultString.indexOf("$ref:");
|
||||||
|
const titleIndex = resultString.indexOf("title:");
|
||||||
|
expect(refIndex).toBeLessThan(titleIndex);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Parameter Object (all versions)", () => {
|
||||||
|
it("should format parameter object with $ref at root correctly", () => {
|
||||||
|
const testData = {
|
||||||
|
isOpenAPI: true,
|
||||||
|
format: "yaml",
|
||||||
|
content: {
|
||||||
|
name: "offset",
|
||||||
|
in: "query",
|
||||||
|
description: "Pagination offset",
|
||||||
|
schema: { type: "integer" },
|
||||||
|
$ref: "#/components/parameters/Offset",
|
||||||
|
},
|
||||||
|
originalText: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
// @ts-expect-error We are mocking things here
|
||||||
|
const result = printer?.print({ getNode: () => testData }, { tabWidth: 2 });
|
||||||
|
expect(result).toBeDefined();
|
||||||
|
|
||||||
|
if (!result) throw new Error("Result is undefined");
|
||||||
|
|
||||||
|
const resultString = result.toString();
|
||||||
|
|
||||||
|
// $ref should be first
|
||||||
|
const refIndex = resultString.indexOf("$ref:");
|
||||||
|
const nameIndex = resultString.indexOf("name:");
|
||||||
|
expect(refIndex).toBeLessThan(nameIndex);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Response Object (all versions)", () => {
|
||||||
|
it("should format response object with $ref at root correctly", () => {
|
||||||
|
const testData = {
|
||||||
|
isOpenAPI: true,
|
||||||
|
format: "yaml",
|
||||||
|
content: {
|
||||||
|
description: "Success response",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: { type: "object" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
$ref: "#/components/responses/Success",
|
||||||
|
},
|
||||||
|
originalText: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
// @ts-expect-error We are mocking things here
|
||||||
|
const result = printer?.print({ getNode: () => testData }, { tabWidth: 2 });
|
||||||
|
expect(result).toBeDefined();
|
||||||
|
|
||||||
|
if (!result) throw new Error("Result is undefined");
|
||||||
|
|
||||||
|
const resultString = result.toString();
|
||||||
|
|
||||||
|
// $ref should be first
|
||||||
|
const refIndex = resultString.indexOf("$ref:");
|
||||||
|
const descriptionIndex = resultString.indexOf("description:");
|
||||||
|
expect(refIndex).toBeLessThan(descriptionIndex);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Header Object (OpenAPI 3.0+)", () => {
|
||||||
|
it("should format header object with $ref at root correctly", () => {
|
||||||
|
const testData = {
|
||||||
|
isOpenAPI: true,
|
||||||
|
format: "yaml",
|
||||||
|
content: {
|
||||||
|
description: "Rate limit header",
|
||||||
|
required: true,
|
||||||
|
schema: { type: "integer" },
|
||||||
|
$ref: "#/components/headers/RateLimit",
|
||||||
|
},
|
||||||
|
originalText: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
// @ts-expect-error We are mocking things here
|
||||||
|
const result = printer?.print({ getNode: () => testData }, { tabWidth: 2 });
|
||||||
|
expect(result).toBeDefined();
|
||||||
|
|
||||||
|
if (!result) throw new Error("Result is undefined");
|
||||||
|
|
||||||
|
const resultString = result.toString();
|
||||||
|
|
||||||
|
// $ref should be first
|
||||||
|
const refIndex = resultString.indexOf("$ref:");
|
||||||
|
const descriptionIndex = resultString.indexOf("description:");
|
||||||
|
expect(refIndex).toBeLessThan(descriptionIndex);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Path Item Object (all versions)", () => {
|
||||||
|
it("should format path item object with $ref at root correctly", () => {
|
||||||
|
const testData = {
|
||||||
|
isOpenAPI: true,
|
||||||
|
format: "yaml",
|
||||||
|
content: {
|
||||||
|
summary: "User operations",
|
||||||
|
description: "Path for user-related operations",
|
||||||
|
get: {
|
||||||
|
summary: "Get user",
|
||||||
|
responses: { "200": { description: "OK" } },
|
||||||
|
},
|
||||||
|
$ref: "#/components/pathItems/UserPath",
|
||||||
|
},
|
||||||
|
originalText: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
// @ts-expect-error We are mocking things here
|
||||||
|
const result = printer?.print({ getNode: () => testData }, { tabWidth: 2 });
|
||||||
|
expect(result).toBeDefined();
|
||||||
|
|
||||||
|
if (!result) throw new Error("Result is undefined");
|
||||||
|
|
||||||
|
const resultString = result.toString();
|
||||||
|
|
||||||
|
// $ref should be first
|
||||||
|
const refIndex = resultString.indexOf("$ref:");
|
||||||
|
const summaryIndex = resultString.indexOf("summary:");
|
||||||
|
expect(refIndex).toBeLessThan(summaryIndex);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Link Object (OpenAPI 3.0+)", () => {
|
||||||
|
it("should format link object with $ref at root correctly", () => {
|
||||||
|
const testData = {
|
||||||
|
isOpenAPI: true,
|
||||||
|
format: "yaml",
|
||||||
|
content: {
|
||||||
|
operationId: "getUser",
|
||||||
|
description: "Link to get user operation",
|
||||||
|
parameters: { userId: "$response.body#/id" },
|
||||||
|
$ref: "#/components/links/UserLink",
|
||||||
|
},
|
||||||
|
originalText: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
// @ts-expect-error We are mocking things here
|
||||||
|
const result = printer?.print({ getNode: () => testData }, { tabWidth: 2 });
|
||||||
|
expect(result).toBeDefined();
|
||||||
|
|
||||||
|
if (!result) throw new Error("Result is undefined");
|
||||||
|
|
||||||
|
const resultString = result.toString();
|
||||||
|
|
||||||
|
// $ref should be first
|
||||||
|
const refIndex = resultString.indexOf("$ref:");
|
||||||
|
const operationIdIndex = resultString.indexOf("operationId:");
|
||||||
|
expect(refIndex).toBeLessThan(operationIdIndex);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Request Body Object (OpenAPI 3.0+)", () => {
|
||||||
|
it("should format request body object with $ref at root correctly", () => {
|
||||||
|
const testData = {
|
||||||
|
isOpenAPI: true,
|
||||||
|
format: "yaml",
|
||||||
|
content: {
|
||||||
|
description: "User data",
|
||||||
|
required: true,
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: { type: "object" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
$ref: "#/components/requestBodies/UserRequestBody",
|
||||||
|
},
|
||||||
|
originalText: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
// @ts-expect-error We are mocking things here
|
||||||
|
const result = printer?.print({ getNode: () => testData }, { tabWidth: 2 });
|
||||||
|
expect(result).toBeDefined();
|
||||||
|
|
||||||
|
if (!result) throw new Error("Result is undefined");
|
||||||
|
|
||||||
|
const resultString = result.toString();
|
||||||
|
|
||||||
|
// $ref should be first
|
||||||
|
const refIndex = resultString.indexOf("$ref:");
|
||||||
|
const descriptionIndex = resultString.indexOf("description:");
|
||||||
|
expect(refIndex).toBeLessThan(descriptionIndex);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Callback Object (OpenAPI 3.0+)", () => {
|
||||||
|
it("should format callback object correctly", () => {
|
||||||
|
// Callbacks are maps where values are Path Item Objects
|
||||||
|
// Each path item already supports $ref, so we test the structure
|
||||||
|
const testData = {
|
||||||
|
isOpenAPI: true,
|
||||||
|
format: "yaml",
|
||||||
|
content: {
|
||||||
|
"{$request.body#/callbackUrl}": {
|
||||||
|
$ref: "#/components/pathItems/CallbackPath",
|
||||||
|
post: {
|
||||||
|
requestBody: {
|
||||||
|
description: "Callback payload",
|
||||||
|
},
|
||||||
|
responses: {
|
||||||
|
"200": { description: "Success" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
originalText: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
// @ts-expect-error We are mocking things here
|
||||||
|
const result = printer?.print({ getNode: () => testData }, { tabWidth: 2 });
|
||||||
|
expect(result).toBeDefined();
|
||||||
|
|
||||||
|
if (!result) throw new Error("Result is undefined");
|
||||||
|
|
||||||
|
const resultString = result.toString();
|
||||||
|
|
||||||
|
// The path item should have $ref first
|
||||||
|
const refIndex = resultString.indexOf("$ref:");
|
||||||
|
const postIndex = resultString.indexOf("post:");
|
||||||
|
expect(refIndex).toBeLessThan(postIndex);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Components Object (OpenAPI 3.0+) - sub-objects", () => {
|
||||||
|
it("should format components/schemas object correctly", () => {
|
||||||
|
const testData = {
|
||||||
|
isOpenAPI: true,
|
||||||
|
format: "yaml",
|
||||||
|
content: {
|
||||||
|
type: "object",
|
||||||
|
$ref: "#/components/schemas/User",
|
||||||
|
properties: { id: { type: "integer" } },
|
||||||
|
},
|
||||||
|
originalText: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
// @ts-expect-error We are mocking things here
|
||||||
|
const result = printer?.print({ getNode: () => testData }, { tabWidth: 2 });
|
||||||
|
expect(result).toBeDefined();
|
||||||
|
|
||||||
|
if (!result) throw new Error("Result is undefined");
|
||||||
|
|
||||||
|
const resultString = result.toString();
|
||||||
|
|
||||||
|
// $ref should be first
|
||||||
|
const refIndex = resultString.indexOf("$ref:");
|
||||||
|
const typeIndex = resultString.indexOf("type:");
|
||||||
|
expect(refIndex).toBeLessThan(typeIndex);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Definitions Object (Swagger 2.0)", () => {
|
||||||
|
it("should format definitions object correctly", () => {
|
||||||
|
const testData = {
|
||||||
|
isOpenAPI: true,
|
||||||
|
format: "yaml",
|
||||||
|
content: {
|
||||||
|
type: "object",
|
||||||
|
$ref: "#/definitions/User",
|
||||||
|
properties: { id: { type: "integer" } },
|
||||||
|
},
|
||||||
|
originalText: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
// @ts-expect-error We are mocking things here
|
||||||
|
const result = printer?.print({ getNode: () => testData }, { tabWidth: 2 });
|
||||||
|
expect(result).toBeDefined();
|
||||||
|
|
||||||
|
if (!result) throw new Error("Result is undefined");
|
||||||
|
|
||||||
|
const resultString = result.toString();
|
||||||
|
|
||||||
|
// $ref should be first
|
||||||
|
const refIndex = resultString.indexOf("$ref:");
|
||||||
|
const typeIndex = resultString.indexOf("type:");
|
||||||
|
expect(refIndex).toBeLessThan(typeIndex);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Parameters Definitions Object (Swagger 2.0)", () => {
|
||||||
|
it("should format parameters definition object correctly", () => {
|
||||||
|
const testData = {
|
||||||
|
isOpenAPI: true,
|
||||||
|
format: "yaml",
|
||||||
|
content: {
|
||||||
|
name: "limit",
|
||||||
|
in: "query",
|
||||||
|
description: "Result limit",
|
||||||
|
$ref: "#/parameters/Limit",
|
||||||
|
type: "integer",
|
||||||
|
},
|
||||||
|
originalText: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
// @ts-expect-error We are mocking things here
|
||||||
|
const result = printer?.print({ getNode: () => testData }, { tabWidth: 2 });
|
||||||
|
expect(result).toBeDefined();
|
||||||
|
|
||||||
|
if (!result) throw new Error("Result is undefined");
|
||||||
|
|
||||||
|
const resultString = result.toString();
|
||||||
|
|
||||||
|
// $ref should be first
|
||||||
|
const refIndex = resultString.indexOf("$ref:");
|
||||||
|
const nameIndex = resultString.indexOf("name:");
|
||||||
|
expect(refIndex).toBeLessThan(nameIndex);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Responses Definitions Object (Swagger 2.0)", () => {
|
||||||
|
it("should format responses definition object correctly", () => {
|
||||||
|
const testData = {
|
||||||
|
isOpenAPI: true,
|
||||||
|
format: "yaml",
|
||||||
|
content: {
|
||||||
|
description: "Error response",
|
||||||
|
$ref: "#/responses/Error",
|
||||||
|
schema: { type: "object" },
|
||||||
|
},
|
||||||
|
originalText: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
// @ts-expect-error We are mocking things here
|
||||||
|
const result = printer?.print({ getNode: () => testData }, { tabWidth: 2 });
|
||||||
|
expect(result).toBeDefined();
|
||||||
|
|
||||||
|
if (!result) throw new Error("Result is undefined");
|
||||||
|
|
||||||
|
const resultString = result.toString();
|
||||||
|
|
||||||
|
// $ref should be first
|
||||||
|
const refIndex = resultString.indexOf("$ref:");
|
||||||
|
const descriptionIndex = resultString.indexOf("description:");
|
||||||
|
expect(refIndex).toBeLessThan(descriptionIndex);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,18 +1,19 @@
|
|||||||
import { describe, expect, it } from 'bun:test';
|
import { describe, expect, it } from 'bun:test';
|
||||||
import prettier from 'prettier';
|
import prettier from 'prettier';
|
||||||
|
import * as plugin from '../src/index.js';
|
||||||
|
|
||||||
describe('Edge Cases and Coverage Improvement', () => {
|
describe('Edge Cases and Coverage Improvement', () => {
|
||||||
describe('Error Handling', () => {
|
describe('Error Handling', () => {
|
||||||
it('should handle non-object content gracefully', async () => {
|
it('should handle non-object content gracefully', async () => {
|
||||||
const stringResult = await prettier.format('"string"', {
|
const stringResult = await prettier.format('"string"', {
|
||||||
parser: 'json',
|
parser: 'json',
|
||||||
plugins: ['.']
|
plugins: [plugin]
|
||||||
});
|
});
|
||||||
expect(stringResult.trim()).toBe('"string"');
|
expect(stringResult.trim()).toBe('"string"');
|
||||||
|
|
||||||
const numberResult = await prettier.format('123', {
|
const numberResult = await prettier.format('123', {
|
||||||
parser: 'json',
|
parser: 'json',
|
||||||
plugins: ['.']
|
plugins: [plugin]
|
||||||
});
|
});
|
||||||
expect(numberResult.trim()).toBe('123');
|
expect(numberResult.trim()).toBe('123');
|
||||||
});
|
});
|
||||||
@@ -20,7 +21,7 @@ describe('Edge Cases and Coverage Improvement', () => {
|
|||||||
it('should handle array content gracefully', async () => {
|
it('should handle array content gracefully', async () => {
|
||||||
const result = await prettier.format('[1, 2, 3]', {
|
const result = await prettier.format('[1, 2, 3]', {
|
||||||
parser: 'json',
|
parser: 'json',
|
||||||
plugins: ['.']
|
plugins: [plugin]
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result.trim()).toBe('[1, 2, 3]');
|
expect(result.trim()).toBe('[1, 2, 3]');
|
||||||
@@ -41,7 +42,7 @@ describe('Edge Cases and Coverage Improvement', () => {
|
|||||||
|
|
||||||
const result = prettier.format(JSON.stringify(content), {
|
const result = prettier.format(JSON.stringify(content), {
|
||||||
parser: 'json',
|
parser: 'json',
|
||||||
plugins: ['.']
|
plugins: [plugin]
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result).toBeDefined();
|
expect(result).toBeDefined();
|
||||||
@@ -58,7 +59,7 @@ describe('Edge Cases and Coverage Improvement', () => {
|
|||||||
|
|
||||||
const result = prettier.format(JSON.stringify(content), {
|
const result = prettier.format(JSON.stringify(content), {
|
||||||
parser: 'json',
|
parser: 'json',
|
||||||
plugins: ['.']
|
plugins: [plugin]
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result).toBeDefined();
|
expect(result).toBeDefined();
|
||||||
@@ -75,7 +76,7 @@ describe('Edge Cases and Coverage Improvement', () => {
|
|||||||
|
|
||||||
const result = prettier.format(JSON.stringify(content), {
|
const result = prettier.format(JSON.stringify(content), {
|
||||||
parser: 'json',
|
parser: 'json',
|
||||||
plugins: ['.']
|
plugins: [plugin]
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result).toBeDefined();
|
expect(result).toBeDefined();
|
||||||
@@ -92,7 +93,7 @@ describe('Edge Cases and Coverage Improvement', () => {
|
|||||||
|
|
||||||
const result = prettier.format(JSON.stringify(content), {
|
const result = prettier.format(JSON.stringify(content), {
|
||||||
parser: 'json',
|
parser: 'json',
|
||||||
plugins: ['.']
|
plugins: [plugin]
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result).toBeDefined();
|
expect(result).toBeDefined();
|
||||||
@@ -109,7 +110,7 @@ describe('Edge Cases and Coverage Improvement', () => {
|
|||||||
|
|
||||||
const result = prettier.format(JSON.stringify(content), {
|
const result = prettier.format(JSON.stringify(content), {
|
||||||
parser: 'json',
|
parser: 'json',
|
||||||
plugins: ['.']
|
plugins: [plugin]
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result).toBeDefined();
|
expect(result).toBeDefined();
|
||||||
@@ -124,7 +125,7 @@ describe('Edge Cases and Coverage Improvement', () => {
|
|||||||
|
|
||||||
const result = prettier.format(JSON.stringify(content), {
|
const result = prettier.format(JSON.stringify(content), {
|
||||||
parser: 'json',
|
parser: 'json',
|
||||||
plugins: ['.']
|
plugins: [plugin]
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result).toBeDefined();
|
expect(result).toBeDefined();
|
||||||
@@ -140,7 +141,7 @@ describe('Edge Cases and Coverage Improvement', () => {
|
|||||||
|
|
||||||
const result = prettier.format(JSON.stringify(content), {
|
const result = prettier.format(JSON.stringify(content), {
|
||||||
parser: 'json',
|
parser: 'json',
|
||||||
plugins: ['.']
|
plugins: [plugin]
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result).toBeDefined();
|
expect(result).toBeDefined();
|
||||||
@@ -156,7 +157,7 @@ describe('Edge Cases and Coverage Improvement', () => {
|
|||||||
|
|
||||||
const result = prettier.format(JSON.stringify(content), {
|
const result = prettier.format(JSON.stringify(content), {
|
||||||
parser: 'json',
|
parser: 'json',
|
||||||
plugins: ['.']
|
plugins: [plugin]
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result).toBeDefined();
|
expect(result).toBeDefined();
|
||||||
@@ -172,7 +173,7 @@ describe('Edge Cases and Coverage Improvement', () => {
|
|||||||
|
|
||||||
const result = prettier.format(JSON.stringify(content), {
|
const result = prettier.format(JSON.stringify(content), {
|
||||||
parser: 'json',
|
parser: 'json',
|
||||||
plugins: ['.']
|
plugins: [plugin]
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result).toBeDefined();
|
expect(result).toBeDefined();
|
||||||
@@ -185,7 +186,7 @@ describe('Edge Cases and Coverage Improvement', () => {
|
|||||||
|
|
||||||
const result = prettier.format(JSON.stringify(content), {
|
const result = prettier.format(JSON.stringify(content), {
|
||||||
parser: 'json',
|
parser: 'json',
|
||||||
plugins: ['.']
|
plugins: [plugin]
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result).toBeDefined();
|
expect(result).toBeDefined();
|
||||||
@@ -201,7 +202,7 @@ describe('Edge Cases and Coverage Improvement', () => {
|
|||||||
|
|
||||||
const result = prettier.format(JSON.stringify(content), {
|
const result = prettier.format(JSON.stringify(content), {
|
||||||
parser: 'json',
|
parser: 'json',
|
||||||
plugins: ['.']
|
plugins: [plugin]
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result).toBeDefined();
|
expect(result).toBeDefined();
|
||||||
@@ -215,7 +216,7 @@ describe('Edge Cases and Coverage Improvement', () => {
|
|||||||
|
|
||||||
const result = prettier.format(JSON.stringify(content), {
|
const result = prettier.format(JSON.stringify(content), {
|
||||||
parser: 'json',
|
parser: 'json',
|
||||||
plugins: ['.']
|
plugins: [plugin]
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result).toBeDefined();
|
expect(result).toBeDefined();
|
||||||
@@ -233,7 +234,7 @@ describe('Edge Cases and Coverage Improvement', () => {
|
|||||||
|
|
||||||
const result = prettier.format(JSON.stringify(content), {
|
const result = prettier.format(JSON.stringify(content), {
|
||||||
parser: 'json',
|
parser: 'json',
|
||||||
plugins: ['.']
|
plugins: [plugin]
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result).toBeDefined();
|
expect(result).toBeDefined();
|
||||||
@@ -249,7 +250,7 @@ describe('Edge Cases and Coverage Improvement', () => {
|
|||||||
|
|
||||||
const result = prettier.format(JSON.stringify(content), {
|
const result = prettier.format(JSON.stringify(content), {
|
||||||
parser: 'json',
|
parser: 'json',
|
||||||
plugins: ['.']
|
plugins: [plugin]
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result).toBeDefined();
|
expect(result).toBeDefined();
|
||||||
@@ -265,7 +266,7 @@ describe('Edge Cases and Coverage Improvement', () => {
|
|||||||
|
|
||||||
const result = prettier.format(JSON.stringify(content), {
|
const result = prettier.format(JSON.stringify(content), {
|
||||||
parser: 'json',
|
parser: 'json',
|
||||||
plugins: ['.']
|
plugins: [plugin]
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result).toBeDefined();
|
expect(result).toBeDefined();
|
||||||
@@ -281,7 +282,7 @@ describe('Edge Cases and Coverage Improvement', () => {
|
|||||||
|
|
||||||
const result = prettier.format(JSON.stringify(content), {
|
const result = prettier.format(JSON.stringify(content), {
|
||||||
parser: 'json',
|
parser: 'json',
|
||||||
plugins: ['.']
|
plugins: [plugin]
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result).toBeDefined();
|
expect(result).toBeDefined();
|
||||||
@@ -297,7 +298,7 @@ describe('Edge Cases and Coverage Improvement', () => {
|
|||||||
|
|
||||||
const result = prettier.format(JSON.stringify(content), {
|
const result = prettier.format(JSON.stringify(content), {
|
||||||
parser: 'json',
|
parser: 'json',
|
||||||
plugins: ['.']
|
plugins: [plugin]
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result).toBeDefined();
|
expect(result).toBeDefined();
|
||||||
@@ -313,7 +314,7 @@ describe('Edge Cases and Coverage Improvement', () => {
|
|||||||
|
|
||||||
const result = prettier.format(JSON.stringify(content), {
|
const result = prettier.format(JSON.stringify(content), {
|
||||||
parser: 'json',
|
parser: 'json',
|
||||||
plugins: ['.']
|
plugins: [plugin]
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result).toBeDefined();
|
expect(result).toBeDefined();
|
||||||
@@ -329,7 +330,7 @@ describe('Edge Cases and Coverage Improvement', () => {
|
|||||||
|
|
||||||
const result = prettier.format(JSON.stringify(content), {
|
const result = prettier.format(JSON.stringify(content), {
|
||||||
parser: 'json',
|
parser: 'json',
|
||||||
plugins: ['.']
|
plugins: [plugin]
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result).toBeDefined();
|
expect(result).toBeDefined();
|
||||||
@@ -345,7 +346,7 @@ describe('Edge Cases and Coverage Improvement', () => {
|
|||||||
|
|
||||||
const result = prettier.format(JSON.stringify(content), {
|
const result = prettier.format(JSON.stringify(content), {
|
||||||
parser: 'json',
|
parser: 'json',
|
||||||
plugins: ['.']
|
plugins: [plugin]
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result).toBeDefined();
|
expect(result).toBeDefined();
|
||||||
@@ -362,7 +363,7 @@ describe('Edge Cases and Coverage Improvement', () => {
|
|||||||
|
|
||||||
const result = prettier.format(JSON.stringify(content), {
|
const result = prettier.format(JSON.stringify(content), {
|
||||||
parser: 'json',
|
parser: 'json',
|
||||||
plugins: ['.']
|
plugins: [plugin]
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result).toBeDefined();
|
expect(result).toBeDefined();
|
||||||
@@ -377,7 +378,7 @@ describe('Edge Cases and Coverage Improvement', () => {
|
|||||||
|
|
||||||
const result = prettier.format(JSON.stringify(content), {
|
const result = prettier.format(JSON.stringify(content), {
|
||||||
parser: 'json',
|
parser: 'json',
|
||||||
plugins: ['.']
|
plugins: [plugin]
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result).toBeDefined();
|
expect(result).toBeDefined();
|
||||||
@@ -393,7 +394,7 @@ describe('Edge Cases and Coverage Improvement', () => {
|
|||||||
|
|
||||||
const result = prettier.format(JSON.stringify(content), {
|
const result = prettier.format(JSON.stringify(content), {
|
||||||
parser: 'json',
|
parser: 'json',
|
||||||
plugins: ['.']
|
plugins: [plugin]
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result).toBeDefined();
|
expect(result).toBeDefined();
|
||||||
@@ -408,7 +409,7 @@ describe('Edge Cases and Coverage Improvement', () => {
|
|||||||
|
|
||||||
const result = prettier.format(JSON.stringify(content), {
|
const result = prettier.format(JSON.stringify(content), {
|
||||||
parser: 'json',
|
parser: 'json',
|
||||||
plugins: ['.']
|
plugins: [plugin]
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result).toBeDefined();
|
expect(result).toBeDefined();
|
||||||
@@ -428,7 +429,7 @@ describe('Edge Cases and Coverage Improvement', () => {
|
|||||||
|
|
||||||
const result = prettier.format(JSON.stringify(content), {
|
const result = prettier.format(JSON.stringify(content), {
|
||||||
parser: 'json',
|
parser: 'json',
|
||||||
plugins: ['.']
|
plugins: [plugin]
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result).toBeDefined();
|
expect(result).toBeDefined();
|
||||||
@@ -447,7 +448,7 @@ describe('Edge Cases and Coverage Improvement', () => {
|
|||||||
|
|
||||||
const result = prettier.format(JSON.stringify(content), {
|
const result = prettier.format(JSON.stringify(content), {
|
||||||
parser: 'json',
|
parser: 'json',
|
||||||
plugins: ['.']
|
plugins: [plugin]
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result).toBeDefined();
|
expect(result).toBeDefined();
|
||||||
@@ -465,7 +466,7 @@ describe('Edge Cases and Coverage Improvement', () => {
|
|||||||
|
|
||||||
const result = prettier.format(JSON.stringify(content), {
|
const result = prettier.format(JSON.stringify(content), {
|
||||||
parser: 'json',
|
parser: 'json',
|
||||||
plugins: ['.']
|
plugins: [plugin]
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result).toBeDefined();
|
expect(result).toBeDefined();
|
||||||
@@ -482,7 +483,7 @@ describe('Edge Cases and Coverage Improvement', () => {
|
|||||||
|
|
||||||
const result = prettier.format(JSON.stringify(content), {
|
const result = prettier.format(JSON.stringify(content), {
|
||||||
parser: 'json',
|
parser: 'json',
|
||||||
plugins: ['.']
|
plugins: [plugin]
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result).toBeDefined();
|
expect(result).toBeDefined();
|
||||||
@@ -497,7 +498,7 @@ describe('Edge Cases and Coverage Improvement', () => {
|
|||||||
|
|
||||||
const result = prettier.format(JSON.stringify(content), {
|
const result = prettier.format(JSON.stringify(content), {
|
||||||
parser: 'json',
|
parser: 'json',
|
||||||
plugins: ['.']
|
plugins: [plugin]
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result).toBeDefined();
|
expect(result).toBeDefined();
|
||||||
@@ -516,7 +517,7 @@ describe('Edge Cases and Coverage Improvement', () => {
|
|||||||
|
|
||||||
const result = prettier.format(JSON.stringify(content), {
|
const result = prettier.format(JSON.stringify(content), {
|
||||||
parser: 'json',
|
parser: 'json',
|
||||||
plugins: ['.']
|
plugins: [plugin]
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result).toBeDefined();
|
expect(result).toBeDefined();
|
||||||
@@ -530,7 +531,7 @@ describe('Edge Cases and Coverage Improvement', () => {
|
|||||||
|
|
||||||
const result = prettier.format(JSON.stringify(content), {
|
const result = prettier.format(JSON.stringify(content), {
|
||||||
parser: 'json',
|
parser: 'json',
|
||||||
plugins: ['.']
|
plugins: [plugin]
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result).toBeDefined();
|
expect(result).toBeDefined();
|
||||||
@@ -546,7 +547,7 @@ describe('Edge Cases and Coverage Improvement', () => {
|
|||||||
|
|
||||||
const result = prettier.format(JSON.stringify(content), {
|
const result = prettier.format(JSON.stringify(content), {
|
||||||
parser: 'json',
|
parser: 'json',
|
||||||
plugins: ['.']
|
plugins: [plugin]
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result).toBeDefined();
|
expect(result).toBeDefined();
|
||||||
@@ -564,7 +565,7 @@ describe('Edge Cases and Coverage Improvement', () => {
|
|||||||
|
|
||||||
const result = prettier.format(JSON.stringify(content), {
|
const result = prettier.format(JSON.stringify(content), {
|
||||||
parser: 'json',
|
parser: 'json',
|
||||||
plugins: ['.']
|
plugins: [plugin]
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result).toBeDefined();
|
expect(result).toBeDefined();
|
||||||
@@ -580,7 +581,7 @@ describe('Edge Cases and Coverage Improvement', () => {
|
|||||||
|
|
||||||
const result = prettier.format(JSON.stringify(content), {
|
const result = prettier.format(JSON.stringify(content), {
|
||||||
parser: 'json',
|
parser: 'json',
|
||||||
plugins: ['.']
|
plugins: [plugin]
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result).toBeDefined();
|
expect(result).toBeDefined();
|
||||||
@@ -597,7 +598,7 @@ describe('Edge Cases and Coverage Improvement', () => {
|
|||||||
|
|
||||||
const result = prettier.format(JSON.stringify(content), {
|
const result = prettier.format(JSON.stringify(content), {
|
||||||
parser: 'json',
|
parser: 'json',
|
||||||
plugins: ['.']
|
plugins: [plugin]
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result).toBeDefined();
|
expect(result).toBeDefined();
|
||||||
@@ -613,7 +614,7 @@ describe('Edge Cases and Coverage Improvement', () => {
|
|||||||
|
|
||||||
const result = prettier.format(JSON.stringify(content), {
|
const result = prettier.format(JSON.stringify(content), {
|
||||||
parser: 'json',
|
parser: 'json',
|
||||||
plugins: ['.']
|
plugins: [plugin]
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result).toBeDefined();
|
expect(result).toBeDefined();
|
||||||
@@ -631,7 +632,7 @@ describe('Edge Cases and Coverage Improvement', () => {
|
|||||||
|
|
||||||
const result = prettier.format(JSON.stringify(content), {
|
const result = prettier.format(JSON.stringify(content), {
|
||||||
parser: 'json',
|
parser: 'json',
|
||||||
plugins: ['.']
|
plugins: [plugin]
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result).toBeDefined();
|
expect(result).toBeDefined();
|
||||||
|
|||||||
@@ -106,9 +106,10 @@ describe('Key Ordering Tests', () => {
|
|||||||
|
|
||||||
expect(operationIdIndex).toBeLessThan(summaryIndex);
|
expect(operationIdIndex).toBeLessThan(summaryIndex);
|
||||||
expect(operationIdIndex).toBeLessThan(descriptionIndex);
|
expect(operationIdIndex).toBeLessThan(descriptionIndex);
|
||||||
expect(descriptionIndex).toBeLessThan(tagsIndex);
|
expect(summaryIndex).toBeLessThan(tagsIndex);
|
||||||
expect(tagsIndex).toBeLessThan(deprecatedIndex);
|
expect(tagsIndex).toBeLessThan(deprecatedIndex);
|
||||||
expect(deprecatedIndex).toBeLessThan(securityIndex);
|
expect(deprecatedIndex).toBeLessThan(descriptionIndex);
|
||||||
|
expect(descriptionIndex).toBeLessThan(securityIndex);
|
||||||
expect(securityIndex).toBeLessThan(serversIndex);
|
expect(securityIndex).toBeLessThan(serversIndex);
|
||||||
expect(serversIndex).toBeLessThan(parametersIndex);
|
expect(serversIndex).toBeLessThan(parametersIndex);
|
||||||
expect(parametersIndex).toBeLessThan(requestBodyIndex);
|
expect(parametersIndex).toBeLessThan(requestBodyIndex);
|
||||||
|
|||||||
623
test/ref-ordering.test.ts
Normal file
623
test/ref-ordering.test.ts
Normal file
@@ -0,0 +1,623 @@
|
|||||||
|
import { describe, expect, it } from "bun:test";
|
||||||
|
import { printers } from "../src/index.js";
|
||||||
|
|
||||||
|
describe("Reference ($ref) Ordering Tests", () => {
|
||||||
|
describe("Operation key ordering with references", () => {
|
||||||
|
it("should sort operation keys correctly with $ref parameters", () => {
|
||||||
|
const printer = printers?.["openapi-ast"];
|
||||||
|
expect(printer).toBeDefined();
|
||||||
|
|
||||||
|
const testData = {
|
||||||
|
isOpenAPI: true,
|
||||||
|
format: "yaml",
|
||||||
|
content: {
|
||||||
|
openapi: "3.0.0",
|
||||||
|
info: { title: "Test API", version: "1.0.0" },
|
||||||
|
paths: {
|
||||||
|
"/test": {
|
||||||
|
get: {
|
||||||
|
// Intentionally out of order to test sorting
|
||||||
|
description: "Get a list of Access Model Metadata Attributes",
|
||||||
|
operationId: "listAccessModelMetadataAttribute",
|
||||||
|
summary: "List access model metadata attributes",
|
||||||
|
tags: ["Access Model Metadata"],
|
||||||
|
security: [{ userAuth: ["idn:access-model-metadata:read"] }],
|
||||||
|
parameters: [
|
||||||
|
{
|
||||||
|
name: "filters",
|
||||||
|
description: "Filter results",
|
||||||
|
in: "query",
|
||||||
|
required: false,
|
||||||
|
schema: { type: "string" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
$ref: "../../v3/parameters/offset.yaml",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
$ref: "../../v3/parameters/limit.yaml",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "sorters",
|
||||||
|
description: "Sort results",
|
||||||
|
in: "query",
|
||||||
|
required: false,
|
||||||
|
schema: { type: "string", format: "comma-separated" },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
responses: {
|
||||||
|
"200": {
|
||||||
|
description: "OK",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: {
|
||||||
|
type: "array",
|
||||||
|
items: {
|
||||||
|
$ref: "../schemas/gov-attributes/AttributeDTO.yaml",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
$ref: "../../v3/responses/400.yaml",
|
||||||
|
},
|
||||||
|
"401": {
|
||||||
|
$ref: "../../v3/responses/401.yaml",
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
$ref: "../../v3/responses/500.yaml",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"x-sailpoint-userLevels": ["ORG_ADMIN"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
originalText: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
// @ts-expect-error We are mocking things here
|
||||||
|
const result = printer?.print({ getNode: () => testData }, { tabWidth: 2 });
|
||||||
|
expect(result).toBeDefined();
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
throw new Error("Result is undefined");
|
||||||
|
}
|
||||||
|
|
||||||
|
const resultString = result.toString();
|
||||||
|
|
||||||
|
// Check operation keys appear in correct order
|
||||||
|
const operationIdIndex = resultString.indexOf("operationId");
|
||||||
|
const summaryIndex = resultString.indexOf("summary");
|
||||||
|
const tagsIndex = resultString.indexOf("tags");
|
||||||
|
const descriptionIndex = resultString.indexOf("description");
|
||||||
|
const securityIndex = resultString.indexOf("security");
|
||||||
|
const parametersIndex = resultString.indexOf("parameters");
|
||||||
|
const responsesIndex = resultString.indexOf("responses");
|
||||||
|
const xSailpointIndex = resultString.indexOf("x-sailpoint-userLevels");
|
||||||
|
|
||||||
|
// Verify operation key order: operationId -> summary -> tags -> description -> security -> parameters -> responses
|
||||||
|
expect(operationIdIndex).toBeLessThan(summaryIndex);
|
||||||
|
expect(summaryIndex).toBeLessThan(tagsIndex);
|
||||||
|
expect(tagsIndex).toBeLessThan(descriptionIndex);
|
||||||
|
expect(descriptionIndex).toBeLessThan(securityIndex);
|
||||||
|
expect(securityIndex).toBeLessThan(parametersIndex);
|
||||||
|
expect(parametersIndex).toBeLessThan(responsesIndex);
|
||||||
|
|
||||||
|
// Custom extensions should come after all standard keys
|
||||||
|
expect(responsesIndex).toBeLessThan(xSailpointIndex);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should sort parameters array with $ref items first", () => {
|
||||||
|
const printer = printers?.["openapi-ast"];
|
||||||
|
expect(printer).toBeDefined();
|
||||||
|
|
||||||
|
const testData = {
|
||||||
|
isOpenAPI: true,
|
||||||
|
format: "yaml",
|
||||||
|
content: {
|
||||||
|
openapi: "3.0.0",
|
||||||
|
info: { title: "Test API", version: "1.0.0" },
|
||||||
|
paths: {
|
||||||
|
"/test": {
|
||||||
|
get: {
|
||||||
|
operationId: "test",
|
||||||
|
responses: { "200": { description: "OK" } },
|
||||||
|
parameters: [
|
||||||
|
{
|
||||||
|
name: "filter",
|
||||||
|
in: "query",
|
||||||
|
schema: { type: "string" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
$ref: "../../v3/parameters/offset.yaml",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "sort",
|
||||||
|
in: "query",
|
||||||
|
schema: { type: "string" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
$ref: "../../v3/parameters/limit.yaml",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
originalText: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
// @ts-expect-error We are mocking things here
|
||||||
|
const result = printer?.print({ getNode: () => testData }, { tabWidth: 2 });
|
||||||
|
expect(result).toBeDefined();
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
throw new Error("Result is undefined");
|
||||||
|
}
|
||||||
|
|
||||||
|
const resultString = result.toString();
|
||||||
|
|
||||||
|
// Find indices of parameter items in the output
|
||||||
|
// $ref parameters should come before named parameters
|
||||||
|
const parametersSection = resultString.substring(resultString.indexOf("parameters:"));
|
||||||
|
|
||||||
|
// Check that $ref items appear before named parameters
|
||||||
|
const offsetRefIndex = parametersSection.indexOf("offset.yaml");
|
||||||
|
const limitRefIndex = parametersSection.indexOf("limit.yaml");
|
||||||
|
const filterIndex = parametersSection.indexOf("name: filter");
|
||||||
|
const sortIndex = parametersSection.indexOf("name: sort");
|
||||||
|
|
||||||
|
// $ref items should come first in parameters array
|
||||||
|
expect(offsetRefIndex).toBeLessThan(filterIndex);
|
||||||
|
expect(limitRefIndex).toBeLessThan(filterIndex);
|
||||||
|
expect(limitRefIndex).toBeLessThan(sortIndex);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should sort responses with $ref items correctly", () => {
|
||||||
|
const printer = printers?.["openapi-ast"];
|
||||||
|
expect(printer).toBeDefined();
|
||||||
|
|
||||||
|
const testData = {
|
||||||
|
isOpenAPI: true,
|
||||||
|
format: "yaml",
|
||||||
|
content: {
|
||||||
|
openapi: "3.0.0",
|
||||||
|
info: { title: "Test API", version: "1.0.0" },
|
||||||
|
paths: {
|
||||||
|
"/test": {
|
||||||
|
get: {
|
||||||
|
operationId: "test",
|
||||||
|
responses: {
|
||||||
|
"500": {
|
||||||
|
$ref: "../../v3/responses/500.yaml",
|
||||||
|
},
|
||||||
|
"200": {
|
||||||
|
description: "OK",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: { type: "object" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
$ref: "../../v3/responses/400.yaml",
|
||||||
|
},
|
||||||
|
"401": {
|
||||||
|
$ref: "../../v3/responses/401.yaml",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
originalText: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
// @ts-expect-error We are mocking things here
|
||||||
|
const result = printer?.print({ getNode: () => testData }, { tabWidth: 2 });
|
||||||
|
expect(result).toBeDefined();
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
throw new Error("Result is undefined");
|
||||||
|
}
|
||||||
|
|
||||||
|
const resultString = result.toString();
|
||||||
|
|
||||||
|
// Response codes should be sorted numerically: 200, 400, 401, 500
|
||||||
|
const responsesSection = resultString.substring(resultString.indexOf("responses:"));
|
||||||
|
|
||||||
|
const code200Index = responsesSection.indexOf('"200":');
|
||||||
|
const code400Index = responsesSection.indexOf('"400":');
|
||||||
|
const code401Index = responsesSection.indexOf('"401":');
|
||||||
|
const code500Index = responsesSection.indexOf('"500":');
|
||||||
|
|
||||||
|
expect(code200Index).toBeLessThan(code400Index);
|
||||||
|
expect(code400Index).toBeLessThan(code401Index);
|
||||||
|
expect(code401Index).toBeLessThan(code500Index);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should handle parameter objects with $ref at the top", () => {
|
||||||
|
const printer = printers?.["openapi-ast"];
|
||||||
|
expect(printer).toBeDefined();
|
||||||
|
|
||||||
|
const testData = {
|
||||||
|
isOpenAPI: true,
|
||||||
|
format: "yaml",
|
||||||
|
content: {
|
||||||
|
openapi: "3.0.0",
|
||||||
|
info: { title: "Test API", version: "1.0.0" },
|
||||||
|
components: {
|
||||||
|
parameters: {
|
||||||
|
OffsetParam: {
|
||||||
|
description: "Pagination offset",
|
||||||
|
in: "query",
|
||||||
|
name: "offset",
|
||||||
|
schema: { type: "integer" },
|
||||||
|
$ref: "../../v3/parameters/offset.yaml",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
originalText: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
// @ts-expect-error We are mocking things here
|
||||||
|
const result = printer?.print({ getNode: () => testData }, { tabWidth: 2 });
|
||||||
|
expect(result).toBeDefined();
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
throw new Error("Result is undefined");
|
||||||
|
}
|
||||||
|
|
||||||
|
const resultString = result.toString();
|
||||||
|
|
||||||
|
// In a parameter object, if both $ref and other keys exist, $ref should come first
|
||||||
|
// But typically in OpenAPI, if $ref exists, other keys (except $ref) are ignored
|
||||||
|
// However, we should still verify the ordering
|
||||||
|
const offsetParamSection = resultString.substring(resultString.indexOf("OffsetParam:"));
|
||||||
|
|
||||||
|
// $ref should be at the top of the parameter object
|
||||||
|
const refIndex = offsetParamSection.indexOf("$ref:");
|
||||||
|
const nameIndex = offsetParamSection.indexOf("name:");
|
||||||
|
const descriptionIndex = offsetParamSection.indexOf("description:");
|
||||||
|
|
||||||
|
// When $ref is present, it should be first (though other keys should typically be removed)
|
||||||
|
if (refIndex !== -1 && nameIndex !== -1) {
|
||||||
|
expect(refIndex).toBeLessThan(nameIndex);
|
||||||
|
expect(refIndex).toBeLessThan(descriptionIndex);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should handle response objects with $ref correctly", () => {
|
||||||
|
const printer = printers?.["openapi-ast"];
|
||||||
|
expect(printer).toBeDefined();
|
||||||
|
|
||||||
|
const testData = {
|
||||||
|
isOpenAPI: true,
|
||||||
|
format: "yaml",
|
||||||
|
content: {
|
||||||
|
openapi: "3.0.0",
|
||||||
|
info: { title: "Test API", version: "1.0.0" },
|
||||||
|
components: {
|
||||||
|
responses: {
|
||||||
|
ErrorResponse: {
|
||||||
|
description: "Error response",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: { type: "object" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
$ref: "../../v3/responses/error.yaml",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
originalText: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
// @ts-expect-error We are mocking things here
|
||||||
|
const result = printer?.print({ getNode: () => testData }, { tabWidth: 2 });
|
||||||
|
expect(result).toBeDefined();
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
throw new Error("Result is undefined");
|
||||||
|
}
|
||||||
|
|
||||||
|
const resultString = result.toString();
|
||||||
|
|
||||||
|
// In a response object with $ref, $ref should be first
|
||||||
|
const errorResponseSection = resultString.substring(resultString.indexOf("ErrorResponse:"));
|
||||||
|
|
||||||
|
const refIndex = errorResponseSection.indexOf("$ref:");
|
||||||
|
const descriptionIndex = errorResponseSection.indexOf("description:");
|
||||||
|
const contentIndex = errorResponseSection.indexOf("content:");
|
||||||
|
|
||||||
|
if (refIndex !== -1 && descriptionIndex !== -1) {
|
||||||
|
expect(refIndex).toBeLessThan(descriptionIndex);
|
||||||
|
expect(refIndex).toBeLessThan(contentIndex);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should handle complete operation with mixed inline and $ref parameters", () => {
|
||||||
|
const printer = printers?.["openapi-ast"];
|
||||||
|
expect(printer).toBeDefined();
|
||||||
|
|
||||||
|
const testData = {
|
||||||
|
isOpenAPI: true,
|
||||||
|
format: "yaml",
|
||||||
|
content: {
|
||||||
|
openapi: "3.0.0",
|
||||||
|
info: { title: "Test API", version: "1.0.0" },
|
||||||
|
paths: {
|
||||||
|
"/test": {
|
||||||
|
get: {
|
||||||
|
// All keys intentionally out of order
|
||||||
|
tags: ["Test"],
|
||||||
|
description: "Test endpoint",
|
||||||
|
summary: "Get test",
|
||||||
|
operationId: "getTest",
|
||||||
|
security: [{ apiKey: [] }],
|
||||||
|
parameters: [
|
||||||
|
{
|
||||||
|
name: "custom",
|
||||||
|
in: "query",
|
||||||
|
schema: { type: "string" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
$ref: "#/components/parameters/Offset",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "another",
|
||||||
|
in: "query",
|
||||||
|
schema: { type: "string" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
$ref: "#/components/parameters/Limit",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
$ref: "#/components/parameters/Count",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
responses: {
|
||||||
|
"200": {
|
||||||
|
description: "Success",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"x-custom": "value",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
originalText: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
// @ts-expect-error We are mocking things here
|
||||||
|
const result = printer?.print({ getNode: () => testData }, { tabWidth: 2 });
|
||||||
|
expect(result).toBeDefined();
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
throw new Error("Result is undefined");
|
||||||
|
}
|
||||||
|
|
||||||
|
const resultString = result.toString();
|
||||||
|
|
||||||
|
// Verify operation key order
|
||||||
|
const operationIdIndex = resultString.indexOf("operationId");
|
||||||
|
const summaryIndex = resultString.indexOf("summary");
|
||||||
|
const tagsIndex = resultString.indexOf("tags");
|
||||||
|
const descriptionIndex = resultString.indexOf("description");
|
||||||
|
const securityIndex = resultString.indexOf("security");
|
||||||
|
const parametersIndex = resultString.indexOf("parameters");
|
||||||
|
const responsesIndex = resultString.indexOf("responses");
|
||||||
|
const xCustomIndex = resultString.indexOf("x-custom");
|
||||||
|
|
||||||
|
expect(operationIdIndex).toBeLessThan(summaryIndex);
|
||||||
|
expect(summaryIndex).toBeLessThan(tagsIndex);
|
||||||
|
expect(tagsIndex).toBeLessThan(descriptionIndex);
|
||||||
|
expect(descriptionIndex).toBeLessThan(securityIndex);
|
||||||
|
expect(securityIndex).toBeLessThan(parametersIndex);
|
||||||
|
expect(parametersIndex).toBeLessThan(responsesIndex);
|
||||||
|
expect(responsesIndex).toBeLessThan(xCustomIndex);
|
||||||
|
|
||||||
|
// Verify $ref parameters come first in parameters array
|
||||||
|
const parametersSection = resultString.substring(resultString.indexOf("parameters:"));
|
||||||
|
const offsetRefIndex = parametersSection.indexOf("Offset");
|
||||||
|
const limitRefIndex = parametersSection.indexOf("Limit");
|
||||||
|
const countRefIndex = parametersSection.indexOf("Count");
|
||||||
|
const customIndex = parametersSection.indexOf("name: custom");
|
||||||
|
const anotherIndex = parametersSection.indexOf("name: another");
|
||||||
|
|
||||||
|
// All $ref items should come before inline parameters
|
||||||
|
expect(offsetRefIndex).toBeLessThan(customIndex);
|
||||||
|
expect(limitRefIndex).toBeLessThan(customIndex);
|
||||||
|
expect(countRefIndex).toBeLessThan(customIndex);
|
||||||
|
expect(offsetRefIndex).toBeLessThan(anotherIndex);
|
||||||
|
expect(limitRefIndex).toBeLessThan(anotherIndex);
|
||||||
|
expect(countRefIndex).toBeLessThan(anotherIndex);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should handle root-level operation file (file with HTTP method at root)", () => {
|
||||||
|
const printer = printers?.["openapi-ast"];
|
||||||
|
expect(printer).toBeDefined();
|
||||||
|
|
||||||
|
// This simulates a file that is referenced via $ref and contains just an operation
|
||||||
|
const testData = {
|
||||||
|
isOpenAPI: true,
|
||||||
|
format: "yaml",
|
||||||
|
content: {
|
||||||
|
get: {
|
||||||
|
// Keys intentionally out of order
|
||||||
|
description: "Get a list of Access Model Metadata Attributes",
|
||||||
|
operationId: "listAccessModelMetadataAttribute",
|
||||||
|
summary: "List access model metadata attributes",
|
||||||
|
tags: ["Access Model Metadata"],
|
||||||
|
security: [{ userAuth: ["idn:access-model-metadata:read"] }],
|
||||||
|
parameters: [
|
||||||
|
{
|
||||||
|
name: "filters",
|
||||||
|
description: "Filter results",
|
||||||
|
in: "query",
|
||||||
|
required: false,
|
||||||
|
schema: { type: "string" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
$ref: "../../v3/parameters/offset.yaml",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
$ref: "../../v3/parameters/limit.yaml",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "sorters",
|
||||||
|
description: "Sort results",
|
||||||
|
in: "query",
|
||||||
|
required: false,
|
||||||
|
schema: { type: "string", format: "comma-separated" },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
responses: {
|
||||||
|
"200": {
|
||||||
|
description: "OK",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: {
|
||||||
|
type: "array",
|
||||||
|
items: {
|
||||||
|
$ref: "../schemas/gov-attributes/AttributeDTO.yaml",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
$ref: "../../v3/responses/400.yaml",
|
||||||
|
},
|
||||||
|
"401": {
|
||||||
|
$ref: "../../v3/responses/401.yaml",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"x-sailpoint-userLevels": ["ORG_ADMIN"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
originalText: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
// @ts-expect-error We are mocking things here
|
||||||
|
const result = printer?.print({ getNode: () => testData }, { tabWidth: 2 });
|
||||||
|
expect(result).toBeDefined();
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
throw new Error("Result is undefined");
|
||||||
|
}
|
||||||
|
|
||||||
|
const resultString = result.toString();
|
||||||
|
|
||||||
|
// Find the operation keys within the get: block
|
||||||
|
const getBlockStart = resultString.indexOf("get:");
|
||||||
|
const getBlock = resultString.substring(getBlockStart);
|
||||||
|
|
||||||
|
// Check operation keys appear in correct order
|
||||||
|
const operationIdIndex = getBlock.indexOf("operationId");
|
||||||
|
const summaryIndex = getBlock.indexOf("summary");
|
||||||
|
const tagsIndex = getBlock.indexOf("tags");
|
||||||
|
const descriptionIndex = getBlock.indexOf("description");
|
||||||
|
const securityIndex = getBlock.indexOf("security");
|
||||||
|
const parametersIndex = getBlock.indexOf("parameters");
|
||||||
|
const responsesIndex = getBlock.indexOf("responses");
|
||||||
|
const xSailpointIndex = getBlock.indexOf("x-sailpoint-userLevels");
|
||||||
|
|
||||||
|
// Verify operation key order: operationId -> summary -> tags -> description -> security -> parameters -> responses
|
||||||
|
expect(operationIdIndex).toBeLessThan(summaryIndex);
|
||||||
|
expect(summaryIndex).toBeLessThan(tagsIndex);
|
||||||
|
expect(tagsIndex).toBeLessThan(descriptionIndex);
|
||||||
|
expect(descriptionIndex).toBeLessThan(securityIndex);
|
||||||
|
expect(securityIndex).toBeLessThan(parametersIndex);
|
||||||
|
expect(parametersIndex).toBeLessThan(responsesIndex);
|
||||||
|
|
||||||
|
// Custom extensions should come after all standard keys
|
||||||
|
expect(responsesIndex).toBeLessThan(xSailpointIndex);
|
||||||
|
|
||||||
|
// Verify $ref parameters come first in parameters array
|
||||||
|
const parametersSection = getBlock.substring(getBlock.indexOf("parameters:"));
|
||||||
|
const offsetRefIndex = parametersSection.indexOf("offset.yaml");
|
||||||
|
const limitRefIndex = parametersSection.indexOf("limit.yaml");
|
||||||
|
const filtersIndex = parametersSection.indexOf("name: filters");
|
||||||
|
const sortersIndex = parametersSection.indexOf("name: sorters");
|
||||||
|
|
||||||
|
// $ref items should come before inline parameters
|
||||||
|
expect(offsetRefIndex).toBeLessThan(filtersIndex);
|
||||||
|
expect(limitRefIndex).toBeLessThan(filtersIndex);
|
||||||
|
expect(offsetRefIndex).toBeLessThan(sortersIndex);
|
||||||
|
expect(limitRefIndex).toBeLessThan(sortersIndex);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should handle direct operation object at root (no HTTP method wrapper)", () => {
|
||||||
|
const printer = printers?.["openapi-ast"];
|
||||||
|
expect(printer).toBeDefined();
|
||||||
|
|
||||||
|
// This simulates a file that contains just the operation object directly
|
||||||
|
const testData = {
|
||||||
|
isOpenAPI: true,
|
||||||
|
format: "yaml",
|
||||||
|
content: {
|
||||||
|
// Direct operation object (no HTTP method wrapper)
|
||||||
|
operationId: "testOperation",
|
||||||
|
description: "Test operation",
|
||||||
|
summary: "Test summary",
|
||||||
|
tags: ["Test"],
|
||||||
|
security: [{ apiKey: [] }],
|
||||||
|
parameters: [
|
||||||
|
{
|
||||||
|
$ref: "#/components/parameters/Offset",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "custom",
|
||||||
|
in: "query",
|
||||||
|
schema: { type: "string" },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
responses: {
|
||||||
|
"200": {
|
||||||
|
description: "Success",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
originalText: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
// @ts-expect-error We are mocking things here
|
||||||
|
const result = printer?.print({ getNode: () => testData }, { tabWidth: 2 });
|
||||||
|
expect(result).toBeDefined();
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
throw new Error("Result is undefined");
|
||||||
|
}
|
||||||
|
|
||||||
|
const resultString = result.toString();
|
||||||
|
|
||||||
|
// Check operation keys appear in correct order
|
||||||
|
const operationIdIndex = resultString.indexOf("operationId");
|
||||||
|
const summaryIndex = resultString.indexOf("summary");
|
||||||
|
const tagsIndex = resultString.indexOf("tags");
|
||||||
|
const descriptionIndex = resultString.indexOf("description");
|
||||||
|
const securityIndex = resultString.indexOf("security");
|
||||||
|
const parametersIndex = resultString.indexOf("parameters");
|
||||||
|
const responsesIndex = resultString.indexOf("responses");
|
||||||
|
|
||||||
|
// Verify operation key order
|
||||||
|
expect(operationIdIndex).toBeLessThan(summaryIndex);
|
||||||
|
expect(summaryIndex).toBeLessThan(tagsIndex);
|
||||||
|
expect(tagsIndex).toBeLessThan(descriptionIndex);
|
||||||
|
expect(descriptionIndex).toBeLessThan(securityIndex);
|
||||||
|
expect(securityIndex).toBeLessThan(parametersIndex);
|
||||||
|
expect(parametersIndex).toBeLessThan(responsesIndex);
|
||||||
|
|
||||||
|
// Verify $ref parameters come first
|
||||||
|
const parametersSection = resultString.substring(resultString.indexOf("parameters:"));
|
||||||
|
const offsetRefIndex = parametersSection.indexOf("Offset");
|
||||||
|
const customIndex = parametersSection.indexOf("name: custom");
|
||||||
|
|
||||||
|
expect(offsetRefIndex).toBeLessThan(customIndex);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -102,9 +102,10 @@ describe('Simple Key Ordering Tests', () => {
|
|||||||
|
|
||||||
expect(operationIdIndex).toBeLessThan(summaryIndex);
|
expect(operationIdIndex).toBeLessThan(summaryIndex);
|
||||||
expect(operationIdIndex).toBeLessThan(descriptionIndex);
|
expect(operationIdIndex).toBeLessThan(descriptionIndex);
|
||||||
expect(tagsIndex).toBeLessThan(descriptionIndex);
|
expect(summaryIndex).toBeLessThan(tagsIndex);
|
||||||
expect(tagsIndex).toBeLessThan(deprecatedIndex);
|
expect(tagsIndex).toBeLessThan(deprecatedIndex);
|
||||||
expect(securityIndex).toBeLessThan(deprecatedIndex);
|
expect(deprecatedIndex).toBeLessThan(descriptionIndex);
|
||||||
|
expect(descriptionIndex).toBeLessThan(securityIndex);
|
||||||
expect(securityIndex).toBeLessThan(serversIndex);
|
expect(securityIndex).toBeLessThan(serversIndex);
|
||||||
expect(serversIndex).toBeLessThan(parametersIndex);
|
expect(serversIndex).toBeLessThan(parametersIndex);
|
||||||
expect(parametersIndex).toBeLessThan(requestBodyIndex);
|
expect(parametersIndex).toBeLessThan(requestBodyIndex);
|
||||||
|
|||||||
Reference in New Issue
Block a user