Skip to content
111 linesCodeBlameRaw
1import assert from "node:assert/strict";
2import { test } from "node:test";
3
4import type { AgentTeam, PresenceEntry } from "@g1t/contracts";
5
6import { agentIdsOn, localTime, showing, teamBudgetBlock, teamsSection, whoToPage, type TeamsHere } from "./teammates.ts";
7
8const NOW = new Date("2026-10-09T01:12:00Z");
9
10const person = (username: string, extra: Partial<AgentTeam["people"][number]> = {}): AgentTeam["people"][number] => ({
11 user_id: `usr_${username}`,
12 username,
13 name: null,
14 title: null,
15 timezone: null,
16 owns: [],
17 manager: null,
18 maintainer: false,
19 ...extra,
20});
21
22const entry = (username: string, presence: PresenceEntry["presence"], extra: Partial<PresenceEntry> = {}): PresenceEntry => ({
23 user_id: `usr_${username}`,
24 username,
25 presence,
26 dnd_until: null,
27 status: null,
28 at: 1,
29 ...extra,
30});
31
32const sales: AgentTeam = {
33 slug: "sales",
34 name: "Sales",
35 description: "Two people and one agent",
36 lead: { kind: "user", username: "sofia", name: "Sofia Reyes", avatar: null },
37 channel: { id: "chn_1", name: "sales" },
38 budget_micros: 150_000_000,
39 people: [
40 person("sofia", { name: "Sofia Reyes", title: "Head of Sales", owns: ["the forecast"], manager: "chase", maintainer: true, timezone: "America/Chicago" }),
41 person("jordan", { name: "Jordan Lee", title: "Account Executive", owns: ["Halcyon", "Bluebird"], manager: "sofia" }),
42 ],
43 agent_ids: ["agt_david"],
44};
45
46const here = (presence: PresenceEntry[]): TeamsHere => ({
47 teams: [sales],
48 agents: [
49 { id: "agt_david", handle: "david", display_name: "David", title: "CRM keeper" },
50 { id: "agt_pax", handle: "pax", display_name: "Pax", title: "Billing" },
51 ],
52 presence,
53});
54
55test("an agent is on the teams it was added to, and only those", () => {
56 assert.deepEqual(agentIdsOn(sales), ["agt_david"]);
57 assert.deepEqual(agentIdsOn({ ...sales, agent_ids: ["agt_pax", "agt_pax"] }), ["agt_pax"]);
58 assert.deepEqual(agentIdsOn({ ...sales, agent_ids: [] }), []);
59});
60
61test("an agent is told who leads, who owns what and who is around", () => {
62 const dnd = new Date(NOW.getTime() + 3 * 3600_000).toISOString();
63 const text = teamsSection("agt_david", here([entry("sofia", "active", { dnd_until: dnd }), entry("jordan", "active")]), NOW)!;
64 assert.match(text, /^## Your teams/);
65 assert.match(text, /### Sales/);
66 assert.match(text, /Led by Sofia Reyes \(@sofia\)\./);
67 assert.match(text, /Its channel is #sales\./);
68 assert.match(text, /share a budget of \$150\.00 a month/);
69 assert.match(text, /Sofia Reyes \(@sofia\); Head of Sales, the lead, a maintainer; owns the forecast; reports to @chase \(focusing in Do Not Disturb until 23:12 their time; it's 20:12 for them\)/);
70 assert.match(text, /Jordan Lee \(@jordan\); Account Executive; owns Halcyon, Bluebird; reports to @sofia \(online\)/);
71 assert.match(text, /- @david: you/);
72 // Sofia is focusing, so Jordan is the one to page.
73 assert.match(text, /When a person is needed: Jordan Lee \(@jordan\), who is online now\./);
74});
75
76test("with no one reachable, the agent is told to post and wait", () => {
77 const text = teamsSection("agt_david", here([entry("sofia", "away"), entry("jordan", "offline")]), NOW)!;
78 assert.match(text, /No one on Sales can be reached now; post in #sales and say they'll see it when they're back\./);
79});
80
81test("without presence, nobody is said to be offline", () => {
82 const text = teamsSection("agt_david", here([]), NOW)!;
83 assert.doesNotMatch(text, /offline/);
84 assert.doesNotMatch(text, /When a person is needed/);
85});
86
87test("an agent on no team is told nothing", () => {
88 assert.equal(teamsSection("agt_david", { teams: [], agents: [], presence: [] }, NOW), null);
89 assert.equal(teamsSection("agt_david", null, NOW), null);
90});
91
92test("who to page: the lead, then a maintainer, then anyone reachable", () => {
93 const presence = new Map([entry("sofia", "active"), entry("jordan", "active")].map((e) => [e.user_id, e]));
94 assert.equal(whoToPage(sales, presence, NOW, true)?.username, "sofia");
95 presence.set("usr_sofia", entry("sofia", "away"));
96 assert.equal(whoToPage(sales, presence, NOW, true)?.username, "jordan");
97 assert.equal(whoToPage(sales, presence, NOW, false), null);
98});
99
100test("presence and local time read plainly", () => {
101 assert.equal(showing(undefined, null, NOW, true), "offline");
102 assert.equal(showing(entry("a", "away", { status: { emoji: "🌴", text: "On leave", clear_at: null, source: "manual", set_at: "" } }), null, NOW, true), 'away; status "🌴 On leave"');
103 assert.equal(showing(entry("a", "active"), null, NOW, false), null);
104 assert.equal(localTime("Europe/London", NOW), "02:12");
105 assert.equal(localTime("Not/AZone", NOW), null);
106});
107
108test("a team's budget stops its agents once they have spent it together", () => {
109 assert.equal(teamBudgetBlock([{ name: "Sales", budget_micros: 150_000_000, spent_micros: 149_000_000 }]), null);
110 assert.match(teamBudgetBlock([{ name: "Sales", budget_micros: 150_000_000, spent_micros: 150_000_000 }])!, /^Sales's agents have used the team's budget of \$150\.00/);
111});