From f1fdce1743fb030a7aac47dcf4da07a3e4d7de95 Mon Sep 17 00:00:00 2001 From: Jinwoo-H Date: Wed, 16 Sep 2026 16:43:00 -0400 Subject: [PATCH] test(cloud-auth): cover post-exchange stale connect and pending Sign in Pin the branch that discards an earlier token exchange after a later wait has already linked, keep Sign in enabled while connect is still pending, and suppress a failed toast when auth is already connected. --- ...file-cloud-service-connect-overlap.test.ts | 89 +++++++++++++++++++ .../settings/OrcaAccountSettingsPane.test.tsx | 1 + .../slices/orca-profiles-auth-actions.ts | 6 +- 3 files changed, 95 insertions(+), 1 deletion(-) diff --git a/src/main/orca-profiles/profile-cloud-service-connect-overlap.test.ts b/src/main/orca-profiles/profile-cloud-service-connect-overlap.test.ts index 3b4504903b6..3321fa06877 100644 --- a/src/main/orca-profiles/profile-cloud-service-connect-overlap.test.ts +++ b/src/main/orca-profiles/profile-cloud-service-connect-overlap.test.ts @@ -134,4 +134,93 @@ describe('Orca cloud overlapping connect', () => { ) expect(getCurrentOrcaProfileAuthStatus(userDataPath).cloud?.email).toBe('ada@example.com') }) + + it('discards an earlier token exchange that finishes after a later wait has linked', async () => { + type PkceCode = { + code: string + codeVerifier: string + nonce: string + redirectUri: string + state: string + } + let finishEarlierPkce!: (value: PkceCode) => void + let finishLaterPkce!: (value: PkceCode) => void + let finishEarlierExchange!: (value: { + accessToken: string + refreshToken: string + expiresAt: number + cloud: OrcaProfileCloudSummary + organizations: OrcaCloudOrgSummary[] + capabilities: OrcaCloudCapabilities + }) => void + let finishLaterExchange!: (value: { + accessToken: string + refreshToken: string + expiresAt: number + cloud: OrcaProfileCloudSummary + organizations: OrcaCloudOrgSummary[] + capabilities: OrcaCloudCapabilities + }) => void + beginOrcaCloudPkceFlowMock + .mockReturnValueOnce( + new Promise((resolve) => { + finishEarlierPkce = resolve + }) + ) + .mockReturnValueOnce( + new Promise((resolve) => { + finishLaterPkce = resolve + }) + ) + exchangeOrcaCloudAuthCodeMock.mockImplementation( + (_config, args) => + new Promise((resolve) => { + if (args.code === 'later-code') { + finishLaterExchange = resolve + } else { + finishEarlierExchange = resolve + } + }) + ) + + const earlier = connectCurrentOrcaProfile(userDataPath) + const later = connectCurrentOrcaProfile(userDataPath) + finishEarlierPkce({ + code: 'earlier-code', + codeVerifier: 'earlier-verifier', + nonce: 'earlier-nonce', + redirectUri: 'http://127.0.0.1:4100/auth/callback', + state: 'earlier-state' + }) + finishLaterPkce({ + code: 'later-code', + codeVerifier: 'later-verifier', + nonce: 'later-nonce', + redirectUri: 'http://127.0.0.1:4101/auth/callback', + state: 'later-state' + }) + await vi.waitFor(() => expect(exchangeOrcaCloudAuthCodeMock).toHaveBeenCalledTimes(2)) + + finishLaterExchange({ + accessToken: 'later-access', + refreshToken: 'later-refresh', + expiresAt: Date.now() + 3_600_000, + cloud: laterCloud, + organizations, + capabilities + }) + await expect(later).resolves.toMatchObject({ status: 'connected' }) + expect(getCurrentOrcaProfileAuthStatus(userDataPath).cloud?.email).toBe('ada@example.com') + + finishEarlierExchange({ + accessToken: 'earlier-access', + refreshToken: 'earlier-refresh', + expiresAt: Date.now() + 3_600_000, + cloud: earlierCloud, + organizations, + capabilities + }) + await expect(earlier).resolves.toMatchObject({ status: 'cancelled' }) + expect(getCurrentOrcaProfileAuthStatus(userDataPath).cloud?.email).toBe('ada@example.com') + }) }) diff --git a/src/renderer/src/components/settings/OrcaAccountSettingsPane.test.tsx b/src/renderer/src/components/settings/OrcaAccountSettingsPane.test.tsx index 90a909487e4..1b3f35c4360 100644 --- a/src/renderer/src/components/settings/OrcaAccountSettingsPane.test.tsx +++ b/src/renderer/src/components/settings/OrcaAccountSettingsPane.test.tsx @@ -87,6 +87,7 @@ describe('OrcaAccountSettingsPane', () => { it('offers sign in for a local profile', async () => { const user = userEvent.setup() mocks.state.orcaProfileAuthStatus = { configured: true, state: 'local' } + mocks.connect.mockReturnValue(new Promise(() => {})) render() expect( diff --git a/src/renderer/src/store/slices/orca-profiles-auth-actions.ts b/src/renderer/src/store/slices/orca-profiles-auth-actions.ts index 7222fb67922..93218c87645 100644 --- a/src/renderer/src/store/slices/orca-profiles-auth-actions.ts +++ b/src/renderer/src/store/slices/orca-profiles-auth-actions.ts @@ -113,7 +113,11 @@ export const createOrcaProfilesAuthActions: StateCreator< description: result.auth.setupMessage } ) - } else if (result.status === 'failed' && !alreadyConnected) { + } else if ( + result.status === 'failed' && + !alreadyConnected && + result.auth.state !== 'connected' + ) { toast.error( translate('auto.store.slices.orca.profiles.33290e88ed', 'Failed to connect profile'), { description: result.error }