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.

g1t.sh
246 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 = [c("e", "chase@example.com"), c("d", "agent@g1t.sh"), c("m", "chase@example.com", 2), c("b", "chase@example.com"), c("a", "chase@example.com")];
100 const byG1t = (commit: { author: { email: string } }) => commit.author.email === "agent@g1t.sh";
101 assert.deepEqual(pushedCommits(history, "a", byG1t).map((x) => x.hash), ["e", "b"]);
102 // Where it pointed before was not read: everything read counts.
103 assert.deepEqual(pushedCommits(history, "zz", byG1t).map((x) => x.hash), ["e", "b", "a"]);
104});
105
106test("who pushed is the account that signed in, not the name on the commits", () => {
107 assert.ok(pushedByPerson({ actor: "usr_chase", data: {} }));
108 assert.ok(pushedByPerson({ actor: null, data: {} }));
109 assert.ok(!pushedByPerson({ actor: "usr_g1t_agent", data: {} }));
110 assert.ok(!pushedByPerson({ actor: "g1t_policy", data: {} }));
111 // A workflow job's own token is not a person either.
112 assert.ok(!pushedByPerson({ actor: "usr_chase", data: { causedByJob: "run_1" } }));
113
114 const c = (hash: string, name: string) => ({ hash, parents: ["p"], author: { name, email: `${hash}@example.com` } });
115 // A person whose git name is "g1t" pushed m1; g1t pushed w1 under a person's name.
116 const history = [c("w1", "Chase Pierce"), c("m1", "g1t"), c("old", "Chase Pierce")];
117 const pushes = [
118 { time: "2026-10-07T12:00:00Z", actor: "usr_g1t_agent", data: { after: "w1", before: "m1" } },
119 { time: "2026-10-05T12:00:00Z", actor: "usr_chase", data: { after: "m1", before: "old" } },
120 ];
121 assert.deepEqual(placePushes(history, pushes), [{ hash: "m1", at: "2026-10-05T12:00:00Z" }]);
122});
123
124test("a short read of the branch is enough when it reaches the oldest push", () => {
125 const h = (...hashes: string[]) => hashes.map((hash) => ({ hash }));
126 const pushes = [
127 { time: "2026-10-07T12:00:00Z", data: { after: "c1", before: "c2" } },
128 { time: "2026-10-05T12:00:00Z", data: { after: "c2", before: "c3" } },
129 ];
130 // Holds the oldest push's before.
131 assert.ok(historyCovers(h("c1", "c2", "c3"), pushes, 3));
132 // Full, and the oldest push reaches past it: read further.
133 assert.ok(!historyCovers(h("c1", "c2", "x"), pushes, 3));
134 // Shorter than asked: the whole branch.
135 assert.ok(historyCovers(h("c1", "c2"), pushes, 3));
136 // The oldest push made the branch: only the whole branch will do.
137 assert.ok(!historyCovers(h("c1", "c2", "c3"), [{ time: "", data: { after: "c1" } }], 3));
138});
139
140test("each push to the default branch places the commits it brought, at its time, from one read of the history", () => {
141 const c = (hash: string, parents = ["p"], email = "chase@example.com") => ({ hash, parents, author: { name: "Chase Pierce", email } });
142 // Newest first: a Wednesday push of two, a Monday push of one, a merge, and g1t's own commit.
143 const history = [c("w2"), c("w1"), c("m1"), c("merge", ["a", "b"]), c("bot", ["p"], "g1t@users.noreply.g1t.sh"), c("old")];
144 const pushes = [
145 { time: "2026-10-07T12:00:00Z", actor: "usr_chase", data: { after: "w2", before: "m1" } },
146 { time: "2026-10-05T12:00:00Z", actor: "usr_chase", data: { after: "m1", before: "old" } },
147 { time: "2026-10-01T12:00:00Z", actor: "usr_chase", data: { after: "gone", before: "older" } },
148 ];
149 const byG1t = (commit: { author: { email: string } }) => commit.author.email.endsWith("@users.noreply.g1t.sh");
150 assert.deepEqual(placePushes(history, pushes, byG1t), [
151 { hash: "m1", at: "2026-10-05T12:00:00Z" },
152 { hash: "w2", at: "2026-10-07T12:00:00Z" },
153 { hash: "w1", at: "2026-10-07T12:00:00Z" },
154 ]);
155});
156
157test("a change merged automatically when g1t merged it", () => {
158 assert.ok(mergedAutomatically({ mergedBy: "g1t" }));
159 assert.ok(mergedAutomatically({ mergedBy: null }));
160 assert.ok(!mergedAutomatically({ mergedBy: "syntaqx" }));
161});
162
163test("the week is seven days of landed changes, with the week before", () => {
164 const week = weekOf(
165 [merged(0, "g1t"), merged(0, "syntaqx"), merged(0, "syntaqx", 2), merged(1, "g1t", 3), merged(1, "g1t"), merged(6, "g1t"), merged(8, "g1t"), merged(10, "alex"), merged(20, "g1t")],
166 NOW,
167 "UTC",
168 );
169 assert.equal(week.days.length, 7);
170 assert.equal(week.days[6].key, "2026-10-05");
171 assert.equal(week.days[6].label, "Mon");
172 assert.deepEqual(
173 week.days.map((d) => d.count),
174 [1, 0, 0, 0, 0, 2, 3],
175 );
176 assert.equal(week.total, 6);
177 assert.equal(week.previous, 2);
178 // Not knowing the week before is not the same as nothing in it.
179 assert.equal(weekOf([merged(0, "g1t")], NOW, "UTC", false).previous, null);
180});
181
182test("days follow the viewer's time zone", () => {
183 // 02:00 UTC on the 5th is still the 4th in Los Angeles.
184 const late = Date.parse("2026-10-05T02:00:00Z");
185 assert.equal(dayKey(late, "America/Los_Angeles"), "2026-10-04");
186 assert.equal(dayKey(late, "UTC"), "2026-10-05");
187 assert.equal(dayKey(late, "Not/AZone"), "2026-10-05");
188 const week = weekOf([{ mergedAt: new Date(late).toISOString() }], NOW, "America/Los_Angeles");
189 assert.equal(week.days.find((d) => d.key === "2026-10-04")?.count, 1);
190});
191
192test("the change from last week, and how it reads", () => {
193 assert.equal(change(12, 10), 0.2);
194 assert.equal(change(5, 0), null);
195 assert.equal(change(5, null), null);
196 assert.equal(signedPercent(0.2), "+20%");
197 assert.equal(signedPercent(-0.054), "−5%");
198 assert.equal(signedPercent(0.001), "0%");
199});
200
201test("a repository's merged list reaches back when it is not full or its oldest is old enough", () => {
202 const since = NOW - 14 * DAY;
203 const at = (days: number) => ({ mergedAt: new Date(NOW - days * DAY).toISOString(), updatedAt: new Date(NOW).toISOString() });
204 assert.ok(reachesBack([at(1), at(2)], since, 100));
205 assert.ok(reachesBack([at(1), at(20)], since, 2));
206 assert.ok(!reachesBack([at(1), at(3)], since, 2));
207});
208
209test("landed today is the viewer's calendar day, newest first", () => {
210 const rows = landedToday([merged(0.1, "g1t", 3), merged(0.5, "syntaqx", 4), merged(1, "g1t", 2)], NOW, "UTC");
211 assert.deepEqual(
212 rows.map((r) => [r.ref, r.automatic]),
213 [
214 ["#3", true],
215 ["#4", false],
216 ],
217 );
218 assert.equal(rows[1].by?.name, "syntaqx");
219});
220
221test("tabs and sorting come from the address", () => {
222 assert.equal(parseTab("landed"), "landed");
223 assert.equal(parseTab("waiting"), null);
224 assert.equal(parseTab("nope"), null);
225 assert.equal(parseSort("newest"), "newest");
226 assert.equal(parseSort(null), "impact");
227 const rows = [{ at: 1 }, { at: 3 }, { at: 2 }];
228 assert.deepEqual(sortRows(rows, "impact"), rows);
229 assert.deepEqual(
230 sortRows(rows, "newest").map((r) => r.at),
231 [3, 2, 1],
232 );
233});
234
235test("the summary says the week plainly", () => {
236 assert.equal(summaryLine({ total: 47, needs: 0 }), "47 changes landed in the last 7 days.");
237 assert.equal(summaryLine({ total: 1, needs: 2 }), "1 change landed in the last 7 days.");
238 assert.equal(summaryLine({ total: 0, needs: 2 }), "Nothing has landed this week. What is waiting on you is below.");
239 assert.equal(summaryLine({ total: 0, needs: 0 }), "Nothing has landed this week yet.");
240});
241
242test("a job waiting for a self-hosted runner says so", () => {
243 const runner = { kind: "runner" as const };
244 assert.equal(reasonFor(runner), "stalled");
245 assert.match(whyFor(reasonFor(runner), runner), /self-hosted runner/);
246});