test(stores): cover settings merge/clamp + notification sanitizer (29→42 tests)

Extract two pure helpers so they're testable without zustand/expo:
- settings-merge.ts: clampPageSize + forward-compatible mergeStoredSettings (the
  upgrade path where stored data predates a new notification category must yield
  the default, not undefined). stores/settings.ts now delegates.
- notify-format.ts: sanitizeBody (strip C0/DEL control chars, trim, cap at 200)
  used for server-supplied notification text. stores/events.ts now imports it.
Behavior unchanged; typecheck clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
engineer
2026-06-03 14:01:29 -07:00
parent dd55ca471d
commit 48bc314dfc
6 changed files with 137 additions and 8 deletions

View File

@@ -2,10 +2,7 @@ import { create } from "zustand"
import { useConnections } from "./connections"
import { useSessions } from "./sessions"
import { send as notify } from "../lib/notifications"
const MAX_NOTIF_BODY = 200
const sanitizeBody = (s: string | undefined, fallback: string): string =>
(s ? s.replace(/[\x00-\x1f\x7f]/g, " ").trim().slice(0, MAX_NOTIF_BODY) : "") || fallback
import { sanitizeBody } from "../lib/notify-format"
import { addBreadcrumb } from "../lib/sentry"
import type { Client, Part, Session, Message } from "../lib/sdk"

View File

@@ -1,6 +1,7 @@
import { create } from "zustand"
import * as SecureStore from "expo-secure-store"
import { type Category, defaultPreferences } from "../lib/notifications"
import { clampPageSize, mergeStoredSettings } from "../lib/settings-merge"
const SETTINGS_KEY = "opencode_settings"
@@ -37,16 +38,15 @@ export const useSettings = create<SettingsState>((set, get) => ({
const raw = await SecureStore.getItemAsync(SETTINGS_KEY)
if (raw) {
const parsed = JSON.parse(raw) as Partial<Settings>
// Merge stored notifications with defaults so new categories get their default
const notifications = { ...DEFAULTS.notifications, ...parsed.notifications }
set({ ...DEFAULTS, ...parsed, notifications, loaded: true })
// Merge stored settings with defaults so new fields/categories get their default
set({ ...mergeStoredSettings(DEFAULTS, parsed), loaded: true })
return
}
set({ loaded: true })
},
setPageSize: async (size) => {
const clamped = Math.max(10, Math.min(200, size))
const clamped = clampPageSize(size)
set({ pageSize: clamped })
await persist({ ...snapshot(get), pageSize: clamped })
},