test: localStorage-backed state modules (+38)
CI / Build & Quality Checks (push) Successful in 11m15s
CI / Trigger Desktop Build (push) Successful in 10s

Via subagent, no bugs:
- state/utils/atomWithLocalStorage (9): get/set helpers + atom write-through.
- state/scheduledMessages (6): Map<->Record round-trip, persistence, mount-gated
  hydration (atomWithStorage w/o getOnInit — modeled with a subscription).
- state/spaceRooms (9): Set dedupe + no-write-when-unchanged + serialization.
- state/navToActivePath (8): per-user Map<->Object serialization.
- state/callPreferences (6): the privacy rule forcing video=false on load+persist.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-30 14:53:52 -04:00
co-authored by Claude Opus 4.8
parent 230ef8ed7c
commit 24662fa994
5 changed files with 617 additions and 0 deletions
+124
View File
@@ -0,0 +1,124 @@
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { createStore } from 'jotai';
import { enableMapSet } from 'immer';
// `spaceRoomsAtom` is backed by `atomWithLocalStorage`, which reads
// `localStorage` AT MODULE LOAD to seed the atom (absent in node). We install an
// in-memory mock first. The reducers also `produce` over an immer-managed Set,
// so immer's Map/Set plugin must be enabled (the app does this at startup).
const SPACE_ROOMS = 'spaceRooms';
const installStorage = (seed?: string[]): Map<string, string> => {
const map = new Map<string, string>();
if (seed) map.set(SPACE_ROOMS, JSON.stringify(seed));
(globalThis as { localStorage?: unknown }).localStorage = {
getItem: (k: string) => (map.has(k) ? map.get(k)! : null),
setItem: (k: string, v: string) => {
map.set(k, v);
},
removeItem: (k: string) => {
map.delete(k);
},
};
return map;
};
enableMapSet();
installStorage();
const { spaceRoomsAtom } = await import('./spaceRooms');
// `atomWithLocalStorage` seeds the atom from storage exactly once, when the
// module is evaluated. To exercise hydration we install seeded storage and then
// import a *fresh* copy of the module (cache-busted) so the seed is read.
let freshCounter = 0;
const importWithStorage = async (
seed?: string[],
): Promise<typeof import('./spaceRooms').spaceRoomsAtom> => {
installStorage(seed);
freshCounter += 1;
const mod = await import(`./spaceRooms?fresh=${freshCounter}`);
return mod.spaceRoomsAtom;
};
const sorted = (s: Set<string>): string[] => [...s].sort();
test('starts empty when nothing is stored', () => {
installStorage();
const store = createStore();
assert.equal(store.get(spaceRoomsAtom).size, 0);
});
test('PUT adds new room ids to the Set', () => {
installStorage();
const store = createStore();
store.set(spaceRoomsAtom, { type: 'PUT', roomIds: ['!a:s', '!b:s'] });
assert.deepEqual(sorted(store.get(spaceRoomsAtom)), ['!a:s', '!b:s']);
});
test('PUT dedupes against existing entries', () => {
installStorage();
const store = createStore();
store.set(spaceRoomsAtom, { type: 'PUT', roomIds: ['!a:s'] });
store.set(spaceRoomsAtom, { type: 'PUT', roomIds: ['!a:s', '!b:s'] });
assert.deepEqual(sorted(store.get(spaceRoomsAtom)), ['!a:s', '!b:s']);
});
test('PUT with only already-present ids does not write (Set identity unchanged)', () => {
installStorage();
const store = createStore();
store.set(spaceRoomsAtom, { type: 'PUT', roomIds: ['!a:s'] });
const before = store.get(spaceRoomsAtom);
store.set(spaceRoomsAtom, { type: 'PUT', roomIds: ['!a:s'] });
const after = store.get(spaceRoomsAtom);
// No new entries -> the reducer skips set(), so the Set reference is the same.
assert.equal(before, after);
});
test('DELETE removes present members', () => {
installStorage();
const store = createStore();
store.set(spaceRoomsAtom, { type: 'PUT', roomIds: ['!a:s', '!b:s'] });
store.set(spaceRoomsAtom, { type: 'DELETE', roomIds: ['!a:s'] });
assert.deepEqual(sorted(store.get(spaceRoomsAtom)), ['!b:s']);
});
test('DELETE of an absent member does not write (Set identity unchanged)', () => {
installStorage();
const store = createStore();
store.set(spaceRoomsAtom, { type: 'PUT', roomIds: ['!a:s'] });
const before = store.get(spaceRoomsAtom);
store.set(spaceRoomsAtom, { type: 'DELETE', roomIds: ['!nope:s'] });
const after = store.get(spaceRoomsAtom);
assert.equal(before, after);
assert.deepEqual(sorted(after), ['!a:s']);
});
test('DELETE acts when at least one of the ids is present', () => {
installStorage();
const store = createStore();
store.set(spaceRoomsAtom, { type: 'PUT', roomIds: ['!a:s', '!b:s'] });
store.set(spaceRoomsAtom, { type: 'DELETE', roomIds: ['!a:s', '!nope:s'] });
assert.deepEqual(sorted(store.get(spaceRoomsAtom)), ['!b:s']);
});
test('hydrates the Set from a stored array', async () => {
const freshAtom = await importWithStorage(['!x:s', '!y:s']);
const store = createStore();
assert.deepEqual(sorted(store.get(freshAtom)), ['!x:s', '!y:s']);
});
test('persists the Set to localStorage as an array', () => {
const backing = installStorage();
const store = createStore();
store.set(spaceRoomsAtom, { type: 'PUT', roomIds: ['!a:s', '!b:s'] });
const raw = backing.get(SPACE_ROOMS);
assert.ok(raw);
const arr = JSON.parse(raw!) as string[];
assert.deepEqual([...arr].sort(), ['!a:s', '!b:s']);
});