Skip to content

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

268 linesCodeBlameRaw
1import assert from "node:assert/strict";
2import { test } from "node:test";
3
4import {
5 type Merged,
6 placePushes,
7 historyCovers,
8 change,
9 checksFact,
10 dayKey,
11 isTestFile,
12 landedToday,
13 mergedAutomatically,
14 parseSort,
15 parseTab,
16 pullFacts,
17 reachesBack,
18 reasonFor,
19 signedPercent,
20 sortRows,
21 summaryLine,
22 pushedByPerson,
23 pushedCommits,
24 weekOf,
25 whyFor,
26} from "./mission-control.ts";
27
28const HOUR = 3_600_000;
29const DAY = 24 * HOUR;
30// Monday 2026-10-05, 18:00 UTC.
31const NOW = Date.parse("2026-10-05T18:00:00Z");
32const repo = { namespace: "acme", name: "web" };
33
34test("each kind of need gets its reason chip", () => {
35 assert.equal(reasonFor({ kind: "deploy" }), "blocking");
36 assert.equal(reasonFor({ kind: "review" }), "needs_review");
37 assert.equal(reasonFor({ kind: "invitation" }), "invited");
38 assert.equal(reasonFor({ kind: "checks" }), "checks_failing");
39 assert.equal(reasonFor({ kind: "runner" }), "stalled");
40 // Ready to merge is not a review asked of anyone.
41 assert.equal(reasonFor({ kind: "ready" }), "ready");
42});
43
44test("why a need needs a person is said for every reason", () => {
45 for (const kind of ["deploy", "invitation", "review", "stuck", "checks", "ready", "conflict", "stalled", "runner"] as const) {
46 const need = { kind };
47 assert.ok(whyFor(reasonFor(need), need).length > 15, kind);
48 }
49});
50
51test("what is known lists only what is known", () => {
52 const facts = pullFacts({
53 checkStatus: "failed",
54 files: [
55 { path: "src/retry.ts", additions: 40, deletions: 2 },
56 { path: "src/retry.test.ts", additions: 60, deletions: 0 },
57 ],
58 });
59 const by = Object.fromEntries(facts.map((f) => [f.label, f]));
60 assert.equal(by["Required checks"].value, "Failed in the merge queue");
61 assert.equal(by["Required checks"].tone, "bad");
62 assert.equal(by["Files changed"].value, "2");
63 assert.equal(by.Lines.value, "+100 −2");
64 assert.equal(by.Tests.value, "1 file");
65
66 const bare = pullFacts({ checkStatus: null, files: [] });
67 assert.deepEqual(bare, [{ label: "Required checks", value: "Not known yet", tone: null }]);
68});
69
70test("the required checks are read from the pull request's check status", () => {
71 assert.deepEqual(checksFact("running"), { label: "Required checks", value: "Running", tone: null });
72 assert.equal(checksFact("passed").tone, "good");
73 assert.equal(checksFact("failed").value, "Failed in the merge queue");
74});
75
76test("test files are recognised by the names test runners use", () => {
77 assert.ok(isTestFile("src/a.test.ts"));
78 assert.ok(isTestFile("src/__tests__/a.ts"));
79 assert.ok(isTestFile("tests/api.rs"));
80 assert.ok(isTestFile("pkg/store_test.go"));
81 assert.ok(!isTestFile("src/testing-utils.ts"));
82 assert.ok(!isTestFile("src/contest.ts"));
83 assert.ok(isTestFile("test.js"));
84 assert.ok(isTestFile("lib/spec.rb"));
85});
86
87const merged = (daysAgo: number, mergedBy: string | null, number = 1): Merged => ({
88 repo,
89 number,
90 title: `Change ${number}`,
91 mergedBy,
92 mergedAt: new Date(NOW - daysAgo * DAY).toISOString(),
93 files: [],
94});
95
96test("a push to the default branch counts a person's own commits, not merges or g1t's", () => {
97 const c = (hash: string, email: string, parents = 1) => ({ hash, author: { name: "g1t", email }, parents: Array(parents).fill("p") });
98 // Every commit is named "g1t": the name decides nothing, the address does.
99 const history = [
100 c("e", "chase@example.com"),
101 c("d", "agent@g1t.sh"),
102 c("m", "chase@example.com", 2),
103 c("b", "chase@example.com"),
104 c("a", "chase@example.com"),
105 ];
106 const byG1t = (commit: { author: { email: string } }) => commit.author.email === "agent@g1t.sh";
107 assert.deepEqual(
108 pushedCommits(history, "a", byG1t).map((x) => x.hash),
109 ["e", "b"],
110 );
111 // Where it pointed before was not read: everything read counts.
112 assert.deepEqual(
113 pushedCommits(history, "zz", byG1t).map((x) => x.hash),
114 ["e", "b", "a"],
115 );
116});
117
118test("who pushed is the account that signed in, not the name on the commits", () => {
119 assert.ok(pushedByPerson({ actor: "usr_chase", data: {} }));
120 assert.ok(pushedByPerson({ actor: null, data: {} }));
121 assert.ok(!pushedByPerson({ actor: "usr_g1t_agent", data: {} }));
122 assert.ok(!pushedByPerson({ actor: "g1t_policy", data: {} }));
123 // A workflow job's own token is not a person either.
124 assert.ok(!pushedByPerson({ actor: "usr_chase", data: { causedByJob: "run_1" } }));
125
126 const c = (hash: string, name: string) => ({ hash, parents: ["p"], author: { name, email: `${hash}@example.com` } });
127 // A person whose git name is "g1t" pushed m1; g1t pushed w1 under a person's name.
128 const history = [c("w1", "Chase Pierce"), c("m1", "g1t"), c("old", "Chase Pierce")];
129 const pushes = [
130 { time: "2026-10-07T12:00:00Z", actor: "usr_g1t_agent", data: { after: "w1", before: "m1" } },
131 { time: "2026-10-05T12:00:00Z", actor: "usr_chase", data: { after: "m1", before: "old" } },
132 ];
133 assert.deepEqual(placePushes(history, pushes), [{ hash: "m1", at: "2026-10-05T12:00:00Z" }]);
134});
135
136test("a short read of the branch is enough when it reaches the oldest push", () => {
137 const h = (...hashes: string[]) => hashes.map((hash) => ({ hash }));
138 const pushes = [
139 { time: "2026-10-07T12:00:00Z", data: { after: "c1", before: "c2" } },
140 { time: "2026-10-05T12:00:00Z", data: { after: "c2", before: "c3" } },
141 ];
142 // Holds the oldest push's before.
143 assert.ok(historyCovers(h("c1", "c2", "c3"), pushes, 3));
144 // Full, and the oldest push reaches past it: read further.
145 assert.ok(!historyCovers(h("c1", "c2", "x"), pushes, 3));
146 // Shorter than asked: the whole branch.
147 assert.ok(historyCovers(h("c1", "c2"), pushes, 3));
148 // The oldest push made the branch: only the whole branch will do.
149 assert.ok(!historyCovers(h("c1", "c2", "c3"), [{ time: "", data: { after: "c1" } }], 3));
150});
151
152test("each push to the default branch places the commits it brought, at its time, from one read of the history", () => {
153 const c = (hash: string, parents = ["p"], email = "chase@example.com") => ({ hash, parents, author: { name: "Chase Pierce", email } });
154 // Newest first: a Wednesday push of two, a Monday push of one, a merge, and g1t's own commit.
155 const history = [c("w2"), c("w1"), c("m1"), c("merge", ["a", "b"]), c("bot", ["p"], "g1t@users.noreply.g1t.sh"), c("old")];
156 const pushes = [
157 { time: "2026-10-07T12:00:00Z", actor: "usr_chase", data: { after: "w2", before: "m1" } },
158 { time: "2026-10-05T12:00:00Z", actor: "usr_chase", data: { after: "m1", before: "old" } },
159 { time: "2026-10-01T12:00:00Z", actor: "usr_chase", data: { after: "gone", before: "older" } },
160 ];
161 const byG1t = (commit: { author: { email: string } }) => commit.author.email.endsWith("@users.noreply.g1t.sh");
162 assert.deepEqual(placePushes(history, pushes, byG1t), [
163 { hash: "m1", at: "2026-10-05T12:00:00Z" },
164 { hash: "w2", at: "2026-10-07T12:00:00Z" },
165 { hash: "w1", at: "2026-10-07T12:00:00Z" },
166 ]);
167});
168
169test("a change merged automatically when g1t merged it", () => {
170 assert.ok(mergedAutomatically({ mergedBy: "g1t" }));
171 assert.ok(mergedAutomatically({ mergedBy: null }));
172 assert.ok(!mergedAutomatically({ mergedBy: "syntaqx" }));
173});
174
175test("the week is seven days of landed changes, with the week before", () => {
176 const week = weekOf(
177 [
178 merged(0, "g1t"),
179 merged(0, "syntaqx"),
180 merged(0, "syntaqx", 2),
181 merged(1, "g1t", 3),
182 merged(1, "g1t"),
183 merged(6, "g1t"),
184 merged(8, "g1t"),
185 merged(10, "alex"),
186 merged(20, "g1t"),
187 ],
188 NOW,
189 "UTC",
190 );
191 assert.equal(week.days.length, 7);
192 assert.equal(week.days[6].key, "2026-10-05");
193 assert.equal(week.days[6].label, "Mon");
194 assert.deepEqual(
195 week.days.map((d) => d.count),
196 [1, 0, 0, 0, 0, 2, 3],
197 );
198 assert.equal(week.total, 6);
199 assert.equal(week.previous, 2);
200 // Not knowing the week before is not the same as nothing in it.
201 assert.equal(weekOf([merged(0, "g1t")], NOW, "UTC", false).previous, null);
202});
203
204test("days follow the viewer's time zone", () => {
205 // 02:00 UTC on the 5th is still the 4th in Los Angeles.
206 const late = Date.parse("2026-10-05T02:00:00Z");
207 assert.equal(dayKey(late, "America/Los_Angeles"), "2026-10-04");
208 assert.equal(dayKey(late, "UTC"), "2026-10-05");
209 assert.equal(dayKey(late, "Not/AZone"), "2026-10-05");
210 const week = weekOf([{ mergedAt: new Date(late).toISOString() }], NOW, "America/Los_Angeles");
211 assert.equal(week.days.find((d) => d.key === "2026-10-04")?.count, 1);
212});
213
214test("the change from last week, and how it reads", () => {
215 assert.equal(change(12, 10), 0.2);
216 assert.equal(change(5, 0), null);
217 assert.equal(change(5, null), null);
218 assert.equal(signedPercent(0.2), "+20%");
219 assert.equal(signedPercent(-0.054), "−5%");
220 assert.equal(signedPercent(0.001), "0%");
221});
222
223test("a repository's merged list reaches back when it is not full or its oldest is old enough", () => {
224 const since = NOW - 14 * DAY;
225 const at = (days: number) => ({ mergedAt: new Date(NOW - days * DAY).toISOString(), updatedAt: new Date(NOW).toISOString() });
226 assert.ok(reachesBack([at(1), at(2)], since, 100));
227 assert.ok(reachesBack([at(1), at(20)], since, 2));
228 assert.ok(!reachesBack([at(1), at(3)], since, 2));
229});
230
231test("landed today is the viewer's calendar day, newest first", () => {
232 const rows = landedToday([merged(0.1, "g1t", 3), merged(0.5, "syntaqx", 4), merged(1, "g1t", 2)], NOW, "UTC");
233 assert.deepEqual(
234 rows.map((r) => [r.ref, r.automatic]),
235 [
236 ["#3", true],
237 ["#4", false],
238 ],
239 );
240 assert.equal(rows[1].by?.name, "syntaqx");
241});
242
243test("tabs and sorting come from the address", () => {
244 assert.equal(parseTab("landed"), "landed");
245 assert.equal(parseTab("waiting"), null);
246 assert.equal(parseTab("nope"), null);
247 assert.equal(parseSort("newest"), "newest");
248 assert.equal(parseSort(null), "impact");
249 const rows = [{ at: 1 }, { at: 3 }, { at: 2 }];
250 assert.deepEqual(sortRows(rows, "impact"), rows);
251 assert.deepEqual(
252 sortRows(rows, "newest").map((r) => r.at),
253 [3, 2, 1],
254 );
255});
256
257test("the summary says the week plainly", () => {
258 assert.equal(summaryLine({ total: 47, needs: 0 }), "47 changes landed in the last 7 days.");
259 assert.equal(summaryLine({ total: 1, needs: 2 }), "1 change landed in the last 7 days.");
260 assert.equal(summaryLine({ total: 0, needs: 2 }), "Nothing has landed this week. What is waiting on you is below.");
261 assert.equal(summaryLine({ total: 0, needs: 0 }), "Nothing has landed this week yet.");
262});
263
264test("a job waiting for a self-hosted runner says so", () => {
265 const runner = { kind: "runner" as const };
266 assert.equal(reasonFor(runner), "stalled");
267 assert.match(whyFor(reasonFor(runner), runner), /self-hosted runner/);
268});