docfast/src/__tests__/templates-route.test.ts
OpenClawd 0d90c333c7
All checks were successful
Build & Deploy to Staging / Build & Deploy to Staging (push) Successful in 13m42s
test: add db retry and templates route tests
2026-02-27 10:05:34 +00:00

144 lines
4.7 KiB
TypeScript

import { describe, it, expect, vi, beforeEach } from "vitest";
import express from "express";
import request from "supertest";
let app: express.Express;
beforeEach(async () => {
vi.clearAllMocks();
const { renderPdf } = await import("../services/browser.js");
vi.mocked(renderPdf).mockResolvedValue(Buffer.from("%PDF-1.4 mock"));
const { authMiddleware } = await import("../middleware/auth.js");
const { usageMiddleware } = await import("../middleware/usage.js");
const { templatesRouter } = await import("../routes/templates.js");
app = express();
app.use(express.json());
app.use("/v1/templates", authMiddleware, usageMiddleware, templatesRouter);
});
describe("GET /v1/templates", () => {
it("returns template list with id, name, description, fields", async () => {
const res = await request(app)
.get("/v1/templates")
.set("Authorization", "Bearer test-key");
expect(res.status).toBe(200);
expect(res.body.templates).toBeInstanceOf(Array);
expect(res.body.templates.length).toBeGreaterThan(0);
const invoice = res.body.templates.find((t: any) => t.id === "invoice");
expect(invoice).toBeDefined();
expect(invoice.name).toBe("Invoice");
expect(invoice.description).toBeTruthy();
expect(invoice.fields).toBeInstanceOf(Array);
expect(invoice.fields.length).toBeGreaterThan(0);
});
it("requires auth (401 without key)", async () => {
const res = await request(app).get("/v1/templates");
expect(res.status).toBe(401);
});
});
describe("POST /v1/templates/:id/render", () => {
const validInvoiceData = {
invoiceNumber: "INV-001",
date: "2026-01-15",
from: { name: "Acme Corp" },
to: { name: "Client Inc" },
items: [{ description: "Service", quantity: 1, unitPrice: 100 }],
};
it("returns 404 for unknown template", async () => {
const res = await request(app)
.post("/v1/templates/nonexistent/render")
.set("Authorization", "Bearer test-key")
.send({ foo: "bar" });
expect(res.status).toBe(404);
expect(res.body.error).toContain("not found");
});
it("returns 400 with missing required fields listed", async () => {
const res = await request(app)
.post("/v1/templates/invoice/render")
.set("Authorization", "Bearer test-key")
.send({ invoiceNumber: "INV-001" });
expect(res.status).toBe(400);
expect(res.body.missing).toBeInstanceOf(Array);
expect(res.body.missing).toContain("date");
expect(res.body.missing).toContain("from");
});
it("renders invoice successfully, returns PDF", async () => {
const res = await request(app)
.post("/v1/templates/invoice/render")
.set("Authorization", "Bearer test-key")
.send(validInvoiceData);
expect(res.status).toBe(200);
expect(res.headers["content-type"]).toMatch(/application\/pdf/);
expect(res.headers["content-disposition"]).toContain("invoice.pdf");
});
it("accepts data in req.body.data wrapper", async () => {
const res = await request(app)
.post("/v1/templates/invoice/render")
.set("Authorization", "Bearer test-key")
.send({ data: validInvoiceData });
expect(res.status).toBe(200);
expect(res.headers["content-type"]).toMatch(/application\/pdf/);
});
it("passes _format, _margin to renderPdf", async () => {
const { renderPdf } = await import("../services/browser.js");
await request(app)
.post("/v1/templates/invoice/render")
.set("Authorization", "Bearer test-key")
.send({
...validInvoiceData,
_format: "Letter",
_margin: { top: "20mm", bottom: "20mm" },
});
expect(vi.mocked(renderPdf)).toHaveBeenCalledWith(
expect.any(String),
expect.objectContaining({
format: "Letter",
margin: { top: "20mm", bottom: "20mm" },
})
);
});
it("sanitizes _filename in Content-Disposition", async () => {
const res = await request(app)
.post("/v1/templates/invoice/render")
.set("Authorization", "Bearer test-key")
.send({ ...validInvoiceData, _filename: 'evil"file\nname.pdf' });
expect(res.status).toBe(200);
const disposition = res.headers["content-disposition"];
// The quote and newline in the input should be sanitized to underscores
expect(disposition).toContain("evil_file_name.pdf");
expect(disposition).not.toContain("\n");
});
it("returns 500 on render error", async () => {
const { renderPdf } = await import("../services/browser.js");
vi.mocked(renderPdf).mockRejectedValue(new Error("Browser crashed"));
const res = await request(app)
.post("/v1/templates/invoice/render")
.set("Authorization", "Bearer test-key")
.send(validInvoiceData);
expect(res.status).toBe(500);
expect(res.body.error).toContain("failed");
});
});