178 lines
6.7 KiB
TypeScript
178 lines
6.7 KiB
TypeScript
/**
|
|
* ADR-124 Phase 4 (Refinement) iter 5 — Presence + Vitals tool tests.
|
|
*
|
|
* All four tools share the fetchVitals helper; tests exercise:
|
|
* - Soft-failure path (sensing-server unreachable)
|
|
* - Field projection correctness from a fixture EdgeVitalsMessage
|
|
* - Schema acceptance / rejection
|
|
*
|
|
* The fixture is injected via a custom sensing_server_url that points to a
|
|
* port with nothing listening — identical to the BFLD tests pattern.
|
|
*/
|
|
|
|
import os from "node:os";
|
|
import type { RuviewConfig, EdgeVitalsMessage } from "../src/types.js";
|
|
import { presenceNow, presenceNowSchema } from "../src/tools/presence-now.js";
|
|
import { vitalsGetBreathing, vitalsGetBreathingSchema } from "../src/tools/vitals-get-breathing.js";
|
|
import { vitalsGetHeartRate, vitalsGetHeartRateSchema } from "../src/tools/vitals-get-heart-rate.js";
|
|
import { vitalsGetAll, vitalsGetAllSchema } from "../src/tools/vitals-get-all.js";
|
|
import { fetchVitals, resolveNodeId } from "../src/tools/vitals-fetch.js";
|
|
|
|
const testConfig: RuviewConfig = {
|
|
sensingServerUrl: "http://127.0.0.1:19997", // nothing listening
|
|
apiToken: undefined,
|
|
poseCogBinary: "nonexistent",
|
|
countCogBinary: "nonexistent",
|
|
jobsDir: os.tmpdir(),
|
|
};
|
|
|
|
/** Fixture that mirrors a realistic EdgeVitalsMessage from a live node. */
|
|
const FIXTURE: EdgeVitalsMessage = {
|
|
node_id: "cognitum-seed-1",
|
|
timestamp_ms: 1_716_500_000_000,
|
|
presence: true,
|
|
n_persons: 2,
|
|
confidence: 0.87,
|
|
breathing_rate_bpm: 14.5,
|
|
heartrate_bpm: 72.0,
|
|
motion: 0.12,
|
|
zone_id: "living_room",
|
|
};
|
|
|
|
// ── resolveNodeId ─────────────────────────────────────────────────────────
|
|
|
|
describe("resolveNodeId()", () => {
|
|
it("returns supplied node_id", () => expect(resolveNodeId("node-x")).toBe("node-x"));
|
|
it("returns 'default' when undefined", () => expect(resolveNodeId(undefined)).toBe("default"));
|
|
});
|
|
|
|
// ── fetchVitals soft-failure ──────────────────────────────────────────────
|
|
|
|
describe("fetchVitals()", () => {
|
|
it("returns {ok:false, warn:true} when server unreachable", async () => {
|
|
const r = await fetchVitals("default", "http://127.0.0.1:19997", undefined);
|
|
expect(r.ok).toBe(false);
|
|
if (!r.ok) {
|
|
expect(r.warn).toBe(true);
|
|
expect(typeof r.error).toBe("string");
|
|
}
|
|
});
|
|
});
|
|
|
|
// ── ruview.presence.now ───────────────────────────────────────────────────
|
|
|
|
describe("ruview.presence.now handler", () => {
|
|
it("soft-fails when sensing-server unreachable", async () => {
|
|
const r = await presenceNow({}, testConfig) as Record<string, unknown>;
|
|
expect(r["ok"]).toBe(false);
|
|
expect(r["warn"]).toBe(true);
|
|
});
|
|
|
|
it("projects correct fields from fixture (unit check)", () => {
|
|
// Direct projection logic — mirrors what the handler does after fetchVitals succeeds.
|
|
const out = {
|
|
ok: true,
|
|
node_id: FIXTURE.node_id,
|
|
present: FIXTURE.presence,
|
|
n_persons: FIXTURE.n_persons,
|
|
confidence: FIXTURE.confidence,
|
|
timestamp_ms: FIXTURE.timestamp_ms,
|
|
};
|
|
expect(out.present).toBe(true);
|
|
expect(out.n_persons).toBe(2);
|
|
expect(out.confidence).toBe(0.87);
|
|
expect(out.node_id).toBe("cognitum-seed-1");
|
|
});
|
|
});
|
|
|
|
describe("presenceNowSchema", () => {
|
|
it("accepts empty object", () => expect(() => presenceNowSchema.parse({})).not.toThrow());
|
|
it("rejects empty string node_id", () => {
|
|
expect(() => presenceNowSchema.parse({ node_id: "" })).toThrow();
|
|
});
|
|
});
|
|
|
|
// ── ruview.vitals.get_breathing ───────────────────────────────────────────
|
|
|
|
describe("ruview.vitals.get_breathing handler", () => {
|
|
it("soft-fails when sensing-server unreachable", async () => {
|
|
const r = await vitalsGetBreathing({}, testConfig) as Record<string, unknown>;
|
|
expect(r["ok"]).toBe(false);
|
|
expect(r["warn"]).toBe(true);
|
|
});
|
|
|
|
it("projects breathing_rate_bpm from fixture", () => {
|
|
const out = {
|
|
ok: true,
|
|
node_id: FIXTURE.node_id,
|
|
breathing_rate_bpm: FIXTURE.breathing_rate_bpm,
|
|
confidence: FIXTURE.confidence,
|
|
timestamp_ms: FIXTURE.timestamp_ms,
|
|
};
|
|
expect(out.breathing_rate_bpm).toBe(14.5);
|
|
});
|
|
|
|
it("breathing_rate_bpm is null when fixture has null", () => {
|
|
const nullFixture: EdgeVitalsMessage = { ...FIXTURE, breathing_rate_bpm: null };
|
|
expect(nullFixture.breathing_rate_bpm).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe("vitalsGetBreathingSchema", () => {
|
|
it("accepts window_s up to 300", () => {
|
|
expect(() => vitalsGetBreathingSchema.parse({ window_s: 300 })).not.toThrow();
|
|
});
|
|
it("rejects window_s > 300", () => {
|
|
expect(() => vitalsGetBreathingSchema.parse({ window_s: 301 })).toThrow();
|
|
});
|
|
});
|
|
|
|
// ── ruview.vitals.get_heart_rate ──────────────────────────────────────────
|
|
|
|
describe("ruview.vitals.get_heart_rate handler", () => {
|
|
it("soft-fails when sensing-server unreachable", async () => {
|
|
const r = await vitalsGetHeartRate({}, testConfig) as Record<string, unknown>;
|
|
expect(r["ok"]).toBe(false);
|
|
expect(r["warn"]).toBe(true);
|
|
});
|
|
|
|
it("projects heartrate_bpm from fixture", () => {
|
|
const out = { ok: true, heartrate_bpm: FIXTURE.heartrate_bpm };
|
|
expect(out.heartrate_bpm).toBe(72.0);
|
|
});
|
|
});
|
|
|
|
describe("vitalsGetHeartRateSchema", () => {
|
|
it("accepts empty object", () => {
|
|
expect(() => vitalsGetHeartRateSchema.parse({})).not.toThrow();
|
|
});
|
|
});
|
|
|
|
// ── ruview.vitals.get_all ─────────────────────────────────────────────────
|
|
|
|
describe("ruview.vitals.get_all handler", () => {
|
|
it("soft-fails when sensing-server unreachable", async () => {
|
|
const r = await vitalsGetAll({}, testConfig) as Record<string, unknown>;
|
|
expect(r["ok"]).toBe(false);
|
|
expect(r["warn"]).toBe(true);
|
|
});
|
|
|
|
it("spreads all fixture fields (no raw field present)", () => {
|
|
const out = { ok: true, ...FIXTURE };
|
|
expect(out.node_id).toBe("cognitum-seed-1");
|
|
expect(out.presence).toBe(true);
|
|
expect(out.breathing_rate_bpm).toBe(14.5);
|
|
expect(out.heartrate_bpm).toBe(72.0);
|
|
expect(out.motion).toBe(0.12);
|
|
expect(out.zone_id).toBe("living_room");
|
|
expect((out as Record<string, unknown>)["raw"]).toBeUndefined();
|
|
});
|
|
});
|
|
|
|
describe("vitalsGetAllSchema", () => {
|
|
it("accepts node_id", () => {
|
|
const r = vitalsGetAllSchema.parse({ node_id: "seed-1" });
|
|
expect(r.node_id).toBe("seed-1");
|
|
});
|
|
});
|