mirror of
https://github.com/MeshCore-Beacon/beacon-web.git
synced 2026-09-03 20:23:56 +00:00
The WS observerStatus event never says offline (the backend only derives it at REST read time, 5-minute window) and the list never refetches, so badges stayed green all session once an observer went quiet. Status is now re-derived on a tick from lastStatusAt — the WS patch keeps the timestamp fresh — using the same 5-minute rule as the server.
74 lines
3.2 KiB
TypeScript
74 lines
3.2 KiB
TypeScript
import { describe, it, expect } from "vitest";
|
|
import { patchObserverSummary } from "../../../src/features/observers/observer-updates";
|
|
import type { ObserverSummary } from "../../../src/features/observers/types";
|
|
import type { WsObserverStatus } from "../../../src/types/ws";
|
|
|
|
function observer(overrides: Partial<ObserverSummary>): ObserverSummary {
|
|
return { id: "o1", iata: "YOW", status: "offline", displayName: "Obs 1", ...overrides };
|
|
}
|
|
|
|
function update(overrides: Partial<WsObserverStatus["data"]>): WsObserverStatus["data"] {
|
|
return {
|
|
observerId: "o1",
|
|
displayName: "New",
|
|
iata: "YOW",
|
|
online: true,
|
|
batteryMv: null,
|
|
uptimeSeconds: null,
|
|
lastStatusAt: 0,
|
|
fields: [],
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
describe("patchObserverSummary", () => {
|
|
it("returns undefined when the list is undefined", () => {
|
|
expect(patchObserverSummary(undefined, update({}))).toBeUndefined();
|
|
});
|
|
|
|
it("returns the same list (same ref) when the observer is not present", () => {
|
|
const list = [observer({ id: "a" })];
|
|
expect(patchObserverSummary(list, update({ observerId: "missing" }))).toBe(list);
|
|
});
|
|
|
|
it("patches status and displayName of the matching observer immutably", () => {
|
|
const list = [observer({ id: "a" }), observer({ id: "b", status: "offline" })];
|
|
const out = patchObserverSummary(list, update({ observerId: "b", online: true, displayName: "Renamed" }))!;
|
|
expect(out).not.toBe(list);
|
|
expect(out[0]).toBe(list[0]); // untouched observer keeps its reference
|
|
expect(out[1]).toMatchObject({ id: "b", status: "online", displayName: "Renamed" });
|
|
});
|
|
|
|
it("maps online=false to offline status", () => {
|
|
const list = [observer({ id: "a", status: "online" })];
|
|
const out = patchObserverSummary(list, update({ observerId: "a", online: false }))!;
|
|
expect(out[0]!.status).toBe("offline");
|
|
});
|
|
|
|
it("keeps the previous displayName when the update name is empty", () => {
|
|
const list = [observer({ id: "a", displayName: "Keep" })];
|
|
const out = patchObserverSummary(list, update({ observerId: "a", displayName: "" }))!;
|
|
expect(out[0]!.displayName).toBe("Keep");
|
|
});
|
|
|
|
it("returns the same list ref when the update changes nothing (no needless re-render)", () => {
|
|
const list = [observer({ id: "a", status: "online", displayName: "Keep" })];
|
|
// same online state + an empty displayName that resolves back to the prev value
|
|
const out = patchObserverSummary(list, update({ observerId: "a", online: true, displayName: "" }));
|
|
expect(out).toBe(list);
|
|
});
|
|
|
|
it("carries lastStatusAt from the event so recency derivation stays live", () => {
|
|
const list = [observer({ id: "a", status: "offline" })];
|
|
const out = patchObserverSummary(list, update({ observerId: "a", online: true, lastStatusAt: 123_456 }))!;
|
|
expect(out[0]!.lastStatusAt).toBe(123_456);
|
|
});
|
|
|
|
it("updates lastStatusAt even when status and displayName are unchanged", () => {
|
|
const list = [observer({ id: "a", status: "online", displayName: "Keep", lastStatusAt: 100 })];
|
|
const out = patchObserverSummary(list, update({ observerId: "a", online: true, displayName: "", lastStatusAt: 200 }))!;
|
|
expect(out).not.toBe(list);
|
|
expect(out[0]!.lastStatusAt).toBe(200);
|
|
});
|
|
});
|