From 24662fa9949b59194290bfbe3e20acd542090174 Mon Sep 17 00:00:00 2001 From: Jared Vititoe Date: Tue, 30 Jun 2026 14:53:52 -0400 Subject: [PATCH] test: localStorage-backed state modules (+38) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- src/app/state/callPreferences.test.ts | 116 ++++++++++++++ src/app/state/navToActivePath.test.ts | 123 +++++++++++++++ src/app/state/scheduledMessages.test.ts | 145 ++++++++++++++++++ src/app/state/spaceRooms.test.ts | 124 +++++++++++++++ .../state/utils/atomWithLocalStorage.test.ts | 109 +++++++++++++ 5 files changed, 617 insertions(+) create mode 100644 src/app/state/callPreferences.test.ts create mode 100644 src/app/state/navToActivePath.test.ts create mode 100644 src/app/state/scheduledMessages.test.ts create mode 100644 src/app/state/spaceRooms.test.ts create mode 100644 src/app/state/utils/atomWithLocalStorage.test.ts diff --git a/src/app/state/callPreferences.test.ts b/src/app/state/callPreferences.test.ts new file mode 100644 index 000000000..aae90b035 --- /dev/null +++ b/src/app/state/callPreferences.test.ts @@ -0,0 +1,116 @@ +import { test } from 'node:test'; +import assert from 'node:assert/strict'; +import { createStore } from 'jotai'; +import type { CallPreferences } from './callPreferences'; + +// `makeCallPreferencesAtom(userId)` is a factory backed by `atomWithLocalStorage`. +// localStorage is read when the returned atom is created; we install an +// in-memory mock first so reads/writes resolve against it. +const CALL_PREFERENCES = 'callPreferences'; +const storeKeyFor = (userId: string): string => `${CALL_PREFERENCES}${userId}`; + +const installStorage = (): Map => { + const map = new Map(); + (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; +}; + +installStorage(); +// eslint-disable-next-line import/first +import { makeCallPreferencesAtom } from './callPreferences'; + +const USER = '@user:server'; + +test('defaults to mic on, sound on, video off', () => { + installStorage(); + const prefsAtom = makeCallPreferencesAtom(USER); + const store = createStore(); + assert.deepEqual(store.get(prefsAtom), { + microphone: true, + video: false, + sound: true, + }); +}); + +test('forces video to false on LOAD even when stored as true', () => { + const backing = installStorage(); + backing.set(storeKeyFor(USER), JSON.stringify({ microphone: false, video: true, sound: false })); + + const prefsAtom = makeCallPreferencesAtom(USER); + const store = createStore(); + const prefs = store.get(prefsAtom); + + // video is overridden, but mic/sound are preserved from storage. + assert.equal(prefs.video, false); + assert.equal(prefs.microphone, false); + assert.equal(prefs.sound, false); +}); + +test('forces video to false on PERSIST even when set to true', () => { + const backing = installStorage(); + const prefsAtom = makeCallPreferencesAtom(USER); + const store = createStore(); + + store.set(prefsAtom, { microphone: true, video: true, sound: true }); + + const raw = backing.get(storeKeyFor(USER)); + assert.ok(raw); + const persisted = JSON.parse(raw!) as CallPreferences; + assert.equal(persisted.video, false); + // mic/sound persisted as given. + assert.equal(persisted.microphone, true); + assert.equal(persisted.sound, true); +}); + +test('the in-memory atom value also has video forced off after a write', () => { + installStorage(); + const prefsAtom = makeCallPreferencesAtom(USER); + const store = createStore(); + + // atomWithLocalStorage write-through stores newValue verbatim on the atom, + // while only the persisted copy is sanitized. The next load re-sanitizes. + store.set(prefsAtom, { microphone: true, video: true, sound: true }); + + // Re-create the atom to model a fresh load from the (sanitized) storage. + const reloaded = makeCallPreferencesAtom(USER); + assert.equal(store.get(reloaded).video, false); +}); + +test('preserves mic/sound toggles across a persist + reload cycle', () => { + installStorage(); + const prefsAtom = makeCallPreferencesAtom(USER); + const store = createStore(); + + store.set(prefsAtom, { microphone: false, video: false, sound: false }); + + const reloaded = makeCallPreferencesAtom(USER); + const prefs = store.get(reloaded); + assert.equal(prefs.microphone, false); + assert.equal(prefs.sound, false); + assert.equal(prefs.video, false); +}); + +test('preferences are scoped per userId', () => { + const backing = installStorage(); + const userA = '@a:s'; + const userB = '@b:s'; + const atomA = makeCallPreferencesAtom(userA); + const atomB = makeCallPreferencesAtom(userB); + const store = createStore(); + + store.set(atomA, { microphone: false, video: false, sound: true }); + store.set(atomB, { microphone: true, video: false, sound: false }); + + assert.ok(backing.has(storeKeyFor(userA))); + assert.ok(backing.has(storeKeyFor(userB))); + assert.equal(store.get(atomA).microphone, false); + assert.equal(store.get(atomB).microphone, true); +}); diff --git a/src/app/state/navToActivePath.test.ts b/src/app/state/navToActivePath.test.ts new file mode 100644 index 000000000..dbdf04e4c --- /dev/null +++ b/src/app/state/navToActivePath.test.ts @@ -0,0 +1,123 @@ +import { test } from 'node:test'; +import assert from 'node:assert/strict'; +import { createStore } from 'jotai'; +import { enableMapSet } from 'immer'; +import type { Path } from 'react-router-dom'; + +// `makeNavToActivePathAtom(userId)` is a factory: localStorage is read when the +// returned atom is first created/accessed (not at module load), but we still +// install the mock up front. The reducers `produce` over an immer-managed Map, +// so immer's Map/Set plugin must be enabled. +const NAV_TO_ACTIVE_PATH = 'navToActivePath'; +const storeKeyFor = (userId: string): string => `${NAV_TO_ACTIVE_PATH}${userId}`; + +const installStorage = (): Map => { + const map = new Map(); + (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(); +// eslint-disable-next-line import/first +import { makeNavToActivePathAtom } from './navToActivePath'; + +const USER = '@user:server'; +const path = (pathname: string): Path => ({ pathname, search: '', hash: '' }); + +test('starts as an empty Map', () => { + installStorage(); + const navAtom = makeNavToActivePathAtom(USER); + const store = createStore(); + const map = store.get(navAtom); + assert.ok(map instanceof Map); + assert.equal(map.size, 0); +}); + +test('PUT stores a path under its navId', () => { + installStorage(); + const navAtom = makeNavToActivePathAtom(USER); + const store = createStore(); + store.set(navAtom, { type: 'PUT', navId: 'home', path: path('/home') }); + assert.deepEqual(store.get(navAtom).get('home'), path('/home')); +}); + +test('PUT overwrites the path for an existing navId', () => { + installStorage(); + const navAtom = makeNavToActivePathAtom(USER); + const store = createStore(); + store.set(navAtom, { type: 'PUT', navId: 'home', path: path('/old') }); + store.set(navAtom, { type: 'PUT', navId: 'home', path: path('/new') }); + const map = store.get(navAtom); + assert.equal(map.size, 1); + assert.deepEqual(map.get('home'), path('/new')); +}); + +test('DELETE removes a navId', () => { + installStorage(); + const navAtom = makeNavToActivePathAtom(USER); + const store = createStore(); + store.set(navAtom, { type: 'PUT', navId: 'home', path: path('/home') }); + store.set(navAtom, { type: 'PUT', navId: 'dms', path: path('/dms') }); + store.set(navAtom, { type: 'DELETE', navId: 'home' }); + + const map = store.get(navAtom); + assert.equal(map.has('home'), false); + assert.deepEqual(map.get('dms'), path('/dms')); +}); + +test('DELETE of an absent navId is a no-op', () => { + installStorage(); + const navAtom = makeNavToActivePathAtom(USER); + const store = createStore(); + store.set(navAtom, { type: 'PUT', navId: 'home', path: path('/home') }); + store.set(navAtom, { type: 'DELETE', navId: 'ghost' }); + assert.deepEqual([...store.get(navAtom).keys()], ['home']); +}); + +test('persists to localStorage as an Object keyed per user', () => { + const backing = installStorage(); + const navAtom = makeNavToActivePathAtom(USER); + const store = createStore(); + store.set(navAtom, { type: 'PUT', navId: 'home', path: path('/home') }); + + const raw = backing.get(storeKeyFor(USER)); + assert.ok(raw); + const obj = JSON.parse(raw!) as Record; + assert.deepEqual(obj, { home: path('/home') }); +}); + +test('hydrates the Map from a stored Object', () => { + const backing = installStorage(); + backing.set(storeKeyFor(USER), JSON.stringify({ home: path('/home') })); + + const navAtom = makeNavToActivePathAtom(USER); + const store = createStore(); + assert.deepEqual(store.get(navAtom).get('home'), path('/home')); +}); + +test('storage is scoped per userId', () => { + const backing = installStorage(); + const userA = '@a:s'; + const userB = '@b:s'; + + const atomA = makeNavToActivePathAtom(userA); + const atomB = makeNavToActivePathAtom(userB); + const store = createStore(); + + store.set(atomA, { type: 'PUT', navId: 'home', path: path('/a-home') }); + store.set(atomB, { type: 'PUT', navId: 'home', path: path('/b-home') }); + + assert.ok(backing.has(storeKeyFor(userA))); + assert.ok(backing.has(storeKeyFor(userB))); + assert.deepEqual(store.get(atomA).get('home'), path('/a-home')); + assert.deepEqual(store.get(atomB).get('home'), path('/b-home')); +}); diff --git a/src/app/state/scheduledMessages.test.ts b/src/app/state/scheduledMessages.test.ts new file mode 100644 index 000000000..45f58181f --- /dev/null +++ b/src/app/state/scheduledMessages.test.ts @@ -0,0 +1,145 @@ +import { test } from 'node:test'; +import assert from 'node:assert/strict'; +import { createStore } from 'jotai'; +import type { ScheduledMessage } from './scheduledMessages'; + +// scheduledMessagesAtom is backed by jotai's atomWithStorage over +// `createJSONStorage(() => localStorage)`, which dereferences `localStorage` +// (absent in node) lazily on first store access. We install an in-memory mock +// before importing the module so both module init and the storage reads/writes +// resolve against it. +const STORAGE_KEY = 'cinny_scheduled_messages_v1'; + +const installStorage = (): Map => { + const map = new Map(); + (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; +}; + +installStorage(); +const { scheduledMessagesAtom } = await import('./scheduledMessages'); + +// jotai's `atomWithStorage` binds to the `localStorage` captured at module +// evaluation. To exercise hydration from pre-existing storage we install seeded +// storage and then import a *fresh* (cache-busted) copy of the module. +let freshCounter = 0; +const importWithStorage = async ( + seed?: Record, +): Promise => { + const backing = installStorage(); + if (seed) backing.set(STORAGE_KEY, JSON.stringify(seed)); + freshCounter += 1; + const mod = await import(`./scheduledMessages?fresh=${freshCounter}`); + return mod.scheduledMessagesAtom; +}; + +const msg = (delayId: string, roomId: string): ScheduledMessage => ({ + delayId, + roomId, + content: { body: delayId, msgtype: 'm.text' }, + sendAt: 1000, +}); + +test('starts as an empty Map', () => { + installStorage(); + const store = createStore(); + const map = store.get(scheduledMessagesAtom); + assert.ok(map instanceof Map); + assert.equal(map.size, 0); +}); + +test('setting a Map is readable back as an equivalent Map (round-trip)', () => { + installStorage(); + const store = createStore(); + + const next = new Map([['!room:s', [msg('d1', '!room:s')]]]); + store.set(scheduledMessagesAtom, next); + + const got = store.get(scheduledMessagesAtom); + assert.ok(got instanceof Map); + assert.deepEqual(got.get('!room:s'), [msg('d1', '!room:s')]); +}); + +test('functional-updater form receives the previous Map', () => { + installStorage(); + const store = createStore(); + + store.set(scheduledMessagesAtom, new Map([['!a:s', [msg('d1', '!a:s')]]])); + + let seenPrev: Map | undefined; + store.set(scheduledMessagesAtom, (prev) => { + seenPrev = prev; + const copy = new Map(prev); + copy.set('!b:s', [msg('d2', '!b:s')]); + return copy; + }); + + assert.ok(seenPrev instanceof Map); + assert.deepEqual(seenPrev?.get('!a:s'), [msg('d1', '!a:s')]); + + const got = store.get(scheduledMessagesAtom); + assert.deepEqual([...got.keys()].sort(), ['!a:s', '!b:s']); +}); + +test('persists to localStorage as a plain Record keyed by roomId', () => { + const backing = installStorage(); + const store = createStore(); + + store.set( + scheduledMessagesAtom, + new Map([ + ['!a:s', [msg('d1', '!a:s')]], + ['!b:s', [msg('d2', '!b:s'), msg('d3', '!b:s')]], + ]), + ); + + const raw = backing.get(STORAGE_KEY); + assert.ok(raw, 'expected the storage key to be written'); + const parsed = JSON.parse(raw!) as Record; + assert.deepEqual(Object.keys(parsed).sort(), ['!a:s', '!b:s']); + assert.equal(parsed['!b:s'].length, 2); + assert.equal(parsed['!a:s'][0].delayId, 'd1'); +}); + +test('hydrates the Map from a previously stored Record once the atom is mounted', async () => { + const freshAtom = await importWithStorage({ '!room:s': [msg('stored', '!room:s')] }); + const store = createStore(); + + // The underlying jotai atomWithStorage is created without `getOnInit`, so a + // bare `store.get` returns the default ({}); storage is synced on mount. We + // model the React mount with `store.sub`, which fires the onMount hydration. + const unsub = store.sub(freshAtom, () => {}); + try { + assert.deepEqual(store.get(freshAtom).get('!room:s'), [msg('stored', '!room:s')]); + } finally { + unsub(); + } +}); + +test('supports multiple rooms independently', () => { + installStorage(); + const store = createStore(); + + store.set(scheduledMessagesAtom, (prev) => { + const copy = new Map(prev); + copy.set('!r1:s', [msg('a', '!r1:s')]); + return copy; + }); + store.set(scheduledMessagesAtom, (prev) => { + const copy = new Map(prev); + copy.set('!r2:s', [msg('b', '!r2:s')]); + return copy; + }); + + const map = store.get(scheduledMessagesAtom); + assert.deepEqual(map.get('!r1:s'), [msg('a', '!r1:s')]); + assert.deepEqual(map.get('!r2:s'), [msg('b', '!r2:s')]); +}); diff --git a/src/app/state/spaceRooms.test.ts b/src/app/state/spaceRooms.test.ts new file mode 100644 index 000000000..77566f75e --- /dev/null +++ b/src/app/state/spaceRooms.test.ts @@ -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 => { + const map = new Map(); + 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 => { + installStorage(seed); + freshCounter += 1; + const mod = await import(`./spaceRooms?fresh=${freshCounter}`); + return mod.spaceRoomsAtom; +}; + +const sorted = (s: Set): 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']); +}); diff --git a/src/app/state/utils/atomWithLocalStorage.test.ts b/src/app/state/utils/atomWithLocalStorage.test.ts new file mode 100644 index 000000000..8fa31348e --- /dev/null +++ b/src/app/state/utils/atomWithLocalStorage.test.ts @@ -0,0 +1,109 @@ +import { test } from 'node:test'; +import assert from 'node:assert/strict'; +import { createStore } from 'jotai'; +import { + getLocalStorageItem, + setLocalStorageItem, + atomWithLocalStorage, +} from './atomWithLocalStorage'; + +// These helpers read/write the real `localStorage` global, which node lacks, so +// we install a small in-memory mock before each case. `atomWithLocalStorage` +// also registers a `window` storage listener via `onMount`; we only drive the +// pure read/write path through a jotai store (no onMount), so `window` is not +// required here. +const installStorage = (): Map => { + const map = new Map(); + (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; +}; + +test('getLocalStorageItem returns the default when the key is absent', () => { + installStorage(); + assert.deepEqual(getLocalStorageItem('missing', { a: 1 }), { a: 1 }); + assert.equal(getLocalStorageItem('missing', 7), 7); +}); + +test('getLocalStorageItem maps the literal string "undefined" to undefined', () => { + const store = installStorage(); + store.set('k', 'undefined'); + assert.equal(getLocalStorageItem('k', 'fallback'), undefined); +}); + +test('getLocalStorageItem parses stored JSON', () => { + const store = installStorage(); + store.set('k', JSON.stringify({ nested: [1, 2, 3] })); + assert.deepEqual(getLocalStorageItem('k', null), { nested: [1, 2, 3] }); +}); + +test('getLocalStorageItem returns the default on malformed JSON', () => { + const store = installStorage(); + store.set('k', '{ not valid json'); + assert.equal(getLocalStorageItem('k', 'fallback'), 'fallback'); +}); + +test('setLocalStorageItem writes the JSON-serialized value', () => { + const store = installStorage(); + setLocalStorageItem('k', { hello: 'world' }); + assert.equal(store.get('k'), JSON.stringify({ hello: 'world' })); +}); + +test('round-trips a value through set + get', () => { + installStorage(); + setLocalStorageItem('k', [1, 'two', { three: true }]); + assert.deepEqual(getLocalStorageItem('k', null), [1, 'two', { three: true }]); +}); + +test('atomWithLocalStorage seeds the atom from getItem on creation', () => { + installStorage(); + const seeded = atomWithLocalStorage( + 'k', + () => 42, + () => undefined, + ); + const store = createStore(); + assert.equal(store.get(seeded), 42); +}); + +test('atomWithLocalStorage write-through updates BOTH the atom and storage', () => { + installStorage(); + const writes: Array<[string, number]> = []; + const theAtom = atomWithLocalStorage( + 'k', + () => 0, + (key, value) => { + writes.push([key, value]); + }, + ); + + const store = createStore(); + store.set(theAtom, 5); + + // Atom value reflects the write... + assert.equal(store.get(theAtom), 5); + // ...and setItem was invoked with the key + new value. + assert.deepEqual(writes, [['k', 5]]); +}); + +test('atomWithLocalStorage persists through the real setLocalStorageItem helper', () => { + const backing = installStorage(); + const theAtom = atomWithLocalStorage<{ count: number }>( + 'k', + (key) => getLocalStorageItem(key, { count: 0 }), + (key, value) => setLocalStorageItem(key, value), + ); + + const store = createStore(); + store.set(theAtom, { count: 9 }); + + assert.equal(backing.get('k'), JSON.stringify({ count: 9 })); + assert.deepEqual(store.get(theAtom), { count: 9 }); +});