From 93b5bd908a87bf1f37d5aaad59b23caa7282c7a8 Mon Sep 17 00:00:00 2001 From: Jack Kingsman Date: Fri, 6 Mar 2026 14:41:34 -0800 Subject: [PATCH] Extend tests and fix docs --- frontend/AGENTS.md | 4 +- frontend/src/hooks/useConversationMessages.ts | 2 +- .../src/test/useConversationMessages.test.ts | 77 ++++++++++++++++++- 3 files changed, 79 insertions(+), 4 deletions(-) diff --git a/frontend/AGENTS.md b/frontend/AGENTS.md index 3505742..61bcc5e 100644 --- a/frontend/AGENTS.md +++ b/frontend/AGENTS.md @@ -82,8 +82,8 @@ frontend/src/ │ ├── NeighborsMiniMap.tsx # Leaflet mini-map for repeater neighbor locations │ ├── settings/ │ │ ├── settingsConstants.ts # Settings section type, ordering, labels -│ │ ├── SettingsRadioSection.tsx # Preset, freq/bw/sf/cr, txPower, lat/lon -│ │ ├── SettingsLocalSection.tsx # Name, keys, advert interval, max contacts, reboot +│ │ ├── SettingsRadioSection.tsx # Name, keys, advert interval, max contacts, radio preset, freq/bw/sf/cr, txPower, lat/lon, reboot +│ │ ├── SettingsLocalSection.tsx # Browser-local settings: theme, local label, reopen last conversation │ │ ├── SettingsMqttSection.tsx # MQTT broker config, TLS, publish toggles │ │ ├── SettingsDatabaseSection.tsx # DB size, cleanup, auto-decrypt, local label │ │ ├── SettingsBotSection.tsx # Bot list, code editor, add/delete/reset diff --git a/frontend/src/hooks/useConversationMessages.ts b/frontend/src/hooks/useConversationMessages.ts index 7e31fc3..f75980d 100644 --- a/frontend/src/hooks/useConversationMessages.ts +++ b/frontend/src/hooks/useConversationMessages.ts @@ -12,7 +12,7 @@ interface PendingAckUpdate { paths?: MessagePath[]; } -function mergePendingAck( +export function mergePendingAck( existing: PendingAckUpdate | undefined, ackCount: number, paths?: MessagePath[] diff --git a/frontend/src/test/useConversationMessages.test.ts b/frontend/src/test/useConversationMessages.test.ts index c3e8fbf..27e5143 100644 --- a/frontend/src/test/useConversationMessages.test.ts +++ b/frontend/src/test/useConversationMessages.test.ts @@ -5,7 +5,7 @@ */ import { describe, it, expect } from 'vitest'; -import { getMessageContentKey } from '../hooks/useConversationMessages'; +import { getMessageContentKey, mergePendingAck } from '../hooks/useConversationMessages'; import type { Message } from '../types'; function createMessage(overrides: Partial = {}): Message { @@ -161,3 +161,78 @@ describe('getMessageContentKey', () => { expect(getMessageContentKey(msg1)).toBe(getMessageContentKey(msg2)); }); }); + +describe('mergePendingAck', () => { + const paths1 = [{ path: 'A1B2', received_at: 1700000000 }]; + const paths2 = [ + { path: 'A1B2', received_at: 1700000000 }, + { path: 'C3D4', received_at: 1700000001 }, + ]; + + it('creates new entry when no existing state', () => { + const result = mergePendingAck(undefined, 1, paths1); + expect(result).toEqual({ ackCount: 1, paths: paths1 }); + }); + + it('creates new entry without paths when paths not provided', () => { + const result = mergePendingAck(undefined, 1); + expect(result).toEqual({ ackCount: 1 }); + expect('paths' in result).toBe(false); + }); + + it('higher ack count replaces existing', () => { + const existing = { ackCount: 1, paths: paths1 }; + const result = mergePendingAck(existing, 3, paths2); + expect(result).toEqual({ ackCount: 3, paths: paths2 }); + }); + + it('higher ack count preserves existing paths when new paths undefined', () => { + const existing = { ackCount: 1, paths: paths1 }; + const result = mergePendingAck(existing, 3); + expect(result).toEqual({ ackCount: 3, paths: paths1 }); + }); + + it('higher ack count drops existing paths when new paths explicitly provided', () => { + const existing = { ackCount: 1, paths: paths2 }; + const result = mergePendingAck(existing, 3, paths1); + expect(result).toEqual({ ackCount: 3, paths: paths1 }); + }); + + it('lower ack count is ignored entirely', () => { + const existing = { ackCount: 5, paths: paths2 }; + const result = mergePendingAck(existing, 2, paths1); + expect(result).toBe(existing); + }); + + it('same ack count with no new paths returns existing', () => { + const existing = { ackCount: 3, paths: paths1 }; + const result = mergePendingAck(existing, 3); + expect(result).toBe(existing); + }); + + it('same ack count with more paths replaces', () => { + const existing = { ackCount: 3, paths: paths1 }; + const result = mergePendingAck(existing, 3, paths2); + expect(result).toEqual({ ackCount: 3, paths: paths2 }); + }); + + it('same ack count with fewer paths keeps existing', () => { + const existing = { ackCount: 3, paths: paths2 }; + const result = mergePendingAck(existing, 3, paths1); + expect(result).toBe(existing); + }); + + it('same ack count with equal-length paths replaces (uses >=)', () => { + const existing = { ackCount: 3, paths: paths1 }; + const newPaths = [{ path: 'X1Y2', received_at: 1700000005 }]; + const result = mergePendingAck(existing, 3, newPaths); + expect(result).toEqual({ ackCount: 3, paths: newPaths }); + }); + + it('same ack count with paths when existing has no paths', () => { + const existing = { ackCount: 2 }; + const result = mergePendingAck(existing, 2, paths1); + // existing.paths is undefined → length -1, paths1.length (1) >= -1 → replaces + expect(result).toEqual({ ackCount: 2, paths: paths1 }); + }); +});