Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/pr-labeler.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: "PR: Labeler"
on:
pull_request:
pull_request_target:

permissions:
contents: read
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -766,4 +766,302 @@ describe("checkMatchingRoutes", () => {
})
);
});

// ############################################################
// Status code validation - extra status codes are allowed
// ############################################################

it("should ignore extra status codes in implementation", () => {
// Arrange - Base has 200 and 400, impl adds 401 and 403
const baseDoc: OpenAPIV3.Document = {
openapi: "3.0.0",
info: { title: "Base", version: "1.0.0" },
paths: {
"/foo": {
get: {
responses: {
"200": { description: "OK" },
"400": { description: "Bad Request" },
},
},
},
},
};

const implDoc: OpenAPIV3.Document = {
openapi: "3.0.0",
info: { title: "Impl", version: "1.0.0" },
paths: {
"/foo": {
get: {
responses: {
"200": { description: "OK" },
"400": { description: "Bad Request" },
"401": { description: "Unauthorized" },
"403": { description: "Forbidden" },
},
},
},
},
};

// Act
const errors = checkMatchingRoutes(baseDoc, implDoc);

// Assert - No errors because extra status codes are allowed
expect(errors.getErrorCount()).toBe(0);
});

// ############################################################
// MIME type validation - extra mime types are allowed
// ############################################################

it("should ignore extra mime types in implementation response", () => {
// Arrange - Base has application/json, impl adds application/xml
const baseDoc: OpenAPIV3.Document = {
openapi: "3.0.0",
info: { title: "Base", version: "1.0.0" },
paths: {
"/foo": {
get: {
responses: {
"200": {
description: "OK",
content: {
"application/json": {
schema: { type: "object" },
},
},
},
},
},
},
},
};

const implDoc: OpenAPIV3.Document = {
openapi: "3.0.0",
info: { title: "Impl", version: "1.0.0" },
paths: {
"/foo": {
get: {
responses: {
"200": {
description: "OK",
content: {
"application/json": {
schema: { type: "object" },
},
"application/xml": {
schema: { type: "object" },
},
},
},
},
},
},
},
};

// Act
const errors = checkMatchingRoutes(baseDoc, implDoc);

// Assert - No errors because extra mime types are allowed
expect(errors.getErrorCount()).toBe(0);
});

// ############################################################
// MIME type validation - missing mime type is an error
// ############################################################

it("should detect missing mime type in implementation response", () => {
// Arrange - Base has json and xml, impl only has json
const baseDoc: OpenAPIV3.Document = {
openapi: "3.0.0",
info: { title: "Base", version: "1.0.0" },
paths: {
"/foo": {
get: {
responses: {
"200": {
description: "OK",
content: {
"application/json": {
schema: { type: "object" },
},
"application/xml": {
schema: { type: "object" },
},
},
},
},
},
},
},
};

const implDoc: OpenAPIV3.Document = {
openapi: "3.0.0",
info: { title: "Impl", version: "1.0.0" },
paths: {
"/foo": {
get: {
responses: {
"200": {
description: "OK",
content: {
"application/json": {
schema: { type: "object" },
},
},
},
},
},
},
},
};

// Act
const errors = checkMatchingRoutes(baseDoc, implDoc);

// Assert
expect(errors.getErrorCount()).toBe(1);
expect(errors.get(0)).toEqual(
expect.objectContaining({
type: "ROUTE_CONFLICT",
subType: "RESPONSE_BODY_CONFLICT",
endpoint: "GET /foo",
message: expect.stringMatching(
/Implementation missing schema for expected mime type \[application\/xml\]/
),
})
);
});

// ############################################################
// Query parameter validation - extra params not flagged
// ############################################################

// TODO: README Query param case 1 says extra query params should produce a
// warning. Current impl only iterates over base params, so extras are silently
// ignored. Update test to assert a warning when impl is fixed.
it("should not flag extra query parameters in implementation (known deviation from README spec)", () => {
// Arrange - Impl has an extra parameter not in base
const baseDoc: OpenAPIV3.Document = {
openapi: "3.0.0",
info: { title: "Base", version: "1.0.0" },
paths: {
"/search": {
get: {
parameters: [
{
name: "required_param",
in: "query",
required: true,
schema: { type: "string" },
},
],
responses: {
"200": { description: "OK" },
},
},
},
},
};

const implDoc: OpenAPIV3.Document = {
openapi: "3.0.0",
info: { title: "Impl", version: "1.0.0" },
paths: {
"/search": {
get: {
parameters: [
{
name: "required_param",
in: "query",
required: true,
schema: { type: "string" },
},
{
name: "extra_param",
in: "query",
required: false,
schema: { type: "string" },
},
],
responses: {
"200": { description: "OK" },
},
},
},
},
};

// Act
const errors = checkMatchingRoutes(baseDoc, implDoc);

// Assert
expect(errors.getErrorCount()).toBe(0);
});

// ############################################################
// Query parameter validation - missing optional param
// ############################################################

// TODO: README Query param case 2.2 says missing optional params should warn,
// not error. Current impl flags all missing params as "Missing required query
// parameter" regardless of required status. Update test when impl is fixed.
it("should flag missing optional query parameter as error (known deviation from README spec)", () => {
// Arrange - Base has an optional param, impl doesn't have it
const baseDoc: OpenAPIV3.Document = {
openapi: "3.0.0",
info: { title: "Base", version: "1.0.0" },
paths: {
"/search": {
get: {
parameters: [
{
name: "optional_param",
in: "query",
required: false,
schema: { type: "string" },
},
],
responses: {
"200": { description: "OK" },
},
},
},
},
};

const implDoc: OpenAPIV3.Document = {
openapi: "3.0.0",
info: { title: "Impl", version: "1.0.0" },
paths: {
"/search": {
get: {
parameters: [],
responses: {
"200": { description: "OK" },
},
},
},
},
};

// Act
const errors = checkMatchingRoutes(baseDoc, implDoc);

// Assert
expect(errors.getErrorCount()).toBe(1);
expect(errors.get(0)).toEqual(
expect.objectContaining({
type: "ROUTE_CONFLICT",
subType: "QUERY_PARAM_CONFLICT",
endpoint: "GET /search",
message: expect.stringMatching(/Missing required query parameter \[optional_param\]/),
})
);
});
});
Loading