flagon-io/g1t

public

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

g1t/apps/status/src/subscribers.test.ts

105 lines6,176 bytesCodeBlame

Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.

status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas1import assert from "node:assert/strict";
2import { test } from "node:test";
3
4import { alertLetter, bindingSender, confirmLetter, memorySender, render, unsubscribeHeaders, updateLetter } from "./email.ts";
5import { atom, feedItems, jsonFeed } from "./feed.ts";
6import { chosenParts, hashToken, newToken, normalizeEmail, parseParts, readUnsubscribeToken, unsubscribeToken, wants } from "./subscribers.ts";
7
8test("addresses are trimmed, lowercased and checked", () => {
9 assert.equal(normalizeEmail(" Ana@Example.COM "), "ana@example.com");
10 assert.equal(normalizeEmail("nope"), null);
11 assert.equal(normalizeEmail("a@b"), null);
12 assert.equal(normalizeEmail("a b@example.com"), null);
13 assert.equal(normalizeEmail(42), null);
14});
15
16test("confirmation tokens: random, and only their hash is kept", async () => {
17 const a = newToken();
18 assert.match(a, /^[A-Za-z0-9_-]{43}$/);
19 assert.notEqual(newToken(), a);
20 const hash = await hashToken(a);
21 assert.match(hash, /^[0-9a-f]{64}$/);
22 assert.equal(await hashToken(a), hash, "the same token hashes the same");
23 assert.ok(!hash.includes(a));
24});
25
26test("unsubscribe links are signed, not stored, and refuse tampering", async () => {
27 const token = await unsubscribeToken("secret", "sub-1");
28 assert.ok(token.startsWith("sub-1."));
29 assert.equal(await readUnsubscribeToken("secret", token), "sub-1");
30 assert.equal(await readUnsubscribeToken("other", token), null, "another secret");
31 assert.equal(await readUnsubscribeToken("secret", token.replace("sub-1", "sub-2")), null, "another subscriber");
32 assert.equal(await readUnsubscribeToken("secret", `${token}x`), null);
33 assert.equal(await readUnsubscribeToken("secret", "garbage"), null);
34});
35
36test("per-part subscriptions", () => {
37 assert.equal(chosenParts([], ["git", "api"]), null, "none chosen is everything");
38 assert.equal(chosenParts(["git", "api"], ["git", "api"]), null, "all chosen is everything");
39 assert.deepEqual(chosenParts(["git", "git", "zzz"], ["git", "api"]), ["git"]);
40 assert.equal(parseParts(null), null);
41 assert.deepEqual(parseParts('["git"]'), ["git"]);
42 assert.equal(parseParts("not json"), null);
43 assert.equal(wants(null, ["api"]), true);
44 assert.equal(wants(["git"], ["api"]), false);
45 assert.equal(wants(["git"], ["api", "git"]), true);
46 assert.equal(wants(["git"], []), true, "news about no part in particular goes to everyone");
47});
48
49test("letters: escaped HTML, plain text, and an unsubscribe link in every update", () => {
50 const { text, html } = render(updateLetter({ heading: "Identified: <Pushes>", text: "A bad deploy.", url: "https://status.g1t.sh/incidents/a", affects: ["Git"], unsubscribe: "https://status.g1t.sh/unsubscribe?token=x" }));
51 assert.ok(html.includes("Identified: &lt;Pushes&gt;") && !html.includes("<Pushes>"));
52 assert.ok(text.includes("Unsubscribe: https://status.g1t.sh/unsubscribe?token=x"));
53 assert.ok(text.includes("Affects: Git."));
54 assert.equal(unsubscribeHeaders("https://u")["List-Unsubscribe-Post"], "List-Unsubscribe=One-Click");
55 assert.ok(render(confirmLetter("https://c", ["Git"])).text.includes("affecting Git"));
Fast pages, required checks on the branch, self-hosted runners, honest incidents56 assert.ok(render(alertLetter({ title: "Detected: API not answering", lines: ["API has not answered on 3 checks in a row since 5 Oct 12:00 UTC."], link: "https://sudo.g1t.sh/incidents/x" })).text.includes("https://sudo.g1t.sh/incidents/x"));
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas57});
58
59test("senders: the binding when there is one, none otherwise", async () => {
60 assert.equal(bindingSender(undefined), null);
61 const calls: unknown[] = [];
62 const sender = bindingSender({ send: async (m) => void calls.push(m) }, "Status <s@g1t.sh>");
63 await sender!.send({ to: "a@b.co", subject: "Hi", text: "t", html: "h" });
64 assert.deepEqual(calls, [{ to: "a@b.co", from: "Status <s@g1t.sh>", subject: "Hi", text: "t", html: "h" }]);
65 const memory = memorySender();
66 await memory.send({ to: "a@b.co", subject: "Hi", text: "t", html: "h" });
67 assert.equal(memory.sent.length, 1);
68});
69
70test("feeds: every public update and maintenance notice, newest first, in Atom and JSON Feed", () => {
71 const incidents = [
72 {
73 id: "a",
74 title: "Pushes & pulls failing",
75 impact: "down" as const,
76 status: "resolved" as const,
77 components: ["git"],
78 component_impacts: [{ key: "git", impact: "major_outage" as const }],
79 started_at: "2026-10-05T11:00:00Z",
80 resolved_at: "2026-10-05T12:00:00Z",
81 url: "https://status.g1t.sh/incidents/a",
82 postmortem_published_at: null,
83 updates: [
84 { id: "u2", at: "2026-10-05T12:00:00Z", status: "resolved" as const, text: "Fixed." },
85 { id: "u1", at: "2026-10-05T11:05:00Z", status: "investigating" as const, text: "Looking <now>." },
86 ],
87 },
88 ];
89 const maintenance = [
90 { id: "m", title: "Upgrade", message: "m", components: ["git"], starts_at: "2026-10-06T02:00:00Z", ends_at: "2026-10-06T03:00:00Z", state: "scheduled" as const, url: "https://status.g1t.sh/maintenance/m", updates: [{ id: "mu", at: "2026-10-05T11:30:00Z", text: "Scheduled." }] },
91 ];
92 const items = feedItems(incidents, maintenance);
93 assert.deepEqual(items.map((i) => i.title), ["Resolved: Pushes & pulls failing", "Maintenance: Upgrade", "Investigating: Pushes & pulls failing"]);
94 assert.equal(items[0]!.url, "https://status.g1t.sh/incidents/a#update-u2");
95 const xml = atom(items, { origin: "https://status.g1t.sh", title: "g1t status", updated: "2026-10-05T13:00:00Z" });
96 assert.ok(xml.startsWith('<?xml version="1.0" encoding="utf-8"?>\n<feed xmlns="http://www.w3.org/2005/Atom">'));
97 assert.ok(xml.includes("<title>Resolved: Pushes &amp; pulls failing</title>"));
98 assert.ok(xml.includes("Looking &lt;now&gt;."));
99 assert.ok(xml.includes("<updated>2026-10-05T12:00:00Z</updated>"));
100 assert.equal((xml.match(/<entry>/g) ?? []).length, 3);
101 const json = jsonFeed(items, { origin: "https://status.g1t.sh", title: "g1t status", updated: "" }) as { version: string; items: { id: string; content_text: string }[] };
102 assert.equal(json.version, "https://jsonfeed.org/version/1.1");
103 assert.equal(json.items[2]!.content_text, "Looking <now>.");
104 assert.equal(feedItems(incidents, maintenance, 1).length, 1);
105});