@@ -17,15 +17,25 @@ package com.firebase.ui.auth.compose
1717import androidx.test.core.app.ApplicationProvider
1818import com.google.common.truth.Truth.assertThat
1919import com.google.firebase.FirebaseApp
20+ import com.google.firebase.FirebaseException
2021import com.google.firebase.FirebaseOptions
2122import 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
2229import org.junit.After
2330import org.junit.Before
2431import org.junit.Test
2532import org.junit.runner.RunWith
2633import org.mockito.Mock
2734import org.mockito.Mockito.`when`
2835import org.mockito.Mockito.mock
36+ import org.mockito.Mockito.verify
37+ import org.mockito.Mockito.doNothing
38+ import org.mockito.Mockito.doThrow
2939import org.mockito.MockitoAnnotations
3040import org.robolectric.RobolectricTestRunner
3141import org.robolectric.annotation.Config
@@ -323,4 +333,193 @@ class FirebaseAuthUITest {
323333 // Only one instance should be cached
324334 assertThat(FirebaseAuthUI .getCacheSize()).isEqualTo(1 )
325335 }
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+ }
326525}
0 commit comments