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
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:
parent
fb68cf5546
commit
db35a0e521
2 changed files with 318 additions and 0 deletions
131
src/__tests__/pages-integration.test.ts
Normal file
131
src/__tests__/pages-integration.test.ts
Normal 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);
|
||||
});
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue