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.

162 linesCodeBlameRaw
1import assert from "node:assert/strict";
2import { test } from "node:test";
3
4import type { Contributors, Profile, ProfileWorkspace, RepoPath, Result, Viewer } from "@g1t/contracts";
5
6import { type CardSources, buildCard, cardHref, committedLabel, committedWithin, parseRepo } from "./hovercard.ts";
7
8const NOW = Date.parse("2026-10-08T12:00:00Z");
9const DAY = 86_400_000;
10
11const ada: Profile = {
12 username: "ada",
13 name: "Ada Lovelace",
14 bio: "Engines.",
15 location: "London",
16 website: "https://ada.example",
17 pronouns: "she/her",
18 timezone: "Europe/London",
19 avatar: "cafe",
20 createdAt: "2026-01-01T00:00:00Z",
21};
22
23const me: Viewer = { id: "usr_me", username: "me", kind: "user", verified: true } as Viewer;
24
25/**
26 * Stand-ins for identity and repos that behave as they do: workspaces are
27 * shown only when the viewer shares them, or when ada has a public project
28 * there; the repository answers only viewers who may read it.
29 */
30function sources(over: Partial<CardSources> = {}): CardSources & { asked: { viewer: Viewer; publicIn: string[] }[] } {
31 const asked: { viewer: Viewer; publicIn: string[] }[] = [];
32 const memberships: ProfileWorkspace[] = [
33 { slug: "acme", name: "Acme", avatar: null },
34 { slug: "secret-lab", name: "Secret Lab", avatar: null },
35 ];
36 return {
37 asked,
38 profile: async (username) => (username === "ada" ? ada : null),
39 accountId: async (username) => (username === "ada" ? "usr_ada" : null),
40 publicNamespaces: async () => ["acme"],
41 profileWorkspaces: async (_username, viewer, publicIn) => {
42 asked.push({ viewer, publicIn });
43 // secret-lab is private: only someone who belongs to it sees it.
44 return memberships.filter((one) => publicIn.includes(one.slug) || (viewer?.username === "insider" && one.slug === "secret-lab"));
45 },
46 contributors: async (path: RepoPath, viewer: Viewer): Promise<Result<Contributors>> => {
47 if (path.name === "private" && viewer?.username !== "insider") {
48 return { ok: false, error: { code: "not_found", message: "Repository not found." } } as Result<Contributors>;
49 }
50 return {
51 ok: true,
52 value: {
53 head: "h",
54 commit: "h",
55 computedAt: null,
56 pending: false,
57 partial: false,
58 total: 1,
59 commits: 3,
60 weeks: [],
61 contributors: [
62 {
63 kind: "user",
64 name: "ada",
65 username: "ada",
66 avatar: "cafe",
67 commits: 3,
68 firstAt: "2026-09-01T00:00:00Z",
69 lastAt: new Date(NOW - 3 * DAY).toISOString(),
70 weeks: [],
71 },
72 ],
73 },
74 };
75 },
76 ...over,
77 };
78}
79
80test("the card is the public profile, signed out too", async () => {
81 const card = await buildCard("Ada", null, null, sources(), NOW);
82 assert.ok(card && card.kind === "user");
83 assert.equal(card.username, "ada");
84 assert.equal(card.name, "Ada Lovelace");
85 assert.equal(card.pronouns, "she/her");
86 assert.equal(card.bio, "Engines.");
87 assert.equal(card.location, "London");
88 assert.equal(card.avatar, "cafe");
89 assert.equal(card.committed, null, "no repository, nothing about commits");
90 // Never an address.
91 assert.ok(!JSON.stringify(card).includes("@"));
92});
93
94test("a private workspace is hidden from someone outside it", async () => {
95 const outside = sources();
96 const outsider = await buildCard("ada", me, null, outside, NOW);
97 assert.deepEqual(outsider?.kind === "user" && outsider.workspaces.map((one) => one.slug), ["acme"]);
98 // The viewer is passed through, for identity to decide.
99 assert.equal(outside.asked[0]!.viewer, me);
100 assert.deepEqual(outside.asked[0]!.publicIn, ["acme"]);
101 const insider = await buildCard("ada", { ...me, username: "insider" } as Viewer, null, sources(), NOW);
102 assert.deepEqual(insider?.kind === "user" && insider.workspaces.map((one) => one.slug), ["acme", "secret-lab"]);
103});
104
105test("commits to a repository show only to those who may read it", async () => {
106 const open = await buildCard("ada", null, { namespace: "acme", name: "web" }, sources(), NOW);
107 assert.equal(open?.kind === "user" && open.committed, "week");
108 const hidden = await buildCard("ada", me, { namespace: "acme", name: "private" }, sources(), NOW);
109 assert.equal(hidden?.kind === "user" && hidden.committed, null);
110});
111
112test("the card carries the time zone a profile gives, and none when it gives none", async () => {
113 const card = await buildCard("ada", me, null, sources(), NOW);
114 assert.equal(card?.kind === "user" && card.timezone, "Europe/London");
115 const without = await buildCard("ada", me, null, sources({ profile: async () => ({ ...ada, timezone: null }) }), NOW);
116 assert.equal(without?.kind === "user" && without.timezone, null);
117});
118
119test("a failing service leaves its part out, not the card", async () => {
120 const card = await buildCard(
121 "ada",
122 me,
123 { namespace: "acme", name: "web" },
124 sources({ profileWorkspaces: async () => Promise.reject(new Error("down")), contributors: async () => Promise.reject(new Error("down")) }),
125 NOW,
126 );
127 assert.ok(card && card.kind === "user");
128 assert.deepEqual(card.workspaces, []);
129 assert.equal(card.committed, null);
130});
131
132test("g1t has its own card; ghost and strangers have none", async () => {
133 assert.deepEqual(await buildCard("g1t", null, null, sources(), NOW), { kind: "g1t", username: "g1t" });
134 assert.equal(await buildCard("ghost", me, null, sources(), NOW), null);
135 assert.equal(await buildCard("nobody", me, null, sources(), NOW), null);
136 assert.equal(await buildCard("../etc", me, null, sources(), NOW), null);
137});
138
139test("workspaces past three are counted", async () => {
140 const many = Array.from({ length: 5 }, (_, index) => ({ slug: `w${index}`, name: `W${index}`, avatar: null }));
141 const card = await buildCard("ada", me, null, sources({ profileWorkspaces: async () => many }), NOW);
142 assert.ok(card && card.kind === "user");
143 assert.equal(card.workspaces.length, 3);
144 assert.equal(card.more_workspaces, 2);
145});
146
147test("how recently, in the words the card uses", () => {
148 assert.equal(committedWithin(new Date(NOW - 2 * 3_600_000).toISOString(), NOW), "day");
149 assert.equal(committedWithin(new Date(NOW - 6 * DAY).toISOString(), NOW), "week");
150 assert.equal(committedWithin(new Date(NOW - 20 * DAY).toISOString(), NOW), "month");
151 assert.equal(committedWithin(new Date(NOW - 60 * DAY).toISOString(), NOW), null);
152 assert.equal(committedWithin(null, NOW), null);
153 assert.equal(committedLabel("week"), "Committed to this repository in the past week");
154});
155
156test("the card's address and its repository", () => {
157 assert.equal(cardHref("Ada", "acme/web"), "/-/hovercard/user/ada?repo=acme%2Fweb");
158 assert.equal(cardHref("ada", null), "/-/hovercard/user/ada");
159 assert.deepEqual(parseRepo("acme/web"), { namespace: "acme", name: "web" });
160 assert.equal(parseRepo("acme"), null);
161 assert.equal(parseRepo("../x/y"), null);
162});