Browse Source

Additional waypoint methods, update mock, update tests

pull/860/head
philon- 9 months ago
parent
commit
092b90b989
  1. 9
      packages/web/src/core/stores/deviceStore/deviceStore.mock.ts
  2. 39
      packages/web/src/core/stores/deviceStore/deviceStore.test.ts
  3. 112
      packages/web/src/core/stores/deviceStore/index.ts

9
packages/web/src/core/stores/deviceStore/deviceStore.mock.ts

@ -45,8 +45,13 @@ export const mockDeviceStore: Device = {
deleteMessages: false, deleteMessages: false,
managedMode: false, managedMode: false,
clientNotification: false, clientNotification: false,
resetNodeDb: false,
clearAllStores: false,
factoryResetConfig: false,
factoryResetDevice: false,
}, },
clientNotifications: [], clientNotifications: [],
neighborInfo: new Map(),
setStatus: vi.fn(), setStatus: vi.fn(),
setConfig: vi.fn(), setConfig: vi.fn(),
@ -67,6 +72,8 @@ export const mockDeviceStore: Device = {
setPendingSettingsChanges: vi.fn(), setPendingSettingsChanges: vi.fn(),
addChannel: vi.fn(), addChannel: vi.fn(),
addWaypoint: vi.fn(), addWaypoint: vi.fn(),
removeWaypoint: vi.fn(),
getWaypoint: vi.fn(),
addConnection: vi.fn(), addConnection: vi.fn(),
addTraceRoute: vi.fn(), addTraceRoute: vi.fn(),
addMetadata: vi.fn(), addMetadata: vi.fn(),
@ -81,4 +88,6 @@ export const mockDeviceStore: Device = {
getClientNotification: vi.fn(), getClientNotification: vi.fn(),
getAllUnreadCount: vi.fn().mockReturnValue(0), getAllUnreadCount: vi.fn().mockReturnValue(0),
getUnreadCount: vi.fn().mockReturnValue(0), getUnreadCount: vi.fn().mockReturnValue(0),
getNeighborInfo: vi.fn(),
addNeighborInfo: vi.fn(),
}; };

39
packages/web/src/core/stores/deviceStore/deviceStore.test.ts

@ -349,19 +349,16 @@ describe("DeviceStore – traceroutes & waypoints retention + merge on setHardwa
// Old device with myNodeNum=777 and some waypoints (one expired) // Old device with myNodeNum=777 and some waypoints (one expired)
const oldDevice = state.addDevice(1); const oldDevice = state.addDevice(1);
oldDevice.connection = { sendWaypoint: vi.fn() } as any;
oldDevice.setHardware(makeHardware(777)); oldDevice.setHardware(makeHardware(777));
oldDevice.addWaypoint( oldDevice.addWaypoint(
makeWaypoint(1, Date.parse("2024-12-31T23:59:59Z")), makeWaypoint(1, Date.parse("2024-12-31T23:59:59Z")), // This is expired, will not be added
0, 0,
0, 0,
new Date(), new Date(),
); // expired ); // expired
oldDevice.addWaypoint( oldDevice.addWaypoint(makeWaypoint(2, 0), 0, 0, new Date()); // no expire
makeWaypoint(2, Date.parse("2028-01-01T00:00:00Z")),
0,
0,
new Date(),
); // ok
oldDevice.addWaypoint( oldDevice.addWaypoint(
makeWaypoint(3, Date.parse("2026-01-01T00:00:00Z")), makeWaypoint(3, Date.parse("2026-01-01T00:00:00Z")),
0, 0,
@ -380,12 +377,12 @@ describe("DeviceStore – traceroutes & waypoints retention + merge on setHardwa
); );
const wps = useDeviceStore.getState().devices.get(1)!.waypoints; const wps = useDeviceStore.getState().devices.get(1)!.waypoints;
expect(wps.length).toBe(3); expect(wps.length).toBe(2);
expect(wps.find((w) => w.id === 2)?.expire).toBe( expect(wps.find((w) => w.id === 2)?.expire).toBe(
Date.parse("2027-01-01T00:00:00Z"), Date.parse("2027-01-01T00:00:00Z"),
); );
// Retention: push 102 total waypoints -> capped at 100. Oldest (id=1,2) evicted // Retention: push 102 total waypoints -> capped at 100. Oldest evicted
for (let i = 3; i <= 102; i++) { for (let i = 3; i <= 102; i++) {
oldDevice.addWaypoint(makeWaypoint(i), 0, 0, new Date()); oldDevice.addWaypoint(makeWaypoint(i), 0, 0, new Date());
} }
@ -394,24 +391,32 @@ describe("DeviceStore – traceroutes & waypoints retention + merge on setHardwa
100, 100,
); );
// Remove waypoint
oldDevice.removeWaypoint(102, false);
expect(oldDevice.connection?.sendWaypoint).not.toHaveBeenCalled();
await oldDevice.removeWaypoint(101, true); // toMesh=true
expect(oldDevice.connection?.sendWaypoint).toHaveBeenCalled();
expect(useDeviceStore.getState().devices.get(1)!.waypoints.length).toBe(98);
// New device shares myNodeNum; setHardware should: // New device shares myNodeNum; setHardware should:
// - move traceroutes from old device // - move traceroutes from old device
// - copy waypoints minus expired // - copy waypoints minus expired
// - delete old device entry // - delete old device entry
const newDev = state.addDevice(2); const newDevice = state.addDevice(2);
newDev.setHardware(makeHardware(777)); newDevice.setHardware(makeHardware(777));
expect(state.getDevice(1)).toBeUndefined(); expect(state.getDevice(1)).toBeUndefined();
expect(state.getDevice(2)).toBeDefined(); expect(state.getDevice(2)).toBeDefined();
// traceroutes moved: // traceroutes moved:
expect(state.getDevice(2)!.traceroutes.size).toBeGreaterThan(0); expect(state.getDevice(2)!.traceroutes.size).toBe(2);
// expired waypoint removed, last non-expired retained: // Getter for waypoint by id works
const newWps = state.getDevice(2)!.waypoints; expect(newDevice.getWaypoint(1)).toBeUndefined();
expect(newWps.find((w) => w.id === 1)).toBeUndefined(); expect(newDevice.getWaypoint(2)).toBeUndefined();
expect(newWps.find((w) => w.id === 2)).toBeUndefined(); expect(newDevice.getWaypoint(3)).toBeTruthy();
expect(newWps.find((w) => w.id === 3)).toBeTruthy();
vi.useRealTimers(); vi.useRealTimers();
}); });

112
packages/web/src/core/stores/deviceStore/index.ts

@ -89,6 +89,8 @@ export interface Device extends DeviceData {
from: number, from: number,
rxTime: Date, rxTime: Date,
) => void; ) => void;
removeWaypoint: (waypointId: number, toMesh: boolean) => Promise<void>;
getWaypoint: (waypointId: number) => WaypointWithMetadata | undefined;
addConnection: (connection: MeshDevice) => void; addConnection: (connection: MeshDevice) => void;
addTraceRoute: ( addTraceRoute: (
traceroute: Types.PacketMetadata<Protobuf.Mesh.RouteDiscovery>, traceroute: Types.PacketMetadata<Protobuf.Mesh.RouteDiscovery>,
@ -566,32 +568,102 @@ function deviceFactory(
set( set(
produce<PrivateDeviceState>((draft) => { produce<PrivateDeviceState>((draft) => {
const device = draft.devices.get(id); const device = draft.devices.get(id);
if (device) { if (!device) {
const index = device.waypoints.findIndex( return undefined;
(wp) => wp.id === waypoint.id, }
);
if (index !== -1) { const index = device.waypoints.findIndex(
const created = (wp) => wp.id === waypoint.id,
device.waypoints[index]?.metadata.created ?? new Date(); );
const updatedWaypoint = {
...waypoint, if (index !== -1) {
metadata: { created, updated: rxTime, from, channel }, const created =
}; device.waypoints[index]?.metadata.created ?? new Date();
const updatedWaypoint = {
device.waypoints[index] = updatedWaypoint; ...waypoint,
} else { metadata: { created, updated: rxTime, from, channel },
device.waypoints.push({ };
...waypoint,
metadata: { created: rxTime, from, channel }, // Remove existing waypoint
}); device.waypoints.splice(index, 1);
// Push new if no expiry or not expired
if (waypoint.expire === 0 || waypoint.expire > Date.now()) {
device.waypoints.push(updatedWaypoint);
} }
} else if (
// only add if set to never expire or not already expired
waypoint.expire === 0 ||
(waypoint.expire !== 0 && waypoint.expire < Date.now())
) {
device.waypoints.push({
...waypoint,
metadata: { created: rxTime, from, channel },
});
}
// Enforce retention limit
evictOldestEntries(device.waypoints, WAYPOINT_RETENTION_NUM);
}),
);
},
removeWaypoint: async (waypointId: number, toMesh: boolean) => {
const device = get().devices.get(id);
if (!device) {
return;
}
const waypoint = device.waypoints.find((wp) => wp.id === waypointId);
if (!waypoint) {
return;
}
// Enforce retention limit if (toMesh) {
evictOldestEntries(device.waypoints, WAYPOINT_RETENTION_NUM); if (!device.connection) {
return;
}
const waypointToBroadcast = create(Protobuf.Mesh.WaypointSchema, {
id: waypoint.id, // Bare minimum to delete a waypoint
lockedTo: 0,
name: "",
description: "",
icon: 0,
expire: 1,
});
await device.connection.sendWaypoint(
waypointToBroadcast,
"broadcast",
waypoint.metadata.channel,
);
}
// Remove from store
set(
produce<PrivateDeviceState>((draft) => {
const device = draft.devices.get(id);
if (!device) {
return;
}
const idx = device.waypoints.findIndex(
(waypoint) => waypoint.id === waypointId,
);
if (idx >= 0) {
device.waypoints.splice(idx, 1);
} }
}), }),
); );
}, },
getWaypoint: (waypointId: number) => {
const device = get().devices.get(id);
if (!device) {
return;
}
return device.waypoints.find((waypoint) => waypoint.id === waypointId);
},
setActiveNode: (node) => { setActiveNode: (node) => {
set( set(
produce<PrivateDeviceState>((draft) => { produce<PrivateDeviceState>((draft) => {

Loading…
Cancel
Save