@@ -274,6 +274,164 @@ describe("setup.ts", () => {
274274 ) ;
275275 } ) ;
276276
277+ test ( "reloads config after migrating legacy user state" , async ( ) => {
278+ const initialConfig = {
279+ resetConfig : vi . fn ( ) ,
280+ } ;
281+ const migratedConfig = {
282+ get : vi . fn ( ) . mockReturnValue ( {
283+ environmentId : "env_123" ,
284+ appUrl : "https://my.url" ,
285+ environment : {
286+ data : { surveys : [ ] } ,
287+ expiresAt : new Date ( Date . now ( ) + 60_000 ) ,
288+ } ,
289+ user : {
290+ data : {
291+ userId : "user_abc" ,
292+ contactId : null ,
293+ segments : [ ] ,
294+ displays : [ ] ,
295+ responses : [ ] ,
296+ lastDisplayAt : null ,
297+ } ,
298+ expiresAt : null ,
299+ } ,
300+ filteredSurveys : [ ] ,
301+ status : { value : "success" , expiresAt : null } ,
302+ } ) ,
303+ update : vi . fn ( ) ,
304+ } ;
305+
306+ ( AsyncStorage . getItem as Mock ) . mockResolvedValueOnce (
307+ JSON . stringify ( {
308+ user : {
309+ data : {
310+ userId : "user_abc" ,
311+ contactId : null ,
312+ } ,
313+ } ,
314+ } )
315+ ) ;
316+ getInstanceConfigMock
317+ . mockReturnValueOnce ( initialConfig as unknown as Promise < RNConfig > )
318+ . mockReturnValueOnce ( migratedConfig as unknown as Promise < RNConfig > ) ;
319+ ( filterSurveys as unknown as Mock ) . mockReturnValueOnce ( [ ] ) ;
320+
321+ const result = await setup ( {
322+ environmentId : "env_123" ,
323+ appUrl : "https://my.url" ,
324+ } ) ;
325+
326+ expect ( result . ok ) . toBe ( true ) ;
327+ expect ( initialConfig . resetConfig ) . toHaveBeenCalledTimes ( 1 ) ;
328+ expect ( getInstanceConfigMock ) . toHaveBeenCalledTimes ( 2 ) ;
329+ expect ( migratedConfig . update ) . toHaveBeenCalled ( ) ;
330+ } ) ;
331+
332+ test ( "returns an error when environment sync fails" , async ( ) => {
333+ const mockConfig = {
334+ get : vi . fn ( ) . mockReturnValue ( {
335+ environmentId : "env_123" ,
336+ appUrl : "https://my.url" ,
337+ environment : {
338+ data : { surveys : [ ] } ,
339+ expiresAt : new Date ( Date . now ( ) - 5000 ) ,
340+ } ,
341+ user : {
342+ data : {
343+ userId : "user_abc" ,
344+ contactId : null ,
345+ segments : [ ] ,
346+ displays : [ ] ,
347+ responses : [ ] ,
348+ lastDisplayAt : null ,
349+ } ,
350+ expiresAt : new Date ( Date . now ( ) - 5000 ) ,
351+ } ,
352+ filteredSurveys : [ ] ,
353+ status : { value : "success" , expiresAt : null } ,
354+ } ) ,
355+ update : vi . fn ( ) ,
356+ } ;
357+
358+ getInstanceConfigMock . mockReturnValue (
359+ mockConfig as unknown as Promise < RNConfig >
360+ ) ;
361+ ( isNowExpired as unknown as Mock ) . mockReturnValue ( true ) ;
362+ ( fetchEnvironmentState as unknown as Mock ) . mockResolvedValueOnce ( {
363+ ok : false ,
364+ error : {
365+ code : "network_error" ,
366+ message : "Backend unavailable" ,
367+ } ,
368+ } ) ;
369+
370+ const result = await setup ( {
371+ environmentId : "env_123" ,
372+ appUrl : "https://my.url" ,
373+ } ) ;
374+
375+ expect ( result . ok ) . toBe ( false ) ;
376+ if ( ! result . ok ) {
377+ expect ( result . error . code ) . toBe ( "network_error" ) ;
378+ expect ( result . error . message ) . toBe ( "Error fetching environment state" ) ;
379+ }
380+ expect ( sendUpdatesToBackend ) . not . toHaveBeenCalled ( ) ;
381+ } ) ;
382+
383+ test ( "returns an error when user sync fails" , async ( ) => {
384+ const mockConfig = {
385+ get : vi . fn ( ) . mockReturnValue ( {
386+ environmentId : "env_123" ,
387+ appUrl : "https://my.url" ,
388+ environment : {
389+ data : { surveys : [ ] } ,
390+ expiresAt : new Date ( Date . now ( ) + 60_000 ) ,
391+ } ,
392+ user : {
393+ data : {
394+ userId : "user_abc" ,
395+ contactId : null ,
396+ segments : [ ] ,
397+ displays : [ ] ,
398+ responses : [ ] ,
399+ lastDisplayAt : null ,
400+ } ,
401+ expiresAt : new Date ( Date . now ( ) - 5000 ) ,
402+ } ,
403+ filteredSurveys : [ ] ,
404+ status : { value : "success" , expiresAt : null } ,
405+ } ) ,
406+ update : vi . fn ( ) ,
407+ } ;
408+
409+ getInstanceConfigMock . mockReturnValue (
410+ mockConfig as unknown as Promise < RNConfig >
411+ ) ;
412+ ( isNowExpired as unknown as Mock )
413+ . mockReturnValueOnce ( false )
414+ . mockReturnValueOnce ( true ) ;
415+ ( sendUpdatesToBackend as unknown as Mock ) . mockResolvedValueOnce ( {
416+ ok : false ,
417+ error : {
418+ code : "network_error" ,
419+ message : "User sync failed" ,
420+ } ,
421+ } ) ;
422+
423+ const result = await setup ( {
424+ environmentId : "env_123" ,
425+ appUrl : "https://my.url" ,
426+ } ) ;
427+
428+ expect ( result . ok ) . toBe ( false ) ;
429+ if ( ! result . ok ) {
430+ expect ( result . error . code ) . toBe ( "network_error" ) ;
431+ expect ( result . error . message ) . toBe ( "Error updating user state" ) ;
432+ }
433+ } ) ;
434+
277435 test ( "resets config if no valid config found, fetches environment, sets default user" , async ( ) => {
278436 const mockConfig = {
279437 get : ( ) => {
@@ -350,6 +508,33 @@ describe("setup.ts", () => {
350508 ) . rejects . toThrow ( "Could not set up formbricks" ) ;
351509 } ) ;
352510
511+ test ( "falls back to an unknown network error when setup throws a non-object" , async ( ) => {
512+ const mockConfig = {
513+ get : ( ) => {
514+ throw new Error ( "no config found" ) ;
515+ } ,
516+ update : vi . fn ( ) ,
517+ resetConfig : vi . fn ( ) ,
518+ } ;
519+
520+ getInstanceConfigMock . mockReturnValueOnce (
521+ mockConfig as unknown as Promise < RNConfig >
522+ ) ;
523+ ( fetchEnvironmentState as unknown as Mock ) . mockRejectedValueOnce ( "boom" ) ;
524+
525+ await expect (
526+ setup ( { environmentId : "envX" , appUrl : "https://urlX" } )
527+ ) . rejects . toThrow ( "Could not set up formbricks" ) ;
528+
529+ expect ( mockLogger . error ) . toHaveBeenCalledWith (
530+ "Error during first setup: network_error - Unknown error. Please try again later."
531+ ) ;
532+ expect ( AsyncStorage . setItem ) . toHaveBeenCalledWith (
533+ RN_ASYNC_STORAGE_KEY ,
534+ expect . stringContaining ( '"value":"error"' )
535+ ) ;
536+ } ) ;
537+
353538 test ( "adds event listeners and sets isSetup" , async ( ) => {
354539 const mockConfig = {
355540 get : vi . fn ( ) . mockReturnValue ( {
0 commit comments