import assert from "node:assert/strict"; import test from "node:test"; import { renderToStaticMarkup } from "react-dom/server"; import { createElement } from "react"; import { parseReleasePost, RELEASE_NEWS_CHANNEL, RELEASE_WEBHOOK_ID, } from "../shared/changelog"; import { readChangelogFeed } from "../lib/changelog-feed"; import { ReleaseNotes } from "../components/ReleaseNotes"; const post = (content: string) => ({ id: "1561021045211660402", channel_id: RELEASE_NEWS_CHANNEL, webhook_id: RELEASE_WEBHOOK_ID, timestamp: "2026-10-20T08:00:00Z", content, }); test("release posts remove roles, punctuation and each published footer format", () => { for (const kind of ["Alpha", "Beta", "Production", "Bug fix"]) { for (const footer of [ "", "", '[https://minegeo.site/](https://minegeo.site/ "https://minegeo.site/")', ]) { const entry = parseReleasePost( post( `<@&1558052962817933322>, <@&1558053208008433705>.\n## MineGeo 1.0.4.1 ${kind}\n\nA new release. 😄\n\n- **Fixed:** Shared timers.\n\nPLAY THE MINEGEO AT: ${footer}`, ), ); assert.equal(entry?.kind, kind); assert.equal( entry?.body, "A new release. 😄\n\n- **Fixed:** Shared timers.", ); assert.equal(entry?.version, "1.0.4.1"); } } }); test("only the release webhook's complete release posts appear", () => { const valid = post("## MineGeo 1.0.4 Beta\nHello"); assert.equal( parseReleasePost({ ...valid, webhook_id: "111111111111111111" }), null, ); assert.equal(parseReleasePost({ ...valid, webhook_id: undefined }), null); assert.equal(parseReleasePost({ ...valid, timestamp: "invalid" }), null); assert.equal( parseReleasePost(post("## Community tournament\nStarts soon")), null, ); assert.equal(parseReleasePost(post("## MineGeo 1.0.4 Beta\n")), null); }); test("release headings can include a descriptive subtitle", () => { const entry = parseReleasePost( post( "## MineGeo 1.0.5 Beta — Changelog Update\n\nA new release.\n\nPLAY THE MINEGEO AT: ", ), ); assert.equal(entry?.title, "MineGeo 1.0.5 Beta — Changelog Update"); assert.equal(entry?.body, "A new release."); }); test("feed uses only GET, preserves older pagination through non-release posts", async () => { let calls = 0; const request: typeof fetch = async (url, options) => { calls++; assert.equal(options?.method, "GET"); assert.equal( new URL(String(url)).searchParams.get("before"), "1661021045211660402", ); assert.equal( new Headers(options?.headers).get("Authorization"), "Bot test-token", ); const posts = Array.from({ length: 100 }, (_, i) => ({ ...post("Tournament news"), id: String(1561021045211660500n - BigInt(i)), })); posts[0].content = "## MineGeo 1.0.4 Beta\n- **New:** Void"; return Response.json(posts); }; const feed = await readChangelogFeed({ token: "test-token", request, before: "1661021045211660402", }); assert.equal(calls, 1); assert.equal(feed.entries.length, 1); assert.equal(feed.before, "1561021045211660401"); }); test("feed fails cleanly on denied access, rate limits and wrong channel data", async () => { for (const status of [401, 403, 429, 500]) { await assert.rejects( readChangelogFeed({ token: "test", request: async () => new Response("", { status }), }), ); } await assert.rejects( readChangelogFeed({ token: "test", request: async () => Response.json([{ ...post("test"), channel_id: "other" }]), }), ); }); test("redacted webhook messages are unavailable rather than an empty release history", async () => { await assert.rejects( readChangelogFeed({ token: "test", request: async () => Response.json([post("")]), }), /Message Content/, ); }); test("release Markdown renders emphasis and safe links, escaping executable HTML", () => { const html = renderToStaticMarkup( createElement(ReleaseNotes, { body: "- **New:** Void\n- \n\n[Guide](https://minegeo.site/docs) [bad](javascript:alert)", }), ); assert.match(html, /New:<\/strong>/); assert.match(html, /<script>/); assert.match(html, /href="https:\/\/minegeo.site\/docs"/); assert.doesNotMatch(html, /href="javascript:/); });