fix(settings): case sync tests follow lastPickedCase, a failed pick is not undone, rollback is covered

- sync-case-state browser and stale-refresh tests use lastPickedCase (the stale test now fails without its guard)
- saveLastUsedCase runs on the case-sync chain, and the follow is skipped while this device's own pick was not stored
- _caseStateSyncOn tolerates a harness without loadAppSettingsFromStorage
- lastPickedCase added to the config-backup volatile-key test and docs; rename rollback tests cover caseSettings and lastPickedCase
- wiki row moved to System
This commit is contained in:
Devvyn committed 2026-11-09 12:31:30 +08:00
1 parent 4808ee7085
commit bb088bcd3e
9 files changed
+104 -32

No files matched your search

+34 -7
View File
@@ -302,7 +302,13 @@ describe('POST /api/cases/:name/rename (a linked case)', () => {
const real = join(work, 'my-project');
mkdirSync(real);
writeFileSync(LINKED(), JSON.stringify({ proj: real }));
writeFileSync(SETTINGS_PATH, JSON.stringify({ caseOrder: ['a', 'proj', 'b'], lastUsedCase: 'proj' }));
const seeded = {
caseOrder: ['a', 'proj', 'b'],
lastUsedCase: 'proj',
lastPickedCase: 'proj',
caseSettings: { proj: { agentTeams: true, opusContext1m: false } },
};
writeFileSync(SETTINGS_PATH, JSON.stringify(seeded));
// the registry reads fine but cannot be written: the rename must undo the folder move
const spy = failRegistryWrite();
let res;
@@ -316,7 +322,7 @@ describe('POST /api/cases/:name/rename (a linked case)', () => {
expect(linked()).toEqual({ proj: real });
expect(existsSync(join(work, 'renamed'))).toBe(false);
// settings.json is written first, so it must be put back too
expect(settings()).toEqual({ caseOrder: ['a', 'proj', 'b'], lastUsedCase: 'proj' });
expect(settings()).toEqual(seeded);
});
it('puts back only the keys it wrote, keeping a settings write made in between', async () => {
@@ -324,17 +330,38 @@ describe('POST /api/cases/:name/rename (a linked case)', () => {
const real = join(work, 'my-project');
mkdirSync(real);
writeFileSync(LINKED(), JSON.stringify({ proj: real }));
writeFileSync(SETTINGS_PATH, JSON.stringify({ caseOrder: ['a', 'proj', 'b'], lastUsedCase: 'proj' }));
// Another writer (a settings save) lands after the rename wrote settings.json and before the
// registry write fails.
const spy = failRegistryWrite(() => mutateSettingsFile((current) => ({ ...current, agentSkillEnabled: true })));
const entry = { agentTeams: true, opusContext1m: false };
writeFileSync(
SETTINGS_PATH,
JSON.stringify({
caseOrder: ['a', 'proj', 'b'],
lastUsedCase: 'proj',
lastPickedCase: 'proj',
caseSettings: { proj: entry },
})
);
// Another writer (a settings save, and a per-case save from another device) lands after the rename wrote
// settings.json and before the registry write fails.
const spy = failRegistryWrite(() =>
mutateSettingsFile((current) => ({
...current,
agentSkillEnabled: true,
caseSettings: { ...(current.caseSettings as object), other: { agentTeams: false, opusContext1m: true } },
}))
);
try {
const res = await rename(app, 'proj', { newName: 'renamed', renameFolder: true });
expect(res.statusCode).toBe(500);
} finally {
spy.mockRestore();
}
expect(settings()).toEqual({ caseOrder: ['a', 'proj', 'b'], lastUsedCase: 'proj', agentSkillEnabled: true });
expect(settings()).toEqual({
caseOrder: ['a', 'proj', 'b'],
lastUsedCase: 'proj',
lastPickedCase: 'proj',
caseSettings: { proj: entry, other: { agentTeams: false, opusContext1m: true } },
agentSkillEnabled: true,
});
});
describe('when the registry changes after the first check', () => {