import { describe, expect, it, vi } from "vitest"; import type { PluginBridgeManifestContract } from "../contracts/pluginBridge"; import { createPluginBridgeDispatcher, createPluginBridgeHostContext, filterAllowedPermissions, parsePluginArtifactReference, validateBridgeExecutionRequest } from "./pluginBridgeHost"; const plugin: PluginBridgeManifestContract = { id: "game.example", declaredPermissions: ["server.read", "server.logs.read", "server.files.read", "server.artifacts.read", "server.remote.access", "ai.invoke"], bridgeActions: ["server.instances.read", "logs.query", "files.request", "artifacts.open", "remote.access.request", "ai.invoke"], pages: [ { key: "logs", title: "Logs", path: "/logs", permissions: ["server.logs.read", "server.files.read", "server.artifacts.read", "ai.invoke"], bridgeActions: ["logs.query", "files.request", "artifacts.open", "ai.invoke"] }, { key: "remote", title: "Remote", path: "/remote", permissions: ["server.remote.access"], bridgeActions: ["remote.access.request"] } ], aiPurposes: ["logs.diagnose"] }; describe("plugin bridge host utilities", () => { it("filters page permissions against manifest permissions", () => { expect(filterAllowedPermissions(plugin.declaredPermissions, ["server.logs.read", "server.artifacts.read"])).toEqual(["server.logs.read", "server.artifacts.read"]); }); it("creates safe host context without secret-bearing fields", () => { const context = createPluginBridgeHostContext({ plugin, routeKey: "logs", serverInstanceId: "server-1", themeTokens: { colorScheme: "dark", accentColor: "#22c55e" } }); expect(context).toEqual({ pluginId: "game.example", routeKey: "logs", serverInstanceId: "server-1", themeTokens: { colorScheme: "dark", accentColor: "#22c55e" }, permissions: ["server.logs.read", "server.files.read", "server.artifacts.read", "ai.invoke"], bridgeActions: ["logs.query", "files.request", "artifacts.open", "ai.invoke"], aiPurposes: ["logs.diagnose"] }); expect(context).not.toHaveProperty("apiKey"); expect(context).not.toHaveProperty("runCredential"); expect(context).not.toHaveProperty("hostPath"); }); it("builds bridge execution requests through the platform client", async () => { const context = createPluginBridgeHostContext({ plugin, routeKey: "logs", serverInstanceId: "server-1", themeTokens: { colorScheme: "dark", accentColor: "#22c55e" } }); const client = { executePluginBridge: vi.fn(async () => ({ requestId: "req-1", pluginId: "game.example", routeKey: "logs", serverInstanceId: "server-1", action: "logs.query", status: "ok", result: { entryCount: "0" } })) }; const dispatch = createPluginBridgeDispatcher(context, client); await expect(dispatch({ requestId: "req-1", action: "logs.query", payload: { logStreamId: "log-1" } })).resolves.toMatchObject({ status: "ok", result: { entryCount: "0" } }); expect(client.executePluginBridge).toHaveBeenCalledWith({ requestId: "req-1", pluginId: "game.example", routeKey: "logs", serverInstanceId: "server-1", action: "logs.query", aiPurpose: undefined, payload: { logStreamId: "log-1" } }); }); it("rejects denied, unsafe, and cancelled bridge execution locally", async () => { const context = createPluginBridgeHostContext({ plugin, routeKey: "logs", serverInstanceId: "server-1", themeTokens: { colorScheme: "dark", accentColor: "#22c55e" } }); expect(validateBridgeExecutionRequest(context, { requestId: "req-denied", action: "server.instances.read" })).toMatchObject({ code: "unsupported_action" }); expect( validateBridgeExecutionRequest(context, { requestId: "req-unsafe", action: "files.request", payload: { key: "/Users/tasia/.ssh/id_rsa" } }) ).toMatchObject({ code: "unsafe_payload" }); const client = { executePluginBridge: vi.fn() }; const controller = new AbortController(); controller.abort(); const dispatch = createPluginBridgeDispatcher(context, client); await expect(dispatch({ requestId: "req-cancel", action: "logs.query", payload: { logStreamId: "log-1" } }, controller.signal)).resolves.toMatchObject({ status: "cancelled", error: { code: "cancelled" } }); expect(client.executePluginBridge).not.toHaveBeenCalled(); }); it("allows mediated remote SQL execute payloads without direct connection material", () => { const context = createPluginBridgeHostContext({ plugin, routeKey: "remote", serverInstanceId: "server-1", themeTokens: { colorScheme: "dark", accentColor: "#22c55e" } }); expect(validateBridgeExecutionRequest(context, { requestId: "sql-1", action: "remote.access.request", payload: { capability: "remote.run.db.sqlite.execute", declarationKey: "sqlite-db", targetKey: "scum-db", idempotencyKey: "sql-1", "input.sqlText": "UPDATE prisoner SET stamina = 855 WHERE id = 'steam-123';" } })).toBeNull(); }); it("dispatches mediated AI requests without provider configuration", async () => { const context = createPluginBridgeHostContext({ plugin, routeKey: "logs", serverInstanceId: "server-1", themeTokens: { colorScheme: "dark", accentColor: "#22c55e" } }); expect(validateBridgeExecutionRequest(context, { requestId: "ai-denied", action: "ai.invoke", aiPurpose: "config.suggest" })).toMatchObject({ code: "ai_purpose_denied" }); const client = { executePluginBridge: vi.fn(async () => ({ requestId: "ai-1", pluginId: "game.example", routeKey: "logs", serverInstanceId: "server-1", action: "ai.invoke", status: "ok", result: { recommendation: "Mock AI recommendation", mocked: "true" } })) }; const dispatch = createPluginBridgeDispatcher(context, client); const response = await dispatch({ requestId: "ai-1", action: "ai.invoke", aiPurpose: "logs.diagnose", payload: { prompt: "Summarize logs" } }); expect(response).toMatchObject({ status: "ok", result: { mocked: "true" } }); const serialized = JSON.stringify(response); expect(serialized).not.toContain("apiKeyRef"); expect(serialized).not.toContain("rawApiKey"); expect(serialized).not.toContain("baseUrl"); }); it("parses safe artifact bridge references and rejects backend internals", () => { expect( parsePluginArtifactReference({ artifactId: "artifact-1", filename: "artifact-1.bin", contentType: "application/octet-stream", sizeBytes: "128", checksum: "sha256:abc", downloadUrl: "/api/v1/artifacts/artifact-1/content", expiresAt: "2026-07-03T00:15:00Z", rangeSupported: "true", chunkSizeBytes: "1048576", storageBehavior: "platform-memory-transfer-session" }) ).toMatchObject({ artifactId: "artifact-1", downloadUrl: "/api/v1/artifacts/artifact-1/content", rangeSupported: true }); expect( parsePluginArtifactReference({ artifactId: "artifact-1", filename: "/Users/tasia/artifact.bin", contentType: "application/octet-stream", sizeBytes: "128", checksum: "sha256:abc", downloadUrl: "storage://bucket/artifact-1", expiresAt: "2026-07-03T00:15:00Z", rangeSupported: "true", chunkSizeBytes: "1048576" }) ).toBeNull(); }); });