Skip to content
166 linesCodeBlameRaw
1import assert from "node:assert/strict";
2import { test } from "node:test";
3
4import type { InstallRequest } from "@g1t/contracts";
5import { connectorById, connectorsFor } from "@g1t/contracts/connectors";
6
7import { FIRST_PARTY_EXTENSIONS, extensionById } from "@g1t/contracts/marketplace";
8
9import {
10 STARTER_KITS,
11 TIERS,
12 addPath,
13 categoryTitle,
14 comingIntegrations,
15 comingListings,
16 extensionListings,
17 integrationListings,
18 integrationMatches,
19 integrationUses,
20 isConnectedSystem,
21 marketplaceForm,
22 passes,
23 readFilters,
24 tiersShown,
25} from "./marketplace.ts";
26import { modeOf } from "./workspace-nav.ts";
27
28const request = (listing: string, by: string, status: InstallRequest["status"] = "open"): InstallRequest => ({
29 id: `ins_${listing}_${by}`,
30 listing,
31 kind: listing.startsWith("extension:") ? "extension" : "integration",
32 name: listing,
33 note: null,
34 requested_by: by,
35 requested_at: "2026-10-10T12:00:00Z",
36 status,
37 resolved_by: null,
38 resolved_at: null,
39});
40
41test("only integrations a workspace can connect today are listed, connected first", () => {
42 const views = connectorsFor("workspace");
43 const listings = integrationListings(views, { sentry: { detail: "acme", problem: null, manage: "/acme/-/integrations/alerts" } }, [], "ana", "acme");
44 assert.ok(listings.every((l) => l.view.status === "available"));
45 assert.equal(listings[0]!.view.id, "sentry");
46 assert.equal(listings[0]!.href, "/acme/-/integrations/alerts", "managed where it is managed");
47 const linear = listings.find((l) => l.view.id === "linear")!;
48 assert.equal(linear.href, "/acme/-/integrations/trackers?add=linear#add", "connected on its setup page");
49 assert.ok(!listings.some((l) => l.view.id === "slack"));
50 assert.ok(comingIntegrations(views).some((v) => v.id === "slack"));
51 assert.ok(integrationMatches(linear, "tracker"));
52 assert.ok(!integrationMatches(linear, "deploys"));
53});
54
55test("an owner adds what a request asks for where it is added", () => {
56 const views = connectorsFor("workspace");
57 assert.equal(addPath({ listing: "agent:qa" }, "acme", views), null, "agents aren't added from the Marketplace");
58 assert.equal(addPath({ listing: "integration:jira" }, "acme", views), "/acme/-/integrations/trackers?add=jira#add");
59 assert.equal(addPath({ listing: "nonsense" }, "acme", views), null);
60});
61
62test("the Marketplace's forms are read strictly", () => {
63 const form = (fields: Record<string, string>) => {
64 const data = new FormData();
65 for (const [k, v] of Object.entries(fields)) data.set(k, v);
66 return data;
67 };
68 assert.deepEqual(marketplaceForm(form({ intent: "request", listing: "integration:jira", note: " flaky " })), { intent: "request", listing: "integration:jira", note: "flaky" });
69 assert.deepEqual(marketplaceForm(form({ intent: "request", listing: "extension:support" })), { intent: "request", listing: "extension:support", note: null });
70 assert.equal(marketplaceForm(form({ intent: "request", listing: "agent:qa" })), null, "agents start from templates in Agents");
71 assert.equal(marketplaceForm(form({ intent: "request", listing: "app:qa" })), null);
72 assert.deepEqual(marketplaceForm(form({ intent: "resolve", id: "ins_1", status: "declined" })), { intent: "resolve", id: "ins_1", status: "declined" });
73 assert.equal(marketplaceForm(form({ intent: "resolve", id: "ins_1", status: "open" })), null);
74 assert.equal(marketplaceForm(form({ intent: "install" })), null);
75});
76
77test("extensions list published ones first, each with its install", () => {
78 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" };
79 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 };
80 const listings = extensionListings([...FIRST_PARTY_EXTENSIONS, published], [install], [request("extension:support", "ana")], "ana");
81 assert.equal(listings[0]!.manifest.id, "standup");
82 assert.equal(listings[0]!.install?.version, "1.0.0");
83 assert.equal(listings.find((l) => l.manifest.id === "support")!.requested, true);
84 assert.equal(listings.find((l) => l.manifest.id === "mail")!.install, null);
85});
86
87test("coming integrations include the ones each person connects, once", () => {
88 const workspace = connectorsFor("workspace");
89 const personal = connectorsFor("personal");
90 const coming = comingIntegrations(workspace, personal).map((v) => v.id);
91 assert.ok(coming.includes("slack"));
92 assert.ok(coming.includes("gmail"), "Gmail is each person's");
93 assert.ok(coming.includes("google-calendar"));
94 assert.equal(new Set(coming).size, coming.length);
95});
96
97test("installing, switching and removing an extension are read from forms", () => {
98 const form = (fields: Record<string, string>) => {
99 const data = new FormData();
100 for (const [k, v] of Object.entries(fields)) data.set(k, v);
101 return data;
102 };
103 assert.deepEqual(marketplaceForm(form({ intent: "install", extension: "support" })), { intent: "install", extension: "support" });
104 assert.deepEqual(marketplaceForm(form({ intent: "switch", listing: "extension:support", enabled: "off" })), { intent: "switch", listing: "extension:support", enabled: false });
105 assert.equal(marketplaceForm(form({ intent: "switch", listing: "integration:sentry", enabled: "off" })), null);
106 assert.deepEqual(marketplaceForm(form({ intent: "uninstall", listing: "extension:support" })), { intent: "uninstall", listing: "extension:support" });
107 assert.equal(addPath({ listing: "extension:mail" }, "acme", []), "/acme/-/marketplace/extensions/mail");
108});
109
110test("starter kits name extensions that are listed, and connected systems are told apart", () => {
111 for (const kit of STARTER_KITS) {
112 assert.ok(kit.extensions.length > 0, kit.id);
113 for (const id of kit.extensions) assert.ok(extensionById(id), `${kit.id}: ${id}`);
114 }
115 const systems = FIRST_PARTY_EXTENSIONS.filter(isConnectedSystem).map((m) => m.id);
116 assert.deepEqual(systems, ["helpdesk-bridge", "crm-bridge", "erp-bridge"]);
117 assert.ok(FIRST_PARTY_EXTENSIONS.filter(isConnectedSystem).every((m) => m.bridges), "a connected system says what it bridges");
118});
119
120test("every listing says who builds it and whether it can be added here", () => {
121 const views = connectorsFor("workspace");
122 const connected = { sentry: { detail: "acme", problem: null, manage: "/acme/-/integrations/alerts" } };
123 const listings = integrationListings(views, connected, [], "ana", "acme", { github: "This g1t has no GitHub App set up." });
124 assert.ok(listings.every((l) => l.tier === "official" && l.publisher === "g1t"), "the connector catalog is g1t's own");
125 assert.equal(listings.find((l) => l.view.id === "sentry")!.availability, "added");
126 assert.equal(listings.find((l) => l.view.id === "jira")!.availability, "available");
127 const github = listings.find((l) => l.view.id === "github")!;
128 assert.equal(github.availability, "unavailable");
129 assert.equal(github.why, "This g1t has no GitHub App set up.");
130 assert.equal(github.href, null, "nothing to connect where it can't be connected");
131 assert.equal(listings.find((l) => l.view.id === "linear")!.path, "/acme/-/marketplace/integrations/linear");
132
133 const coming = comingListings(views, connectorsFor("personal").filter((v) => v.id === "gmail"), "acme");
134 assert.ok(coming.every((l) => l.availability === "soon" && l.href === null && l.tier === "official"));
135 assert.equal(coming.find((l) => l.view.id === "gmail")!.scope, "personal");
136
137 const extensions = extensionListings(FIRST_PARTY_EXTENSIONS, [], [], "ana");
138 assert.ok(extensions.every((l) => l.tier === "official" && l.availability === "soon"), "g1t's own, none published yet");
139});
140
141test("an integration's page says what it does today and what is still Soon", () => {
142 const linear = integrationUses(connectorById("linear")!);
143 assert.deepEqual(linear.today.map((u) => u.scope), ["workspace"]);
144 assert.deepEqual(linear.today[0]!.capabilities, ["Agents can read", "Writes back"]);
145 assert.deepEqual(linear.soon.map((u) => u.scope), ["personal"], "each person's Linear inbox is Soon");
146 const slack = integrationUses(connectorById("slack")!);
147 assert.equal(slack.today.length, 0);
148 assert.equal(slack.soon.length, 2);
149 assert.equal(categoryTitle("issues"), "Issues & projects");
150});
151
152test("filters by tier and availability are read from the address, and unknown ones are All", () => {
153 assert.deepEqual(readFilters(new URLSearchParams("tier=verified&availability=soon")), { tier: "verified", availability: "soon" });
154 assert.deepEqual(readFilters(new URLSearchParams("tier=gold&availability=maybe")), { tier: "all", availability: "all" });
155 assert.deepEqual(tiersShown({ tier: "all", availability: "all" }), ["official", "verified", "community", "internal"]);
156 assert.deepEqual(tiersShown({ tier: "community", availability: "all" }), ["community"]);
157 assert.ok(passes({ tier: "official", availability: "soon" }, { tier: "all", availability: "soon" }));
158 assert.ok(!passes({ tier: "official", availability: "soon" }, { tier: "verified", availability: "all" }));
159 for (const tier of ["verified", "community", "internal"] as const) assert.ok(TIERS[tier].none.extension.length > 0, `${tier} says when it is empty`);
160});
161
162test("the Marketplace is part of Apps, not a mode of its own", () => {
163 assert.equal(modeOf("/acme/-/marketplace", "acme"), "apps");
164 assert.equal(modeOf("/acme/-/marketplace/extensions/mail", "acme"), "apps");
165 assert.equal(modeOf("/acme/-/agents/templates", "acme"), "agents", "agent templates are Agents'");
166});