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>
125 lines
4.4 KiB
TypeScript
125 lines
4.4 KiB
TypeScript
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']);
|
|
});
|