Skip to content
140 linesCodeBlameRaw
1import assert from "node:assert/strict";
2import { test } from "node:test";
3
4import type { AgentTemplate, InstallRequest } from "@g1t/contracts";
5import { connectorsFor } from "@g1t/contracts/connectors";
6
7import { FIRST_PARTY_EXTENSIONS } from "@g1t/contracts/marketplace";
8
9import { addPath, agentListings, comingIntegrations, extensionListings, integrationListings, integrationMatches, marketplaceForm, routingWords } from "./marketplace.ts";
10import { modeOf } from "./workspace-nav.ts";
11
12const template = (id: string, title: string): AgentTemplate => ({
13 id,
14 display_name: title.split(" ")[0]!,
15 handle: id,
16 name_ideas: [],
17 role: title,
18 title,
19 department: "Engineering",
20 responsibilities: ["Do the work"],
21 subagents: [],
22 instructions: "",
23 personality_preset: "crisp",
24 routing: { floor: null, ceiling: null, providers: [], pinned: null },
25});
26
27const request = (listing: string, by: string, status: InstallRequest["status"] = "open"): InstallRequest => ({
28 id: `ins_${listing}_${by}`,
29 listing,
30 kind: listing.startsWith("agent:") ? "agent" : "integration",
31 name: listing,
32 note: null,
33 requested_by: by,
34 requested_at: "2026-10-10T12:00:00Z",
35 status,
36 resolved_by: null,
37 resolved_at: null,
38});
39
40test("a role counts the agents hired into it, not archived ones", () => {
41 const [eng, qa] = agentListings(
42 [template("engineering", "Software Engineer"), template("qa", "QA Engineer")],
43 [
44 { id: "a1", handle: "otto", display_name: "Otto", template: "engineering", archived_at: null },
45 { id: "a2", handle: "bolt", display_name: "Bolt", template: "engineering", archived_at: "2026-10-01T00:00:00Z" },
46 { id: "a3", handle: "g1t", display_name: "g1t", template: null, archived_at: null },
47 ],
48 [request("agent:qa", "ana"), request("agent:qa", "bo"), request("agent:engineering", "ana", "declined")],
49 "Ana",
50 );
51 assert.deepEqual(eng!.hired.map((a) => a.handle), ["otto"]);
52 assert.equal(eng!.requested, false, "a turned-down request isn't waiting");
53 assert.equal(qa!.hired.length, 0);
54 assert.equal(qa!.requested, true, "usernames match whatever their case");
55 assert.equal(qa!.waiting, 2);
56});
57
58test("without the agents service, nobody counts as hired", () => {
59 const [eng] = agentListings([template("engineering", "Software Engineer")], null, [], "ana");
60 assert.deepEqual(eng!.hired, []);
61});
62
63test("only integrations a workspace can connect today are listed, connected first", () => {
64 const views = connectorsFor("workspace");
65 const listings = integrationListings(views, { sentry: { detail: "acme", problem: null, manage: "/acme/-/integrations/alerts" } }, [], "ana", "acme");
66 assert.ok(listings.every((l) => l.view.status === "available"));
67 assert.equal(listings[0]!.view.id, "sentry");
68 assert.equal(listings[0]!.href, "/acme/-/integrations/alerts", "managed where it is managed");
69 const linear = listings.find((l) => l.view.id === "linear")!;
70 assert.equal(linear.href, "/acme/-/integrations/trackers?add=linear#add", "connected on its setup page");
71 assert.ok(!listings.some((l) => l.view.id === "slack"));
72 assert.ok(comingIntegrations(views).some((v) => v.id === "slack"));
73 assert.ok(integrationMatches(linear, "tracker"));
74 assert.ok(!integrationMatches(linear, "deploys"));
75});
76
77test("an owner adds what a request asks for where it is added", () => {
78 const views = connectorsFor("workspace");
79 assert.equal(addPath({ listing: "agent:qa" }, "acme", views), "/acme/-/agents/new?template=qa");
80 assert.equal(addPath({ listing: "integration:jira" }, "acme", views), "/acme/-/integrations/trackers?add=jira#add");
81 assert.equal(addPath({ listing: "nonsense" }, "acme", views), null);
82});
83
84test("the Marketplace's forms are read strictly", () => {
85 const form = (fields: Record<string, string>) => {
86 const data = new FormData();
87 for (const [k, v] of Object.entries(fields)) data.set(k, v);
88 return data;
89 };
90 assert.deepEqual(marketplaceForm(form({ intent: "request", listing: "agent:qa", note: " flaky " })), { intent: "request", listing: "agent:qa", note: "flaky" });
91 assert.deepEqual(marketplaceForm(form({ intent: "request", listing: "agent:qa" })), { intent: "request", listing: "agent:qa", note: null });
92 assert.equal(marketplaceForm(form({ intent: "request", listing: "app:qa" })), null);
93 assert.deepEqual(marketplaceForm(form({ intent: "resolve", id: "ins_1", status: "declined" })), { intent: "resolve", id: "ins_1", status: "declined" });
94 assert.equal(marketplaceForm(form({ intent: "resolve", id: "ins_1", status: "open" })), null);
95 assert.equal(marketplaceForm(form({ intent: "install" })), null);
96});
97
98test("extensions list published ones first, each with its install", () => {
99 const published = { ...FIRST_PARTY_EXTENSIONS[1]!, id: "standup", status: "available" as const, source: { repo: "flagon-io/standup", tag: "v1.0.0" }, version: "1.0.0" };
100 const install = { id: "ins_1", listing: "extension:standup", version: "1.0.0", plan: "free", installed_by: "chase", installed_at: "2026-10-10T00:00:00Z", enabled: true, disabled_by: null, disabled_at: null, budget_monthly_micros: null };
101 const listings = extensionListings([...FIRST_PARTY_EXTENSIONS, published], [install], [request("extension:support", "ana")], "ana");
102 assert.equal(listings[0]!.manifest.id, "standup");
103 assert.equal(listings[0]!.install?.version, "1.0.0");
104 assert.equal(listings.find((l) => l.manifest.id === "support")!.requested, true);
105 assert.equal(listings.find((l) => l.manifest.id === "mail")!.install, null);
106});
107
108test("coming integrations include the ones each person connects, once", () => {
109 const workspace = connectorsFor("workspace");
110 const personal = connectorsFor("personal");
111 const coming = comingIntegrations(workspace, personal).map((v) => v.id);
112 assert.ok(coming.includes("slack"));
113 assert.ok(coming.includes("gmail"), "Gmail is each person's");
114 assert.ok(coming.includes("google-calendar"));
115 assert.equal(new Set(coming).size, coming.length);
116});
117
118test("installing, switching and removing an extension are read from forms", () => {
119 const form = (fields: Record<string, string>) => {
120 const data = new FormData();
121 for (const [k, v] of Object.entries(fields)) data.set(k, v);
122 return data;
123 };
124 assert.deepEqual(marketplaceForm(form({ intent: "install", extension: "support" })), { intent: "install", extension: "support" });
125 assert.deepEqual(marketplaceForm(form({ intent: "switch", listing: "extension:support", enabled: "off" })), { intent: "switch", listing: "extension:support", enabled: false });
126 assert.equal(marketplaceForm(form({ intent: "switch", listing: "integration:sentry", enabled: "off" })), null);
127 assert.deepEqual(marketplaceForm(form({ intent: "uninstall", listing: "extension:support" })), { intent: "uninstall", listing: "extension:support" });
128 assert.equal(addPath({ listing: "extension:mail" }, "acme", []), "/acme/-/marketplace/extensions/mail");
129});
130
131test("routing limits read as words", () => {
132 assert.equal(routingWords({ floor: null, ceiling: null }), "Any model the work needs");
133 assert.equal(routingWords({ floor: "large", ceiling: null }), "Large models or better");
134 assert.equal(routingWords({ floor: null, ceiling: "large" }), "Up to large models");
135});
136
137test("the Marketplace is part of Apps, not a mode of its own", () => {
138 assert.equal(modeOf("/acme/-/marketplace", "acme"), "apps");
139 assert.equal(modeOf("/acme/-/marketplace/agents/qa", "acme"), "apps");
140});