Skip to content
80 linesCodeBlameRaw
1import assert from "node:assert/strict";
2import { test } from "node:test";
3
4import type { AgentDelivery } from "@g1t/contracts";
5
6import { g1tSurface } from "./surface.ts";
7
8const delivery: AgentDelivery = {
9 workspace: "acme",
10 workspace_id: "wsp_1",
11 channel_id: "chn_1",
12 channel_kind: "channel",
13 channel_name: "releases",
14 agent_id: "agt_ship",
15 message_id: "msg_1",
16 thread_root: "msg_0",
17 asked_by: "usr_dana",
18 hops: 2,
19 asker: { username: "dana", role: "member", can_write: false },
20 surface: "g1t",
21};
22
23/** A chat binding that records each call and answers `answers[method]`. */
24function fakeChat(answers: Record<string, unknown>) {
25 const calls: { method: string; body: any }[] = [];
26 const binding = {
27 async fetch(url: string, init: { body: string }) {
28 const method = new URL(url).pathname.replace("/rpc/", "");
29 calls.push({ method, body: JSON.parse(init.body) });
30 return Response.json(answers[method] ?? { ok: true, value: null });
31 },
32 };
33 return { binding: binding as any, calls };
34}
35
36test("the g1t adapter posts in the thread, handing on hops, asked_by and the asker's access", async () => {
37 const chat = fakeChat({ post_as_agent: { ok: true, value: { id: "msg_2" } } });
38 const id = await g1tSurface(chat.binding, delivery).post("Tagged 1.4.");
39 assert.equal(id, "msg_2");
40 const [call] = chat.calls;
41 assert.equal(call.method, "post_as_agent");
42 assert.equal(call.body.agent_id, "agt_ship");
43 assert.equal(call.body.channel_id, "chn_1");
44 assert.deepEqual(call.body.message, {
45 body: "Tagged 1.4.",
46 thread_root: "msg_0",
47 hops: 2,
48 asked_by: "usr_dana",
49 asker: { username: "dana", role: "member", can_write: false },
50 });
51});
52
53test("the g1t adapter reads the thread as the agent, and leaves out deleted messages", async () => {
54 const message = (id: string, deleted: string | null) => ({
55 id,
56 channel_id: "chn_1",
57 author: { kind: "user", id: "usr_dana", name: "dana", display_name: "Dana", avatar: null, role: null },
58 kind: "text",
59 body: "hi",
60 card: null,
61 thread_root: "msg_0",
62 reply_count: 0,
63 last_reply_at: null,
64 created_at: "2026-10-08T12:00:00Z",
65 edited_at: null,
66 deleted_at: deleted,
67 });
68 const chat = fakeChat({ history_for_agent: { ok: true, value: [message("msg_0", null), message("msg_1", "2026-10-08T12:01:00Z")] } });
69 const history = await g1tSurface(chat.binding, delivery).history(30);
70 assert.deepEqual(chat.calls[0].body, { workspace: "acme", channel_id: "chn_1", agent_id: "agt_ship", thread_root: "msg_0", limit: 30 });
71 assert.deepEqual(history.map((m) => m.id), ["msg_0"]);
72 assert.equal(history[0].author.name, "dana");
73});
74
75test("a failed post is an error the reply loop records; typing never throws", async () => {
76 const chat = fakeChat({ post_as_agent: { ok: false, error: { code: "forbidden", message: "not a member" } } });
77 await assert.rejects(g1tSurface(chat.binding, delivery).post("x"), /not a member/);
78 const broken = { fetch: async () => new Response("down", { status: 500 }) } as any;
79 await g1tSurface(broken, delivery).typing();
80});