import { renderToStaticMarkup } from "react-dom/server"; import { describe, expect, it } from "vitest"; import hostSource from "./PluginPageHostPage.tsx?raw"; import type { GamePluginResponse } from "../api/types"; import type { PageComponentProps } from "../contracts/page"; import { capabilitiesForRoles } from "../contracts/workspace"; import type { OperationTracker } from "../stores/operations"; import { PluginPageHostPage } from "./PluginPageHostPage"; const operations: OperationTracker = { operations: [], begin: () => "operation-test", update: () => undefined, succeed: () => undefined, fail: () => undefined, isPending: () => false }; const plugin: GamePluginResponse = { id: "game.scum", name: "SCUM Server", version: "1.0.0", serverType: "scum", serverDisplayName: "SCUM Server", manifestRef: "artifact://manifests/game.scum/1.0.0", createFormSchemaRef: "schemas/create-form.schema.json", requiredRunCapabilities: [], declaredPermissions: ["server.read", "server.logs.read", "server.game-client.read", "server.game-client.command"], permissions: { ai: false, logs: true, files: false, jobs: true, artifacts: false, remoteAccess: false }, lifecycleActions: {}, bridgeActions: ["server.instances.read", "logs.query"], pages: [{ key: "files-config", title: "文件与配置", path: "/files-config", bundleKey: "scum-server-plugin", bundleVersion: "1.0.1", bundleIntegritySha256: "sha256:8de5ec67248be72a6fa47df5e6f8c98e10092ade99e6fc066ceeba678b119c64", permissions: ["server.game-client.read", "server.game-client.command", "server.logs.read"], bridgeActions: ["server.instances.read", "logs.query"] }], tags: ["scum"], aiPurposes: [], productionLifecycle: { operations: ["install", "enable", "disable", "upgrade", "rollback", "retire", "dependency-check"], dependencyPolicy: "required", approvalRequired: ["disable", "rollback", "retire"] }, gameClientBridge: { commands: [{ type: "announcement.send", title: "Send announcement", permission: "server.game-client.command", approvalLevel: "operator", payloadSchemaRef: "schemas/bridge/announcement.json", timeoutSeconds: 30, maxPayloadBytes: 4096 }], snapshots: [{ type: "companion.health", schemaVersion: "1", schemaRef: "schemas/bridge/health.json", keepForSeconds: 3600, maxRecords: 24 }], commandRetentionSeconds: 86400, maxCommands: 1000, pages: [{ pageKey: "files-config", commandTypes: ["announcement.send"], snapshotTypes: ["companion.health"] }] }, status: "installed" }; function props(serverId = "server-1"): PageComponentProps { const session = { id: "operator-1", displayName: "Operator", status: "active" as const, roles: ["platformAdmin" as const], capabilities: capabilitiesForRoles(["platformAdmin"]), profile: {}, source: "local" as const }; return { session, params: { pluginId: "game.scum", routeKey: "files-config", serverId }, operations, onNavigate: () => undefined, onLogout: async () => undefined, onProfileSave: async () => session, onThemePreferenceSave: async () => ({ userId: session.id, paletteId: "mecha-black", backgroundPresetId: "mecha-grid", persistence: "api", updatedAt: "2026-07-20T00:00:00Z" }) }; } describe("PluginPageHostPage", () => { it("renders a generic manifest-owned bundle declaration", () => { const html = renderToStaticMarkup(); expect(html).toContain("文件与配置"); expect(html).toContain("平台托管上下文"); expect(html).toContain("scum-server-plugin@1.0.1"); expect(html).toContain("完整性"); expect(html).toContain("返回服务器"); expect(html).not.toMatch(/sessionToken|componentKey|hostPath|dsn|runSocket|credential/i); }); it("does not mount a bundle without client-side availability validation", () => { const html = renderToStaticMarkup(); expect(html).toContain("未绑定服务器"); expect(html).toContain("正在校验并加载插件页面 bundle"); }); it("remains a manifest-driven host without SCUM component imports or game branches", () => { expect(hostSource).toContain("loadPluginPageBundle"); expect(hostSource).not.toMatch(/ScumFileConfigWorkbench|GamePlayerIntelligencePanel|GameGiftCatalogPanel|ScumMapTrajectoryPanel|game\.scum/); }); it("keeps file workspace callbacks stable across parent operational refreshes", () => { expect(hostSource).toContain("readyPluginRef.current = readyPlugin"); expect(hostSource).toContain("hostContextRef.current = hostContext"); expect(hostSource).toContain("}, [pluginId, serverId]);"); expect(hostSource).not.toContain("}, [hostContext, readyPlugin, serverId]);"); }); });