@@ -3070,8 +3070,10 @@ public class WebAuthProviderTest {
30703070 verify(options, Mockito .never()).copyWithEphemeralBrowsing()
30713071 }
30723072
3073+ // --- LifecycleAwareCallback tests ---
3074+
30733075 @Test
3074- public fun shouldCacheLoginResultWhenLifecycleCallbackIsDetachedOnDestroy () {
3076+ public fun shouldInvokeOnDetachedWithLoginResultAfterDestroy () {
30753077 val credentials = Mockito .mock(Credentials ::class .java)
30763078 var capturedSuccess: Credentials ? = null
30773079 var capturedError: AuthenticationException ? = null
@@ -3094,11 +3096,11 @@ public class WebAuthProviderTest {
30943096
30953097 Assert .assertEquals(credentials, capturedSuccess)
30963098 Assert .assertNull(capturedError)
3097- verify(callback, Mockito .never()).onSuccess(any()) // old callback never called
3099+ verify(callback, Mockito .never()).onSuccess(any())
30983100 }
30993101
31003102 @Test
3101- public fun shouldCacheLoginFailureWhenLifecycleCallbackIsDetachedOnDestroy () {
3103+ public fun shouldInvokeOnDetachedWithLoginFailureAfterDestroy () {
31023104 val error = AuthenticationException (" canceled" , " User canceled" )
31033105 var capturedSuccess: Credentials ? = null
31043106 var capturedError: AuthenticationException ? = null
@@ -3167,8 +3169,8 @@ public class WebAuthProviderTest {
31673169 }
31683170
31693171 @Test
3170- public fun shouldCacheLogoutSuccessWhenLifecycleCallbackIsDetachedOnDestroy () {
3171- var capturedSuccess = false
3172+ public fun shouldInvokeOnDetachedWithLogoutSuccessAfterDestroy () {
3173+ var onDetachedCalled = false
31723174 var capturedError: AuthenticationException ? = null
31733175
31743176 val lifecycleOwner = Mockito .mock(LifecycleOwner ::class .java)
@@ -3179,23 +3181,23 @@ public class WebAuthProviderTest {
31793181 delegateCallback = voidCallback,
31803182 lifecycleOwner = lifecycleOwner,
31813183 onDetached = { _, error ->
3184+ onDetachedCalled = true
31823185 capturedError = error
3183- if (error == null ) capturedSuccess = true
31843186 }
31853187 )
31863188
31873189 lifecycleCallback.onDestroy(lifecycleOwner)
31883190 lifecycleCallback.onSuccess(null )
31893191
3190- Assert .assertTrue(capturedSuccess )
3192+ Assert .assertTrue(onDetachedCalled )
31913193 Assert .assertNull(capturedError)
31923194 verify(voidCallback, Mockito .never()).onSuccess(any())
31933195 }
31943196
31953197 @Test
3196- public fun shouldCacheLogoutFailureWhenLifecycleCallbackIsDetachedOnDestroy () {
3198+ public fun shouldInvokeOnDetachedWithLogoutFailureAfterDestroy () {
31973199 val error = AuthenticationException (" canceled" , " User closed the browser" )
3198- var capturedSuccess = false
3200+ var onDetachedCalled = false
31993201 var capturedError: AuthenticationException ? = null
32003202
32013203 val lifecycleOwner = Mockito .mock(LifecycleOwner ::class .java)
@@ -3206,15 +3208,15 @@ public class WebAuthProviderTest {
32063208 delegateCallback = voidCallback,
32073209 lifecycleOwner = lifecycleOwner,
32083210 onDetached = { _, detachedError ->
3211+ onDetachedCalled = true
32093212 capturedError = detachedError
3210- if (detachedError == null ) capturedSuccess = true
32113213 }
32123214 )
32133215
32143216 lifecycleCallback.onDestroy(lifecycleOwner)
32153217 lifecycleCallback.onFailure(error)
32163218
3217- Assert .assertFalse(capturedSuccess )
3219+ Assert .assertTrue(onDetachedCalled )
32183220 Assert .assertEquals(error, capturedError)
32193221 verify(voidCallback, Mockito .never()).onFailure(any())
32203222 }
@@ -3271,25 +3273,72 @@ public class WebAuthProviderTest {
32713273 verify(lifecycle).removeObserver(lifecycleCallback)
32723274 }
32733275
3276+
32743277 @Test
3275- public fun shouldClearStaleResultWhenNewLoginStarts () {
3276- // Start a new login — should not crash or deliver stale data
3277- login(account).start(activity, callback)
3278+ public fun shouldWrapCallbackWithLifecycleAwareCallbackWhenStartedWithLifecycleOwner () {
3279+ val lifecycle = Mockito .mock(Lifecycle ::class .java)
3280+ val lifecycleActivity = Mockito .mock(TestLifecycleOwnerActivity ::class .java)
3281+ Mockito .`when `(lifecycleActivity.lifecycle).thenReturn(lifecycle)
3282+ Mockito .`when `(lifecycleActivity.applicationContext).thenReturn(lifecycleActivity)
3283+ Mockito .`when `(lifecycleActivity.packageName).thenReturn(" com.auth0.test" )
3284+ Mockito .doReturn(false ).`when `(lifecycleActivity).bindService(any(), any(), ArgumentMatchers .anyInt())
3285+ BrowserPickerTest .setupBrowserContext(lifecycleActivity, listOf (" com.auth0.browser" ), null , null )
32783286
3279- // The new callback should not have received any stale result
3280- verify(callback, Mockito .never()).onSuccess(any())
3287+ login(account).start(lifecycleActivity, callback)
3288+
3289+ verify(lifecycle).addObserver(any<LifecycleAwareCallback <Credentials >>())
32813290 }
32823291
3292+
32833293 @Test
3284- public fun shouldClearStaleResultWhenNewLogoutStarts () {
3285- // Start a new logout — should not crash or deliver stale data
3286- logout(account).start(activity, voidCallback)
3294+ public fun shouldRegisterLifecycleObserverOnRegisterCallbacks () {
3295+ val lifecycleOwner = Mockito .mock(LifecycleOwner ::class .java)
3296+ val lifecycle = Mockito .mock(Lifecycle ::class .java)
3297+ Mockito .`when `(lifecycleOwner.lifecycle).thenReturn(lifecycle)
32873298
3288- verify(voidCallback, Mockito .never()).onSuccess(any())
3299+ WebAuthProvider .registerCallbacks(lifecycleOwner, loginCallback = callback, logoutCallback = voidCallback)
3300+
3301+ verify(lifecycle).addObserver(isA<DefaultLifecycleObserver >())
32893302 }
32903303
32913304 @Test
3292- public fun shouldNotDeliverLoginResultOnResumeWhenNoPendingResult () {
3305+ public fun shouldRemoveLifecycleObserverOnDestroyAfterRegisterCallbacks () {
3306+ val lifecycleOwner = Mockito .mock(LifecycleOwner ::class .java)
3307+ val lifecycle = Mockito .mock(Lifecycle ::class .java)
3308+ Mockito .`when `(lifecycleOwner.lifecycle).thenReturn(lifecycle)
3309+
3310+ val observerCaptor = argumentCaptor<DefaultLifecycleObserver >()
3311+ WebAuthProvider .registerCallbacks(lifecycleOwner, loginCallback = callback, logoutCallback = voidCallback)
3312+ verify(lifecycle).addObserver(observerCaptor.capture())
3313+
3314+ observerCaptor.firstValue.onDestroy(lifecycleOwner)
3315+
3316+ verify(lifecycle).removeObserver(observerCaptor.firstValue)
3317+ }
3318+
3319+ @Test
3320+ public fun shouldRemoveLoginCallbackFromCallbacksOnDestroy () {
3321+ val lifecycleOwner = Mockito .mock(LifecycleOwner ::class .java)
3322+ val lifecycle = Mockito .mock(Lifecycle ::class .java)
3323+ Mockito .`when `(lifecycleOwner.lifecycle).thenReturn(lifecycle)
3324+
3325+ val observerCaptor = argumentCaptor<DefaultLifecycleObserver >()
3326+ WebAuthProvider .registerCallbacks(lifecycleOwner, loginCallback = callback, logoutCallback = voidCallback)
3327+ verify(lifecycle).addObserver(observerCaptor.capture())
3328+
3329+ Assert .assertEquals(1 , WebAuthProvider .callbacksCount())
3330+
3331+ observerCaptor.firstValue.onDestroy(lifecycleOwner)
3332+
3333+ Assert .assertEquals(0 , WebAuthProvider .callbacksCount())
3334+ }
3335+
3336+
3337+ @Test
3338+ public fun shouldDeliverPendingLoginResultOnResume () {
3339+ val credentials = Mockito .mock(Credentials ::class .java)
3340+ WebAuthProvider .setPendingLoginResult(credentials)
3341+
32933342 val lifecycleOwner = Mockito .mock(LifecycleOwner ::class .java)
32943343 val lifecycle = Mockito .mock(Lifecycle ::class .java)
32953344 Mockito .`when `(lifecycleOwner.lifecycle).thenReturn(lifecycle)
@@ -3298,14 +3347,16 @@ public class WebAuthProviderTest {
32983347 WebAuthProvider .registerCallbacks(lifecycleOwner, loginCallback = callback, logoutCallback = voidCallback)
32993348 verify(lifecycle).addObserver(observerCaptor.capture())
33003349
3301- // Trigger onResume with no pending result — should not deliver anything
33023350 observerCaptor.firstValue.onResume(lifecycleOwner)
3303- verify(callback, Mockito .never()).onSuccess(any())
3304- verify(callback, Mockito .never()).onFailure(any())
3351+
3352+ verify(callback).onSuccess(credentials)
3353+ Assert .assertFalse(WebAuthProvider .hasPendingLoginResult())
33053354 }
33063355
33073356 @Test
3308- public fun shouldNotDeliverLogoutResultOnResumeWhenNoPendingResult () {
3357+ public fun shouldDeliverPendingLogoutResultOnResume () {
3358+ WebAuthProvider .setPendingLogoutResult()
3359+
33093360 val lifecycleOwner = Mockito .mock(LifecycleOwner ::class .java)
33103361 val lifecycle = Mockito .mock(Lifecycle ::class .java)
33113362 Mockito .`when `(lifecycleOwner.lifecycle).thenReturn(lifecycle)
@@ -3315,23 +3366,28 @@ public class WebAuthProviderTest {
33153366 verify(lifecycle).addObserver(observerCaptor.capture())
33163367
33173368 observerCaptor.firstValue.onResume(lifecycleOwner)
3318- verify(voidCallback, Mockito .never()).onSuccess(any())
3319- verify(voidCallback, Mockito .never()).onFailure(any())
3369+
3370+ verify(voidCallback).onSuccess(null )
3371+ Assert .assertFalse(WebAuthProvider .hasPendingLogoutResult())
33203372 }
33213373
33223374 @Test
3323- public fun shouldRegisterLifecycleObserverOnRegisterCallbacks () {
3375+ public fun shouldNotDeliverLoginResultOnResumeWhenNoPendingResult () {
33243376 val lifecycleOwner = Mockito .mock(LifecycleOwner ::class .java)
33253377 val lifecycle = Mockito .mock(Lifecycle ::class .java)
33263378 Mockito .`when `(lifecycleOwner.lifecycle).thenReturn(lifecycle)
33273379
3380+ val observerCaptor = argumentCaptor<DefaultLifecycleObserver >()
33283381 WebAuthProvider .registerCallbacks(lifecycleOwner, loginCallback = callback, logoutCallback = voidCallback)
3382+ verify(lifecycle).addObserver(observerCaptor.capture())
33293383
3330- verify(lifecycle).addObserver(any())
3384+ observerCaptor.firstValue.onResume(lifecycleOwner)
3385+ verify(callback, Mockito .never()).onSuccess(any())
3386+ verify(callback, Mockito .never()).onFailure(any())
33313387 }
33323388
33333389 @Test
3334- public fun shouldRemoveLifecycleObserverOnDestroyAfterRegisterCallbacks () {
3390+ public fun shouldNotDeliverLogoutResultOnResumeWhenNoPendingResult () {
33353391 val lifecycleOwner = Mockito .mock(LifecycleOwner ::class .java)
33363392 val lifecycle = Mockito .mock(Lifecycle ::class .java)
33373393 Mockito .`when `(lifecycleOwner.lifecycle).thenReturn(lifecycle)
@@ -3340,16 +3396,36 @@ public class WebAuthProviderTest {
33403396 WebAuthProvider .registerCallbacks(lifecycleOwner, loginCallback = callback, logoutCallback = voidCallback)
33413397 verify(lifecycle).addObserver(observerCaptor.capture())
33423398
3343- // Simulate onDestroy
3344- observerCaptor.firstValue.onDestroy(lifecycleOwner)
3399+ observerCaptor.firstValue.onResume(lifecycleOwner)
3400+ verify(voidCallback, Mockito .never()).onSuccess(any())
3401+ verify(voidCallback, Mockito .never()).onFailure(any())
3402+ }
33453403
3346- verify(lifecycle).removeObserver(observerCaptor.firstValue)
3404+ @Test
3405+ public fun shouldClearPendingLoginResultOnNewLoginStart () {
3406+ val credentials = Mockito .mock(Credentials ::class .java)
3407+ WebAuthProvider .setPendingLoginResult(credentials)
3408+ Assert .assertTrue(WebAuthProvider .hasPendingLoginResult())
3409+
3410+ login(account).start(activity, callback)
3411+
3412+ Assert .assertFalse(WebAuthProvider .hasPendingLoginResult())
33473413 }
33483414
3415+ @Test
3416+ public fun shouldClearPendingLogoutResultOnNewLogoutStart () {
3417+ WebAuthProvider .setPendingLogoutResult()
3418+ Assert .assertTrue(WebAuthProvider .hasPendingLogoutResult())
33493419
3420+ logout(account).start(activity, voidCallback)
3421+
3422+ Assert .assertFalse(WebAuthProvider .hasPendingLogoutResult())
3423+ }
33503424
33513425 private companion object {
33523426 private const val KEY_STATE = " state"
33533427 private const val KEY_NONCE = " nonce"
33543428 }
3355- }
3429+ }
3430+
3431+ internal abstract class TestLifecycleOwnerActivity : Activity (), LifecycleOwner
0 commit comments