@@ -17,15 +17,25 @@ package com.firebase.ui.auth.compose
17
17
import androidx.test.core.app.ApplicationProvider
18
18
import com.google.common.truth.Truth.assertThat
19
19
import com.google.firebase.FirebaseApp
20
+ import com.google.firebase.FirebaseException
20
21
import com.google.firebase.FirebaseOptions
21
22
import com.google.firebase.auth.FirebaseAuth
23
+ import com.google.firebase.auth.FirebaseAuthRecentLoginRequiredException
24
+ import com.google.firebase.auth.FirebaseUser
25
+ import com.google.android.gms.tasks.Task
26
+ import com.google.android.gms.tasks.TaskCompletionSource
27
+ import kotlinx.coroutines.CancellationException
28
+ import kotlinx.coroutines.test.runTest
22
29
import org.junit.After
23
30
import org.junit.Before
24
31
import org.junit.Test
25
32
import org.junit.runner.RunWith
26
33
import org.mockito.Mock
27
34
import org.mockito.Mockito.`when`
28
35
import org.mockito.Mockito.mock
36
+ import org.mockito.Mockito.verify
37
+ import org.mockito.Mockito.doNothing
38
+ import org.mockito.Mockito.doThrow
29
39
import org.mockito.MockitoAnnotations
30
40
import org.robolectric.RobolectricTestRunner
31
41
import org.robolectric.annotation.Config
@@ -323,4 +333,193 @@ class FirebaseAuthUITest {
323
333
// Only one instance should be cached
324
334
assertThat(FirebaseAuthUI .getCacheSize()).isEqualTo(1 )
325
335
}
336
+
337
+ // =============================================================================================
338
+ // Sign Out Tests
339
+ // =============================================================================================
340
+
341
+ @Test
342
+ fun `signOut() successfully signs out user and updates state` () = runTest {
343
+ // Setup mock auth
344
+ val mockAuth = mock(FirebaseAuth ::class .java)
345
+ doNothing().`when `(mockAuth).signOut()
346
+
347
+ // Create instance with mock auth
348
+ val instance = FirebaseAuthUI .create(defaultApp, mockAuth)
349
+ val context = ApplicationProvider .getApplicationContext< android.content.Context > ()
350
+
351
+ // Perform sign out
352
+ instance.signOut(context)
353
+
354
+ // Verify signOut was called on Firebase Auth
355
+ verify(mockAuth).signOut()
356
+ }
357
+
358
+ @Test
359
+ fun `signOut() handles Firebase exception and maps to AuthException` () = runTest {
360
+ // Setup mock auth that throws exception
361
+ val mockAuth = mock(FirebaseAuth ::class .java)
362
+ val runtimeException = RuntimeException (" Network error" )
363
+ doThrow(runtimeException).`when `(mockAuth).signOut()
364
+
365
+ // Create instance with mock auth
366
+ val instance = FirebaseAuthUI .create(defaultApp, mockAuth)
367
+ val context = ApplicationProvider .getApplicationContext< android.content.Context > ()
368
+
369
+ // Perform sign out and expect exception
370
+ try {
371
+ instance.signOut(context)
372
+ assertThat(false ).isTrue() // Should not reach here
373
+ } catch (e: AuthException ) {
374
+ assertThat(e).isInstanceOf(AuthException .UnknownException ::class .java)
375
+ assertThat(e.cause).isEqualTo(runtimeException)
376
+ }
377
+ }
378
+
379
+ @Test
380
+ fun `signOut() handles cancellation and maps to AuthCancelledException` () = runTest {
381
+ // Setup mock auth
382
+ val mockAuth = mock(FirebaseAuth ::class .java)
383
+ val cancellationException = CancellationException (" Operation cancelled" )
384
+ doThrow(cancellationException).`when `(mockAuth).signOut()
385
+
386
+ // Create instance with mock auth
387
+ val instance = FirebaseAuthUI .create(defaultApp, mockAuth)
388
+ val context = ApplicationProvider .getApplicationContext< android.content.Context > ()
389
+
390
+ // Perform sign out and expect cancellation exception
391
+ try {
392
+ instance.signOut(context)
393
+ assertThat(false ).isTrue() // Should not reach here
394
+ } catch (e: AuthException .AuthCancelledException ) {
395
+ assertThat(e.message).contains(" cancelled" )
396
+ assertThat(e.cause).isInstanceOf(CancellationException ::class .java)
397
+ }
398
+ }
399
+
400
+ // =============================================================================================
401
+ // Delete Account Tests
402
+ // =============================================================================================
403
+
404
+ @Test
405
+ fun `delete() successfully deletes user account and updates state` () = runTest {
406
+ // Setup mock user and auth
407
+ val mockUser = mock(FirebaseUser ::class .java)
408
+ val mockAuth = mock(FirebaseAuth ::class .java)
409
+ val taskCompletionSource = TaskCompletionSource <Void >()
410
+ taskCompletionSource.setResult(null ) // Simulate successful deletion
411
+
412
+ `when `(mockAuth.currentUser).thenReturn(mockUser)
413
+ `when `(mockUser.delete()).thenReturn(taskCompletionSource.task)
414
+
415
+ // Create instance with mock auth
416
+ val instance = FirebaseAuthUI .create(defaultApp, mockAuth)
417
+ val context = ApplicationProvider .getApplicationContext< android.content.Context > ()
418
+
419
+ // Perform delete
420
+ instance.delete(context)
421
+
422
+ // Verify delete was called on user
423
+ verify(mockUser).delete()
424
+ }
425
+
426
+ @Test
427
+ fun `delete() throws UserNotFoundException when no user is signed in` () = runTest {
428
+ // Setup mock auth with no current user
429
+ val mockAuth = mock(FirebaseAuth ::class .java)
430
+ `when `(mockAuth.currentUser).thenReturn(null )
431
+
432
+ // Create instance with mock auth
433
+ val instance = FirebaseAuthUI .create(defaultApp, mockAuth)
434
+ val context = ApplicationProvider .getApplicationContext< android.content.Context > ()
435
+
436
+ // Perform delete and expect exception
437
+ try {
438
+ instance.delete(context)
439
+ assertThat(false ).isTrue() // Should not reach here
440
+ } catch (e: AuthException .UserNotFoundException ) {
441
+ assertThat(e.message).contains(" No user is currently signed in" )
442
+ }
443
+ }
444
+
445
+ @Test
446
+ fun `delete() handles recent login required exception` () = runTest {
447
+ // Setup mock user and auth
448
+ val mockUser = mock(FirebaseUser ::class .java)
449
+ val mockAuth = mock(FirebaseAuth ::class .java)
450
+ val taskCompletionSource = TaskCompletionSource <Void >()
451
+ val recentLoginException = FirebaseAuthRecentLoginRequiredException (
452
+ " ERROR_REQUIRES_RECENT_LOGIN" ,
453
+ " Recent login required"
454
+ )
455
+ taskCompletionSource.setException(recentLoginException)
456
+
457
+ `when `(mockAuth.currentUser).thenReturn(mockUser)
458
+ `when `(mockUser.delete()).thenReturn(taskCompletionSource.task)
459
+
460
+ // Create instance with mock auth
461
+ val instance = FirebaseAuthUI .create(defaultApp, mockAuth)
462
+ val context = ApplicationProvider .getApplicationContext< android.content.Context > ()
463
+
464
+ // Perform delete and expect mapped exception
465
+ try {
466
+ instance.delete(context)
467
+ assertThat(false ).isTrue() // Should not reach here
468
+ } catch (e: AuthException .InvalidCredentialsException ) {
469
+ assertThat(e.message).contains(" Recent login required" )
470
+ assertThat(e.cause).isEqualTo(recentLoginException)
471
+ }
472
+ }
473
+
474
+ @Test
475
+ fun `delete() handles cancellation and maps to AuthCancelledException` () = runTest {
476
+ // Setup mock user and auth
477
+ val mockUser = mock(FirebaseUser ::class .java)
478
+ val mockAuth = mock(FirebaseAuth ::class .java)
479
+ val taskCompletionSource = TaskCompletionSource <Void >()
480
+ val cancellationException = CancellationException (" Operation cancelled" )
481
+ taskCompletionSource.setException(cancellationException)
482
+
483
+ `when `(mockAuth.currentUser).thenReturn(mockUser)
484
+ `when `(mockUser.delete()).thenReturn(taskCompletionSource.task)
485
+
486
+ // Create instance with mock auth
487
+ val instance = FirebaseAuthUI .create(defaultApp, mockAuth)
488
+ val context = ApplicationProvider .getApplicationContext< android.content.Context > ()
489
+
490
+ // Perform delete and expect cancellation exception
491
+ try {
492
+ instance.delete(context)
493
+ assertThat(false ).isTrue() // Should not reach here
494
+ } catch (e: AuthException .AuthCancelledException ) {
495
+ assertThat(e.message).contains(" cancelled" )
496
+ assertThat(e.cause).isInstanceOf(CancellationException ::class .java)
497
+ }
498
+ }
499
+
500
+ @Test
501
+ fun `delete() handles Firebase network exception` () = runTest {
502
+ // Setup mock user and auth
503
+ val mockUser = mock(FirebaseUser ::class .java)
504
+ val mockAuth = mock(FirebaseAuth ::class .java)
505
+ val taskCompletionSource = TaskCompletionSource <Void >()
506
+ val networkException = FirebaseException (" Network error" )
507
+ taskCompletionSource.setException(networkException)
508
+
509
+ `when `(mockAuth.currentUser).thenReturn(mockUser)
510
+ `when `(mockUser.delete()).thenReturn(taskCompletionSource.task)
511
+
512
+ // Create instance with mock auth
513
+ val instance = FirebaseAuthUI .create(defaultApp, mockAuth)
514
+ val context = ApplicationProvider .getApplicationContext< android.content.Context > ()
515
+
516
+ // Perform delete and expect mapped exception
517
+ try {
518
+ instance.delete(context)
519
+ assertThat(false ).isTrue() // Should not reach here
520
+ } catch (e: AuthException .NetworkException ) {
521
+ assertThat(e.message).contains(" Network error" )
522
+ assertThat(e.cause).isEqualTo(networkException)
523
+ }
524
+ }
326
525
}
0 commit comments