From 1b36d1daa683290c7a252e3b1b67f14c74a8ecf8 Mon Sep 17 00:00:00 2001 From: 777genius Date: Tue, 26 May 2026 18:47:16 +0300 Subject: [PATCH] fix(context): clear project loading on ssh reset --- src/renderer/store/slices/connectionSlice.ts | 12 ++++ .../store/contextSliceTeamReset.test.ts | 68 +++++++++++++++++++ 2 files changed, 80 insertions(+) diff --git a/src/renderer/store/slices/connectionSlice.ts b/src/renderer/store/slices/connectionSlice.ts index 7b551f0f..461a1cad 100644 --- a/src/renderer/store/slices/connectionSlice.ts +++ b/src/renderer/store/slices/connectionSlice.ts @@ -85,7 +85,13 @@ export const createConnectionSlice: StateCreator(): { return { promise, resolve }; } +async function flushMicrotasks(): Promise { + await Promise.resolve(); + await Promise.resolve(); + await Promise.resolve(); +} + describe('context slice team/task reset', () => { beforeEach(() => { vi.clearAllMocks(); @@ -374,6 +380,40 @@ describe('context slice team/task reset', () => { expect(apiMock.teams.getAllTasks).toHaveBeenCalledTimes(1); }); + it('clears project and repository loading guards before direct SSH connect refetches', async () => { + const projectScan = deferred(); + const repositoryScan = deferred(); + apiMock.getProjects.mockReturnValue(projectScan.promise); + apiMock.getRepositoryGroups.mockReturnValue(repositoryScan.promise); + const store = createTestStore(); + store.setState({ + activeContextId: 'local', + projectsLoading: true, + repositoryGroupsLoading: true, + } as never); + + await store.getState().connectSsh({ + host: 'dev', + port: 22, + username: 'me', + authMethod: 'privateKey', + privateKeyPath: '/tmp/key', + }); + + expect(apiMock.getProjects).toHaveBeenCalledTimes(1); + expect(apiMock.getRepositoryGroups).toHaveBeenCalledTimes(1); + expect(store.getState().projectsLoading).toBe(true); + expect(store.getState().repositoryGroupsLoading).toBe(true); + + projectScan.resolve([]); + repositoryScan.resolve([]); + await Promise.all([projectScan.promise, repositoryScan.promise]); + await flushMicrotasks(); + + expect(store.getState().projectsLoading).toBe(false); + expect(store.getState().repositoryGroupsLoading).toBe(false); + }); + it('drops previous-context team and task caches on direct SSH disconnect', async () => { const store = createTestStore(); store.setState({ @@ -419,4 +459,32 @@ describe('context slice team/task reset', () => { expect(apiMock.teams.list).toHaveBeenCalledTimes(1); expect(apiMock.teams.getAllTasks).toHaveBeenCalledTimes(1); }); + + it('clears project and repository loading guards before direct SSH disconnect refetches', async () => { + const projectScan = deferred(); + const repositoryScan = deferred(); + apiMock.getProjects.mockReturnValue(projectScan.promise); + apiMock.getRepositoryGroups.mockReturnValue(repositoryScan.promise); + const store = createTestStore(); + store.setState({ + activeContextId: 'ssh-dev', + projectsLoading: true, + repositoryGroupsLoading: true, + } as never); + + await store.getState().disconnectSsh(); + + expect(apiMock.getProjects).toHaveBeenCalledTimes(1); + expect(apiMock.getRepositoryGroups).toHaveBeenCalledTimes(1); + expect(store.getState().projectsLoading).toBe(true); + expect(store.getState().repositoryGroupsLoading).toBe(true); + + projectScan.resolve([]); + repositoryScan.resolve([]); + await Promise.all([projectScan.promise, repositoryScan.promise]); + await flushMicrotasks(); + + expect(store.getState().projectsLoading).toBe(false); + expect(store.getState().repositoryGroupsLoading).toBe(false); + }); });