// apps/hq/test/public-share.test.ts import { describe, it, expect } from 'vitest' import express from 'express' import { openDb } from '../src/db' import { seedIfEmpty } from '../src/seed' import { createStaff } from '../src/auth' import { createClient } from '../src/repos-clients' import { createModule, setPrice } from '../src/repos-modules' import { createDraft } from '../src/repos-documents' import { mintShare, revokeShare } from '../src/repos-shares' import { mountPublicShare } from '../src/server' /** * The PUBLIC read route GET /share/:token — mounted OUTSIDE the /api auth router. * A live token renders exactly one document via the existing documentHtml/renderPdf * path and returns it inline (application/pdf); unknown/expired/revoked → a plain 404 * HTML page. No auth header is ever required, and lookups are rate-limited per IP. * * renderPdf is injected (a fake %PDF- buffer) so the route logic is exercised without * launching Chrome — the house pattern for every PDF-touching test in this suite. */ function setup(rateLimit?: { limit: number; windowMs: number }) { const db = openDb(':memory:'); seedIfEmpty(db) createStaff(db, { email: 'owner@test.in', displayName: 'Owner', role: 'owner', password: 'owner-password' }) const c = createClient(db, 'u1', { name: 'Acme Traders', code: 'ACME', stateCode: '32' }) const m = createModule(db, 'u1', { code: 'POS', name: 'POS Billing' }) setPrice(db, 'u1', { moduleId: m.id, kind: 'yearly', pricePaise: 10_000_00, effectiveFrom: '2026-04-01' }) const doc = createDraft(db, 'u1', { docType: 'QUOTATION', clientId: c.id, lines: [{ moduleId: m.id, qty: 1, kind: 'yearly' }] }) const rendered: string[] = [] const fakePdf = async (html: string): Promise => { rendered.push(html); return Buffer.from('%PDF-1.4 fake') } const app = express(); app.use(express.json()); app.locals['db'] = db const deps: Parameters[2] = { renderPdf: fakePdf } if (rateLimit !== undefined) deps.rateLimit = rateLimit mountPublicShare(app, db, deps) const server = app.listen(0) const base = `http://localhost:${(server.address() as { port: number }).port}` return { db, server, base, c, m, doc, rendered } } describe('public GET /share/:token', () => { it('serves the one document inline as a PDF for a live token — with NO auth header', async () => { const ctx = setup() try { const s = mintShare(ctx.db, 'u1', ctx.doc.id) const res = await fetch(`${ctx.base}/share/${s.token}`) // deliberately no authorization header expect(res.status).toBe(200) expect(res.headers.get('content-type')).toContain('application/pdf') const buf = Buffer.from(await res.arrayBuffer()) expect(buf.subarray(0, 5).toString()).toBe('%PDF-') // Rendered exactly one document, and it was THIS document (its client is on the letterhead). expect(ctx.rendered.length).toBe(1) expect(ctx.rendered[0]).toContain('Acme Traders') } finally { ctx.server.close() } }) it('serves the PDF inline (Content-Disposition inline, not attachment)', async () => { const ctx = setup() try { const s = mintShare(ctx.db, 'u1', ctx.doc.id) const res = await fetch(`${ctx.base}/share/${s.token}`) expect(res.headers.get('content-disposition')).toMatch(/^inline/) } finally { ctx.server.close() } }) it('404s with a plain HTML "expired or invalid" page for an unknown token — renders nothing', async () => { const ctx = setup() try { const res = await fetch(`${ctx.base}/share/deadbeefdeadbeef`) expect(res.status).toBe(404) expect(res.headers.get('content-type')).toContain('text/html') const body = (await res.text()).toLowerCase() expect(body).toMatch(/expired|invalid/) expect(ctx.rendered.length).toBe(0) // a miss never touches the renderer } finally { ctx.server.close() } }) it('404s for an expired token', async () => { const ctx = setup() try { const s = mintShare(ctx.db, 'u1', ctx.doc.id, { expiresDays: -1 }) // already expired const res = await fetch(`${ctx.base}/share/${s.token}`) expect(res.status).toBe(404) expect(ctx.rendered.length).toBe(0) } finally { ctx.server.close() } }) it('404s for a revoked token', async () => { const ctx = setup() try { const s = mintShare(ctx.db, 'u1', ctx.doc.id) revokeShare(ctx.db, 'u1', s.id) const res = await fetch(`${ctx.base}/share/${s.token}`) expect(res.status).toBe(404) expect(ctx.rendered.length).toBe(0) } finally { ctx.server.close() } }) it('rate-limits lookups per IP to blunt token brute-forcing', async () => { const ctx = setup({ limit: 3, windowMs: 60_000 }) try { for (let i = 0; i < 3; i++) { const r = await fetch(`${ctx.base}/share/badtoken${i}`) expect(r.status).toBe(404) // allowed (though a miss), counts against the cap } const blocked = await fetch(`${ctx.base}/share/badtoken-final`) expect(blocked.status).toBe(429) } finally { ctx.server.close() } }) })