test: add integration tests for admin.ts and pages.ts routes
All checks were successful
Build & Deploy to Staging / Build & Deploy to Staging (push) Successful in 19m33s

- admin-integration.test.ts: 14 tests covering /v1/usage/me, /v1/usage,
  /v1/concurrency, /admin/cleanup, auth middleware (401/403/503)
- pages-integration.test.ts: 10 tests covering favicon, openapi.json,
  docs, landing page, static pages, status, /api

Both files now at 100% function/statement/branch/line coverage.
All 696 tests pass.
This commit is contained in:
OpenClaw Subagent 2026-03-12 17:10:05 +01:00
parent fb68cf5546
commit db35a0e521
2 changed files with 318 additions and 0 deletions

View file

@ -0,0 +1,131 @@
import { describe, it, expect, vi, beforeAll } from "vitest";
import express from "express";
import request from "supertest";
// Mock heavy deps so we don't need DB
vi.mock("../services/browser.js", () => ({
renderPdf: vi.fn(),
renderUrlPdf: vi.fn(),
initBrowser: vi.fn(),
closeBrowser: vi.fn(),
}));
vi.mock("../services/keys.js", () => ({
loadKeys: vi.fn(),
getAllKeys: vi.fn().mockReturnValue([]),
isValidKey: vi.fn().mockReturnValue(false),
getKeyInfo: vi.fn(),
isProKey: vi.fn(),
keyStore: new Map(),
}));
vi.mock("../services/db.js", () => ({
initDatabase: vi.fn(),
pool: { query: vi.fn(), end: vi.fn() },
queryWithRetry: vi.fn(),
connectWithRetry: vi.fn(),
cleanupStaleData: vi.fn(),
}));
vi.mock("../services/verification.js", () => ({
verifyToken: vi.fn(),
loadVerifications: vi.fn(),
}));
vi.mock("../middleware/usage.js", () => ({
usageMiddleware: (_req: any, _res: any, next: any) => next(),
loadUsageData: vi.fn(),
getUsageStats: vi.fn().mockReturnValue({}),
getUsageForKey: vi.fn().mockReturnValue({ count: 0, monthKey: "2026-03" }),
flushDirtyEntries: vi.fn(),
}));
vi.mock("../middleware/pdfRateLimit.js", () => ({
pdfRateLimitMiddleware: (_req: any, _res: any, next: any) => next(),
getConcurrencyStats: vi.fn().mockReturnValue({}),
}));
describe("Pages integration tests", () => {
let app: express.Express;
// Use a fresh import per suite to avoid cross-test pollution
beforeAll(async () => {
const { pagesRouter } = await import("../routes/pages.js");
app = express();
app.use(pagesRouter);
});
describe("GET /favicon.ico", () => {
it("returns SVG with correct Content-Type and Cache-Control", async () => {
const res = await request(app).get("/favicon.ico");
expect(res.status).toBe(200);
expect(res.headers["content-type"]).toMatch(/image\/svg\+xml/);
expect(res.headers["cache-control"]).toContain("public");
expect(res.headers["cache-control"]).toContain("max-age=604800");
});
});
describe("GET /openapi.json", () => {
it("returns valid JSON with paths", async () => {
const res = await request(app).get("/openapi.json");
expect(res.status).toBe(200);
expect(res.headers["content-type"]).toMatch(/json/);
expect(res.body.paths).toBeDefined();
expect(typeof res.body.paths).toBe("object");
});
});
describe("GET /docs", () => {
it("returns HTML with CSP header", async () => {
const res = await request(app).get("/docs");
expect(res.status).toBe(200);
expect(res.headers["content-type"]).toMatch(/html/);
expect(res.headers["content-security-policy"]).toBeDefined();
expect(res.headers["content-security-policy"]).toContain("unsafe-eval");
expect(res.headers["cache-control"]).toContain("max-age=86400");
});
});
describe("GET /", () => {
it("returns HTML with Cache-Control", async () => {
const res = await request(app).get("/");
expect(res.status).toBe(200);
expect(res.headers["content-type"]).toMatch(/html/);
expect(res.headers["cache-control"]).toContain("public");
expect(res.headers["cache-control"]).toContain("max-age=3600");
});
});
describe("Static pages", () => {
const pages = ["/impressum", "/privacy", "/terms", "/examples"];
for (const page of pages) {
it(`GET ${page} returns 200 with 24h cache`, async () => {
const res = await request(app).get(page);
expect(res.status).toBe(200);
expect(res.headers["cache-control"]).toContain("public");
expect(res.headers["cache-control"]).toContain("max-age=86400");
});
}
});
describe("GET /status", () => {
it("returns 200 with short cache", async () => {
const res = await request(app).get("/status");
expect(res.status).toBe(200);
expect(res.headers["cache-control"]).toContain("max-age=60");
});
});
describe("GET /api", () => {
it("returns JSON with version and endpoints", async () => {
const res = await request(app).get("/api");
expect(res.status).toBe(200);
expect(res.headers["content-type"]).toMatch(/json/);
expect(res.body.name).toBe("DocFast API");
expect(typeof res.body.version).toBe("string");
expect(Array.isArray(res.body.endpoints)).toBe(true);
expect(res.body.endpoints.length).toBeGreaterThan(0);
});
});
});