import assert from "node:assert/strict"; import { existsSync, readFileSync, readdirSync, statSync } from "node:fs"; import { fileURLToPath } from "node:url"; import { dirname, join, resolve } from "node:path"; import { after, before, test } from "node:test"; import { createServer } from "vite"; const testRoot = dirname(fileURLToPath(import.meta.url)); const controlStationRoot = resolve(testRoot, ".."); const repositoryRoot = resolve(controlStationRoot, "../.."); const coreSourceRoot = join(controlStationRoot, "src"); const pluginFrontendRoot = join(repositoryRoot, "plugins/xgrids-k1/frontend/src"); const legacyPluginRoot = join(coreSourceRoot, "device-plugins/xgrids-k1"); function sourceFiles(root) { return readdirSync(root).flatMap((entry) => { const path = join(root, entry); if (statSync(path).isDirectory()) return sourceFiles(path); return /\.(?:css|ts|tsx)$/.test(entry) ? [path] : []; }); } let server; let createDevicePluginRegistry; before(async () => { server = await createServer({ appType: "custom", logLevel: "silent", server: { middlewareMode: true }, }); ({ createDevicePluginRegistry } = await server.ssrLoadModule( "/src/core/device-plugins/registry.ts", )); }); after(async () => { await server?.close(); }); function syntheticPlugin({ pluginId, modelId, componentKey, connectionView, spatialControlsView, }) { return { manifest: { apiVersion: "missioncore.nodedc/v1alpha2", kind: "DevicePlugin", metadata: { id: pluginId, version: "1.0.0", displayName: pluginId }, spec: { hostApiRange: "v1alpha2", runtime: { backendEntrypoint: `${pluginId}:build`, isolation: "transitional-in-process" }, permissions: ["device.read"], actions: [{ id: "state.read", mutating: false, secretFields: [] }], compatibilityProfiles: [{ profileId: `${modelId}.profile.v1`, path: `profiles/${modelId}.json`, modelId, }], models: [{ id: modelId, vendor: pluginId, displayName: modelId, category: "Sensor", description: "Synthetic frontend contribution", verified: true, capabilities: [{ id: "device.read", label: "Read" }], ui: { slot: "device.connection", componentKey }, }], }, }, RuntimeProvider: ({ children }) => children, connectionViews: { [componentKey]: connectionView }, ...(spatialControlsView ? { SpatialControlsView: spatialControlsView } : {}), }; } test("each device plugin contributes its own connection pipeline component", () => { const alphaConnection = () => null; const betaConnection = () => null; const registry = createDevicePluginRegistry([ syntheticPlugin({ pluginId: "synthetic.alpha", modelId: "synthetic.alpha.sensor", componentKey: "alpha.connection", connectionView: alphaConnection, }), syntheticPlugin({ pluginId: "synthetic.beta", modelId: "synthetic.beta.sensor", componentKey: "beta.connection", connectionView: betaConnection, }), ]); assert.equal(registry.resolveModel("synthetic.alpha.sensor").ConnectionView, alphaConnection); assert.equal(registry.resolveModel("synthetic.beta.sensor").ConnectionView, betaConnection); assert.notEqual( registry.resolveModel("synthetic.alpha.sensor").ConnectionView, registry.resolveModel("synthetic.beta.sensor").ConnectionView, ); }); test("registry exposes an optional model-scoped spatial controls contribution", () => { const connectionView = () => null; const spatialControlsView = () => null; const registry = createDevicePluginRegistry([ syntheticPlugin({ pluginId: "synthetic.spatial", modelId: "synthetic.spatial.sensor", componentKey: "spatial.connection", connectionView, spatialControlsView, }), syntheticPlugin({ pluginId: "synthetic.connection-only", modelId: "synthetic.connection-only.sensor", componentKey: "connection-only.connection", connectionView, }), ]); assert.equal( registry.resolveModel("synthetic.spatial.sensor").SpatialControlsView, spatialControlsView, ); assert.equal( registry.resolveModel("synthetic.connection-only.sensor").SpatialControlsView, undefined, ); }); test("XGRIDS frontend is physically plugin-owned and split by operator pipeline", () => { if (existsSync(legacyPluginRoot)) { assert.deepEqual(readdirSync(legacyPluginRoot), []); } for (const relativePath of [ "plugin.ts", "runtimeContext.tsx", "styles.css", "components/K1ProvisioningPipeline.tsx", "components/K1AcquisitionPipeline.tsx", "components/K1SpatialControls.tsx", "components/K1Diagnostics.tsx", "projectName.ts", ]) { assert.equal(existsSync(join(pluginFrontendRoot, relativePath)), true, relativePath); } const spatialControls = readFileSync( join(pluginFrontendRoot, "components/K1SpatialControls.tsx"), "utf8", ); assert.match(spatialControls, /shouldRenderSpatialControls\(state\)/); assert.match(spatialControls, /cleanup_pending/); assert.match(spatialControls, /spatialActionFailure/); assert.match(spatialControls, /role="alert"/); assert.match(spatialControls, /Повторить остановку/); }); test("generic Control Station has one composition import and no K1 implementation knowledge", () => { const compositionPath = join(coreSourceRoot, "composition/devicePlugins.ts"); const composition = readFileSync(compositionPath, "utf8"); assert.match(composition, /from "@xgrids-k1\/frontend\/plugin"/); for (const path of sourceFiles(coreSourceRoot)) { if (path === compositionPath) continue; const source = readFileSync(path, "utf8"); assert.doesNotMatch(source, /xgrids|lixel|\bk1\b/i, path); } for (const path of sourceFiles(pluginFrontendRoot)) { const source = readFileSync(path, "utf8"); assert.doesNotMatch(source, /apps\/control-station|\.\.\/\.\.\/core|\.\.\/\.\.\/components/, path); } });