9740bc64c9
Firmware QEMU Tests (ADR-061) / QEMU Test (edge-tier1) (push) Has been skipped
Firmware QEMU Tests (ADR-061) / QEMU Test (full-adr060) (push) Has been skipped
Firmware QEMU Tests (ADR-061) / QEMU Test (tdm-3node) (push) Has been skipped
Firmware QEMU Tests (ADR-061) / Swarm Test (ADR-062) (push) Has been skipped
npm packages / tools/ruview-mcp (node 22) (push) Failing after 1s
nvsim-server → ghcr.io / build-and-publish (push) Failing after 1s
ruview-swarm CI guard / tests (full+train) (push) Failing after 2s
Bench Regression Guard / bench compile-verify (--no-run) (push) Failing after 0s
Bench Regression Guard / bench fast-run (informational, non-gating) (push) Has been skipped
Firmware CI / Verify version.txt matches release tag (push) Has been skipped
Dashboard a11y + cross-browser / a11y (push) Failing after 0s
nvsim Dashboard → GitHub Pages / build-and-deploy (push) Failing after 2s
Firmware CI / Build firmware (esp32s3 / 4mb) (push) Failing after 15s
Firmware QEMU Tests (ADR-061) / Build Espressif QEMU (push) Failing after 1s
Firmware QEMU Tests (ADR-061) / Fuzz Testing (ADR-061 Layer 6) (push) Failing after 1s
Continuous Deployment / Pre-deployment Checks (push) Has been skipped
Firmware CI / Build firmware (esp32c6 / c6-4mb) (push) Failing after 15s
Firmware CI / Build firmware (esp32s3 / 8mb) (push) Failing after 15s
Firmware QEMU Tests (ADR-061) / QEMU Test (boundary-max) (push) Has been skipped
Firmware QEMU Tests (ADR-061) / QEMU Test (boundary-min) (push) Has been skipped
Firmware QEMU Tests (ADR-061) / QEMU Test (default) (push) Has been skipped
Firmware QEMU Tests (ADR-061) / QEMU Test (edge-tier0) (push) Has been skipped
Firmware QEMU Tests (ADR-061) / NVS Matrix Generation (push) Failing after 1s
Security Scanning / Security Policy Compliance (push) Failing after 0s
Security Scanning / Dependency Vulnerability Scan (push) Failing after 0s
Security Scanning / Static Application Security Testing (push) Failing after 1s
Security Scanning / Infrastructure Security Scan (push) Failing after 1s
Security Scanning / Secret Scanning (push) Failing after 1s
npm packages / harness/ruview (node 22) (push) Failing after 17s
Security Scanning / License Compliance Scan (push) Failing after 1s
Security Scanning / Container Security Scan (push) Failing after 4s
three.js demos → GitHub Pages / build-and-deploy (push) Failing after 1s
Verify Pipeline Determinism / Verify Pipeline Determinism (3.11) (push) Failing after 1s
Fix-Marker Regression Guard / Verify fix markers (push) Failing after 1s
ADR-115 MQTT integration tests / mqtt-integration (push) Failing after 1s
npm packages / harness/ruview (node 20) (push) Failing after 1s
npm packages / tools/ruview-mcp (node 20) (push) Failing after 1s
npm packages / tools/ruview-cli (node 20) (push) Failing after 1s
npm packages / tools/ruview-cli (node 22) (push) Failing after 1s
BFLD MQTT Integration / cargo test --features mqtt (live mosquitto) (push) Failing after 29s
ruview-swarm CI guard / build train_marl bin (push) Failing after 2s
ruview-swarm CI guard / clippy (-D warnings, --no-deps) (push) Failing after 3s
ruview-swarm CI guard / tests (ruflo) (push) Failing after 1s
ruview-swarm CI guard / tests (train) (push) Failing after 2s
ruview-swarm CI guard / tests (default) (push) Failing after 2s
Point Cloud Viewer → GitHub Pages / build-and-deploy (push) Failing after 8s
ruview-swarm CI guard / ITAR / publish guard (push) Failing after 0s
wifi-densepose sensing-server → Docker Hub + ghcr.io / build · push · smoke-test (push) Failing after 1s
Continuous Deployment / Deploy to Production (push) Has been cancelled
Continuous Deployment / Rollback Deployment (push) Has been cancelled
Continuous Deployment / Post-deployment Monitoring (push) Has been cancelled
Continuous Deployment / Notify Deployment Status (push) Has been cancelled
Continuous Deployment / Deploy to Staging (push) Has been cancelled
Security Scanning / Security Report (push) Has been cancelled
133 lines
4.3 KiB
TypeScript
133 lines
4.3 KiB
TypeScript
/**
|
||
* Tests for runtime schema validators (validate.ts).
|
||
*
|
||
* Pinned to sensing-server schema_version 2 (ADR-101).
|
||
* These tests document the exact shapes we accept and reject so that
|
||
* any schema drift from the sensing-server is caught immediately.
|
||
*/
|
||
|
||
import { validateCsiWindow, validateSensingLatestResponse } from "../src/validate.js";
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// Helpers
|
||
// ---------------------------------------------------------------------------
|
||
|
||
function makeAmplitudes(rows = 56, cols = 20): number[][] {
|
||
return Array.from({ length: rows }, () => Array.from({ length: cols }, () => 0));
|
||
}
|
||
|
||
function makeValidWindow(): unknown {
|
||
return {
|
||
ts: 1716300000.0,
|
||
n_paths: 3,
|
||
amplitudes: makeAmplitudes(),
|
||
};
|
||
}
|
||
|
||
function makeValidResponse(): unknown {
|
||
return {
|
||
schema_version: 2,
|
||
captured_at: "2026-05-21T20:00:00.000Z",
|
||
window: makeValidWindow(),
|
||
};
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// validateCsiWindow
|
||
// ---------------------------------------------------------------------------
|
||
|
||
describe("validateCsiWindow", () => {
|
||
it("accepts a valid 56×20 window", () => {
|
||
const result = validateCsiWindow(makeValidWindow());
|
||
expect(result.valid).toBe(true);
|
||
});
|
||
|
||
it("rejects null", () => {
|
||
const result = validateCsiWindow(null);
|
||
expect(result.valid).toBe(false);
|
||
if (!result.valid) {
|
||
expect(result.errors).toContain("window is not an object");
|
||
}
|
||
});
|
||
|
||
it("rejects wrong subcarrier count (e.g. 57)", () => {
|
||
const w = makeValidWindow() as Record<string, unknown>;
|
||
w["amplitudes"] = makeAmplitudes(57, 20);
|
||
const result = validateCsiWindow(w);
|
||
expect(result.valid).toBe(false);
|
||
if (!result.valid) {
|
||
expect(result.errors.some((e) => e.includes("56 rows"))).toBe(true);
|
||
}
|
||
});
|
||
|
||
it("rejects wrong frame count (e.g. 10 instead of 20)", () => {
|
||
const w = makeValidWindow() as Record<string, unknown>;
|
||
w["amplitudes"] = makeAmplitudes(56, 10);
|
||
const result = validateCsiWindow(w);
|
||
expect(result.valid).toBe(false);
|
||
if (!result.valid) {
|
||
expect(result.errors.some((e) => e.includes("20 frames"))).toBe(true);
|
||
}
|
||
});
|
||
|
||
it("rejects missing ts field", () => {
|
||
const w = makeValidWindow() as Record<string, unknown>;
|
||
delete w["ts"];
|
||
const result = validateCsiWindow(w);
|
||
expect(result.valid).toBe(false);
|
||
if (!result.valid) {
|
||
expect(result.errors.some((e) => e.includes("ts"))).toBe(true);
|
||
}
|
||
});
|
||
});
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// validateSensingLatestResponse
|
||
// ---------------------------------------------------------------------------
|
||
|
||
describe("validateSensingLatestResponse", () => {
|
||
it("accepts a valid schema_version 2 response", () => {
|
||
const result = validateSensingLatestResponse(makeValidResponse());
|
||
expect(result.valid).toBe(true);
|
||
});
|
||
|
||
it("rejects schema_version 3 (not yet supported)", () => {
|
||
const d = makeValidResponse() as Record<string, unknown>;
|
||
d["schema_version"] = 3;
|
||
const result = validateSensingLatestResponse(d);
|
||
expect(result.valid).toBe(false);
|
||
if (!result.valid) {
|
||
expect(result.errors.some((e) => e.includes("schema_version 3 is not supported"))).toBe(true);
|
||
}
|
||
});
|
||
|
||
it("rejects missing captured_at", () => {
|
||
const d = makeValidResponse() as Record<string, unknown>;
|
||
delete d["captured_at"];
|
||
const result = validateSensingLatestResponse(d);
|
||
expect(result.valid).toBe(false);
|
||
if (!result.valid) {
|
||
expect(result.errors.some((e) => e.includes("captured_at"))).toBe(true);
|
||
}
|
||
});
|
||
|
||
it("rejects null response", () => {
|
||
const result = validateSensingLatestResponse(null);
|
||
expect(result.valid).toBe(false);
|
||
if (!result.valid) {
|
||
expect(result.errors.some((e) => e.includes("not an object"))).toBe(true);
|
||
}
|
||
});
|
||
|
||
it("propagates window validation errors with 'window:' prefix", () => {
|
||
const d = makeValidResponse() as Record<string, unknown>;
|
||
const w = (d["window"] as Record<string, unknown>);
|
||
w["amplitudes"] = makeAmplitudes(57, 20);
|
||
const result = validateSensingLatestResponse(d);
|
||
expect(result.valid).toBe(false);
|
||
if (!result.valid) {
|
||
expect(result.errors.some((e) => e.startsWith("window:"))).toBe(true);
|
||
}
|
||
});
|
||
});
|