Files
kill-the-news/src/routes/entries.test.ts
T
Julien Herr 766f2717a7 feat(entries): list email attachments with download links
The email detail page loaded the full EmailData (including attachments)
but never rendered them, so attachments were invisible. Add a conditional
"Attachments" section linking each file to /files/:id/:filename with name
and human-readable size.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-23 14:46:25 +02:00

146 lines
4.3 KiB
TypeScript

import { describe, it, expect, beforeEach } from "vitest";
import { Hono } from "hono";
import { handle } from "./entries";
import { createMockEnv } from "../test/setup";
const FEED_ID = "test-feed";
const RECEIVED_AT = 1700000001000;
const EMAIL_KEY = `feed:${FEED_ID}:${RECEIVED_AT}`;
function makeApp() {
const app = new Hono();
app.get("/:feedId/:entryId", handle);
return app;
}
async function seedFeed(
env: ReturnType<typeof createMockEnv>,
attachments?: {
id: string;
filename: string;
contentType: string;
size: number;
}[],
) {
await env.EMAIL_STORAGE.put(
EMAIL_KEY,
JSON.stringify({
subject: "Test Subject",
from: "sender@example.com",
content: "<p>Email body</p>",
receivedAt: RECEIVED_AT,
headers: {},
...(attachments ? { attachments } : {}),
}),
);
await env.EMAIL_STORAGE.put(
`feed:${FEED_ID}:metadata`,
JSON.stringify({
emails: [
{ key: EMAIL_KEY, subject: "Test Subject", receivedAt: RECEIVED_AT },
],
}),
);
}
describe("GET /entries/:feedId/:entryId", () => {
let env: ReturnType<typeof createMockEnv>;
beforeEach(() => {
env = createMockEnv();
});
it("returns 404 when feed does not exist", async () => {
const app = makeApp();
const res = await app.request(`/${FEED_ID}/999`, {}, env as any);
expect(res.status).toBe(404);
expect(await res.text()).toContain("Feed not found");
});
it("returns 404 when entry does not exist in metadata", async () => {
const app = makeApp();
await env.EMAIL_STORAGE.put(
`feed:${FEED_ID}:metadata`,
JSON.stringify({ emails: [] }),
);
const res = await app.request(`/${FEED_ID}/999`, {}, env as any);
expect(res.status).toBe(404);
expect(await res.text()).toContain("Entry not found");
});
it("returns 404 when entryId is not a number", async () => {
const app = makeApp();
const res = await app.request(`/${FEED_ID}/not-a-number`, {}, env as any);
expect(res.status).toBe(404);
});
it("returns 200 with HTML for valid entry", async () => {
await seedFeed(env);
const app = makeApp();
const res = await app.request(`/${FEED_ID}/${RECEIVED_AT}`, {}, env as any);
expect(res.status).toBe(200);
expect(res.headers.get("Content-Type")).toContain("text/html");
});
it("includes email subject in HTML title", async () => {
await seedFeed(env);
const app = makeApp();
const res = await app.request(`/${FEED_ID}/${RECEIVED_AT}`, {}, env as any);
const body = await res.text();
expect(body).toContain("Test Subject");
});
it("includes email content in HTML body", async () => {
await seedFeed(env);
const app = makeApp();
const res = await app.request(`/${FEED_ID}/${RECEIVED_AT}`, {}, env as any);
const body = await res.text();
expect(body).toContain("<p>Email body</p>");
});
it("includes sender in HTML", async () => {
await seedFeed(env);
const app = makeApp();
const res = await app.request(`/${FEED_ID}/${RECEIVED_AT}`, {}, env as any);
const body = await res.text();
expect(body).toContain("sender@example.com");
});
it("lists attachments with download links when present", async () => {
await seedFeed(env, [
{
id: "att-123",
filename: "report final.pdf",
contentType: "application/pdf",
size: 2048,
},
]);
const app = makeApp();
const res = await app.request(`/${FEED_ID}/${RECEIVED_AT}`, {}, env as any);
const body = await res.text();
expect(body).toContain("Attachments");
expect(body).toContain("report final.pdf");
expect(body).toContain(
`/files/att-123/${encodeURIComponent("report final.pdf")}`,
);
expect(body).toContain("2.0 KB");
});
it("does not render an attachments section when there are none", async () => {
await seedFeed(env);
const app = makeApp();
const res = await app.request(`/${FEED_ID}/${RECEIVED_AT}`, {}, env as any);
const body = await res.text();
expect(body).not.toContain("Attachments");
});
it("sets Content-Security-Policy header", async () => {
await seedFeed(env);
const app = makeApp();
const res = await app.request(`/${FEED_ID}/${RECEIVED_AT}`, {}, env as any);
expect(res.headers.get("Content-Security-Policy")).toContain(
"default-src 'none'",
);
});
});