import assert from "node:assert/strict"; import { test } from "node:test"; import request from "supertest"; import { createApp } from "../src/app"; import { extractRaceCoverImageFromHtml } from "../src/raceCoverImage"; const app = createApp(); test("GET /api/health returns ok", async () => { const res = await request(app).get("/api/health").expect(200); assert.equal(res.body.status, "ok"); assert.equal(typeof res.body.version, "string"); assert.ok(res.body.version.length > 0); }); test("GET /api/meta returns version for UI footer", async () => { const res = await request(app).get("/api/meta").expect(200); assert.equal(typeof res.body.version, "string"); assert.ok(res.body.version.length > 0); }); test("GET /api/ready succeeds with mock database", async () => { const res = await request(app).get("/api/ready").expect(200); assert.equal(res.body.status, "ready"); assert.equal(res.body.db, "connected"); }); test("GET /api/races rejects invalid year", async () => { const res = await request(app).get("/api/races?year=bad").expect(400); assert.equal(res.body.error, "validation_error"); assert.ok(Array.isArray(res.body.details)); }); test("GET /api/races rejects month out of range", async () => { const res = await request(app).get("/api/races?month=13").expect(400); assert.equal(res.body.error, "validation_error"); }); test("GET /api/races accepts year and month", async () => { const res = await request(app).get("/api/races?year=2026&month=5").expect(200); assert.ok(Array.isArray(res.body)); }); test("GET /api/races/:id returns not_found", async () => { const res = await request(app).get("/api/races/does-not-exist").expect(404); assert.equal(res.body.error, "not_found"); assert.ok(Array.isArray(res.body.details)); }); test("extractRaceCoverImageFromHtml prefers runc.run intro image", () => { const html = `
`; assert.equal( extractRaceCoverImageFromHtml(html, "https://aprilrun5km.runc.run/"), "https://aprilrun5km.runc.run/uploads/race_landing_header_backgrounds/header.jpg", ); }); test("extractRaceCoverImageFromHtml reads Open Graph and Twitter images", () => { assert.equal( extractRaceCoverImageFromHtml( '', "https://example.com/race", ), "https://example.com/cover.png", ); assert.equal( extractRaceCoverImageFromHtml( '', "https://example.com/race", ), "https://cdn.example.com/twitter.jpg", ); }); test("POST /api/races stores manual coverImageUrl", async () => { const coverImageUrl = "https://example.com/manual.jpg"; const res = await request(app) .post("/api/races") .send({ id: "2026-06-01-manual-cover", date: "2026-06-01", title: "Manual Cover", distanceKm: 10, officialUrl: "https://example.com/race", coverImageUrl, }) .expect(201); assert.equal(res.body.coverImageUrl, coverImageUrl); }); test("POST /api/races auto extracts coverImageUrl from officialUrl", async () => { const originalFetch = globalThis.fetch; globalThis.fetch = async () => new Response('', { status: 200, headers: { "content-type": "text/html" }, }); try { const res = await request(app) .post("/api/races") .send({ id: "2026-06-02-auto-cover", date: "2026-06-02", title: "Auto Cover", distanceKm: 21.1, officialUrl: "https://example.com/race", }) .expect(201); assert.equal(res.body.coverImageUrl, "https://example.com/auto.jpg"); } finally { globalThis.fetch = originalFetch; } }); test("POST /api/races succeeds when cover extraction fails", async () => { const originalFetch = globalThis.fetch; globalThis.fetch = async () => { throw new Error("network down"); }; try { const res = await request(app) .post("/api/races") .send({ id: "2026-06-03-cover-fail", date: "2026-06-03", title: "Cover Fail", distanceKm: 5, officialUrl: "https://example.com/race", }) .expect(201); assert.equal(res.body.coverImageUrl, null); } finally { globalThis.fetch = originalFetch; } }); test("PATCH /api/races/:id updates coverImageUrl explicitly", async () => { const id = "2026-06-04-patch-cover"; await request(app) .post("/api/races") .send({ id, date: "2026-06-04", title: "Patch Cover", distanceKm: 10, }) .expect(201); const coverImageUrl = "https://example.com/patched.jpg"; const res = await request(app) .patch(`/api/races/${id}`) .send({ coverImageUrl }) .expect(200); assert.equal(res.body.coverImageUrl, coverImageUrl); });