439 lines
14 KiB
TypeScript
439 lines
14 KiB
TypeScript
import { describe, expect, test } from "bun:test";
|
|
|
|
import { createDenClient } from "../src/app/lib/den";
|
|
import {
|
|
listAssignedConnectCapabilities,
|
|
} from "../src/react-app/domains/session/surface/connect-capability-inventory";
|
|
|
|
describe("assigned OpenWork Connect capability inventory", () => {
|
|
test("keeps assigned Workflows returned by Den", async () => {
|
|
const originalFetch = globalThis.fetch;
|
|
const fetchMock: typeof fetch = async () => new Response(JSON.stringify({
|
|
items: [{
|
|
marketplaceId: "marketplace_1",
|
|
pluginId: "plugin_1",
|
|
configObjectId: "script_1",
|
|
objectType: "workflow",
|
|
}],
|
|
}), {
|
|
headers: { "Content-Type": "application/json" },
|
|
status: 200,
|
|
});
|
|
Object.defineProperty(globalThis, "fetch", { configurable: true, value: fetchMock });
|
|
|
|
try {
|
|
const capabilities = await createDenClient({ baseUrl: "http://den.local", token: "token" })
|
|
.listAssignedMarketplaceCapabilities("organization_1");
|
|
|
|
expect(capabilities).toEqual([{
|
|
marketplaceId: "marketplace_1",
|
|
pluginId: "plugin_1",
|
|
configObjectId: "script_1",
|
|
objectType: "workflow",
|
|
}]);
|
|
} finally {
|
|
Object.defineProperty(globalThis, "fetch", { configurable: true, value: originalFetch });
|
|
}
|
|
});
|
|
|
|
test("normalizes legacy script inventory objects to Workflows", async () => {
|
|
const originalFetch = globalThis.fetch;
|
|
const fetchMock: typeof fetch = async () => new Response(JSON.stringify({
|
|
items: [{
|
|
marketplaceId: "marketplace_1",
|
|
pluginId: "plugin_1",
|
|
configObjectId: "script_1",
|
|
objectType: "script",
|
|
}],
|
|
}), {
|
|
headers: { "Content-Type": "application/json" },
|
|
status: 200,
|
|
});
|
|
Object.defineProperty(globalThis, "fetch", { configurable: true, value: fetchMock });
|
|
|
|
try {
|
|
const capabilities = await createDenClient({ baseUrl: "http://den.local", token: "token" })
|
|
.listAssignedMarketplaceCapabilities("organization_1");
|
|
|
|
expect(capabilities).toEqual([{
|
|
marketplaceId: "marketplace_1",
|
|
pluginId: "plugin_1",
|
|
configObjectId: "script_1",
|
|
objectType: "workflow",
|
|
}]);
|
|
} finally {
|
|
Object.defineProperty(globalThis, "fetch", { configurable: true, value: originalFetch });
|
|
}
|
|
});
|
|
|
|
test("returns active marketplace skills and MCPs with Connect provenance", async () => {
|
|
const inventory = await listAssignedConnectCapabilities({
|
|
organizationId: "org_1",
|
|
client: {
|
|
listAssignedMarketplaceCapabilities: async () => [
|
|
{
|
|
marketplaceId: "marketplace_1",
|
|
pluginId: "plugin_1",
|
|
configObjectId: "skill_1",
|
|
objectType: "skill",
|
|
},
|
|
{
|
|
marketplaceId: "marketplace_1",
|
|
pluginId: "plugin_1",
|
|
configObjectId: "mcp_1",
|
|
objectType: "mcp",
|
|
},
|
|
],
|
|
listOrgMarketplaces: async () => [
|
|
{
|
|
id: "marketplace_1",
|
|
name: "Team tools",
|
|
description: null,
|
|
status: "active",
|
|
pluginCount: 1,
|
|
updatedAt: null,
|
|
},
|
|
],
|
|
getOrgMarketplaceResolved: async () => ({
|
|
marketplace: {
|
|
id: "marketplace_1",
|
|
name: "Team tools",
|
|
description: null,
|
|
status: "active",
|
|
pluginCount: 1,
|
|
updatedAt: null,
|
|
},
|
|
plugins: [
|
|
{
|
|
id: "plugin_1",
|
|
name: "Support kit",
|
|
description: null,
|
|
status: "active",
|
|
memberCount: 2,
|
|
updatedAt: null,
|
|
componentCounts: { skill: 1, mcp: 1 },
|
|
cloudReadiness: {
|
|
state: "ready",
|
|
hasInstructional: true,
|
|
connections: [
|
|
{
|
|
id: "connection_1",
|
|
name: "Support search",
|
|
url: "https://support.example.test/mcp",
|
|
configObjectId: "mcp_1",
|
|
serverName: "support",
|
|
credentialMode: "shared",
|
|
connectedForMe: true,
|
|
},
|
|
],
|
|
},
|
|
},
|
|
],
|
|
}),
|
|
getOrgPluginResolved: async (_organizationId, plugin) => ({
|
|
plugin,
|
|
memberships: [
|
|
{
|
|
id: "membership_skill",
|
|
pluginId: plugin.id,
|
|
configObjectId: "skill_1",
|
|
configObject: {
|
|
id: "skill_1",
|
|
objectType: "skill",
|
|
title: "Escalate ticket",
|
|
description: "Prepare a support escalation.",
|
|
currentFileName: "SKILL.md",
|
|
currentFileExtension: "md",
|
|
currentRelativePath: "skills/escalate-ticket/SKILL.md",
|
|
status: "active",
|
|
updatedAt: null,
|
|
latestVersion: {
|
|
id: "version_skill",
|
|
rawSourceText: "# Escalate ticket",
|
|
normalizedPayloadJson: null,
|
|
sourceRevisionRef: null,
|
|
createdAt: null,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
id: "membership_mcp",
|
|
pluginId: plugin.id,
|
|
configObjectId: "mcp_1",
|
|
configObject: {
|
|
id: "mcp_1",
|
|
objectType: "mcp",
|
|
title: "Support MCP",
|
|
description: null,
|
|
currentFileName: "support.json",
|
|
currentFileExtension: "json",
|
|
currentRelativePath: "mcp/support.json",
|
|
status: "active",
|
|
updatedAt: null,
|
|
latestVersion: {
|
|
id: "version_mcp",
|
|
rawSourceText: null,
|
|
normalizedPayloadJson: {
|
|
mcpServers: {
|
|
support: {
|
|
url: "https://support.example.test/mcp",
|
|
headers: { Authorization: "Bearer ${SUPPORT_TOKEN}" },
|
|
},
|
|
},
|
|
},
|
|
sourceRevisionRef: null,
|
|
createdAt: null,
|
|
},
|
|
},
|
|
},
|
|
],
|
|
}),
|
|
},
|
|
});
|
|
|
|
expect(inventory.skills).toEqual([
|
|
expect.objectContaining({
|
|
name: "Escalate ticket",
|
|
trigger: "escalate-ticket",
|
|
origin: "openwork-connect",
|
|
marketplaceName: "Team tools",
|
|
pluginName: "Support kit",
|
|
connectCapabilityName: "plugin:plugin_1:skill_1",
|
|
}),
|
|
]);
|
|
expect(inventory.mcpServers).toEqual([
|
|
expect.objectContaining({
|
|
name: "Support MCP",
|
|
origin: "openwork-connect",
|
|
marketplaceName: "Team tools",
|
|
pluginName: "Support kit",
|
|
config: {
|
|
type: "remote",
|
|
url: "https://support.example.test/mcp",
|
|
},
|
|
}),
|
|
]);
|
|
expect(inventory.mcpStatuses[inventory.mcpServers[0]?.id ?? ""]).toEqual({ status: "connected" });
|
|
expect(inventory.plugins).toEqual([
|
|
expect.objectContaining({
|
|
pluginId: "plugin_1",
|
|
name: "Support kit",
|
|
marketplaceName: "Team tools",
|
|
}),
|
|
]);
|
|
expect(inventory.plugins[0]?.files.map((file) => file.objectType)).toEqual(["skill", "mcp"]);
|
|
});
|
|
|
|
test("only uses marketplaces visible to the member and ignores inactive objects", async () => {
|
|
let resolvedMarketplaceIds: string[] = [];
|
|
const inventory = await listAssignedConnectCapabilities({
|
|
organizationId: "org_1",
|
|
client: {
|
|
listAssignedMarketplaceCapabilities: async () => [
|
|
{
|
|
marketplaceId: "marketplace_active",
|
|
pluginId: "plugin_1",
|
|
configObjectId: "skill_inactive",
|
|
objectType: "skill",
|
|
},
|
|
],
|
|
listOrgMarketplaces: async () => [
|
|
{
|
|
id: "marketplace_active",
|
|
name: "Assigned",
|
|
description: null,
|
|
status: "active",
|
|
pluginCount: 1,
|
|
updatedAt: null,
|
|
},
|
|
{
|
|
id: "marketplace_archived",
|
|
name: "Archived",
|
|
description: null,
|
|
status: "archived",
|
|
pluginCount: 1,
|
|
updatedAt: null,
|
|
},
|
|
],
|
|
getOrgMarketplaceResolved: async (_organizationId, marketplaceId) => {
|
|
resolvedMarketplaceIds.push(marketplaceId);
|
|
return {
|
|
marketplace: {
|
|
id: marketplaceId,
|
|
name: "Assigned",
|
|
description: null,
|
|
status: "active",
|
|
pluginCount: 1,
|
|
updatedAt: null,
|
|
},
|
|
plugins: [
|
|
{
|
|
id: "plugin_1",
|
|
name: "Assigned plugin",
|
|
description: null,
|
|
status: "active",
|
|
memberCount: 1,
|
|
updatedAt: null,
|
|
componentCounts: { skill: 1 },
|
|
},
|
|
],
|
|
};
|
|
},
|
|
getOrgPluginResolved: async (_organizationId, plugin) => ({
|
|
plugin,
|
|
memberships: [
|
|
{
|
|
id: "membership_1",
|
|
pluginId: plugin.id,
|
|
configObjectId: "skill_inactive",
|
|
configObject: {
|
|
id: "skill_inactive",
|
|
objectType: "skill",
|
|
title: "Old skill",
|
|
description: null,
|
|
currentFileName: null,
|
|
currentFileExtension: null,
|
|
currentRelativePath: null,
|
|
status: "archived",
|
|
updatedAt: null,
|
|
latestVersion: null,
|
|
},
|
|
},
|
|
],
|
|
}),
|
|
},
|
|
});
|
|
|
|
expect(resolvedMarketplaceIds).toEqual(["marketplace_active"]);
|
|
expect(inventory.skills).toEqual([]);
|
|
expect(inventory.mcpServers).toEqual([]);
|
|
});
|
|
|
|
test("derives the trigger from Windows-style skill paths and omits it when the path is not a SKILL.md", async () => {
|
|
const marketplace = {
|
|
id: "marketplace_1",
|
|
name: "Team tools",
|
|
description: null,
|
|
status: "active" as const,
|
|
pluginCount: 1,
|
|
updatedAt: null,
|
|
};
|
|
const makeSkill = (id: string, title: string, currentRelativePath: string | null) => ({
|
|
id: `membership_${id}`,
|
|
pluginId: "plugin_1",
|
|
configObjectId: id,
|
|
configObject: {
|
|
id,
|
|
objectType: "skill" as const,
|
|
title,
|
|
description: null,
|
|
currentFileName: null,
|
|
currentFileExtension: null,
|
|
currentRelativePath,
|
|
status: "active" as const,
|
|
updatedAt: null,
|
|
latestVersion: null,
|
|
},
|
|
});
|
|
|
|
const inventory = await listAssignedConnectCapabilities({
|
|
organizationId: "org_1",
|
|
client: {
|
|
listAssignedMarketplaceCapabilities: async () => [
|
|
{
|
|
marketplaceId: "marketplace_1",
|
|
pluginId: "plugin_1",
|
|
configObjectId: "skill_win",
|
|
objectType: "skill",
|
|
},
|
|
{
|
|
marketplaceId: "marketplace_1",
|
|
pluginId: "plugin_1",
|
|
configObjectId: "skill_nomatch",
|
|
objectType: "skill",
|
|
},
|
|
],
|
|
listOrgMarketplaces: async () => [marketplace],
|
|
getOrgMarketplaceResolved: async () => ({
|
|
marketplace,
|
|
plugins: [
|
|
{
|
|
id: "plugin_1",
|
|
name: "Support kit",
|
|
description: null,
|
|
status: "active",
|
|
memberCount: 2,
|
|
updatedAt: null,
|
|
componentCounts: { skill: 2 },
|
|
},
|
|
],
|
|
}),
|
|
getOrgPluginResolved: async (_organizationId, plugin) => ({
|
|
plugin,
|
|
memberships: [
|
|
makeSkill("skill_win", "Windows skill", "skills\\escalate-ticket\\SKILL.md"),
|
|
makeSkill("skill_nomatch", "Loose skill", "docs/escalate-ticket/README.md"),
|
|
],
|
|
}),
|
|
},
|
|
});
|
|
|
|
const byName = Object.fromEntries(inventory.skills.map((skill) => [skill.name, skill]));
|
|
expect(byName["Windows skill"]?.trigger).toBe("escalate-ticket");
|
|
expect(byName["Loose skill"]?.trigger).toBeUndefined();
|
|
});
|
|
|
|
test("includes plugins from My Library even when no marketplace is assigned", async () => {
|
|
const inventory = await listAssignedConnectCapabilities({
|
|
organizationId: "org_1",
|
|
client: {
|
|
listAssignedMarketplaceCapabilities: async () => [],
|
|
listMeLibraryPlugins: async () => [
|
|
{ id: "plugin_mine", name: "Briefing kit", description: "My plugin" },
|
|
],
|
|
listOrgMarketplaces: async () => [],
|
|
getOrgMarketplaceResolved: async () => {
|
|
throw new Error("marketplace resolve should not run");
|
|
},
|
|
getOrgPluginResolved: async (_organizationId, plugin) => ({
|
|
plugin,
|
|
memberships: [
|
|
{
|
|
id: "membership_skill",
|
|
pluginId: plugin.id,
|
|
configObjectId: "skill_mine",
|
|
configObject: {
|
|
id: "skill_mine",
|
|
objectType: "skill",
|
|
title: "Customer briefing",
|
|
description: null,
|
|
currentFileName: "SKILL.md",
|
|
currentFileExtension: "md",
|
|
currentRelativePath: "skills/customer-briefing/SKILL.md",
|
|
status: "active",
|
|
updatedAt: null,
|
|
latestVersion: {
|
|
id: "version_skill",
|
|
rawSourceText: "# Brief",
|
|
normalizedPayloadJson: null,
|
|
sourceRevisionRef: null,
|
|
createdAt: null,
|
|
},
|
|
},
|
|
},
|
|
],
|
|
}),
|
|
},
|
|
});
|
|
|
|
expect(inventory.plugins).toEqual([
|
|
expect.objectContaining({
|
|
pluginId: "plugin_mine",
|
|
name: "Briefing kit",
|
|
marketplaceName: "Library",
|
|
}),
|
|
]);
|
|
expect(inventory.skills.map((skill) => skill.name)).toEqual(["Customer briefing"]);
|
|
});
|
|
});
|