| 1 | import assert from "node:assert/strict"; |
| 2 | import { test } from "node:test"; |
| 3 | |
| 4 | import { FIRST_PARTY_EXTENSIONS, type ExtensionManifest, checkManifest, dataDisclosure, extensionFrameUrl, parseListing, versionOfTag } from "./marketplace.ts"; |
| 5 | import { isScope } from "./scopes.ts"; |
| 6 | |
| 7 | const published: ExtensionManifest = { |
| 8 | ...FIRST_PARTY_EXTENSIONS[0]!, |
| 9 | id: "standup", |
| 10 | status: "available", |
| 11 | source: { repo: "flagon-io/standup", tag: "v1.2.0" }, |
| 12 | version: "1.2.0", |
| 13 | }; |
| 14 | |
| 15 | test("g1t's own extensions are listed as coming, with manifests publishing would accept", () => { |
| 16 | const ids = new Set<string>(); |
| 17 | for (const extension of FIRST_PARTY_EXTENSIONS) { |
| 18 | assert.ok(!ids.has(extension.id), extension.id); |
| 19 | ids.add(extension.id); |
| 20 | assert.equal(extension.status, "soon", `${extension.id} isn't published yet`); |
| 21 | assert.equal(extension.source, null); |
| 22 | assert.equal(extension.pricing, null); |
| 23 | assert.equal(extension.publisher.tier, "official"); |
| 24 | const checked = checkManifest(extension, isScope); |
| 25 | assert.ok(checked.ok, `${extension.id}: ${checked.ok ? "" : checked.message}`); |
| 26 | } |
| 27 | }); |
| 28 | |
| 29 | test("publishing checks a manifest strictly", () => { |
| 30 | assert.ok(checkManifest(published, isScope).ok); |
| 31 | const refused = (changes: Partial<ExtensionManifest> | Record<string, unknown>, why: RegExp) => { |
| 32 | const checked = checkManifest({ ...published, ...changes }, isScope); |
| 33 | assert.equal(checked.ok, false, JSON.stringify(changes)); |
| 34 | if (!checked.ok) assert.match(checked.message, why); |
| 35 | }; |
| 36 | refused({ id: "Bad Id" }, /id/); |
| 37 | refused({ scopes: ["everything:admin"] }, /scope/); |
| 38 | refused({ source: null }, /source/); |
| 39 | refused({ domains: ["not a host"] }, /domains/); |
| 40 | refused({ runtime: "connected", domains: [] }, /declares the domains/); |
| 41 | refused({ publisher: { name: "anyone", tier: "community" } }, /Community extensions run on their publisher's servers/); |
| 42 | refused({ pricing: { monthly: 5 } }, /free/); |
| 43 | refused({ permissions: [] }, /permissions/); |
| 44 | refused({ ui: { entry: "index.html" } }, /ui.entry/); |
| 45 | refused({ bridges: " " }, /bridges/); |
| 46 | refused({ adds: { pages: [], agent_roles: [], tools: [], notifications: [] } }, /cards/); |
| 47 | // A community extension that runs on its own servers, declaring them, is fine. |
| 48 | assert.ok(checkManifest({ ...published, runtime: "connected", publisher: { name: "anyone", tier: "community" }, domains: ["api.example.com"] }, isScope).ok); |
| 49 | }); |
| 50 | |
| 51 | test("the install screen says where data goes", () => { |
| 52 | assert.equal(dataDisclosure({ domains: [] }), "Its data stays in g1t."); |
| 53 | assert.equal(dataDisclosure({ domains: ["api.acme.dev", "eu.acme.dev"] }), "Data leaves g1t to api.acme.dev, eu.acme.dev."); |
| 54 | assert.equal(dataDisclosure({ domains: [], bridges: "the CRM you connect" }), "Data leaves g1t to the CRM you connect."); |
| 55 | }); |
| 56 | |
| 57 | test("an extension's page loads from the user-content origin, by version", () => { |
| 58 | assert.equal(extensionFrameUrl(published, "https://g1tusercontent.com/"), "https://g1tusercontent.com/x/standup/1.2.0/index.html"); |
| 59 | assert.equal(extensionFrameUrl({ ...published, ui: null }, "https://g1tusercontent.com"), null); |
| 60 | assert.equal(extensionFrameUrl(FIRST_PARTY_EXTENSIONS[0]!, "https://g1tusercontent.com"), null, "nothing published, nothing to load"); |
| 61 | }); |
| 62 | |
| 63 | test("a tag publishes a version", () => { |
| 64 | assert.equal(versionOfTag("v1.4.0"), "1.4.0"); |
| 65 | assert.equal(versionOfTag("2.0.0-beta.1"), "2.0.0-beta.1"); |
| 66 | assert.equal(versionOfTag("latest"), null); |
| 67 | }); |
| 68 | |
| 69 | test("listings are referenced by kind and id", () => { |
| 70 | assert.deepEqual(parseListing("extension:on-call"), { kind: "extension", id: "on-call" }); |
| 71 | assert.equal(parseListing("app:on-call"), null); |
| 72 | assert.equal(parseListing("agent:engineering"), null, "agents start from templates in Agents, not the Marketplace"); |
| 73 | }); |