diff --git a/apps/desktop/src/stores/__tests__/settingsStore.spec.ts b/apps/desktop/src/stores/__tests__/settingsStore.spec.ts index 717286a4f1..66e7d5e99e 100644 --- a/apps/desktop/src/stores/__tests__/settingsStore.spec.ts +++ b/apps/desktop/src/stores/__tests__/settingsStore.spec.ts @@ -527,6 +527,53 @@ describe("settingsStore MCP policy persistence", () => { }); }); +describe("settingsStore persisted settings initialization", () => { + beforeEach(() => { + vi.resetModules(); + setActivePinia(createPinia()); + }); + + it("does not treat an editor settings read failure as an empty record", async () => { + const loadEditorSettings = vi.fn().mockRejectedValue(new Error("storage temporarily unavailable")); + const saveEditorSettings = vi.fn().mockResolvedValue(undefined); + vi.doMock("@/lib/backend/api", () => ({ loadEditorSettings, saveEditorSettings })); + + const { useSettingsStore } = await import("@/stores/settingsStore"); + const store = useSettingsStore(); + + await expect(store.initEditorSettings()).rejects.toThrow("storage temporarily unavailable"); + expect(store.isEditorSettingsLoaded).toBe(false); + expect(saveEditorSettings).not.toHaveBeenCalled(); + }); + + it("can retry editor settings initialization without losing saved values", async () => { + const loadEditorSettings = vi.fn().mockRejectedValueOnce(new Error("storage temporarily unavailable")).mockResolvedValueOnce({ + fontSize: 17, + theme: "xcode-dark", + executeMode: "all", + executeModeDefaultVersion: 1, + updateNotificationsEnabled: false, + }); + const saveEditorSettings = vi.fn().mockResolvedValue(undefined); + vi.doMock("@/lib/backend/api", () => ({ loadEditorSettings, saveEditorSettings })); + + const { useSettingsStore } = await import("@/stores/settingsStore"); + const store = useSettingsStore(); + + await expect(store.initEditorSettings()).rejects.toThrow("storage temporarily unavailable"); + await store.initEditorSettings(); + + expect(store.isEditorSettingsLoaded).toBe(true); + expect(store.editorSettings).toMatchObject({ + fontSize: 17, + theme: "xcode-dark", + executeMode: "all", + updateNotificationsEnabled: false, + }); + expect(saveEditorSettings).not.toHaveBeenCalled(); + }); +}); + describe("settingsStore sidebar connection sort persistence", () => { beforeEach(() => { vi.resetModules(); diff --git a/apps/desktop/src/stores/settingsStore.ts b/apps/desktop/src/stores/settingsStore.ts index 207dfd93c9..227e6b83c1 100644 --- a/apps/desktop/src/stores/settingsStore.ts +++ b/apps/desktop/src/stores/settingsStore.ts @@ -1191,7 +1191,10 @@ export const useSettingsStore = defineStore("settings", () => { async function initEditorSettings() { if (isEditorSettingsLoaded.value) return; - const saved = await api.loadEditorSettings().catch(() => null); + // A read failure is not the same as an empty settings record. Keeping the + // store unloaded prevents startup migrations from persisting defaults over + // settings that are temporarily unavailable. + const saved = await api.loadEditorSettings(); if (saved && typeof saved === "object" && !Array.isArray(saved)) { const savedSettings = saved as Partial; const normalized = normalizeEditorSettings(savedSettings);