diff --git a/src/__tests__/demo.test.ts b/src/__tests__/demo.test.ts new file mode 100644 index 0000000..e613bfe --- /dev/null +++ b/src/__tests__/demo.test.ts @@ -0,0 +1,148 @@ +import { describe, it, expect, vi, beforeEach } from "vitest"; +import express from "express"; +import request from "supertest"; + +vi.mock("../services/browser.js", () => ({ + renderPdf: vi.fn(), + renderUrlPdf: vi.fn(), +})); + +let app: express.Express; + +beforeEach(async () => { + vi.clearAllMocks(); + vi.resetModules(); + + const { renderPdf } = await import("../services/browser.js"); + vi.mocked(renderPdf).mockResolvedValue(Buffer.from("%PDF-1.4 mock")); + + const { demoRouter } = await import("../routes/demo.js"); + app = express(); + app.use(express.json({ limit: "500kb" })); + app.use("/v1/demo", demoRouter); +}); + +describe("POST /v1/demo/html", () => { + it("returns 400 for missing html", async () => { + const res = await request(app) + .post("/v1/demo/html") + .set("content-type", "application/json") + .send({}); + expect(res.status).toBe(400); + }); + + it("returns 415 for wrong content-type", async () => { + const res = await request(app) + .post("/v1/demo/html") + .set("content-type", "text/plain") + .send("html=

hi

"); + expect(res.status).toBe(415); + }); + + it("returns 503 on QUEUE_FULL", async () => { + const { renderPdf } = await import("../services/browser.js"); + vi.mocked(renderPdf).mockRejectedValue(new Error("QUEUE_FULL")); + const res = await request(app) + .post("/v1/demo/html") + .set("content-type", "application/json") + .send({ html: "

Hello

" }); + expect(res.status).toBe(503); + }); + + it("returns 504 on PDF_TIMEOUT", async () => { + const { renderPdf } = await import("../services/browser.js"); + vi.mocked(renderPdf).mockRejectedValue(new Error("PDF_TIMEOUT")); + const res = await request(app) + .post("/v1/demo/html") + .set("content-type", "application/json") + .send({ html: "

Hello

" }); + expect(res.status).toBe(504); + }); + + it("returns 500 on unexpected error", async () => { + const { renderPdf } = await import("../services/browser.js"); + vi.mocked(renderPdf).mockRejectedValue(new Error("something broke")); + const res = await request(app) + .post("/v1/demo/html") + .set("content-type", "application/json") + .send({ html: "

Hello

" }); + expect(res.status).toBe(500); + expect(res.body.error).toMatch(/PDF generation failed/); + }); + + it("returns PDF with watermark on success", async () => { + const { renderPdf } = await import("../services/browser.js"); + const res = await request(app) + .post("/v1/demo/html") + .set("content-type", "application/json") + .send({ html: "

Hello

" }); + expect(res.status).toBe(200); + expect(res.headers["content-type"]).toMatch(/application\/pdf/); + // Verify watermark was injected into the HTML passed to renderPdf + const calledHtml = vi.mocked(renderPdf).mock.calls[0][0]; + expect(calledHtml).toContain("DEMO"); + expect(calledHtml).toContain("docfast.dev"); + }); +}); + +describe("POST /v1/demo/markdown", () => { + it("returns 400 for missing markdown", async () => { + const res = await request(app) + .post("/v1/demo/markdown") + .set("content-type", "application/json") + .send({}); + expect(res.status).toBe(400); + }); + + it("returns 415 for wrong content-type", async () => { + const res = await request(app) + .post("/v1/demo/markdown") + .set("content-type", "text/plain") + .send("markdown=# hi"); + expect(res.status).toBe(415); + }); + + it("returns 503 on QUEUE_FULL", async () => { + const { renderPdf } = await import("../services/browser.js"); + vi.mocked(renderPdf).mockRejectedValue(new Error("QUEUE_FULL")); + const res = await request(app) + .post("/v1/demo/markdown") + .set("content-type", "application/json") + .send({ markdown: "# Hello" }); + expect(res.status).toBe(503); + }); + + it("returns 504 on PDF_TIMEOUT", async () => { + const { renderPdf } = await import("../services/browser.js"); + vi.mocked(renderPdf).mockRejectedValue(new Error("PDF_TIMEOUT")); + const res = await request(app) + .post("/v1/demo/markdown") + .set("content-type", "application/json") + .send({ markdown: "# Hello" }); + expect(res.status).toBe(504); + }); + + it("returns 500 on unexpected error", async () => { + const { renderPdf } = await import("../services/browser.js"); + vi.mocked(renderPdf).mockRejectedValue(new Error("something broke")); + const res = await request(app) + .post("/v1/demo/markdown") + .set("content-type", "application/json") + .send({ markdown: "# Hello" }); + expect(res.status).toBe(500); + expect(res.body.error).toMatch(/PDF generation failed/); + }); + + it("returns PDF with watermark on success", async () => { + const { renderPdf } = await import("../services/browser.js"); + const res = await request(app) + .post("/v1/demo/markdown") + .set("content-type", "application/json") + .send({ markdown: "# Hello World" }); + expect(res.status).toBe(200); + expect(res.headers["content-type"]).toMatch(/application\/pdf/); + const calledHtml = vi.mocked(renderPdf).mock.calls[0][0]; + expect(calledHtml).toContain("DEMO"); + expect(calledHtml).toContain("docfast.dev"); + }); +});