mirror of
https://github.com/MeshCore-Beacon/beacon-web.git
synced 2026-09-03 20:23:56 +00:00
122 lines
4.7 KiB
TypeScript
122 lines
4.7 KiB
TypeScript
import { describe, it, expect } from "vitest";
|
|
import { buildPathSummary } from "../../../src/features/packets/path-summary";
|
|
import type { PacketSummary } from "../../../src/types/api";
|
|
import { PayloadType } from "../../../src/types/enums";
|
|
|
|
const pkt = (over: Partial<PacketSummary> = {}): PacketSummary => ({
|
|
packetHash: "AA11", payloadType: 1, payloadTypeName: "ADVERT",
|
|
routeType: 1, routeTypeName: "FLOOD",
|
|
firstHeardAt: 0, lastHeardAt: 0, observationCount: 1, ...over,
|
|
});
|
|
const obs = (o: object) => ({ latestObserver: { id: "o1", iata: "YVR", ...o } });
|
|
|
|
describe("buildPathSummary", () => {
|
|
it("is n/a when latestObserver is absent", () => {
|
|
expect(buildPathSummary(pkt()).isNa).toBe(true);
|
|
});
|
|
|
|
it("never claims direct reception for an unknown hop count", () => {
|
|
const s = buildPathSummary(pkt(obs({})));
|
|
expect(s.hopLabel).toBe("n/a");
|
|
expect(s.isNa).toBe(true);
|
|
});
|
|
|
|
it("labels a literal zero hop count as direct", () => {
|
|
const s = buildPathSummary(pkt(obs({ pathLength: { raw: "00", hashSize: 1, hopCount: 0 } })));
|
|
expect(s.hopLabel).toBe("0 hops · direct");
|
|
expect(s.chips).toEqual([]);
|
|
expect(s.isNa).toBe(false);
|
|
});
|
|
|
|
it("is n/a when pathLength is missing even if pathBytes is present", () => {
|
|
expect(buildPathSummary(pkt(obs({ pathBytes: "7fa4" }))).isNa).toBe(true);
|
|
});
|
|
|
|
it("renders hex chips split by hashSize when there is no resolved path", () => {
|
|
const s = buildPathSummary(pkt(obs({
|
|
pathLength: { raw: "42", hashSize: 1, hopCount: 2 }, pathBytes: "7fa4",
|
|
})));
|
|
expect(s.hopLabel).toBe("2 hops");
|
|
expect(s.chips).toEqual([{ kind: "hex", label: "7f" }, { kind: "hex", label: "a4" }]);
|
|
expect(s.overflow).toBe(0);
|
|
});
|
|
|
|
it("drops chips when pathBytes length disagrees with hopCount x hashSize", () => {
|
|
const s = buildPathSummary(pkt(obs({
|
|
pathLength: { raw: "42", hashSize: 1, hopCount: 5 }, pathBytes: "7fa4",
|
|
})));
|
|
expect(s.hopLabel).toBe("5 hops");
|
|
expect(s.chips).toEqual([]);
|
|
});
|
|
|
|
it("renders hop count with no chips when pathBytes is absent entirely", () => {
|
|
const s = buildPathSummary(pkt(obs({
|
|
pathLength: { raw: "43", hashSize: 1, hopCount: 3 },
|
|
})));
|
|
expect(s.hopLabel).toBe("3 hops");
|
|
expect(s.chips).toEqual([]);
|
|
expect(s.isNa).toBe(false);
|
|
});
|
|
|
|
it("truncates past the hop budget and counts remaining hops", () => {
|
|
const s = buildPathSummary(pkt(obs({
|
|
pathLength: { raw: "4e", hashSize: 1, hopCount: 14 },
|
|
pathBytes: "000102030405060708090a0b0c0d",
|
|
})));
|
|
expect(s.chips).toHaveLength(5);
|
|
expect(s.overflow).toBe(9);
|
|
});
|
|
|
|
it("collapses a run of unresolved hops into one chip that costs its length", () => {
|
|
const none = { confidence: "none" as const, nodes: [] };
|
|
const high = { confidence: "high" as const, nodes: [{ id: "n", publicKey: "ab", name: "Raven" }] };
|
|
const s = buildPathSummary(pkt(obs({
|
|
pathLength: { raw: "43", hashSize: 1, hopCount: 4 }, pathBytes: "00010203",
|
|
resolvedPath: [high, none, none, high],
|
|
})));
|
|
expect(s.chips).toEqual([
|
|
{ kind: "node", label: "Raven", confidence: "high" },
|
|
{ kind: "unresolved-run", count: 2 },
|
|
{ kind: "node", label: "Raven", confidence: "high" },
|
|
]);
|
|
expect(s.overflow).toBe(0);
|
|
});
|
|
|
|
it("always shows at least one chip even when the first run exceeds the budget", () => {
|
|
const none = { confidence: "none" as const, nodes: [] };
|
|
const s = buildPathSummary(pkt(obs({
|
|
pathLength: { raw: "4a", hashSize: 1, hopCount: 10 },
|
|
pathBytes: "00010203040506070809",
|
|
resolvedPath: Array(10).fill(none),
|
|
})));
|
|
expect(s.chips).toEqual([{ kind: "unresolved-run", count: 10 }]);
|
|
expect(s.overflow).toBe(0);
|
|
});
|
|
|
|
it("shows hop count only for TRACE packets", () => {
|
|
const s = buildPathSummary(pkt({
|
|
payloadType: PayloadType.TRACE,
|
|
...obs({ pathLength: { raw: "43", hashSize: 1, hopCount: 3 }, pathBytes: "000102" }),
|
|
}));
|
|
expect(s.hopLabel).toBe("3 hops");
|
|
expect(s.chips).toEqual([]);
|
|
});
|
|
|
|
it("carries resolved endpoints through", () => {
|
|
const s = buildPathSummary(pkt(obs({
|
|
pathLength: { raw: "41", hashSize: 1, hopCount: 1 }, pathBytes: "7f",
|
|
resolvedSource: { confidence: "high", nodes: [{ id: "n", publicKey: "ab", name: "Salish" }] },
|
|
})));
|
|
expect(s.source).toEqual({ kind: "node", label: "Salish", confidence: "high" });
|
|
expect(s.destination).toBeNull();
|
|
});
|
|
|
|
it("falls back to an unresolved-run chip for an endpoint with confidence none", () => {
|
|
const s = buildPathSummary(pkt(obs({
|
|
pathLength: { raw: "41", hashSize: 1, hopCount: 1 }, pathBytes: "7f",
|
|
resolvedSource: { confidence: "none", nodes: [] },
|
|
})));
|
|
expect(s.source).toEqual({ kind: "unresolved-run", count: 1 });
|
|
});
|
|
});
|