Merge pull request #5532 from element-hq/feature/bma/multiAccountReport

Add number of accounts info in the rageshake data.
This commit is contained in:
Benoit Marty
2025-10-16 10:26:13 +02:00
committed by GitHub
11 changed files with 72 additions and 10 deletions

View File

@@ -11,7 +11,6 @@ import io.element.android.features.login.impl.accountprovider.AccountProvider
import io.element.android.features.login.impl.changeserver.ChangeServerState
import kotlinx.collections.immutable.ImmutableList
// Do not use default value, so no member get forgotten in the presenters.
data class ChangeAccountProviderState(
val accountProviders: ImmutableList<AccountProvider>,
val canSearchForAccountProviders: Boolean,

View File

@@ -12,7 +12,6 @@ import io.element.android.features.login.impl.login.LoginMode
import io.element.android.libraries.architecture.AsyncData
import kotlinx.collections.immutable.ImmutableList
// Do not use default value, so no member get forgotten in the presenters.
data class ChooseAccountProviderState(
val accountProviders: ImmutableList<AccountProvider>,
val selectedAccountProvider: AccountProvider?,

View File

@@ -11,7 +11,6 @@ import io.element.android.features.login.impl.accountprovider.AccountProvider
import io.element.android.features.login.impl.login.LoginMode
import io.element.android.libraries.architecture.AsyncData
// Do not use default value, so no member get forgotten in the presenters.
data class ConfirmAccountProviderState(
val accountProvider: AccountProvider,
val isAccountCreation: Boolean,

View File

@@ -11,7 +11,6 @@ import io.element.android.features.login.impl.changeserver.ChangeServerState
import io.element.android.features.login.impl.resolver.HomeserverData
import io.element.android.libraries.architecture.AsyncData
// Do not use default value, so no member get forgotten in the presenters.
data class SearchAccountProviderState(
val userInput: String,
val userInputResult: AsyncData<List<HomeserverData>>,

View File

@@ -9,7 +9,6 @@ package io.element.android.features.preferences.impl.analytics
import io.element.android.features.analytics.api.preferences.AnalyticsPreferencesState
// Do not use default value, so no member get forgotten in the presenters.
data class AnalyticsSettingsState(
val analyticsPreferencesState: AnalyticsPreferencesState,
)

View File

@@ -155,6 +155,7 @@ class DefaultBugReporter(
}
}
val sessionData = sessionStore.getLatestSession()
val numberOfAccounts = sessionStore.getAllSessions().size
val deviceId = sessionData?.deviceId ?: "undefined"
val userId = sessionData?.userId?.let { UserId(it) }
// build the multi part request
@@ -163,6 +164,7 @@ class DefaultBugReporter(
.addFormDataPart("app", RageshakeConfig.BUG_REPORT_APP_NAME)
.addFormDataPart("user_agent", userAgentProvider.provide())
.addFormDataPart("user_id", userId?.toString() ?: "undefined")
.addFormDataPart("number_of_accounts", numberOfAccounts.toString())
.addFormDataPart("can_contact", canContact.toString())
.addFormDataPart("device_id", deviceId)
.addFormDataPart("device", Build.MODEL.trim())

View File

@@ -18,6 +18,8 @@ import io.element.android.libraries.matrix.api.MatrixClientProvider
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
import io.element.android.libraries.matrix.api.tracing.TracingService
import io.element.android.libraries.matrix.api.tracing.WriteToFilesConfiguration
import io.element.android.libraries.matrix.test.A_DEVICE_ID
import io.element.android.libraries.matrix.test.A_USER_ID
import io.element.android.libraries.matrix.test.FakeMatrixClient
import io.element.android.libraries.matrix.test.FakeMatrixClientProvider
import io.element.android.libraries.matrix.test.FakeSdkMetadata
@@ -155,6 +157,72 @@ class DefaultBugReporterTest {
assertThat(foundValues["device_id"]).isEqualTo("ABCDEFGH")
assertThat(foundValues["sdk_sha"]).isEqualTo("123456789")
assertThat(foundValues["user_id"]).isEqualTo("@foo:example.com")
assertThat(foundValues["number_of_accounts"]).isEqualTo("1")
assertThat(foundValues["text"]).isEqualTo("a bug occurred")
assertThat(foundValues["device_keys"]).isEqualTo("curve25519:CURVECURVECURVE, ed25519:EDKEYEDKEYEDKY")
// device_key now added given they are not null
assertThat(progressValues.size).isEqualTo(EXPECTED_NUMBER_OF_PROGRESS_VALUE + 1)
server.shutdown()
}
@Test
fun `test sendBugReport multi accounts`() = runTest {
val server = MockWebServer()
server.enqueue(
MockResponse()
.setResponseCode(200)
)
server.start()
val mockSessionStore = InMemorySessionStore(
initialList = listOf(
aSessionData(sessionId = "@foo:example.com", deviceId = "ABCDEFGH"),
aSessionData(sessionId = A_USER_ID.value, deviceId = A_DEVICE_ID.value),
)
)
val fakeEncryptionService = FakeEncryptionService()
val matrixClient = FakeMatrixClient(encryptionService = fakeEncryptionService)
fakeEncryptionService.givenDeviceKeys("CURVECURVECURVE", "EDKEYEDKEYEDKY")
val sut = createDefaultBugReporter(
server = server,
crashDataStore = FakeCrashDataStore(),
sessionStore = mockSessionStore,
matrixClientProvider = FakeMatrixClientProvider(getClient = { Result.success(matrixClient) })
)
val progressValues = mutableListOf<Int>()
sut.sendBugReport(
withDevicesLogs = true,
withCrashLogs = true,
withScreenshot = true,
problemDescription = "a bug occurred",
canContact = true,
listener = object : BugReporterListener {
override fun onUploadCancelled() {}
override fun onUploadFailed(reason: String?) {}
override fun onProgress(progress: Int) {
progressValues.add(progress)
}
override fun onUploadSucceed() {}
},
)
val request = server.takeRequest()
val foundValues = collectValuesFromFormData(request)
assertThat(foundValues["app"]).isEqualTo(RageshakeConfig.BUG_REPORT_APP_NAME)
assertThat(foundValues["can_contact"]).isEqualTo("true")
assertThat(foundValues["device_id"]).isEqualTo("ABCDEFGH")
assertThat(foundValues["sdk_sha"]).isEqualTo("123456789")
assertThat(foundValues["user_id"]).isEqualTo("@foo:example.com")
assertThat(foundValues["number_of_accounts"]).isEqualTo("2")
assertThat(foundValues["text"]).isEqualTo("a bug occurred")
assertThat(foundValues["device_keys"]).isEqualTo("curve25519:CURVECURVECURVE, ed25519:EDKEYEDKEYEDKY")
assertThat(foundValues["file"]).contains(fakePushRules)
@@ -238,6 +306,7 @@ class DefaultBugReporterTest {
assertThat(foundValues["device_keys"]).isNull()
assertThat(foundValues["device_id"]).isEqualTo("undefined")
assertThat(foundValues["user_id"]).isEqualTo("undefined")
assertThat(foundValues["number_of_accounts"]).isEqualTo("0")
assertThat(foundValues["label"]).isEqualTo("crash")
}
@@ -485,6 +554,6 @@ class DefaultBugReporterTest {
}
companion object {
private const val EXPECTED_NUMBER_OF_PROGRESS_VALUE = 17
private const val EXPECTED_NUMBER_OF_PROGRESS_VALUE = 18
}
}

View File

@@ -10,7 +10,6 @@ package io.element.android.features.securebackup.impl.enter
import io.element.android.features.securebackup.impl.setup.views.RecoveryKeyViewState
import io.element.android.libraries.architecture.AsyncAction
// Do not use default value, so no member get forgotten in the presenters.
data class SecureBackupEnterRecoveryKeyState(
val recoveryKeyViewState: RecoveryKeyViewState,
val isSubmitEnabled: Boolean,

View File

@@ -9,7 +9,6 @@ package io.element.android.features.securebackup.impl.setup
import io.element.android.features.securebackup.impl.setup.views.RecoveryKeyViewState
// Do not use default value, so no member get forgotten in the presenters.
data class SecureBackupSetupState(
val isChangeRecoveryKeyUserStory: Boolean,
val recoveryKeyViewState: RecoveryKeyViewState,

View File

@@ -9,7 +9,6 @@ package io.element.android.features.signedout.impl
import io.element.android.libraries.sessionstorage.api.SessionData
// Do not use default value, so no member get forgotten in the presenters.
data class SignedOutState(
val appName: String,
val signedOutSession: SessionData?,

View File

@@ -1,7 +1,6 @@
#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME}#end
// TODO add your ui models. Remove the eventSink if you don't have events.
// Do not use default value, so no member get forgotten in the presenters.
data class ${NAME}State(
val eventSink: (${NAME}Events) -> Unit
)