NODEDC_MISSION_CORE/apps/control-station/test/observationSources.test.mjs

198 lines
6.4 KiB
JavaScript

import assert from "node:assert/strict";
import { after, before, test } from "node:test";
import { createServer } from "vite";
let server;
let xgridsK1Manifest;
let xgridsK1ObservationSources;
before(async () => {
server = await createServer({
appType: "custom",
logLevel: "silent",
server: { middlewareMode: true },
});
({ xgridsK1Manifest } = await server.ssrLoadModule(
"/src/device-plugins/xgrids-k1/manifest.ts",
));
({ xgridsK1ObservationSources } = await server.ssrLoadModule(
"/src/device-plugins/xgrids-k1/observationSources.ts",
));
});
after(async () => {
await server?.close();
});
function model() {
const activeModel = xgridsK1Manifest.spec.models[0];
assert.ok(activeModel, "XGRIDS plugin must declare at least one model");
return activeModel;
}
function declaredState() {
const activeModel = model();
return {
phase: "connected",
source_mode: "idle",
k1_ip: "192.168.7.10",
foxglove_ws_url: "ws://192.168.7.10:8765",
foxglove_viewer_url: "http://192.168.7.10:8765/vendor-viewer",
compatibility: {
profile_id: "xgrids.lixelkity-k1.fw-3.0.2.direct-lan.v1",
camera_preview: "rtsp://192.168.7.10:8554/vendor-preview",
},
device_ref: {
device_id: "device-k1-001",
model_id: activeModel.id,
identity_stability: "stable",
identity_basis: "hardware-identifier",
},
device_session: {
device_session_id: "device-session-001",
device_id: "device-k1-001",
compatibility_profile_id: "xgrids.lixelkity-k1.fw-3.0.2.direct-lan.v1",
connectivity: "connected",
},
sensor_catalog: {
schema_version: "missioncore.sensor-catalog/v1alpha2",
revision: "test-profile",
streams: [
{ stream_id: "spatial.point-cloud.live", modality: "point-cloud", availability: "observed" },
{ stream_id: "camera.preview.live", modality: "encoded-video", availability: "observed" },
],
},
};
}
function streamingState() {
return {
...declaredState(),
phase: "streaming",
source_mode: "live",
rerun_grpc_url: "rerun+http://127.0.0.1:9877/proxy",
acquisition: {
acquisition_id: "acquisition-001",
device_id: "device-k1-001",
device_session_id: "device-session-001",
compatibility_profile_id: "xgrids.lixelkity-k1.fw-3.0.2.direct-lan.v1",
control_mode: "operator-manual",
requested_streams: ["spatial.point-cloud.live"],
target_host: "127.0.0.1",
duration_seconds: 0,
evidence_policy: "required",
state: "acquiring",
state_revision: 3,
},
};
}
function collectUrlLikeStrings(value, found = []) {
if (typeof value === "string") {
if (value.includes("://")) found.push(value);
return found;
}
if (Array.isArray(value)) {
for (const item of value) collectUrlLikeStrings(item, found);
return found;
}
if (value && typeof value === "object") {
for (const item of Object.values(value)) collectUrlLikeStrings(item, found);
}
return found;
}
test("K1 maps to one primary point cloud and two auxiliary camera channels", () => {
const sources = xgridsK1ObservationSources(declaredState(), model());
assert.equal(sources.length, 3);
assert.deepEqual(
sources.map(({ sourceId, semanticChannelId, modality, role }) => ({
sourceId,
semanticChannelId,
modality,
role,
})),
[
{
sourceId: "sensor.lidar.primary",
semanticChannelId: "spatial.point-cloud.live",
modality: "point-cloud",
role: "primary",
},
{
sourceId: "sensor.camera.left",
semanticChannelId: "camera.preview.live",
modality: "video",
role: "auxiliary",
},
{
sourceId: "sensor.camera.right",
semanticChannelId: "camera.preview.live",
modality: "video",
role: "auxiliary",
},
],
);
assert.equal(new Set(sources.map(({ id }) => id)).size, sources.length);
assert.ok(sources.every(({ provider }) => provider.pluginId && provider.modelId));
assert.ok(sources.every(({ provider }) => provider.pluginVersion));
assert.ok(sources.every(({ binding }) => binding.deviceId === "device-k1-001"));
assert.ok(sources.every(({ capabilities }) => capabilities.timelineMode === "live-only"));
});
test("observation source ids remain stable while runtime availability changes", () => {
const declared = xgridsK1ObservationSources(declaredState(), model());
const streaming = xgridsK1ObservationSources(streamingState(), model());
assert.deepEqual(
streaming.map(({ id }) => id),
declared.map(({ id }) => id),
);
assert.equal(declared[0].availability, "available");
assert.equal(streaming[0].availability, "streaming");
assert.equal(streaming[0].previewUrl, "rerun+http://127.0.0.1:9877/proxy");
});
test("camera descriptors stay declared and URL-free until a browser delivery adapter exists", () => {
const sources = xgridsK1ObservationSources(streamingState(), model());
const cameras = sources.filter(({ modality }) => modality === "video");
assert.equal(cameras.length, 2);
for (const camera of cameras) {
assert.equal(camera.availability, "declared");
assert.equal(camera.transport, "rtsp");
assert.equal(camera.previewUrl, null);
assert.equal(camera.capabilities.spatialRegistration, "unresolved");
assert.equal(camera.capabilities.resizable, true);
assert.doesNotMatch(camera.endpointLabel ?? "", /:\/\//);
}
assert.deepEqual(collectUrlLikeStrings(sources), [
"rerun+http://127.0.0.1:9877/proxy",
]);
const serialized = JSON.stringify(sources);
assert.doesNotMatch(serialized, /rtsp:\/\//i);
assert.doesNotMatch(serialized, /192\.168\.7\.10/);
assert.doesNotMatch(serialized, /vendor-(?:preview|viewer)/);
});
test("unattested camera catalog entries remain unverified", () => {
const state = declaredState();
state.compatibility.profile_id = null;
state.device_session.compatibility_profile_id = null;
state.sensor_catalog.streams = state.sensor_catalog.streams.map((stream) =>
stream.stream_id === "camera.preview.live"
? { ...stream, availability: "unverified", decode_status: "profile-not-attested" }
: stream);
const cameras = xgridsK1ObservationSources(state, model()).filter(
({ modality }) => modality === "video",
);
assert.equal(cameras.length, 2);
assert.ok(cameras.every(({ availability }) => availability === "unverified"));
assert.ok(cameras.every(({ previewUrl }) => previewUrl === null));
});