docfast/src/__tests__/openapi-spec.test.ts
OpenClaw Subagent 392fc029fe
Some checks failed
Build & Deploy to Staging / Build & Deploy to Staging (push) Has been cancelled
fix: swagger apis path to src/ + update stale signup/verify test refs
- swagger.ts: changed apis glob from dist/routes/*.js to src/routes/*.ts
  so OpenAPI spec includes demo endpoints during tests (fixes 6 test failures)
- Dockerfile: copy src/ to final stage for runtime swagger-jsdoc
- Updated stale /v1/signup/verify test refs to /v1/signup/free (endpoint
  was removed when free tier was discontinued)
2026-03-18 20:11:17 +01:00

108 lines
4.4 KiB
TypeScript

import { describe, it, expect } from "vitest";
import { swaggerSpec } from "../swagger.js";
describe("OpenAPI spec accuracy", () => {
const spec = swaggerSpec as any;
it("should NOT include /v1/billing/webhook (internal Stripe endpoint)", () => {
expect(spec.paths).not.toHaveProperty("/v1/billing/webhook");
});
it("should NOT include /v1/billing/success (browser redirect page)", () => {
expect(spec.paths).not.toHaveProperty("/v1/billing/success");
});
it("should mark /v1/signup/free as deprecated", () => {
expect(spec.paths["/v1/signup/free"]?.post?.deprecated).toBe(true);
});
describe("Rate limit headers", () => {
it("should define rate limit header components", () => {
expect(spec.components.headers).toBeDefined();
expect(spec.components.headers["X-RateLimit-Limit"]).toBeDefined();
expect(spec.components.headers["X-RateLimit-Remaining"]).toBeDefined();
expect(spec.components.headers["X-RateLimit-Reset"]).toBeDefined();
expect(spec.components.headers["Retry-After"]).toBeDefined();
});
it("X-RateLimit-Limit should be integer type with description", () => {
const header = spec.components.headers["X-RateLimit-Limit"];
expect(header.schema.type).toBe("integer");
expect(header.description).toContain("maximum");
});
it("X-RateLimit-Remaining should be integer type with description", () => {
const header = spec.components.headers["X-RateLimit-Remaining"];
expect(header.schema.type).toBe("integer");
expect(header.description).toContain("remaining");
});
it("X-RateLimit-Reset should be integer type with Unix timestamp description", () => {
const header = spec.components.headers["X-RateLimit-Reset"];
expect(header.schema.type).toBe("integer");
expect(header.description.toLowerCase()).toContain("unix");
expect(header.description.toLowerCase()).toContain("timestamp");
});
it("Retry-After should be integer type with description about seconds", () => {
const header = spec.components.headers["Retry-After"];
expect(header.schema.type).toBe("integer");
expect(header.description.toLowerCase()).toContain("second");
});
const conversionEndpoints = [
"/v1/convert/html",
"/v1/convert/markdown",
"/v1/convert/url",
];
const demoEndpoints = ["/v1/demo/html", "/v1/demo/markdown"];
const allRateLimitedEndpoints = [...conversionEndpoints, ...demoEndpoints];
allRateLimitedEndpoints.forEach((endpoint) => {
describe(`${endpoint}`, () => {
it("should include rate limit headers in 200 response", () => {
const response200 = spec.paths[endpoint]?.post?.responses["200"];
expect(response200).toBeDefined();
expect(response200.headers).toBeDefined();
expect(response200.headers["X-RateLimit-Limit"]).toBeDefined();
expect(response200.headers["X-RateLimit-Remaining"]).toBeDefined();
expect(response200.headers["X-RateLimit-Reset"]).toBeDefined();
});
it("should reference header components in 200 response", () => {
const headers = spec.paths[endpoint]?.post?.responses["200"]?.headers;
expect(headers["X-RateLimit-Limit"].$ref).toBe(
"#/components/headers/X-RateLimit-Limit"
);
expect(headers["X-RateLimit-Remaining"].$ref).toBe(
"#/components/headers/X-RateLimit-Remaining"
);
expect(headers["X-RateLimit-Reset"].$ref).toBe(
"#/components/headers/X-RateLimit-Reset"
);
});
it("should include Retry-After header in 429 response", () => {
const response429 = spec.paths[endpoint]?.post?.responses["429"];
expect(response429).toBeDefined();
expect(response429.headers).toBeDefined();
expect(response429.headers["Retry-After"]).toBeDefined();
expect(response429.headers["Retry-After"].$ref).toBe(
"#/components/headers/Retry-After"
);
});
});
});
it("should mention rate limit headers in API description", () => {
const description = spec.info.description;
expect(description).toContain("X-RateLimit-Limit");
expect(description).toContain("X-RateLimit-Remaining");
expect(description).toContain("X-RateLimit-Reset");
expect(description).toContain("Retry-After");
expect(description).toContain("429");
});
});
});