Reduce anti-slop findings in Persistence (#2953)

* chore(ui): reduce persistence anti-slop findings

* test(ui): cover fallback settings response

* fix(ui): preserve usage model group contract
This commit is contained in:
Bohdan Triapitsyn
2026-08-16 19:21:35 +03:00
committed by GitHub
parent bfa0f9ee2a
commit 7ef6441bf3
2 changed files with 55 additions and 17 deletions
+41
View File
@@ -241,6 +241,47 @@ describe('updateDesktopSettings', () => {
}
});
test('sanitizes a successful fallback settings response before applying it', async () => {
const previousFetch = globalThis.fetch;
const fallbackFetch: typeof fetch = async () => new Response(JSON.stringify({ terminalShell: 'zsh' }), {
headers: { 'Content-Type': 'application/json' },
});
try {
globalThis.fetch = fallbackFetch;
useUIStore.getState().setTerminalShell('fish');
await updateDesktopSettings({ terminalShell: 'zsh' });
expect(useUIStore.getState().terminalShell).toBe('zsh');
expect(getSettingsSaveState()).toBe('idle');
} finally {
globalThis.fetch = previousFetch;
}
});
test('reports an error without applying a malformed fallback settings response', async () => {
const previousFetch = globalThis.fetch;
const fallbackFetch: typeof fetch = async () => new Response(JSON.stringify('ok'), {
headers: { 'Content-Type': 'application/json' },
});
const states: string[] = [];
const unsubscribe = subscribeToSettingsSaveState(() => {
states.push(getSettingsSaveState());
});
try {
globalThis.fetch = fallbackFetch;
useUIStore.getState().setTerminalShell('fish');
await updateDesktopSettings({ terminalShell: 'zsh' });
expect(useUIStore.getState().terminalShell).toBe('fish');
expect(states).toEqual(['saving', 'error']);
} finally {
unsubscribe();
globalThis.fetch = previousFetch;
}
});
test('drains a pending save to the previous runtime and ignores its stale response', async () => {
switchRuntimeEndpoint({ apiBaseUrl: 'https://settings-a.example', runtimeKey: 'settings-a' });
const saveResult = deferred<SettingsPayload>();
+14 -17
View File
@@ -130,7 +130,7 @@ const persistToLocalStorage = (settings: DesktopSettings) => {
if (Array.isArray(settings.projects) && settings.projects.length > 0) {
const collapsed = settings.projects
.filter((project) => (project as unknown as { sidebarCollapsed?: boolean }).sidebarCollapsed === true)
.filter((project) => project.sidebarCollapsed === true)
.map((project) => project.id)
.filter((id): id is string => typeof id === 'string' && id.length > 0);
if (collapsed.length > 0) {
@@ -273,13 +273,14 @@ const sanitizeSkillCatalogs = (value: unknown): DesktopSettings['skillCatalogs']
if (seen.has(id)) continue;
seen.add(id);
result.push({
const catalog: NonNullable<DesktopSettings['skillCatalogs']>[number] = {
id,
label,
source,
...(subpath ? { subpath } : {}),
...(gitIdentityId ? { gitIdentityId } : {}),
});
};
if (subpath) catalog.subpath = subpath;
if (gitIdentityId) catalog.gitIdentityId = gitIdentityId;
result.push(catalog);
}
return result;
@@ -393,7 +394,7 @@ const sanitizeProjects = (value: unknown): DesktopSettings['projects'] | undefin
project.icon = candidate.icon.trim();
}
if (candidate.iconImage === null) {
(project as unknown as Record<string, unknown>).iconImage = null;
project.iconImage = null;
} else if (candidate.iconImage && typeof candidate.iconImage === 'object') {
const iconImage = candidate.iconImage as Record<string, unknown>;
const mime = typeof iconImage.mime === 'string' ? iconImage.mime.trim() : '';
@@ -404,18 +405,18 @@ const sanitizeProjects = (value: unknown): DesktopSettings['projects'] | undefin
? iconImage.source
: null;
if (mime && updatedAt > 0 && source) {
(project as unknown as Record<string, unknown>).iconImage = { mime, updatedAt, source };
project.iconImage = { mime, updatedAt, source };
}
}
if (typeof candidate.color === 'string' && candidate.color.trim().length > 0) {
project.color = candidate.color.trim();
}
if (candidate.iconBackground === null) {
(project as unknown as Record<string, unknown>).iconBackground = null;
project.iconBackground = null;
} else {
const iconBackground = normalizeIconBackground(candidate.iconBackground);
if (iconBackground) {
(project as unknown as Record<string, unknown>).iconBackground = iconBackground;
project.iconBackground = iconBackground;
}
}
if (typeof candidate.addedAt === 'number' && Number.isFinite(candidate.addedAt) && candidate.addedAt >= 0) {
@@ -429,7 +430,7 @@ const sanitizeProjects = (value: unknown): DesktopSettings['projects'] | undefin
project.lastOpenedAt = candidate.lastOpenedAt;
}
if (typeof candidate.sidebarCollapsed === 'boolean') {
(project as unknown as Record<string, unknown>).sidebarCollapsed = candidate.sidebarCollapsed;
project.sidebarCollapsed = candidate.sidebarCollapsed;
}
result.push(project);
}
@@ -507,7 +508,7 @@ const sanitizeModelRefs = (value: unknown, limit: number): Array<{ providerID: s
};
const getPersistApi = (): PersistApi | undefined => {
const candidate = (useUIStore as unknown as { persist?: PersistApi }).persist;
const candidate = useUIStore.persist;
if (candidate && typeof candidate === 'object') {
return candidate;
}
@@ -1325,11 +1326,7 @@ const sanitizeWebSettings = (payload: unknown): DesktopSettings | null => {
for (const [providerId, config] of Object.entries(candidate.usageModelGroups)) {
if (config && typeof config === 'object') {
const typedConfig = config as Record<string, unknown>;
const providerConfig: {
customGroups?: Array<{id: string; label: string; models: string[]; order: number}>;
modelAssignments?: Record<string, string>;
renamedGroups?: Record<string, string>;
} = {};
const providerConfig: NonNullable<DesktopSettings['usageModelGroups']>[string] = {};
// Parse customGroups
if (Array.isArray(typedConfig.customGroups)) {
@@ -1875,7 +1872,7 @@ async function _flushSettingsUpdate(): Promise<void> {
return;
}
const updated = (await response.json().catch(() => null)) as DesktopSettings | null;
const updated = sanitizeWebSettings(await response.json().catch(() => null));
if (!isSettingsRuntimeContextCurrent(context)) return;
if (updated) {
applyDesktopUiPreferences(updated);