fix(k1): harden BLE discovery and bridge recovery
This commit is contained in:
@@ -19,6 +19,7 @@ let presentation;
|
||||
let operatorIntentGeneration;
|
||||
let configuration;
|
||||
let compatibility;
|
||||
let networkProvisionFailureMessage;
|
||||
|
||||
before(async () => {
|
||||
server = await createServer({
|
||||
@@ -62,6 +63,9 @@ before(async () => {
|
||||
({ localizeRuntimeMessage } = await server.ssrLoadModule(
|
||||
"@xgrids-k1/frontend/messages.ts",
|
||||
));
|
||||
({ networkProvisionFailureMessage } = await server.ssrLoadModule(
|
||||
"@xgrids-k1/frontend/useXgridsK1Runtime.ts",
|
||||
));
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
@@ -216,6 +220,7 @@ test("installed XGRIDS frontend manifest exposes the semantic v1alpha2 actions",
|
||||
assert.equal(xgridsK1Actions.acquisitionPrepare, "acquisition.prepare");
|
||||
assert.equal(xgridsK1Actions.acquisitionStart, "acquisition.start");
|
||||
assert.equal(xgridsK1Actions.acquisitionStop, "acquisition.stop");
|
||||
assert.equal(xgridsK1Actions.connectionVerify, "connection.verify");
|
||||
});
|
||||
|
||||
test("one operator action can open at most one K1 control session", () => {
|
||||
@@ -693,3 +698,56 @@ test("device mutations send explicit nested compatibility attestation", async ()
|
||||
assert.deepEqual(prepare.input.compatibility_attestation, attestation);
|
||||
assert.equal(prepare.input.project_name, "Mission 01");
|
||||
});
|
||||
|
||||
test("connection verification supports refresh and explicit read-only adoption", async () => {
|
||||
const originalFetch = globalThis.fetch;
|
||||
const calls = [];
|
||||
globalThis.fetch = async (path, init) => {
|
||||
calls.push({ path, init });
|
||||
return new Response(JSON.stringify({ state: { source_mode: "idle" } }), {
|
||||
status: 200,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
};
|
||||
const attestation = {
|
||||
firmware_version: "3.0.2",
|
||||
topology: "direct-lan",
|
||||
verification: "live-device-info",
|
||||
};
|
||||
|
||||
try {
|
||||
await xgridsK1Api.verifyConnection();
|
||||
await xgridsK1Api.verifyConnection({
|
||||
device_id: "fresh-ble-device",
|
||||
compatibility_attestation: attestation,
|
||||
});
|
||||
} finally {
|
||||
globalThis.fetch = originalFetch;
|
||||
}
|
||||
|
||||
assert.equal(calls.length, 2);
|
||||
assert.match(String(calls[0].path), /actions\/connection\.verify$/);
|
||||
assert.match(String(calls[1].path), /actions\/connection\.verify$/);
|
||||
assert.deepEqual(JSON.parse(calls[0].init.body), { input: {} });
|
||||
const adoption = JSON.parse(calls[1].init.body).input;
|
||||
assert.deepEqual(adoption, {
|
||||
device_id: "fresh-ble-device",
|
||||
compatibility_attestation: attestation,
|
||||
});
|
||||
assert.equal("ssid" in adoption, false);
|
||||
assert.equal("password" in adoption, false);
|
||||
assert.equal("connection_mode" in adoption, false);
|
||||
});
|
||||
|
||||
test("rejected network-profile writes use safe operator copy", () => {
|
||||
const message = networkProvisionFailureMessage({
|
||||
status: "failed",
|
||||
error: { code: "BleakGATTProtocolError" },
|
||||
});
|
||||
|
||||
assert.equal(
|
||||
message,
|
||||
"Сканер отклонил запись сетевого профиля. Результат изменения сети неизвестен; автоматический повтор запрещён. Проверьте текущее состояние K1 или подхватите существующее подключение без изменения настроек Wi‑Fi.",
|
||||
);
|
||||
assert.doesNotMatch(message, /Bleak|GATT|ATT/i);
|
||||
});
|
||||
|
||||
@@ -162,6 +162,30 @@ test("XGRIDS frontend is physically plugin-owned and split by operator pipeline"
|
||||
assert.match(spatialControls, /Повторить остановку/);
|
||||
});
|
||||
|
||||
test("K1 Bridge adoption remains one explicit read-only plugin action", () => {
|
||||
const provisioning = readFileSync(
|
||||
join(pluginFrontendRoot, "components/K1ProvisioningPipeline.tsx"),
|
||||
"utf8",
|
||||
);
|
||||
const selectionEffectStart = provisioning.indexOf("useEffect(() => {");
|
||||
const selectionEffectEnd = provisioning.indexOf(
|
||||
"useEffect(() => {",
|
||||
selectionEffectStart + 1,
|
||||
);
|
||||
const selectionEffect = provisioning.slice(selectionEffectStart, selectionEffectEnd);
|
||||
assert.match(provisioning, /connectionMode === "bridge"/);
|
||||
assert.match(provisioning, /Подхватить существующее подключение/);
|
||||
assert.match(provisioning, /pendingAction === "verify"/);
|
||||
assert.match(
|
||||
provisioning,
|
||||
/compatibility_attestation: profileSelectionForConnectionMode\("bridge"\)/,
|
||||
);
|
||||
assert.match(provisioning, /без изменения настроек Wi‑Fi/);
|
||||
assert.match(provisioning, /deviceSummary !== undefined/);
|
||||
assert.match(selectionEffect, /!state\.devices\.some/);
|
||||
assert.match(selectionEffect, /setSelectedDeviceId\(""\)/);
|
||||
});
|
||||
|
||||
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");
|
||||
|
||||
Reference in New Issue
Block a user