@@ -3,7 +3,7 @@ import type { AxiosResponse } from 'axios'
33import { AxiosError } from 'axios'
44import { setActivePinia } from 'pinia'
55import { beforeEach , describe , expect , it , vi } from 'vitest'
6- import { nextTick , ref } from 'vue'
6+ import { ref } from 'vue'
77
88import { mergeCustomNodesI18n } from '@/i18n'
99import { useSettingStore } from '@/platform/settings/settingStore'
@@ -68,6 +68,16 @@ const mockDistributionTypes = vi.hoisted(() => ({
6868} ) )
6969vi . mock ( '@/platform/distribution/types' , ( ) => mockDistributionTypes )
7070
71+ const mockCaptureException = vi . hoisted ( ( ) => vi . fn ( ) )
72+ vi . mock ( '@sentry/vue' , ( ) => ( {
73+ captureException : mockCaptureException
74+ } ) )
75+
76+ const mockAddError = vi . hoisted ( ( ) => vi . fn ( ) )
77+ vi . mock ( '@datadog/browser-rum' , ( ) => ( {
78+ datadogRum : { addError : mockAddError }
79+ } ) )
80+
7181function requestFailure ( status : number ) {
7282 const error = new AxiosError ( `Request failed with status code ${ status } ` )
7383 error . response = { status } as AxiosResponse
@@ -135,31 +145,77 @@ describe('bootstrapStore', () => {
135145 describe ( 'cloud mode' , ( ) => {
136146 beforeEach ( ( ) => {
137147 mockDistributionTypes . isCloud = true
148+ mockCaptureException . mockReset ( )
149+ mockAddError . mockReset ( )
138150 } )
139151
140- it ( 'waits for Firebase auth before loading stores' , async ( ) => {
152+ it ( 'waits for Firebase init before loading stores, then proceeds regardless of auth state ' , async ( ) => {
141153 const store = useBootstrapStore ( )
142154 const settingStore = useSettingStore ( )
143155 const bootstrapPromise = store . startStoreBootstrap ( )
144156
145157 expect ( store . isI18nReady ) . toBe ( false )
146158 expect ( settingStore . isReady ) . toBe ( false )
147159
148- // Firebase initialized but user not yet authenticated
160+ // Firebase resolves with no user (signed-out) — bootstrap must unblock.
161+ // Previously it also waited for isAuthenticated, which made every
162+ // signed-out load wait 35s and fire a false Sentry timeout.
149163 mockIsAuthInitialized . value = true
150- await nextTick ( )
151-
152- expect ( store . isI18nReady ) . toBe ( false )
153- expect ( settingStore . isReady ) . toBe ( false )
154-
155- // User authenticates (e.g. signs in on login page)
156- mockIsAuthAuthenticated . value = true
157164 await bootstrapPromise
158165
159166 await vi . waitFor ( ( ) => {
160167 expect ( store . isI18nReady ) . toBe ( true )
161168 expect ( settingStore . isReady ) . toBe ( true )
162169 } )
163170 } )
171+
172+ it ( 'retries once and proceeds if Firebase init resolves during the backoff' , async ( ) => {
173+ vi . useFakeTimers ( )
174+ try {
175+ const store = useBootstrapStore ( )
176+ const settingStore = useSettingStore ( )
177+ const bootstrapPromise = store . startStoreBootstrap ( )
178+
179+ // First wait times out with Firebase still not initialized.
180+ await vi . advanceTimersByTimeAsync ( 16_001 )
181+ expect ( settingStore . isReady ) . toBe ( false )
182+
183+ // Firebase resolves during the retry backoff.
184+ mockIsAuthInitialized . value = true
185+ await vi . advanceTimersByTimeAsync ( 3_001 )
186+ await bootstrapPromise
187+
188+ expect ( settingStore . isReady ) . toBe ( true )
189+ expect ( mockCaptureException ) . not . toHaveBeenCalled ( )
190+ } finally {
191+ vi . useRealTimers ( )
192+ }
193+ } )
194+
195+ it ( 'gives up after a second timeout, reports it, and continues bootstrap unauthenticated' , async ( ) => {
196+ vi . useFakeTimers ( )
197+ try {
198+ const store = useBootstrapStore ( )
199+ const settingStore = useSettingStore ( )
200+ const bootstrapPromise = store . startStoreBootstrap ( )
201+
202+ // Firebase never resolves through the initial wait, the backoff, or the retry.
203+ await vi . advanceTimersByTimeAsync ( 16_000 + 3_000 + 16_001 )
204+ await bootstrapPromise
205+
206+ expect ( mockCaptureException ) . toHaveBeenCalledOnce ( )
207+ expect ( mockCaptureException ) . toHaveBeenCalledWith ( expect . any ( Error ) , {
208+ tags : { error_type : 'bootstrap_auth_wait_timeout' }
209+ } )
210+ expect ( mockAddError ) . toHaveBeenCalledOnce ( )
211+ expect ( mockAddError ) . toHaveBeenCalledWith ( expect . any ( Error ) , {
212+ error_type : 'bootstrap_auth_wait_timeout'
213+ } )
214+ // Bootstrap must not stay stuck: stores load even when Firebase never fires.
215+ expect ( settingStore . isReady ) . toBe ( true )
216+ } finally {
217+ vi . useRealTimers ( )
218+ }
219+ } )
164220 } )
165221} )
0 commit comments