feat: add PDF options validation to demo route (TDD)
All checks were successful
Build & Deploy to Staging / Build & Deploy to Staging (push) Successful in 14m58s
All checks were successful
Build & Deploy to Staging / Build & Deploy to Staging (push) Successful in 14m58s
This commit is contained in:
parent
a91b4c53a9
commit
ecc7b9640c
2 changed files with 68 additions and 4 deletions
|
|
@ -83,6 +83,42 @@ describe("POST /v1/demo/html", () => {
|
||||||
expect(calledHtml).toContain("DEMO");
|
expect(calledHtml).toContain("DEMO");
|
||||||
expect(calledHtml).toContain("docfast.dev");
|
expect(calledHtml).toContain("docfast.dev");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("returns 400 for invalid scale", async () => {
|
||||||
|
const res = await request(app)
|
||||||
|
.post("/v1/demo/html")
|
||||||
|
.set("content-type", "application/json")
|
||||||
|
.send({ html: "<h1>Hello</h1>", scale: 99 });
|
||||||
|
expect(res.status).toBe(400);
|
||||||
|
expect(res.body.error).toMatch(/scale/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns 400 for invalid format", async () => {
|
||||||
|
const res = await request(app)
|
||||||
|
.post("/v1/demo/html")
|
||||||
|
.set("content-type", "application/json")
|
||||||
|
.send({ html: "<h1>Hello</h1>", format: "INVALID" });
|
||||||
|
expect(res.status).toBe(400);
|
||||||
|
expect(res.body.error).toMatch(/format/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns 400 for non-boolean landscape", async () => {
|
||||||
|
const res = await request(app)
|
||||||
|
.post("/v1/demo/html")
|
||||||
|
.set("content-type", "application/json")
|
||||||
|
.send({ html: "<h1>Hello</h1>", landscape: "yes" });
|
||||||
|
expect(res.status).toBe(400);
|
||||||
|
expect(res.body.error).toMatch(/landscape/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns 400 for invalid margin", async () => {
|
||||||
|
const res = await request(app)
|
||||||
|
.post("/v1/demo/html")
|
||||||
|
.set("content-type", "application/json")
|
||||||
|
.send({ html: "<h1>Hello</h1>", margin: "10px" });
|
||||||
|
expect(res.status).toBe(400);
|
||||||
|
expect(res.body.error).toMatch(/margin/);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("POST /v1/demo/markdown", () => {
|
describe("POST /v1/demo/markdown", () => {
|
||||||
|
|
@ -145,4 +181,22 @@ describe("POST /v1/demo/markdown", () => {
|
||||||
expect(calledHtml).toContain("DEMO");
|
expect(calledHtml).toContain("DEMO");
|
||||||
expect(calledHtml).toContain("docfast.dev");
|
expect(calledHtml).toContain("docfast.dev");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("returns 400 for invalid scale", async () => {
|
||||||
|
const res = await request(app)
|
||||||
|
.post("/v1/demo/markdown")
|
||||||
|
.set("content-type", "application/json")
|
||||||
|
.send({ markdown: "# Hello", scale: 99 });
|
||||||
|
expect(res.status).toBe(400);
|
||||||
|
expect(res.body.error).toMatch(/scale/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns 400 for invalid format", async () => {
|
||||||
|
const res = await request(app)
|
||||||
|
.post("/v1/demo/markdown")
|
||||||
|
.set("content-type", "application/json")
|
||||||
|
.send({ markdown: "# Hello", format: "INVALID" });
|
||||||
|
expect(res.status).toBe(400);
|
||||||
|
expect(res.body.error).toMatch(/format/);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@ import rateLimit from "express-rate-limit";
|
||||||
import { renderPdf } from "../services/browser.js";
|
import { renderPdf } from "../services/browser.js";
|
||||||
import { markdownToHtml, wrapHtml } from "../services/markdown.js";
|
import { markdownToHtml, wrapHtml } from "../services/markdown.js";
|
||||||
import logger from "../services/logger.js";
|
import logger from "../services/logger.js";
|
||||||
|
import { sanitizeFilename } from "../utils/sanitize.js";
|
||||||
|
import { validatePdfOptions } from "../utils/pdf-options.js";
|
||||||
|
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
|
|
@ -42,10 +44,6 @@ interface DemoBody {
|
||||||
filename?: string;
|
filename?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
function sanitizeFilename(name: string): string {
|
|
||||||
return name.replace(/[\x00-\x1f"\\\r\n]/g, "").trim() || "document.pdf";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @openapi
|
* @openapi
|
||||||
* /v1/demo/html:
|
* /v1/demo/html:
|
||||||
|
|
@ -114,6 +112,12 @@ router.post("/html", async (req: Request & { acquirePdfSlot?: () => Promise<void
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const validation = validatePdfOptions(body);
|
||||||
|
if (!validation.valid) {
|
||||||
|
res.status(400).json({ error: validation.error });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (req.acquirePdfSlot) {
|
if (req.acquirePdfSlot) {
|
||||||
await req.acquirePdfSlot();
|
await req.acquirePdfSlot();
|
||||||
slotAcquired = true;
|
slotAcquired = true;
|
||||||
|
|
@ -209,6 +213,12 @@ router.post("/markdown", async (req: Request & { acquirePdfSlot?: () => Promise<
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const validation = validatePdfOptions(body);
|
||||||
|
if (!validation.valid) {
|
||||||
|
res.status(400).json({ error: validation.error });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (req.acquirePdfSlot) {
|
if (req.acquirePdfSlot) {
|
||||||
await req.acquirePdfSlot();
|
await req.acquirePdfSlot();
|
||||||
slotAcquired = true;
|
slotAcquired = true;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue