wip(k1): checkpoint connection recovery rewrite
Capture the current unreleased K1 connection, recovery, lifecycle, viewer, and test work as a single known-bad baseline for subsequent fixes.
This commit is contained in:
@@ -0,0 +1,219 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { after, before, test } from "node:test";
|
||||
|
||||
import React, { createElement } from "react";
|
||||
import { renderToStaticMarkup } from "react-dom/server";
|
||||
import { createServer } from "vite";
|
||||
|
||||
let server;
|
||||
let DEVICE_PLUGIN_SELECTED_MODEL_STORAGE_KEY;
|
||||
let DevicePluginHostProvider;
|
||||
let commitPersistedDeviceModelId;
|
||||
let restorePersistedDeviceModelId;
|
||||
|
||||
const hostSourceUrl = new URL(
|
||||
"../src/core/device-plugins/DevicePluginHost.tsx",
|
||||
import.meta.url,
|
||||
);
|
||||
|
||||
before(async () => {
|
||||
server = await createServer({
|
||||
appType: "custom",
|
||||
logLevel: "silent",
|
||||
server: { middlewareMode: true },
|
||||
});
|
||||
({
|
||||
DEVICE_PLUGIN_SELECTED_MODEL_STORAGE_KEY,
|
||||
DevicePluginHostProvider,
|
||||
commitPersistedDeviceModelId,
|
||||
restorePersistedDeviceModelId,
|
||||
} = await server.ssrLoadModule("/src/core/device-plugins/DevicePluginHost.tsx"));
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await server?.close();
|
||||
});
|
||||
|
||||
function memoryStorage(seed = {}) {
|
||||
const values = new Map(Object.entries(seed));
|
||||
const calls = [];
|
||||
return {
|
||||
calls,
|
||||
getItem(key) {
|
||||
calls.push(["get", key]);
|
||||
return values.get(key) ?? null;
|
||||
},
|
||||
setItem(key, value) {
|
||||
calls.push(["set", key, value]);
|
||||
values.set(key, value);
|
||||
},
|
||||
removeItem(key) {
|
||||
calls.push(["remove", key]);
|
||||
values.delete(key);
|
||||
},
|
||||
value(key) {
|
||||
return values.get(key) ?? null;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function registryWith(...modelIds) {
|
||||
const registered = new Set(modelIds);
|
||||
return {
|
||||
resolveModel(modelId) {
|
||||
return registered.has(modelId) ? { model: { id: modelId } } : null;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function fakePlugin(modelId) {
|
||||
function RuntimeProvider({ activeModel, children }) {
|
||||
return createElement(
|
||||
"div",
|
||||
{ "data-active-model": activeModel?.id ?? "" },
|
||||
children,
|
||||
);
|
||||
}
|
||||
function ConnectionView() {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
manifest: {
|
||||
apiVersion: "missioncore.nodedc/v1alpha1",
|
||||
kind: "DevicePlugin",
|
||||
metadata: {
|
||||
id: "test.device.plugin",
|
||||
version: "1.0.0",
|
||||
displayName: "Test device",
|
||||
},
|
||||
spec: {
|
||||
hostApiRange: "v1alpha1",
|
||||
runtime: {
|
||||
backendEntrypoint: "test.device:plugin",
|
||||
isolation: "transitional-in-process",
|
||||
},
|
||||
permissions: [],
|
||||
actions: [{ id: "state.read", mutating: false, secretFields: [] }],
|
||||
models: [{
|
||||
id: modelId,
|
||||
vendor: "Test",
|
||||
displayName: "Test model",
|
||||
category: "test",
|
||||
description: "test",
|
||||
verified: true,
|
||||
capabilities: [],
|
||||
ui: {
|
||||
slot: "device.connection",
|
||||
componentKey: "test.connection",
|
||||
},
|
||||
}],
|
||||
},
|
||||
},
|
||||
RuntimeProvider,
|
||||
connectionViews: { "test.connection": ConnectionView },
|
||||
};
|
||||
}
|
||||
|
||||
test("persisted model restore admits only an id in the current plugin registry", () => {
|
||||
const key = DEVICE_PLUGIN_SELECTED_MODEL_STORAGE_KEY;
|
||||
const registry = registryWith("xgrids.lixelkity-k1");
|
||||
const valid = memoryStorage({ [key]: " xgrids.lixelkity-k1 " });
|
||||
|
||||
assert.equal(
|
||||
restorePersistedDeviceModelId(registry, valid),
|
||||
"xgrids.lixelkity-k1",
|
||||
);
|
||||
assert.equal(valid.calls.some(([operation]) => operation === "remove"), false);
|
||||
|
||||
for (const staleValue of ["removed.model", " "]) {
|
||||
const stale = memoryStorage({ [key]: staleValue });
|
||||
assert.equal(restorePersistedDeviceModelId(registry, stale), null);
|
||||
assert.equal(stale.value(key), null, "a stale model id must be cleared");
|
||||
}
|
||||
assert.equal(restorePersistedDeviceModelId(registry, null), null);
|
||||
});
|
||||
|
||||
test("fresh provider mount immediately activates the registry-validated persisted model", () => {
|
||||
const modelId = "test.model.one";
|
||||
const storage = memoryStorage({
|
||||
[DEVICE_PLUGIN_SELECTED_MODEL_STORAGE_KEY]: modelId,
|
||||
});
|
||||
const previousWindow = globalThis.window;
|
||||
globalThis.window = { localStorage: storage };
|
||||
try {
|
||||
const markup = renderToStaticMarkup(createElement(
|
||||
DevicePluginHostProvider,
|
||||
{ plugins: [fakePlugin(modelId)] },
|
||||
createElement("span", null, "runtime child"),
|
||||
));
|
||||
assert.match(markup, /data-active-model="test\.model\.one"/);
|
||||
} finally {
|
||||
if (previousWindow === undefined) delete globalThis.window;
|
||||
else globalThis.window = previousWindow;
|
||||
}
|
||||
});
|
||||
|
||||
test("successful selection commits and explicit clear removes the same durable key", () => {
|
||||
const storage = memoryStorage();
|
||||
commitPersistedDeviceModelId("xgrids.lixelkity-k1", storage);
|
||||
assert.equal(
|
||||
storage.value(DEVICE_PLUGIN_SELECTED_MODEL_STORAGE_KEY),
|
||||
"xgrids.lixelkity-k1",
|
||||
);
|
||||
|
||||
commitPersistedDeviceModelId(null, storage);
|
||||
assert.equal(storage.value(DEVICE_PLUGIN_SELECTED_MODEL_STORAGE_KEY), null);
|
||||
assert.deepEqual(
|
||||
storage.calls.slice(-1)[0],
|
||||
["remove", DEVICE_PLUGIN_SELECTED_MODEL_STORAGE_KEY],
|
||||
);
|
||||
});
|
||||
|
||||
test("unavailable browser storage fails closed without blocking host state", () => {
|
||||
const denied = {
|
||||
getItem() {
|
||||
throw new Error("storage denied");
|
||||
},
|
||||
setItem() {
|
||||
throw new Error("storage denied");
|
||||
},
|
||||
removeItem() {
|
||||
throw new Error("storage denied");
|
||||
},
|
||||
};
|
||||
assert.equal(
|
||||
restorePersistedDeviceModelId(registryWith("xgrids.lixelkity-k1"), denied),
|
||||
null,
|
||||
);
|
||||
assert.doesNotThrow(() =>
|
||||
commitPersistedDeviceModelId("xgrids.lixelkity-k1", denied)
|
||||
);
|
||||
assert.doesNotThrow(() => commitPersistedDeviceModelId(null, denied));
|
||||
});
|
||||
|
||||
test("host writes persistence only after plugin deactivation succeeds", () => {
|
||||
const source = readFileSync(hostSourceUrl, "utf8");
|
||||
const failedDeactivation = source.indexOf("if (!(await deactivate()))");
|
||||
const admittedState = source.indexOf("setSelectedModelId(nextModelId);");
|
||||
const durableCommit = source.indexOf(
|
||||
"commitPersistedDeviceModelId(nextModelId, selectionStorage);",
|
||||
admittedState,
|
||||
);
|
||||
|
||||
assert.ok(failedDeactivation >= 0);
|
||||
assert.ok(
|
||||
failedDeactivation < admittedState && admittedState < durableCommit,
|
||||
"failed deactivation branches must return before in-memory and durable commit",
|
||||
);
|
||||
assert.match(
|
||||
source,
|
||||
/if \(nextModelId === selectedModelId\) \{[\s\S]*?commitPersistedDeviceModelId\(nextModelId, selectionStorage\);[\s\S]*?return true;/,
|
||||
"explicit clear must remove stale persistence even from an already-empty host",
|
||||
);
|
||||
assert.match(
|
||||
source,
|
||||
/useState<string \| null>\(\(\) =>\s*restorePersistedDeviceModelId\(registry, selectionStorage\)/,
|
||||
"a fresh provider mount must restore before runtime providers receive activeModel",
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user