Files
NODEDC_MISSION_CORE/apps/control-station/test/k1ActiveStreamRecovery.test.mjs

978 lines
35 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 activeStreamForceFinishAuthority;
let activeStreamForceFinishAuthorityMatches;
let activeStreamRecoveredBrowserAuthority;
let activeStreamRecoveryPresentation;
let activeStreamRecoveryPresentationAuthority;
let activeStreamRecoveryOwnsPresentationDecision;
let exactActiveStreamRecoveryLineage;
let formatActiveStreamRecoveryElapsed;
let suppressGenericErrorDuringActiveStreamRecovery;
let isXgridsActiveStreamRecovery;
let K1AcquisitionPipeline;
let K1SpatialControlsView;
let runSpatialActiveStreamForceFinish;
let shouldRenderK1GenericRuntimeError;
let shouldRenderK1OperationalPanels;
let xgridsK1Actions;
let xgridsK1Api;
const hookSourceUrl = new URL(
"../../../plugins/xgrids-k1/frontend/src/useXgridsK1Runtime.ts",
import.meta.url,
);
const acquisitionSourceUrl = new URL(
"../../../plugins/xgrids-k1/frontend/src/components/K1AcquisitionPipeline.tsx",
import.meta.url,
);
const recoverySurfaceSourceUrl = new URL(
"../../../plugins/xgrids-k1/frontend/src/components/ActiveStreamRecoverySurface.tsx",
import.meta.url,
);
const spatialControlsSourceUrl = new URL(
"../../../plugins/xgrids-k1/frontend/src/components/K1SpatialControls.tsx",
import.meta.url,
);
const connectionSourceUrl = new URL(
"../../../plugins/xgrids-k1/frontend/src/XgridsK1Connection.tsx",
import.meta.url,
);
before(async () => {
server = await createServer({
appType: "custom",
logLevel: "silent",
server: { middlewareMode: true },
});
({
activeStreamForceFinishAuthority,
activeStreamForceFinishAuthorityMatches,
activeStreamRecoveredBrowserAuthority,
activeStreamRecoveryPresentation,
activeStreamRecoveryPresentationAuthority,
activeStreamRecoveryOwnsPresentationDecision,
exactActiveStreamRecoveryLineage,
formatActiveStreamRecoveryElapsed,
suppressGenericErrorDuringActiveStreamRecovery,
} = await server.ssrLoadModule("@xgrids-k1/frontend/activeStreamRecovery.ts"));
({ isXgridsActiveStreamRecovery, xgridsK1Api } = await server.ssrLoadModule(
"@xgrids-k1/frontend/api.ts",
));
({ xgridsK1Actions } = await server.ssrLoadModule(
"@xgrids-k1/frontend/manifest.ts",
));
({ K1AcquisitionPipeline } = await server.ssrLoadModule(
"@xgrids-k1/frontend/components/K1AcquisitionPipeline.tsx",
));
({
K1SpatialControlsView,
runSpatialActiveStreamForceFinish,
} = await server.ssrLoadModule(
"@xgrids-k1/frontend/components/K1SpatialControls.tsx",
));
({
shouldRenderK1GenericRuntimeError,
shouldRenderK1OperationalPanels,
} = await server.ssrLoadModule(
"@xgrids-k1/frontend/XgridsK1Connection.tsx",
));
});
after(async () => {
await server?.close();
});
function recoveryContract(overrides = {}) {
return {
schema_version: "missioncore.xgrids-k1-active-stream-recovery/v1",
state: "reconnecting",
generation: 7,
acquisition_id: "acquisition-recovery-001",
attempt: 3,
started_at_utc: "2026-08-11T19:31:00Z",
elapsed_ms: 12_400,
reason_code: "read-only-rebind-in-progress",
force_finish_allowed: true,
automatic_read_only_rebind: true,
automatic_command_retry: false,
start_performed: false,
stop_performed: false,
ble_operation_performed: false,
network_mutation_performed: false,
runtime_producer_generation: 11,
camera_recovery: "owned",
camera_media_state: "pending-first-media",
camera_media_ready: false,
camera_epoch: {
generation: 7,
init_committed: true,
init_committed_age_ms: 250,
first_media_committed: false,
first_media_committed_age_ms: null,
committed_media_segment_count: 0,
last_media_segment_age_ms: null,
},
...overrides,
};
}
function recoveryState(recoveryOverrides = {}, stateOverrides = {}) {
return {
snapshot_runtime_id: "snapshot-runtime-recovery-001",
snapshot_revision: 43,
producer_generation: 11,
phase: "reconnecting",
source_mode: "live",
acquisition: {
acquisition_id: "acquisition-recovery-001",
device_id: "device-k1-001",
device_session_id: "device-session-001",
compatibility_profile_id: "xgrids.lixelkity-k1.fw-3.0.2.local-network.v2",
control_mode: "plugin-commanded",
requested_streams: ["spatial.point-cloud.live"],
target_host: "127.0.0.1",
duration_seconds: 0,
evidence_policy: "required",
state: "acquiring",
state_revision: 9,
cleanup_pending: false,
},
connection_recovery: recoveryContract(recoveryOverrides),
...stateOverrides,
};
}
function coldRestartPrePclState() {
const state = recoveryState({
generation: 1,
acquisition_id: "acquisition-before-backend-restart-001",
attempt: 0,
started_at_utc: "2026-08-14T00:31:00Z",
elapsed_ms: 450,
reason_code: "restart-receiver-awaiting-first-pcl",
runtime_producer_generation: 1,
camera_recovery: "inactive",
camera_media_state: "inactive",
camera_media_ready: false,
camera_epoch: null,
}, {
snapshot_runtime_id: "snapshot-runtime-after-cold-restart-001",
snapshot_revision: 2,
producer_generation: 1,
acquisition: {
...recoveryState().acquisition,
acquisition_id: "acquisition-before-backend-restart-001",
state: "awaiting_external_start",
state_revision: 4,
},
camera_preview: {
activation_admission: {
state: "waiting-for-first-authoritative-pcl",
basis: "post-rerun-publish-pcl-frame",
runtime_producer_generation: 1,
device_command_sent: false,
},
},
});
return state;
}
function coldRestartRecoveredState() {
const state = coldRestartPrePclState();
return {
...state,
snapshot_revision: state.snapshot_revision + 1,
phase: "live",
acquisition: {
...state.acquisition,
state: "acquiring",
state_revision: state.acquisition.state_revision + 1,
},
connection_recovery: {
...state.connection_recovery,
state: "recovered",
elapsed_ms: null,
reason_code: null,
force_finish_allowed: false,
camera_recovery: "owned",
camera_media_state: "pending-epoch",
camera_media_ready: false,
camera_epoch: null,
},
camera_preview: {
activation_admission: {
state: "activating",
basis: "post-rerun-publish-pcl-frame",
runtime_producer_generation: 1,
device_command_sent: false,
},
},
};
}
function controller(state, overrides = {}) {
return {
state,
pendingAction: null,
error: null,
physicalStopIntentSpent: false,
physicalStopInFlight: false,
closeApplicationControlSession: async () => false,
prepareCanonicalAcquisition: async () => false,
startPreparedAcquisition: async () => false,
startReplay: async () => false,
stop: async () => false,
stopLocalReceiver: async () => false,
forceFinishActiveStreamLocally: async () => false,
abort: async () => false,
...overrides,
};
}
function renderPipeline(state, overrides = {}) {
return renderToStaticMarkup(createElement(K1AcquisitionPipeline, {
controller: controller(state, overrides),
desiredConnectionMode: "bridge",
openSpatialScene() {},
activateAutomaticSpatialSource() {},
}));
}
function renderSpatialControls(state, overrides = {}) {
return renderToStaticMarkup(createElement(K1SpatialControlsView, {
controller: controller(state, overrides),
}));
}
function buttonsWithText(markup, text) {
return (markup.match(/<button\b[\s\S]*?<\/button>/g) ?? [])
.filter((button) => button.includes(text));
}
function sourceSlice(source, startMarker, endMarker) {
const start = source.indexOf(startMarker);
const end = source.indexOf(endMarker, start + startMarker.length);
assert.notEqual(start, -1, `missing source marker: ${startMarker}`);
assert.notEqual(end, -1, `missing source marker: ${endMarker}`);
return source.slice(start, end);
}
test("active recovery contract is strict about all no-write invariants", () => {
assert.equal(isXgridsActiveStreamRecovery(recoveryContract()), true);
for (const field of [
"automatic_command_retry",
"start_performed",
"stop_performed",
"ble_operation_performed",
"network_mutation_performed",
]) {
assert.equal(
isXgridsActiveStreamRecovery(recoveryContract({ [field]: true })),
false,
field,
);
}
assert.equal(
isXgridsActiveStreamRecovery(recoveryContract({ state: "retrying-command" })),
false,
);
assert.equal(
isXgridsActiveStreamRecovery(recoveryContract({ elapsed_ms: -1 })),
false,
);
const missingMediaState = recoveryContract();
delete missingMediaState.camera_media_state;
assert.equal(isXgridsActiveStreamRecovery(missingMediaState), false);
assert.equal(
isXgridsActiveStreamRecovery(recoveryContract({ camera_media_ready: true })),
false,
);
assert.equal(
isXgridsActiveStreamRecovery(recoveryContract({
camera_media_state: "ready",
camera_media_ready: true,
camera_epoch: {
...recoveryContract().camera_epoch,
generation: 0,
first_media_committed: true,
first_media_committed_age_ms: 1,
committed_media_segment_count: 1,
last_media_segment_age_ms: 1,
},
})),
false,
);
});
test("cold restart before first PCL owns exact reconnect UI without camera or physical actions", () => {
const state = coldRestartPrePclState();
assert.equal(isXgridsActiveStreamRecovery(state.connection_recovery), true);
const lineage = exactActiveStreamRecoveryLineage(state);
assert.deepEqual(lineage && {
runtime: lineage.snapshotRuntimeId,
acquisition: lineage.acquisitionId,
revision: lineage.acquisitionStateRevision,
recovery: lineage.recoveryGeneration,
producer: lineage.runtimeProducerGeneration,
}, {
runtime: "snapshot-runtime-after-cold-restart-001",
acquisition: "acquisition-before-backend-restart-001",
revision: 4,
recovery: 1,
producer: 1,
});
assert.notEqual(activeStreamRecoveryPresentationAuthority(state), null);
assert.notEqual(activeStreamForceFinishAuthority(state), null);
assert.equal(activeStreamRecoveredBrowserAuthority(state), null);
assert.equal(state.connection_recovery.camera_recovery, "inactive");
assert.equal(state.connection_recovery.camera_media_state, "inactive");
assert.equal(state.connection_recovery.camera_media_ready, false);
assert.equal(state.connection_recovery.camera_epoch, null);
assert.deepEqual(state.camera_preview.activation_admission, {
state: "waiting-for-first-authoritative-pcl",
basis: "post-rerun-publish-pcl-frame",
runtime_producer_generation: 1,
device_command_sent: false,
});
const pipeline = renderPipeline(state);
assert.match(pipeline, /Восстанавливаем соединение/);
assert.match(pipeline, /START, STOP, Bluetooth и настройки устройства не отправляются/);
assert.equal(buttonsWithText(pipeline, "Прервать соединение").length, 1);
assert.equal(buttonsWithText(pipeline, "Запустить приём").length, 0);
assert.equal(buttonsWithText(pipeline, "Остановить устройство и запись").length, 0);
assert.equal(buttonsWithText(pipeline, "Остановить сканирование").length, 0);
assert.doesNotMatch(
pipeline,
/СВЯЗЬ ВОССТАНОВЛЕНА|Связь восстановлена · приём продолжается|Продолжаем тот же приём/,
);
const spatial = renderSpatialControls(state);
assert.match(spatial, /Восстанавливаем соединение/);
assert.equal(buttonsWithText(spatial, "Прервать соединение").length, 1);
assert.doesNotMatch(
spatial,
/Остановить устройство|Остановить K1|Завершить локальный приём/,
);
});
test("cold restart first PCL preserves lineage and renders recovered continuation copy", () => {
const beforePcl = coldRestartPrePclState();
const state = coldRestartRecoveredState();
assert.equal(isXgridsActiveStreamRecovery(state.connection_recovery), true);
assert.equal(
state.connection_recovery.acquisition_id,
beforePcl.connection_recovery.acquisition_id,
);
assert.equal(
state.connection_recovery.generation,
beforePcl.connection_recovery.generation,
);
assert.equal(
state.connection_recovery.runtime_producer_generation,
beforePcl.connection_recovery.runtime_producer_generation,
);
const lineage = exactActiveStreamRecoveryLineage(state);
const browserAuthority = activeStreamRecoveredBrowserAuthority(state);
assert.deepEqual(lineage && {
runtime: lineage.snapshotRuntimeId,
acquisition: lineage.acquisitionId,
revision: lineage.acquisitionStateRevision,
recovery: lineage.recoveryGeneration,
producer: lineage.runtimeProducerGeneration,
}, {
runtime: "snapshot-runtime-after-cold-restart-001",
acquisition: "acquisition-before-backend-restart-001",
revision: 5,
recovery: 1,
producer: 1,
});
assert.deepEqual(browserAuthority, lineage);
assert.equal(activeStreamRecoveryPresentationAuthority(state), null);
assert.equal(activeStreamForceFinishAuthority(state), null);
assert.equal(state.connection_recovery.camera_recovery, "owned");
assert.equal(state.camera_preview.activation_admission.device_command_sent, false);
const pipeline = renderPipeline(state);
assert.doesNotMatch(pipeline, /Восстанавливаем соединение|Прервать соединение/);
assert.match(pipeline, /СВЯЗЬ ВОССТАНОВЛЕНА · АКТИВНЫЙ ПРИЁМ/);
assert.match(pipeline, /Связь восстановлена · приём продолжается/);
assert.match(pipeline, /Продолжаем тот же приём без нового START/);
assert.doesNotMatch(pipeline, /Назовите проект и запустите приём|Запустить приём/);
});
test("force-finish authority requires exact runtime, acquisition, revision and generations", () => {
const state = recoveryState();
const authority = activeStreamForceFinishAuthority(state);
assert.deepEqual(authority && {
snapshotRuntimeId: authority.snapshotRuntimeId,
acquisitionId: authority.acquisitionId,
acquisitionStateRevision: authority.acquisitionStateRevision,
recoveryGeneration: authority.recoveryGeneration,
runtimeProducerGeneration: authority.runtimeProducerGeneration,
}, {
snapshotRuntimeId: "snapshot-runtime-recovery-001",
acquisitionId: "acquisition-recovery-001",
acquisitionStateRevision: 9,
recoveryGeneration: 7,
runtimeProducerGeneration: 11,
});
assert.equal(activeStreamForceFinishAuthorityMatches(authority, state), true);
const staleCases = [
(() => {
const value = structuredClone(state);
value.snapshot_runtime_id = "snapshot-runtime-recovery-002";
return value;
})(),
(() => {
const value = structuredClone(state);
value.acquisition.acquisition_id = "acquisition-recovery-002";
return value;
})(),
(() => {
const value = structuredClone(state);
value.acquisition.state_revision += 1;
return value;
})(),
(() => {
const value = structuredClone(state);
value.connection_recovery.generation += 1;
return value;
})(),
(() => {
const value = structuredClone(state);
value.producer_generation += 1;
return value;
})(),
];
for (const stale of staleCases) {
assert.equal(activeStreamForceFinishAuthorityMatches(authority, stale), false);
}
assert.equal(
activeStreamForceFinishAuthority(recoveryState({ state: "recovered", force_finish_allowed: false })),
null,
);
assert.equal(
activeStreamForceFinishAuthority(recoveryState({ force_finish_allowed: false })),
null,
);
});
test("reconnecting presentation is exact and owns stale supervisor projection", () => {
const state = recoveryState({ force_finish_allowed: false });
const authority = activeStreamRecoveryPresentationAuthority(state);
assert.deepEqual(authority && {
runtime: authority.snapshotRuntimeId,
acquisition: authority.acquisitionId,
producer: authority.runtimeProducerGeneration,
recovery: authority.recoveryGeneration,
}, {
runtime: "snapshot-runtime-recovery-001",
acquisition: "acquisition-recovery-001",
producer: 11,
recovery: 7,
});
assert.equal(activeStreamRecoveryOwnsPresentationDecision(state), true);
const wrongProducer = structuredClone(state);
wrongProducer.producer_generation += 1;
assert.equal(activeStreamRecoveryPresentationAuthority(wrongProducer), null);
assert.equal(
activeStreamRecoveryOwnsPresentationDecision(wrongProducer),
true,
"a valid reconnect contract must block stale ordinary data projection",
);
for (const recoveryStateName of [
"blocked",
"standby",
"fault",
"force-finishing",
"force-finished",
]) {
const terminal = recoveryState({ state: recoveryStateName });
assert.equal(activeStreamRecoveryPresentationAuthority(terminal), null);
assert.equal(activeStreamRecoveryOwnsPresentationDecision(terminal), true);
}
assert.equal(
activeStreamRecoveryOwnsPresentationDecision(recoveryState({ state: "recovered" })),
false,
);
});
test("recovered browser carryover keeps exact lineage without restoring recovery controls", () => {
const state = recoveryState({
state: "recovered",
force_finish_allowed: false,
elapsed_ms: null,
reason_code: null,
}, {
phase: "live",
});
const authority = activeStreamRecoveredBrowserAuthority(state);
assert.deepEqual(authority && {
runtime: authority.snapshotRuntimeId,
acquisition: authority.acquisitionId,
revision: authority.acquisitionStateRevision,
producer: authority.runtimeProducerGeneration,
recovery: authority.recoveryGeneration,
}, {
runtime: "snapshot-runtime-recovery-001",
acquisition: "acquisition-recovery-001",
revision: 9,
producer: 11,
recovery: 7,
});
assert.equal(activeStreamRecoveryPresentation(state), null);
assert.equal(activeStreamForceFinishAuthority(state), null);
const staleProducer = structuredClone(state);
staleProducer.producer_generation += 1;
assert.equal(activeStreamRecoveredBrowserAuthority(staleProducer), null);
assert.equal(
activeStreamRecoveredBrowserAuthority(recoveryState({
state: "recovered",
camera_recovery: "blocked",
force_finish_allowed: false,
}, { phase: "live" })),
null,
);
});
test("only an exact reconnecting lineage suppresses the generic red error", () => {
const state = recoveryState();
assert.equal(suppressGenericErrorDuringActiveStreamRecovery(state, null), true);
assert.equal(
shouldRenderK1GenericRuntimeError("Ошибка локального приёмника", false, state, null),
false,
);
assert.equal(
suppressGenericErrorDuringActiveStreamRecovery(state, "force-finish"),
false,
);
assert.equal(
shouldRenderK1GenericRuntimeError(
"Локальное завершение не выполнено",
false,
state,
"force-finish",
),
true,
"a failed explicit local finish must keep the generic error banner visible",
);
const wrongProducer = structuredClone(state);
wrongProducer.producer_generation += 1;
assert.equal(suppressGenericErrorDuringActiveStreamRecovery(wrongProducer), false);
const wrongAcquisition = structuredClone(state);
wrongAcquisition.connection_recovery.acquisition_id = "acquisition-stale";
assert.equal(suppressGenericErrorDuringActiveStreamRecovery(wrongAcquisition), false);
const missingRuntime = structuredClone(state);
delete missingRuntime.snapshot_runtime_id;
assert.equal(suppressGenericErrorDuringActiveStreamRecovery(missingRuntime), false);
const blocked = recoveryState({ state: "blocked" });
assert.equal(suppressGenericErrorDuringActiveStreamRecovery(blocked), false);
const nonOwned = recoveryState({ automatic_read_only_rebind: false });
assert.equal(suppressGenericErrorDuringActiveStreamRecovery(nonOwned), false);
const inactiveCases = [
recoveryState({}, { phase: "error" }),
recoveryState({}, { source_mode: "idle" }),
recoveryState({}, {
acquisition: {
...state.acquisition,
state: "failed",
},
}),
];
for (const inactive of inactiveCases) {
assert.equal(
suppressGenericErrorDuringActiveStreamRecovery(inactive, null),
false,
"stale recovery projection must fail open to the error banner",
);
}
});
test("reconnecting presentation is neutral, timed and exposes explicit local finish", () => {
const state = recoveryState();
const presentation = activeStreamRecoveryPresentation(state);
assert.equal(presentation?.state, "reconnecting");
assert.equal(presentation?.tone, "neutral");
assert.equal(presentation?.progressLabel, "Попытка 3 · 12 с");
assert.equal(presentation?.forceFinishAvailable, true);
const markup = renderPipeline(state);
assert.match(markup, /Восстанавливаем соединение/);
assert.match(markup, /Попытка 3 · 12 с/);
assert.match(markup, /class="nodedc-activity-indicator/);
assert.equal(buttonsWithText(markup, "Прервать соединение").length, 1);
assert.match(markup, /START, STOP, Bluetooth и настройки устройства не отправляются/);
assert.doesNotMatch(markup, /Ошибка локального приёмника/);
assert.equal(shouldRenderK1OperationalPanels(state), true);
});
test("spatial scene owns the same recovery spinner and explicit local finish", () => {
const markup = renderSpatialControls(recoveryState());
assert.match(markup, /Восстанавливаем соединение/);
assert.match(markup, /Попытка 3 · 12 с/);
assert.match(markup, /class="nodedc-activity-indicator/);
assert.match(markup, /data-recovery-state="reconnecting"/);
assert.equal(buttonsWithText(markup, "Прервать соединение").length, 1);
assert.match(markup, /локальный front\/back-приём/);
assert.doesNotMatch(
markup,
/Остановить устройство|Остановить K1|Завершить локальный приём/,
"recovery must not expose canonical STOP or the generic receiver stop",
);
});
test("spatial blocked and fault recovery copy is terminal and truthful", () => {
const blocked = renderSpatialControls(recoveryState({
state: "blocked",
reason_code: "exact-binding-changed",
}));
assert.match(blocked, /Связь не восстановлена/);
assert.match(blocked, /Восстановление остановлено/);
assert.doesNotMatch(blocked, /class="nodedc-activity-indicator/);
assert.equal(buttonsWithText(blocked, "Прервать соединение").length, 1);
const fault = renderSpatialControls(recoveryState({
state: "fault",
force_finish_allowed: false,
reason_code: "active-stream-recovery-system-error",
}));
assert.match(fault, /K1 сообщил об ошибке/);
assert.match(fault, /Автоматических команд и повторов нет/);
assert.equal(buttonsWithText(fault, "Прервать соединение").length, 0);
});
test("spatial recovery interaction routes only to exact local force-finish", async () => {
let forceFinishCalls = 0;
const current = recoveryState();
const invoked = await runSpatialActiveStreamForceFinish({
state: current,
forceFinishActiveStreamLocally: async () => {
forceFinishCalls += 1;
return true;
},
});
assert.equal(invoked, true);
assert.equal(forceFinishCalls, 1);
const stale = structuredClone(current);
stale.connection_recovery.runtime_producer_generation += 1;
const rejected = await runSpatialActiveStreamForceFinish({
state: stale,
forceFinishActiveStreamLocally: async () => {
forceFinishCalls += 1;
return true;
},
});
assert.equal(rejected, false);
assert.equal(forceFinishCalls, 1, "stale lineage must not dispatch any action");
});
test("spatial force-finish pending owns the surface without a second action", () => {
const markup = renderSpatialControls(
recoveryState({
state: "force-finishing",
acquisition_id: null,
force_finish_allowed: false,
automatic_read_only_rebind: false,
camera_recovery: "inactive",
}),
{ pendingAction: "force-finish" },
);
assert.match(markup, /Завершение локального приёма/);
assert.match(markup, /data-recovery-state="force-finishing"/);
assert.match(markup, /Команда STOP устройству не отправляется/);
assert.equal(buttonsWithText(markup, "Прервать соединение").length, 0);
});
test("blocked, camera-blocked, standby and fault copy stay truthful", () => {
const blocked = recoveryState({ state: "blocked", reason_code: "exact-binding-changed" });
const blockedMarkup = renderPipeline(blocked);
assert.match(blockedMarkup, /Связь не восстановлена/);
assert.match(blockedMarkup, /Восстановление остановлено/);
assert.doesNotMatch(blockedMarkup, /class="nodedc-activity-indicator/);
assert.equal(buttonsWithText(blockedMarkup, "Прервать соединение").length, 1);
const cameraBlocked = recoveryState({
state: "blocked",
camera_recovery: "blocked",
reason_code: "camera-recovery-failed",
});
assert.match(renderPipeline(cameraBlocked), /Видеопоток не восстановлен/);
const standby = recoveryState({
state: "standby",
force_finish_allowed: false,
reason_code: "device-reported-standby",
});
const standbyMarkup = renderPipeline(standby);
assert.match(standbyMarkup, /Устройство перешло в ожидание/);
assert.match(standbyMarkup, /без команды STOP/);
assert.equal(buttonsWithText(standbyMarkup, "Прервать соединение").length, 0);
assert.equal(shouldRenderK1OperationalPanels(standby), true);
const fault = recoveryState({
state: "fault",
force_finish_allowed: false,
reason_code: "active-stream-recovery-system-error",
});
const faultMarkup = renderPipeline(fault);
assert.match(faultMarkup, /K1 сообщил об ошибке/);
assert.match(faultMarkup, /Автоматических команд и повторов нет/);
assert.equal(buttonsWithText(faultMarkup, "Прервать соединение").length, 0);
assert.equal(shouldRenderK1OperationalPanels(fault), true);
});
test("recovered active lineage renders the continued session and one exact STOP", () => {
const state = recoveryState({
state: "recovered",
force_finish_allowed: false,
elapsed_ms: null,
reason_code: null,
}, {
phase: "live",
compatibility: {
vendor_writes_enabled: true,
permitted_mode: "active-control",
},
application_control_session: {
session_generation: 5,
state_revision: 8,
state: "scanning",
can_stop: true,
control_socket_open: true,
},
connection_policy: {
schema_version: "missioncore.xgrids-k1-connection-policy/v1",
facts: { retained_context_is_presence: false },
allowed_actions: ["stop-acquisition"],
actions: {
"stop-acquisition": {
allowed: true,
reason_codes: [],
target_source: "connection-supervisor",
required_transport_ref: null,
required_connection_mode: null,
requires_live_gatt_validation: false,
automatic_retry: false,
},
},
},
});
assert.equal(activeStreamRecoveryPresentation(state), null);
const markup = renderPipeline(state);
assert.doesNotMatch(markup, /Восстанавливаем соединение|Прервать соединение/);
assert.match(markup, /СВЯЗЬ ВОССТАНОВЛЕНА · АКТИВНЫЙ ПРИЁМ/);
assert.match(markup, /Связь восстановлена · приём продолжается/);
assert.match(markup, /Продолжаем тот же приём без нового START/);
assert.doesNotMatch(markup, /Назовите проект и запустите приём|Запустить приём/);
const stopButtons = buttonsWithText(markup, "Остановить устройство и запись");
assert.equal(stopButtons.length, 1);
assert.doesNotMatch(stopButtons[0], /\bdisabled(?:=|\s|>)/);
});
test("stale recovered marker on an idle released runtime fails closed to idle UI", () => {
const state = recoveryState({
state: "recovered",
force_finish_allowed: false,
elapsed_ms: null,
reason_code: null,
}, {
phase: "idle",
source_mode: "idle",
acquisition: {
...recoveryState().acquisition,
state: "completed",
cleanup_pending: false,
},
application_control_session: {
session_generation: 5,
state_revision: 9,
state: "completed",
can_stop: false,
control_socket_open: false,
},
});
assert.equal(activeStreamRecoveryPresentation(state), null);
const markup = renderPipeline(state);
assert.doesNotMatch(
markup,
/СВЯЗЬ ВОССТАНОВЛЕНА|Связь восстановлена · приём продолжается|Продолжаем тот же приём/,
);
assert.match(markup, /Назовите проект и запустите приём/);
assert.equal(buttonsWithText(markup, "Остановить устройство и запись").length, 0);
assert.equal(buttonsWithText(markup, "Остановить сканирование").length, 0);
});
test("force-finishing shows one local-only pending owner and no second action", () => {
const state = recoveryState({
state: "force-finishing",
acquisition_id: null,
force_finish_allowed: false,
automatic_read_only_rebind: false,
runtime_producer_generation: 11,
camera_recovery: "inactive",
});
const markup = renderPipeline(state, { pendingAction: "force-finish" });
assert.match(markup, /Завершение локального приёма/);
assert.match(markup, /Команда STOP устройству не отправляется/);
assert.match(markup, /class="nodedc-activity-indicator/);
assert.equal(buttonsWithText(markup, "Прервать соединение").length, 0);
});
test("elapsed presentation is deterministic", () => {
assert.equal(formatActiveStreamRecoveryElapsed(null), null);
assert.equal(formatActiveStreamRecoveryElapsed(-1), null);
assert.equal(formatActiveStreamRecoveryElapsed(999), "0 с");
assert.equal(formatActiveStreamRecoveryElapsed(59_999), "59 с");
assert.equal(formatActiveStreamRecoveryElapsed(60_000), "1 мин");
assert.equal(formatActiveStreamRecoveryElapsed(125_900), "2 мин 5 с");
});
test("force-finish manifest/API sends the exact fenced local-only request", async () => {
assert.equal(
xgridsK1Actions.acquisitionForceFinishLocal,
"acquisition.force-finish-local",
);
const request = {
expected_snapshot_runtime_id: "snapshot-runtime-recovery-001",
acquisition_id: "acquisition-recovery-001",
expected_state_revision: 9,
expected_recovery_generation: 7,
operator_confirmed: true,
operation_id: "op-00000000-0000-4000-8000-000000000321",
idempotency_key:
"acquisition.force-finish-local:op-00000000-0000-4000-8000-000000000321",
deadline_seconds: 30,
};
let capturedUrl = null;
let capturedInit = null;
const originalFetch = globalThis.fetch;
globalThis.fetch = async (input, init) => {
capturedUrl = String(input);
capturedInit = init;
return new Response(JSON.stringify({ state: recoveryState() }), {
status: 200,
headers: { "Content-Type": "application/json" },
});
};
try {
await xgridsK1Api.forceFinishAcquisitionLocally(request);
} finally {
globalThis.fetch = originalFetch;
}
assert.match(
capturedUrl,
/\/actions\/acquisition\.force-finish-local$/,
);
assert.equal(capturedInit.method, "POST");
assert.deepEqual(JSON.parse(capturedInit.body), { input: request });
});
test("state API rejects a drifted active recovery contract", async () => {
const originalFetch = globalThis.fetch;
globalThis.fetch = async () => new Response(JSON.stringify({
state: recoveryState({ stop_performed: true }),
}), {
status: 200,
headers: { "Content-Type": "application/json" },
});
try {
await assert.rejects(
() => xgridsK1Api.getState(),
/некорректное состояние/,
);
} finally {
globalThis.fetch = originalFetch;
}
});
test("frontend boundary keeps recovery automatic work read-only and local finish explicit", () => {
const hookSource = readFileSync(hookSourceUrl, "utf8");
const forceFinish = sourceSlice(
hookSource,
"const forceFinishActiveStreamLocally",
"const abort",
);
assert.match(forceFinish, /run\("force-finish"/);
assert.match(forceFinish, /activeStreamForceFinishAuthority\(latestState\.current\)/);
assert.match(forceFinish, /expected_snapshot_runtime_id:\s*authority\.snapshotRuntimeId/);
assert.match(forceFinish, /acquisition_id:\s*authority\.acquisitionId/);
assert.match(forceFinish, /expected_state_revision:\s*authority\.acquisitionStateRevision/);
assert.match(forceFinish, /expected_recovery_generation:\s*authority\.recoveryGeneration/);
assert.match(forceFinish, /operator_confirmed:\s*true/);
assert.match(
forceFinish,
/newMutationContext\("acquisition\.force-finish-local"\)/,
);
assert.equal(
(forceFinish.match(/forceFinishAcquisitionLocally\(/g) ?? []).length,
1,
);
assert.doesNotMatch(
forceFinish,
/startAcquisition|stopAcquisition|scanBle|selectCameraPreview|connect\(/,
);
const acquisitionSource = readFileSync(acquisitionSourceUrl, "utf8");
assert.equal(
(acquisitionSource.match(/forceFinishActiveStreamLocally\(\)/g) ?? []).length,
1,
"the explicit recovery button is the only frontend caller",
);
const recoverySurface = readFileSync(recoverySurfaceSourceUrl, "utf8");
assert.match(recoverySurface, /<ActivityIndicator/);
assert.match(recoverySurface, /<Button[\s\S]*?variant="secondary"/);
assert.doesNotMatch(recoverySurface, /<button\b|style=\{/);
const spatialSource = readFileSync(spatialControlsSourceUrl, "utf8");
assert.match(spatialSource, /variant="compact"/);
assert.match(
spatialSource,
/runSpatialActiveStreamForceFinish\(\{[\s\S]*?forceFinishActiveStreamLocally/,
);
const spatialForceFinish = sourceSlice(
spatialSource,
"export function runSpatialActiveStreamForceFinish",
"export function K1SpatialControlsView",
);
assert.match(spatialForceFinish, /activeStreamForceFinishAuthority\(controller\.state\)/);
assert.equal(
(spatialForceFinish.match(/controller\.forceFinishActiveStreamLocally\(\)/g) ?? []).length,
1,
);
assert.doesNotMatch(spatialForceFinish, /\bstop\(|stopLocalReceiver|start|connect|scanBle/);
const connectionSource = readFileSync(connectionSourceUrl, "utf8");
assert.match(
connectionSource,
/shouldRenderK1GenericRuntimeError\([\s\S]*?errorCorrelation\?\.action/,
);
assert.match(
connectionSource,
/\{showGenericRuntimeError && error \? \([\s\S]*?<K1OperatorError/,
);
});