Files
beacon-web/tests/features/stats/telemetry-normalize.test.ts
T
MrAlders0n be072bb42c feat(stats): add Mesh + Observer stats tab with ECharts
Integrate the stats feature onto main: live mesh KPIs, observations
time-series, payload/node-type donuts, top-nodes/observers leaderboards,
radio presets, scope stats, and per-observer telemetry (battery, airtime,
noise, queue, receive errors) with 1h/6h/24h bucketing.

- adapt to the multi-IATA region model (useRegion -> single iata for /stats/*)
- rename stats getScopes -> getStatsScopes (avoids /scopes name-list collision)
- donut legend kept right but scrollable + truncating so it never clips
- balanced mesh grid; observer empty-state for telemetry-less observers
- normalize telemetry t to ms (raw path emits seconds, bucketed emits ms)
- add echarts 5.5.1; lazy-load StatsOverview chunk
2026-06-09 22:02:27 -04:00

39 lines
1.5 KiB
TypeScript

import { describe, it, expect } from "vitest";
import { normalizeTelemetry } from "../../../src/features/stats/useTelemetry";
import type { ObserverTelemetry } from "../../../src/features/stats/types";
const SEC = 1_700_000_000; // a second-scale epoch
const MS = SEC * 1000;
describe("normalizeTelemetry", () => {
it("scales raw (1h) second-epoch points up to ms", () => {
const raw: ObserverTelemetry = {
range: "24h",
interval: "1h",
points: [{ t: SEC, batteryMv: 3700, airtimeTxPct: null, airtimeRxPct: null, noiseFloorDb: null, uptimeSeconds: null, queueLength: null, receiveErrors: null }],
};
expect(normalizeTelemetry(raw, "1h").points[0]!.t).toBe(MS);
});
it("leaves bucketed (6h/24h) ms-epoch points untouched", () => {
const bucketed: ObserverTelemetry = {
range: "7d",
interval: "6h",
points: [{ t: MS, batteryMv: 3700, airtimeTxPct: null, airtimeRxPct: null, noiseFloorDb: null, uptimeSeconds: null, queueLength: null, receiveErrors: null }],
};
expect(normalizeTelemetry(bucketed, "6h").points[0]!.t).toBe(MS);
});
it("does not mutate other fields", () => {
const raw: ObserverTelemetry = {
range: "24h",
interval: "1h",
points: [{ t: SEC, batteryMv: 3700, airtimeTxPct: 1.5, airtimeRxPct: 2.5, noiseFloorDb: -110, uptimeSeconds: 42, queueLength: 3, receiveErrors: 1 }],
};
const p = normalizeTelemetry(raw, "1h").points[0]!;
expect(p.batteryMv).toBe(3700);
expect(p.airtimeTxPct).toBe(1.5);
expect(p.receiveErrors).toBe(1);
});
});