Skip to content
This repository was archived by the owner on Jul 9, 2025. It is now read-only.

Commit fe06e97

Browse files
committed
Bug 1888249 - Adding Sync debug menu items for Fxa. r=lina,android-reviewers,boek
Connected the menu items to the existing app-services functions. Changed FxaAccountManager to store a `FirefoxAccount`, rather than the generic `OAuthAccount` from concept.sync. The alternative would be to add these debug methods to `OAuthAccount`, but that it doesn't feel right to me to add them to a generic interface. Reworked the tests to generate mock `FirefoxAccount` objects rather than `OAuthAccount` implementations. The most difficult part was `StatePersistenceTestableAccount`, which implemented OAuthAccount and changing it to implement `FirefoxAccount` was not so easy. Instead I replaced the class with a function that generated a mock object. Differential Revision: https://phabricator.services.mozilla.com/D205898
1 parent 412213f commit fe06e97

File tree

9 files changed

+173
-157
lines changed

9 files changed

+173
-157
lines changed

mobile/android/android-components/components/feature/accounts/src/test/java/mozilla/components/feature/accounts/FirefoxAccountsAuthFeatureTest.kt

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ import mozilla.components.concept.sync.AuthType
1717
import mozilla.components.concept.sync.DeviceConfig
1818
import mozilla.components.concept.sync.DeviceType
1919
import mozilla.components.concept.sync.FxAEntryPoint
20-
import mozilla.components.concept.sync.OAuthAccount
2120
import mozilla.components.concept.sync.Profile
21+
import mozilla.components.service.fxa.FirefoxAccount
2222
import mozilla.components.service.fxa.FxaAuthData
2323
import mozilla.components.service.fxa.ServerConfig
2424
import mozilla.components.service.fxa.StorageWrapper
@@ -45,13 +45,13 @@ internal class TestableStorageWrapper(
4545
manager: FxaAccountManager,
4646
accountEventObserverRegistry: ObserverRegistry<AccountEventsObserver>,
4747
serverConfig: ServerConfig,
48-
private val block: () -> OAuthAccount = {
49-
val account: OAuthAccount = mock()
48+
private val block: () -> FirefoxAccount = {
49+
val account: FirefoxAccount = mock()
5050
`when`(account.deviceConstellation()).thenReturn(mock())
5151
account
5252
},
5353
) : StorageWrapper(manager, accountEventObserverRegistry, serverConfig) {
54-
override fun obtainAccount(): OAuthAccount = block()
54+
override fun obtainAccount(): FirefoxAccount = block()
5555
}
5656

5757
// Same as the actual account manager, except we get to control how FirefoxAccountShaped instances
@@ -63,7 +63,7 @@ class TestableFxaAccountManager(
6363
config: ServerConfig,
6464
scopes: Set<String>,
6565
coroutineContext: CoroutineContext,
66-
block: () -> OAuthAccount = { mock() },
66+
block: () -> FirefoxAccount = { mock() },
6767
) : FxaAccountManager(context, config, DeviceConfig("test", DeviceType.MOBILE, setOf()), null, scopes, null, coroutineContext) {
6868
private val testableStorageWrapper = TestableStorageWrapper(this, accountEventObserverRegistry, serverConfig, block)
6969
override fun getStorageWrapper(): StorageWrapper {
@@ -252,7 +252,7 @@ class FirefoxAccountsAuthFeatureTest {
252252
private suspend fun prepareAccountManagerForSuccessfulAuthentication(
253253
coroutineContext: CoroutineContext,
254254
): TestableFxaAccountManager {
255-
val mockAccount: OAuthAccount = mock()
255+
val mockAccount: FirefoxAccount = mock()
256256
val profile = Profile(uid = "testUID", avatar = null, email = "[email protected]", displayName = "test profile")
257257

258258
`when`(mockAccount.deviceConstellation()).thenReturn(mock())
@@ -279,7 +279,7 @@ class FirefoxAccountsAuthFeatureTest {
279279
private suspend fun prepareAccountManagerForFailedAuthentication(
280280
coroutineContext: CoroutineContext,
281281
): TestableFxaAccountManager {
282-
val mockAccount: OAuthAccount = mock()
282+
val mockAccount: FirefoxAccount = mock()
283283
val profile = Profile(uid = "testUID", avatar = null, email = "[email protected]", displayName = "test profile")
284284

285285
`when`(mockAccount.getProfile(anyBoolean())).thenReturn(profile)

mobile/android/android-components/components/service/firefox-accounts/src/main/java/mozilla/components/service/fxa/AccountStorage.kt

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import mozilla.appservices.fxaclient.FxaRustAuthState
1111
import mozilla.components.concept.base.crash.CrashReporting
1212
import mozilla.components.concept.sync.AccountEvent
1313
import mozilla.components.concept.sync.AccountEventsObserver
14-
import mozilla.components.concept.sync.OAuthAccount
1514
import mozilla.components.concept.sync.StatePersistenceCallback
1615
import mozilla.components.lib.dataprotect.SecureAbove22Preferences
1716
import mozilla.components.service.fxa.manager.FxaAccountManager
@@ -26,16 +25,16 @@ const val FXA_STATE_KEY = "fxaState"
2625
* Represents state of our account on disk - is it new, or restored?
2726
*/
2827
internal sealed class AccountOnDisk : WithAccount {
29-
data class Restored(val account: OAuthAccount) : AccountOnDisk() {
28+
data class Restored(val account: FirefoxAccount) : AccountOnDisk() {
3029
override fun account() = account
3130
}
32-
data class New(val account: OAuthAccount) : AccountOnDisk() {
31+
data class New(val account: FirefoxAccount) : AccountOnDisk() {
3332
override fun account() = account
3433
}
3534
}
3635

3736
internal interface WithAccount {
38-
fun account(): OAuthAccount
37+
fun account(): FirefoxAccount
3938
}
4039

4140
/**
@@ -80,16 +79,16 @@ open class StorageWrapper(
8079
}
8180
}
8281

83-
private fun watchAccount(account: OAuthAccount) {
82+
private fun watchAccount(account: FirefoxAccount) {
8483
account.registerPersistenceCallback(statePersistenceCallback)
8584
account.deviceConstellation().register(accountEventsIntegration)
8685
}
8786

8887
/**
89-
* Exists strictly for testing purposes, allowing tests to specify their own implementation of [OAuthAccount].
88+
* Exists strictly for testing purposes, allowing tests to specify their own implementation of [FirefoxAccount].
9089
*/
9190
@VisibleForTesting
92-
open fun obtainAccount(): OAuthAccount = FirefoxAccount(serverConfig, crashReporter)
91+
open fun obtainAccount(): FirefoxAccount = FirefoxAccount(serverConfig, crashReporter)
9392
}
9493

9594
/**
@@ -110,7 +109,7 @@ internal class AccountEventsIntegration(
110109

111110
internal interface AccountStorage {
112111
@Throws(Exception::class)
113-
fun read(): OAuthAccount?
112+
fun read(): FirefoxAccount?
114113
fun write(accountState: String)
115114
fun clear()
116115
}
@@ -156,7 +155,7 @@ internal class SharedPrefAccountStorage(
156155
* @throws FxaException if JSON failed to parse into a [FirefoxAccount].
157156
*/
158157
@Throws(FxaException::class)
159-
override fun read(): OAuthAccount? {
158+
override fun read(): FirefoxAccount? {
160159
val savedJSON = accountPreferences().getString(FXA_STATE_KEY, null)
161160
?: return null
162161

@@ -244,7 +243,7 @@ internal class SecureAbove22AccountStorage(
244243
* @throws FxaException if JSON failed to parse into a [FirefoxAccount].
245244
*/
246245
@Throws(FxaException::class)
247-
override fun read(): OAuthAccount? {
246+
override fun read(): FirefoxAccount? {
248247
return store.getString(KEY_ACCOUNT_STATE).also {
249248
// If account state is missing, but we expected it to be present, report an exception.
250249
if (it == null && prefs.getBoolean(PREF_KEY_HAS_STATE, false)) {

mobile/android/android-components/components/service/firefox-accounts/src/main/java/mozilla/components/service/fxa/FirefoxAccount.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,10 @@ class FirefoxAccount internal constructor(
9797

9898
internal fun getAuthState() = inner.getAuthState()
9999

100+
internal fun simulateNetworkError() = inner.simulateNetworkError()
101+
internal fun simulateTemporaryAuthTokenIssue() = inner.simulateTemporaryAuthTokenIssue()
102+
internal fun simulatePermanentAuthTokenIssue() = inner.simulatePermanentAuthTokenIssue()
103+
100104
override suspend fun beginOAuthFlow(
101105
scopes: Set<String>,
102106
entryPoint: FxAEntryPoint,

mobile/android/android-components/components/service/firefox-accounts/src/main/java/mozilla/components/service/fxa/manager/FxaAccountManager.kt

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@ import android.content.Context
88
import androidx.annotation.GuardedBy
99
import androidx.annotation.VisibleForTesting
1010
import androidx.lifecycle.LifecycleOwner
11+
import kotlinx.coroutines.CoroutineScope
1112
import kotlinx.coroutines.SupervisorJob
1213
import kotlinx.coroutines.asCoroutineDispatcher
1314
import kotlinx.coroutines.cancel
15+
import kotlinx.coroutines.launch
1416
import kotlinx.coroutines.withContext
1517
import mozilla.appservices.fxaclient.FxaStateCheckerEvent
1618
import mozilla.appservices.fxaclient.FxaStateCheckerState
@@ -949,4 +951,60 @@ open class FxaAccountManager(
949951
accountManager.syncStatusObserverRegistry.notifyObservers { onError(error) }
950952
}
951953
}
954+
955+
/**
956+
* Hook this up to the secret debug menu to simulate a network error
957+
*
958+
* Typical usage is:
959+
* - `adb logcat | grep fxa_client`
960+
* - Trigger this via the secret debug menu item.
961+
* - Watch the logs. You should see the client perform a call to `get_profile', see a
962+
* network error, then recover.
963+
* - Note: the logs will be more clear once we switch the code to using the app-services state
964+
* machine.
965+
* - Check the UI, it should be in an authenticated state.
966+
*/
967+
public fun simulateNetworkError() {
968+
account.simulateNetworkError()
969+
CoroutineScope(coroutineContext).launch {
970+
refreshProfile(true)
971+
}
972+
}
973+
974+
/**
975+
* Hook this up to the secret debug menu to simulate a temporary auth error
976+
*
977+
* Typical usage is:
978+
* - `adb logcat | grep fxa_client`
979+
* - Trigger this via the secret debug menu item.
980+
* - Watch the logs. You should see the client perform a call to `get_profile', see an
981+
* auth error, then recover.
982+
* - Check the UI, it should be in an authenticated state.
983+
*/
984+
public fun simulateTemporaryAuthTokenIssue() {
985+
account.simulateTemporaryAuthTokenIssue()
986+
SyncAuthInfoCache(context).clear()
987+
CoroutineScope(coroutineContext).launch {
988+
refreshProfile(true)
989+
}
990+
}
991+
992+
/**
993+
* Hook this up to the secret debug menu to simulate an unrecoverable auth error
994+
*
995+
* Typical usage is:
996+
* - `adb logcat | grep fxa_client`
997+
* - Trigger this via the secret debug menu item.
998+
* - Initiaite a sync, or perform some other action that requires authentication.
999+
* - Watch the logs. You should see the client perform a call to `get_profile', see an
1000+
* auth error, then fail to recover.
1001+
* - Check the UI, it should be in an authentication problems state.
1002+
*/
1003+
public fun simulatePermanentAuthTokenIssue() {
1004+
account.simulatePermanentAuthTokenIssue()
1005+
SyncAuthInfoCache(context).clear()
1006+
CoroutineScope(coroutineContext).launch {
1007+
refreshProfile(true)
1008+
}
1009+
}
9521010
}

0 commit comments

Comments
 (0)