feat: replace Code conversation archive with deletion

This commit is contained in:
鲨鱼辣椒 committed 2026-10-14 11:09:51 +08:00
1 parent 140f6be8db
commit bf101c1e52
19 files changed
+1001 -292

No files matched your search

+55
View File
@@ -79,6 +79,61 @@ function deferred<T>() {
}
describe('coding workspace store', () => {
it('deletes only the target and rejects stale list and metadata resurrection', async () => {
const first = conversation('conversation-deleted', 'agent-a');
const second = conversation('conversation-kept', 'agent-a');
const stale = deferred<CodingConversationMetadata[]>();
const removeConversation = vi.fn().mockResolvedValue(undefined);
const listConversations = vi.fn().mockResolvedValueOnce([first, second])
.mockReturnValueOnce(stale.promise).mockResolvedValue([second]);
const store = createCodingWorkspaceStore({
listProjects: async () => ({ projects: [project], activeProjectId: project.id }),
getConfig: async () => ({ project, config: config([agent('agent-a')]) }),
listConversations, removeConversation,
});
await store.getState().load();
const refresh = store.getState().refreshConversations(project.id);
await vi.waitFor(() => expect(listConversations).toHaveBeenCalledTimes(2));
await store.getState().removeConversation(first.id);
stale.resolve([first, second]);
await refresh;
store.getState().upsertConversation(project.id, first);
expect(removeConversation).toHaveBeenCalledWith(first.id);
expect(store.getState().conversations).toEqual([second]);
});
it('keeps a conversation available when deletion fails', async () => {
const first = conversation('conversation-delete-failed', 'agent-a');
const store = createCodingWorkspaceStore({
listProjects: async () => ({ projects: [project], activeProjectId: project.id }),
getConfig: async () => ({ project, config: config([agent('agent-a')]) }),
listConversations: async () => [first],
removeConversation: async () => { throw new Error('delete failed'); },
});
await store.getState().load();
await expect(store.getState().removeConversation(first.id)).rejects.toThrow('delete failed');
expect(store.getState().conversations).toEqual([first]);
expect(store.getState().conversationErrorsByProjectId[project.id][first.id]).toBe('delete failed');
});
it('does not overwrite another project after a pending deletion completes', async () => {
const first = conversation('conversation-old-project', 'agent-a');
const other = conversation('conversation-new-project', 'agent-b');
const deletion = deferred<void>();
const store = createCodingWorkspaceStore({
listProjects: async () => ({ projects: [project], activeProjectId: project.id }),
getConfig: async () => ({ project, config: config([agent('agent-a')]) }),
listConversations: async () => [first],
removeConversation: () => deletion.promise,
});
await store.getState().load();
const pending = store.getState().removeConversation(first.id);
store.setState({ activeProjectId: 'other-project', conversations: [other] });
deletion.resolve();
await pending;
expect(store.getState().conversations).toEqual([other]);
});
it('discards a stale list read and refreshes after a manual rename', async () => {
const old = conversation('conversation-a', 'agent-a');
const stale = deferred<CodingConversationMetadata[]>();