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.

95 linesCodeBlameRaw
1import assert from "node:assert/strict";
2import { test } from "node:test";
3
4import type { AuditEntry } from "@g1t/contracts";
5
6import { actorLabel, exportName, filterHref, parseFilters, retainedSince, ruleLabel, targetLabel, toCsv, toQuery, visibilityFor } from "./audit.ts";
7
8const entry: AuditEntry = {
9 id: "aud_1",
10 time: "2026-10-04T12:00:00.000Z",
11 actorKind: "agent",
12 actor: "g1t",
13 actorId: "usr_g1t_agent",
14 agent: "g1t",
15 onBehalfOf: "syntaqx",
16 runId: "run_1",
17 runKind: "implement",
18 credentialId: "tok_1",
19 action: "merge_pull_request",
20 surface: "mcp",
21 workspace: "acme",
22 repo: "acme/rocket",
23 number: 12,
24 gitRef: null,
25 path: null,
26 outcome: "denied",
27 rule: "never",
28 result: "forbidden",
29 message: 'g1t\'s token can never use merge_pull_request: "merging" is for people, too.',
30 requestId: "8c1f",
31};
32
33test("owners see everything, members their projects, others nothing", () => {
34 assert.deepEqual(visibilityFor("owner", "ana"), { kind: "all" });
35 assert.deepEqual(visibilityFor("member", "ana"), { kind: "projects", username: "ana" });
36 assert.equal(visibilityFor(null, "ana"), null);
37});
38
39test("filters are read from the address, and nonsense is dropped", () => {
40 const filters = parseFilters(new URLSearchParams("actor=syntaqx&outcome=maybe&kind=agent&from=2026-10-01&to=yesterday&project=rocket"));
41 assert.equal(filters.actor, "syntaqx");
42 assert.equal(filters.outcome, "");
43 // Agents are no longer a filter: an old link's kind=agent is dropped.
44 assert.equal(filters.kind, "");
45 assert.equal(filters.from, "2026-10-01");
46 assert.equal(filters.to, "");
47 const query = toQuery("acme", { kind: "all" }, { ...filters, to: "2026-10-04" }, 100);
48 assert.equal(query.repo, "acme/rocket");
49 assert.equal(query.since, "2026-10-01T00:00:00.000Z");
50 // The end day is inclusive.
51 assert.equal(query.until, "2026-10-05T00:00:00.000Z");
52 assert.equal(query.actorKind, null);
53 assert.equal(query.outcome, null);
54});
55
56test("links keep the other filters", () => {
57 const filters = parseFilters(new URLSearchParams("actor=ana&outcome=denied"));
58 assert.equal(filterHref("/acme/-/audit", filters, { before: "aud_9" }), "/acme/-/audit?actor=ana&outcome=denied&before=aud_9");
59 assert.equal(filterHref("/acme/-/audit", parseFilters(new URLSearchParams())), "/acme/-/audit");
60});
61
62test("an old agent's entry is shown with whom it acted for", () => {
63 assert.equal(actorLabel(entry), "g1t on behalf of syntaqx");
64 assert.equal(actorLabel({ actor: "ana", agent: null, onBehalfOf: null }), "ana");
65 assert.equal(targetLabel(entry), "acme/rocket#12");
66 assert.equal(targetLabel({ ...entry, number: null, gitRef: "refs/heads/fix" }), "acme/rocket refs/heads/fix");
67 assert.equal(ruleLabel("never"), "never allowed for agents (an old entry)");
68 assert.equal(ruleLabel("run:implement/tools"), "implement run tools");
69 assert.equal(ruleLabel("run:update/runner:push"), "update run runner (push)");
70 assert.equal(ruleLabel("workspace-token"), "the workspace token's access");
71 assert.equal(ruleLabel("deploy-key"), "the deploy key's access");
72});
73
74test("the CSV quotes what it must and keeps formulas as text", () => {
75 const csv = toCsv([entry, { ...entry, id: "aud_2", path: "=HYPERLINK(1)", message: null }]);
76 const lines = csv.trimEnd().split("\r\n");
77 assert.equal(lines.length, 3);
78 assert.ok(lines[0].startsWith("id,time,workspace,actorKind,actor"));
79 assert.ok(lines[1].includes('"g1t\'s token can never use merge_pull_request: ""merging"" is for people, too."'));
80 assert.ok(lines[2].includes("'=HYPERLINK(1)"));
81 assert.equal(exportName("acme", "csv", new Date("2026-10-04T23:00:00Z")), "acme-audit-2026-10-04.csv");
82});
83
84test("the log reads back only as far as the plan keeps it", () => {
85 const now = Date.parse("2026-10-31T00:00:00.000Z");
86 // 90 days on the plan, 7 free.
87 assert.equal(retainedSince(null, 90, now), "2026-08-02T00:00:00.000Z");
88 assert.equal(retainedSince(null, 7, now), "2026-10-24T00:00:00.000Z");
89 assert.equal(retainedSince(null, 30, now), "2026-10-01T00:00:00.000Z");
90 assert.equal(retainedSince("2026-01-01T00:00:00.000Z", 30, now), "2026-10-01T00:00:00.000Z");
91 // A later start than the window is kept.
92 assert.equal(retainedSince("2026-10-20T00:00:00.000Z", 30, now), "2026-10-20T00:00:00.000Z");
93 // A longer window, where one is set.
94 assert.equal(retainedSince("2026-01-01T00:00:00.000Z", 365, now), "2026-01-01T00:00:00.000Z");
95});