| 1 | import assert from "node:assert/strict"; |
| 2 | import { test } from "node:test"; |
| 3 | |
| 4 | import type { InstallRequest } from "@g1t/contracts"; |
| 5 | import { connectorsFor } from "@g1t/contracts/connectors"; |
| 6 | |
| 7 | import { FIRST_PARTY_EXTENSIONS, extensionById } from "@g1t/contracts/marketplace"; |
| 8 | |
| 9 | import { STARTER_KITS, addPath, comingIntegrations, extensionListings, integrationListings, integrationMatches, isConnectedSystem, marketplaceForm } from "./marketplace.ts"; |
| 10 | import { modeOf } from "./workspace-nav.ts"; |
| 11 | |
| 12 | const request = (listing: string, by: string, status: InstallRequest["status"] = "open"): InstallRequest => ({ |
| 13 | id: `ins_${listing}_${by}`, |
| 14 | listing, |
| 15 | kind: listing.startsWith("extension:") ? "extension" : "integration", |
| 16 | name: listing, |
| 17 | note: null, |
| 18 | requested_by: by, |
| 19 | requested_at: "2026-10-10T12:00:00Z", |
| 20 | status, |
| 21 | resolved_by: null, |
| 22 | resolved_at: null, |
| 23 | }); |
| 24 | |
| 25 | test("only integrations a workspace can connect today are listed, connected first", () => { |
| 26 | const views = connectorsFor("workspace"); |
| 27 | const listings = integrationListings(views, { sentry: { detail: "acme", problem: null, manage: "/acme/-/integrations/alerts" } }, [], "ana", "acme"); |
| 28 | assert.ok(listings.every((l) => l.view.status === "available")); |
| 29 | assert.equal(listings[0]!.view.id, "sentry"); |
| 30 | assert.equal(listings[0]!.href, "/acme/-/integrations/alerts", "managed where it is managed"); |
| 31 | const linear = listings.find((l) => l.view.id === "linear")!; |
| 32 | assert.equal(linear.href, "/acme/-/integrations/trackers?add=linear#add", "connected on its setup page"); |
| 33 | assert.ok(!listings.some((l) => l.view.id === "slack")); |
| 34 | assert.ok(comingIntegrations(views).some((v) => v.id === "slack")); |
| 35 | assert.ok(integrationMatches(linear, "tracker")); |
| 36 | assert.ok(!integrationMatches(linear, "deploys")); |
| 37 | }); |
| 38 | |
| 39 | test("an owner adds what a request asks for where it is added", () => { |
| 40 | const views = connectorsFor("workspace"); |
| 41 | assert.equal(addPath({ listing: "agent:qa" }, "acme", views), null, "agents aren't added from the Marketplace"); |
| 42 | assert.equal(addPath({ listing: "integration:jira" }, "acme", views), "/acme/-/integrations/trackers?add=jira#add"); |
| 43 | assert.equal(addPath({ listing: "nonsense" }, "acme", views), null); |
| 44 | }); |
| 45 | |
| 46 | test("the Marketplace's forms are read strictly", () => { |
| 47 | const form = (fields: Record<string, string>) => { |
| 48 | const data = new FormData(); |
| 49 | for (const [k, v] of Object.entries(fields)) data.set(k, v); |
| 50 | return data; |
| 51 | }; |
| 52 | assert.deepEqual(marketplaceForm(form({ intent: "request", listing: "integration:jira", note: " flaky " })), { intent: "request", listing: "integration:jira", note: "flaky" }); |
| 53 | assert.deepEqual(marketplaceForm(form({ intent: "request", listing: "extension:support" })), { intent: "request", listing: "extension:support", note: null }); |
| 54 | assert.equal(marketplaceForm(form({ intent: "request", listing: "agent:qa" })), null, "agents start from templates in Agents"); |
| 55 | assert.equal(marketplaceForm(form({ intent: "request", listing: "app:qa" })), null); |
| 56 | assert.deepEqual(marketplaceForm(form({ intent: "resolve", id: "ins_1", status: "declined" })), { intent: "resolve", id: "ins_1", status: "declined" }); |
| 57 | assert.equal(marketplaceForm(form({ intent: "resolve", id: "ins_1", status: "open" })), null); |
| 58 | assert.equal(marketplaceForm(form({ intent: "install" })), null); |
| 59 | }); |
| 60 | |
| 61 | test("extensions list published ones first, each with its install", () => { |
| 62 | 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" }; |
| 63 | 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 }; |
| 64 | const listings = extensionListings([...FIRST_PARTY_EXTENSIONS, published], [install], [request("extension:support", "ana")], "ana"); |
| 65 | assert.equal(listings[0]!.manifest.id, "standup"); |
| 66 | assert.equal(listings[0]!.install?.version, "1.0.0"); |
| 67 | assert.equal(listings.find((l) => l.manifest.id === "support")!.requested, true); |
| 68 | assert.equal(listings.find((l) => l.manifest.id === "mail")!.install, null); |
| 69 | }); |
| 70 | |
| 71 | test("coming integrations include the ones each person connects, once", () => { |
| 72 | const workspace = connectorsFor("workspace"); |
| 73 | const personal = connectorsFor("personal"); |
| 74 | const coming = comingIntegrations(workspace, personal).map((v) => v.id); |
| 75 | assert.ok(coming.includes("slack")); |
| 76 | assert.ok(coming.includes("gmail"), "Gmail is each person's"); |
| 77 | assert.ok(coming.includes("google-calendar")); |
| 78 | assert.equal(new Set(coming).size, coming.length); |
| 79 | }); |
| 80 | |
| 81 | test("installing, switching and removing an extension are read from forms", () => { |
| 82 | const form = (fields: Record<string, string>) => { |
| 83 | const data = new FormData(); |
| 84 | for (const [k, v] of Object.entries(fields)) data.set(k, v); |
| 85 | return data; |
| 86 | }; |
| 87 | assert.deepEqual(marketplaceForm(form({ intent: "install", extension: "support" })), { intent: "install", extension: "support" }); |
| 88 | assert.deepEqual(marketplaceForm(form({ intent: "switch", listing: "extension:support", enabled: "off" })), { intent: "switch", listing: "extension:support", enabled: false }); |
| 89 | assert.equal(marketplaceForm(form({ intent: "switch", listing: "integration:sentry", enabled: "off" })), null); |
| 90 | assert.deepEqual(marketplaceForm(form({ intent: "uninstall", listing: "extension:support" })), { intent: "uninstall", listing: "extension:support" }); |
| 91 | assert.equal(addPath({ listing: "extension:mail" }, "acme", []), "/acme/-/marketplace/extensions/mail"); |
| 92 | }); |
| 93 | |
| 94 | test("starter kits name extensions that are listed, and connected systems are told apart", () => { |
| 95 | for (const kit of STARTER_KITS) { |
| 96 | assert.ok(kit.extensions.length > 0, kit.id); |
| 97 | for (const id of kit.extensions) assert.ok(extensionById(id), `${kit.id}: ${id}`); |
| 98 | } |
| 99 | const systems = FIRST_PARTY_EXTENSIONS.filter(isConnectedSystem).map((m) => m.id); |
| 100 | assert.deepEqual(systems, ["helpdesk-bridge", "crm-bridge", "erp-bridge"]); |
| 101 | assert.ok(FIRST_PARTY_EXTENSIONS.filter(isConnectedSystem).every((m) => m.bridges), "a connected system says what it bridges"); |
| 102 | }); |
| 103 | |
| 104 | test("the Marketplace is part of Apps, not a mode of its own", () => { |
| 105 | assert.equal(modeOf("/acme/-/marketplace", "acme"), "apps"); |
| 106 | assert.equal(modeOf("/acme/-/marketplace/extensions/mail", "acme"), "apps"); |
| 107 | assert.equal(modeOf("/acme/-/agents/templates", "acme"), "agents", "agent templates are Agents'"); |
| 108 | }); |