From bd73f31f976927690972a48038a7d05a07a145ac Mon Sep 17 00:00:00 2001 From: Valere Date: Wed, 22 May 2024 09:40:07 +0200 Subject: [PATCH 1/7] Add public device keys to rageshakes --- .../impl/reporter/DefaultBugReporter.kt | 17 ++++ .../impl/reporter/DefaultBugReporterTest.kt | 93 +++++++++++++++++++ .../api/encryption/EncryptionService.kt | 12 +++ .../impl/encryption/RustEncryptionService.kt | 8 ++ .../test/encryption/FakeEncryptionService.kt | 12 +++ 5 files changed, 142 insertions(+) diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporter.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporter.kt index 295fa17563..91a06b289d 100755 --- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporter.kt +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporter.kt @@ -36,7 +36,10 @@ import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.ApplicationContext import io.element.android.libraries.di.SingleIn +import io.element.android.libraries.matrix.api.MatrixClientProvider import io.element.android.libraries.matrix.api.SdkMetadata +import io.element.android.libraries.matrix.api.core.MatrixPatterns +import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.network.useragent.UserAgentProvider import io.element.android.libraries.sessionstorage.api.SessionStore import kotlinx.coroutines.CancellationException @@ -79,6 +82,7 @@ class DefaultBugReporter @Inject constructor( private val buildMeta: BuildMeta, private val bugReporterUrlProvider: BugReporterUrlProvider, private val sdkMetadata: SdkMetadata, + private val matrixClientsProvider: MatrixClientProvider, ) : BugReporter { companion object { // filenames @@ -156,6 +160,19 @@ class DefaultBugReporter @Inject constructor( .addFormDataPart("user_id", userId) .addFormDataPart("can_contact", canContact.toString()) .addFormDataPart("device_id", deviceId) + .apply { + userId.takeIf { MatrixPatterns.isUserId(it) }?.let { + SessionId(it) + }?.let { sessionId -> + matrixClientsProvider.getOrNull(sessionId)?.let { client -> + val curveKey = client.encryptionService().deviceCurve25519() + val edKey = client.encryptionService().deviceEd25519() + if (curveKey != null && edKey != null) { + addFormDataPart("device_keys", "curve25519:$curveKey, ed25519:$edKey") + } + } + } + } .addFormDataPart("device", Build.MODEL.trim()) .addFormDataPart("locale", Locale.getDefault().toString()) .addFormDataPart("sdk_sha", sdkMetadata.sdkGitSha) diff --git a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterTest.kt b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterTest.kt index f2c8d0d0a8..3aa0136f41 100755 --- a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterTest.kt +++ b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterTest.kt @@ -20,16 +20,24 @@ import com.google.common.truth.Truth.assertThat import io.element.android.features.rageshake.api.reporter.BugReporterListener import io.element.android.features.rageshake.test.crash.FakeCrashDataStore import io.element.android.features.rageshake.test.screenshot.FakeScreenshotHolder +import io.element.android.libraries.matrix.test.FakeMatrixClient +import io.element.android.libraries.matrix.test.FakeMatrixClientProvider import io.element.android.libraries.matrix.test.FakeSdkMetadata import io.element.android.libraries.matrix.test.core.aBuildMeta +import io.element.android.libraries.matrix.test.encryption.FakeEncryptionService import io.element.android.libraries.network.useragent.DefaultUserAgentProvider +import io.element.android.libraries.sessionstorage.api.LoginType +import io.element.android.libraries.sessionstorage.api.SessionData import io.element.android.libraries.sessionstorage.impl.memory.InMemorySessionStore import io.element.android.tests.testutils.testCoroutineDispatchers import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runTest +import okhttp3.MultipartReader import okhttp3.OkHttpClient import okhttp3.mockwebserver.MockResponse import okhttp3.mockwebserver.MockWebServer +import okio.buffer +import okio.source import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner @@ -84,6 +92,90 @@ class DefaultBugReporterTest { assertThat(onUploadSucceedCalled).isTrue() } + @Test + fun `test sendBugReport form data`() = runTest { + val server = MockWebServer() + server.enqueue( + MockResponse() + .setResponseCode(200) + ) + server.start() + + val mockSessionStore = InMemorySessionStore().apply { + storeData( + SessionData( + userId = "@foo:eample.com", + deviceId = "ABCDEFGH", + homeserverUrl = "example.com", + accessToken = "AA", + isTokenValid = true, + loginType = LoginType.DIRECT, + loginTimestamp = null, + oidcData = null, + refreshToken = null, + slidingSyncProxy = null, + passphrase = null + ) + ) + } + + val buildMeta = aBuildMeta() + val fakeEncryptionService = FakeEncryptionService() + val matrixClient = FakeMatrixClient(encryptionService = fakeEncryptionService) + + fakeEncryptionService.givenDeviceKeys("CURVECURVECURVE", "EDKEYEDKEYEDKY") + val sut = DefaultBugReporter( + context = RuntimeEnvironment.getApplication(), + screenshotHolder = FakeScreenshotHolder(), + crashDataStore = FakeCrashDataStore(), + coroutineDispatchers = testCoroutineDispatchers(), + okHttpClient = { OkHttpClient.Builder().build() }, + userAgentProvider = DefaultUserAgentProvider(buildMeta, FakeSdkMetadata("123456789")), + sessionStore = mockSessionStore, + buildMeta = buildMeta, + bugReporterUrlProvider = { server.url("/") }, + sdkMetadata = FakeSdkMetadata("123456789"), + matrixClientsProvider = FakeMatrixClientProvider(getClient = { Result.success(matrixClient) }) + ) + + sut.sendBugReport( + withDevicesLogs = true, + withCrashLogs = true, + withScreenshot = true, + theBugDescription = "a bug occurred", + canContact = true, + listener = null + ) + val request = server.takeRequest() + + val boundary = request.headers["Content-Type"]!!.split("=").last() + val foundValues = HashMap() + request.body.inputStream().source().buffer().use { + val multipartReader = MultipartReader(it, boundary) + // Just use simple parsing to detect basic properties + val regex = "form-data; name=\"(\\w*)\".*".toRegex() + multipartReader.use { + while (true) { + val part = multipartReader.nextPart() ?: break + val contentDisposition = part.headers["Content-Disposition"] ?: continue + regex.find(contentDisposition)?.groupValues?.get(1)?.let { name -> + foundValues.put(name, part.body.readUtf8()) + } + } + } + } + + assertThat(foundValues["app"]).isEqualTo("element-x-android") + assertThat(foundValues["can_contact"]).isEqualTo("true") + assertThat(foundValues["device_id"]).isEqualTo("ABCDEFGH") + assertThat(foundValues["sdk_sha"]).isEqualTo("123456789") + assertThat(foundValues["user_id"]).isEqualTo("@foo:eample.com") + assertThat(foundValues["text"]).isEqualTo("a bug occurred") + assertThat(foundValues["device_keys"]).isEqualTo("curve25519:CURVECURVECURVE, ed25519:EDKEYEDKEYEDKY") + + server.shutdown() + } + @Test fun `test sendBugReport error`() = runTest { val server = MockWebServer() @@ -150,6 +242,7 @@ class DefaultBugReporterTest { buildMeta = buildMeta, bugReporterUrlProvider = { server.url("/") }, sdkMetadata = FakeSdkMetadata("123456789"), + matrixClientsProvider = FakeMatrixClientProvider() ) } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/encryption/EncryptionService.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/encryption/EncryptionService.kt index 36c786a26f..f47487c634 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/encryption/EncryptionService.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/encryption/EncryptionService.kt @@ -50,4 +50,16 @@ interface EncryptionService { * Wait for backup upload steady state. */ fun waitForBackupUploadSteadyState(): Flow + + /** + * Get the public curve25519 key of our own device in base64. This is usually what is + * called the identity key of the device. + */ + suspend fun deviceCurve25519(): String? + + /** + * Get the public ed25519 key of our own device. This is usually what is + * called the fingerprint of the device. + */ + suspend fun deviceEd25519(): String? } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/RustEncryptionService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/RustEncryptionService.kt index f5a6390989..68ab4a611e 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/RustEncryptionService.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/RustEncryptionService.kt @@ -190,4 +190,12 @@ internal class RustEncryptionService( it.mapRecoveryException() } } + + override suspend fun deviceCurve25519(): String? { + return service.curve25519Key() + } + + override suspend fun deviceEd25519(): String? { + return service.ed25519Key() + } } diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/encryption/FakeEncryptionService.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/encryption/FakeEncryptionService.kt index cc7f53eca3..b864c69b0b 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/encryption/FakeEncryptionService.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/encryption/FakeEncryptionService.kt @@ -39,6 +39,9 @@ class FakeEncryptionService : EncryptionService { private var enableBackupsFailure: Exception? = null + private var curve25519: String? = null + private var ed25519: String? = null + fun givenEnableBackupsFailure(exception: Exception?) { enableBackupsFailure = exception } @@ -94,6 +97,15 @@ class FakeEncryptionService : EncryptionService { return waitForBackupUploadSteadyStateFlow } + fun givenDeviceKeys(curve25519: String?, ed25519: String?) { + this.curve25519 = curve25519 + this.ed25519 = ed25519 + } + + override suspend fun deviceCurve25519(): String? = curve25519 + + override suspend fun deviceEd25519(): String? = ed25519 + suspend fun emitBackupState(state: BackupState) { backupStateStateFlow.emit(state) } From 0a4a7f563d4f25479941c7f83bf4c5589ed93007 Mon Sep 17 00:00:00 2001 From: Valere Date: Wed, 22 May 2024 09:44:43 +0200 Subject: [PATCH 2/7] Add changelog --- changelog.d/2893.misc | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/2893.misc diff --git a/changelog.d/2893.misc b/changelog.d/2893.misc new file mode 100644 index 0000000000..ee122ae436 --- /dev/null +++ b/changelog.d/2893.misc @@ -0,0 +1 @@ +BugReporting | Add public device keys to rageshakes From cc5fbc2b640acdda96e85b26c3177097bf35dd15 Mon Sep 17 00:00:00 2001 From: Valere Date: Wed, 22 May 2024 11:31:45 +0200 Subject: [PATCH 3/7] Review: Cleaning / detekt / improve test --- .../impl/reporter/DefaultBugReporter.kt | 15 ++++----- .../impl/reporter/DefaultBugReporterTest.kt | 32 ++++++++++++++----- 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporter.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporter.kt index 91a06b289d..ddde3bd420 100755 --- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporter.kt +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporter.kt @@ -38,8 +38,7 @@ import io.element.android.libraries.di.ApplicationContext import io.element.android.libraries.di.SingleIn import io.element.android.libraries.matrix.api.MatrixClientProvider import io.element.android.libraries.matrix.api.SdkMetadata -import io.element.android.libraries.matrix.api.core.MatrixPatterns -import io.element.android.libraries.matrix.api.core.SessionId +import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.network.useragent.UserAgentProvider import io.element.android.libraries.sessionstorage.api.SessionStore import kotlinx.coroutines.CancellationException @@ -82,7 +81,7 @@ class DefaultBugReporter @Inject constructor( private val buildMeta: BuildMeta, private val bugReporterUrlProvider: BugReporterUrlProvider, private val sdkMetadata: SdkMetadata, - private val matrixClientsProvider: MatrixClientProvider, + private val matrixClientProvider: MatrixClientProvider, ) : BugReporter { companion object { // filenames @@ -149,7 +148,7 @@ class DefaultBugReporter @Inject constructor( val sessionData = sessionStore.getLatestSession() val deviceId = sessionData?.deviceId ?: "undefined" - val userId = sessionData?.userId ?: "undefined" + val userId = sessionData?.userId?.let { UserId(it) } if (!isCancelled) { // build the multi part request @@ -157,14 +156,12 @@ class DefaultBugReporter @Inject constructor( .addFormDataPart("text", bugDescription) .addFormDataPart("app", context.getString(R.string.bug_report_app_name)) .addFormDataPart("user_agent", userAgentProvider.provide()) - .addFormDataPart("user_id", userId) + .addFormDataPart("user_id", userId?.toString() ?: "undefined") .addFormDataPart("can_contact", canContact.toString()) .addFormDataPart("device_id", deviceId) .apply { - userId.takeIf { MatrixPatterns.isUserId(it) }?.let { - SessionId(it) - }?.let { sessionId -> - matrixClientsProvider.getOrNull(sessionId)?.let { client -> + userId?.let { + matrixClientProvider.getOrNull(it)?.let { client -> val curveKey = client.encryptionService().deviceCurve25519() val edKey = client.encryptionService().deviceEd25519() if (curveKey != null && edKey != null) { diff --git a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterTest.kt b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterTest.kt index 3aa0136f41..6abbc6094f 100755 --- a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterTest.kt +++ b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterTest.kt @@ -135,16 +135,27 @@ class DefaultBugReporterTest { buildMeta = buildMeta, bugReporterUrlProvider = { server.url("/") }, sdkMetadata = FakeSdkMetadata("123456789"), - matrixClientsProvider = FakeMatrixClientProvider(getClient = { Result.success(matrixClient) }) + matrixClientProvider = FakeMatrixClientProvider(getClient = { Result.success(matrixClient) }) ) + val progressValues = mutableListOf() sut.sendBugReport( withDevicesLogs = true, withCrashLogs = true, withScreenshot = true, theBugDescription = "a bug occurred", canContact = true, - listener = null + 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() @@ -155,12 +166,14 @@ class DefaultBugReporterTest { // Just use simple parsing to detect basic properties val regex = "form-data; name=\"(\\w*)\".*".toRegex() multipartReader.use { - while (true) { - val part = multipartReader.nextPart() ?: break - val contentDisposition = part.headers["Content-Disposition"] ?: continue - regex.find(contentDisposition)?.groupValues?.get(1)?.let { name -> - foundValues.put(name, part.body.readUtf8()) + var part = multipartReader.nextPart() + while (part != null) { + part.headers["Content-Disposition"]?.let { contentDisposition -> + regex.find(contentDisposition)?.groupValues?.get(1)?.let { name -> + foundValues.put(name, part!!.body.readUtf8()) + } } + part = multipartReader.nextPart() } } } @@ -173,6 +186,9 @@ class DefaultBugReporterTest { 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() } @@ -242,7 +258,7 @@ class DefaultBugReporterTest { buildMeta = buildMeta, bugReporterUrlProvider = { server.url("/") }, sdkMetadata = FakeSdkMetadata("123456789"), - matrixClientsProvider = FakeMatrixClientProvider() + matrixClientProvider = FakeMatrixClientProvider() ) } From 60e2c5aae8fe7be71e57796e1c716c6c71895aa5 Mon Sep 17 00:00:00 2001 From: Valere Date: Thu, 23 May 2024 16:46:25 +0200 Subject: [PATCH 4/7] Improve coverage --- .../impl/reporter/DefaultBugReporterTest.kt | 110 +++++++++++++----- 1 file changed, 81 insertions(+), 29 deletions(-) diff --git a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterTest.kt b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterTest.kt index 6abbc6094f..0d03cce7cf 100755 --- a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterTest.kt +++ b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterTest.kt @@ -36,6 +36,7 @@ import okhttp3.MultipartReader import okhttp3.OkHttpClient import okhttp3.mockwebserver.MockResponse import okhttp3.mockwebserver.MockWebServer +import okhttp3.mockwebserver.RecordedRequest import okio.buffer import okio.source import org.junit.Test @@ -102,21 +103,7 @@ class DefaultBugReporterTest { server.start() val mockSessionStore = InMemorySessionStore().apply { - storeData( - SessionData( - userId = "@foo:eample.com", - deviceId = "ABCDEFGH", - homeserverUrl = "example.com", - accessToken = "AA", - isTokenValid = true, - loginType = LoginType.DIRECT, - loginTimestamp = null, - oidcData = null, - refreshToken = null, - slidingSyncProxy = null, - passphrase = null - ) - ) + storeData(mockSessionData("@foo:eample.com", "ABCDEFGH")) } val buildMeta = aBuildMeta() @@ -159,6 +146,70 @@ class DefaultBugReporterTest { ) val request = server.takeRequest() + val foundValues = collectValuesFromFormData(request) + + assertThat(foundValues["app"]).isEqualTo("element-x-android") + assertThat(foundValues["can_contact"]).isEqualTo("true") + assertThat(foundValues["device_id"]).isEqualTo("ABCDEFGH") + assertThat(foundValues["sdk_sha"]).isEqualTo("123456789") + assertThat(foundValues["user_id"]).isEqualTo("@foo:eample.com") + 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 should not report device_keys if not known`() = runTest { + val server = MockWebServer() + server.enqueue( + MockResponse() + .setResponseCode(200) + ) + server.start() + + val mockSessionStore = InMemorySessionStore().apply { + storeData(mockSessionData("@foo:eample.com", null)) + } + + val buildMeta = aBuildMeta() + val fakeEncryptionService = FakeEncryptionService() + val matrixClient = FakeMatrixClient(encryptionService = fakeEncryptionService) + + fakeEncryptionService.givenDeviceKeys(null, null) + val sut = DefaultBugReporter( + context = RuntimeEnvironment.getApplication(), + screenshotHolder = FakeScreenshotHolder(), + crashDataStore = FakeCrashDataStore(), + coroutineDispatchers = testCoroutineDispatchers(), + okHttpClient = { OkHttpClient.Builder().build() }, + userAgentProvider = DefaultUserAgentProvider(buildMeta, FakeSdkMetadata("123456789")), + sessionStore = mockSessionStore, + buildMeta = buildMeta, + bugReporterUrlProvider = { server.url("/") }, + sdkMetadata = FakeSdkMetadata("123456789"), + matrixClientProvider = FakeMatrixClientProvider(getClient = { Result.success(matrixClient) }) + ) + + sut.sendBugReport( + withDevicesLogs = true, + withCrashLogs = true, + withScreenshot = true, + theBugDescription = "a bug occurred", + canContact = true, + listener = null + ) + val request = server.takeRequest() + + val foundValues = collectValuesFromFormData(request) + assertThat(foundValues["device_keys"]).isNull() + server.shutdown() + } + + private fun collectValuesFromFormData(request: RecordedRequest): HashMap { val boundary = request.headers["Content-Type"]!!.split("=").last() val foundValues = HashMap() request.body.inputStream().source().buffer().use { @@ -168,7 +219,7 @@ class DefaultBugReporterTest { multipartReader.use { var part = multipartReader.nextPart() while (part != null) { - part.headers["Content-Disposition"]?.let { contentDisposition -> + part.headers["Content-Disposition"]?.let { contentDisposition -> regex.find(contentDisposition)?.groupValues?.get(1)?.let { name -> foundValues.put(name, part!!.body.readUtf8()) } @@ -177,21 +228,22 @@ class DefaultBugReporterTest { } } } - - assertThat(foundValues["app"]).isEqualTo("element-x-android") - assertThat(foundValues["can_contact"]).isEqualTo("true") - assertThat(foundValues["device_id"]).isEqualTo("ABCDEFGH") - assertThat(foundValues["sdk_sha"]).isEqualTo("123456789") - assertThat(foundValues["user_id"]).isEqualTo("@foo:eample.com") - 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() + return foundValues } + private fun mockSessionData(userId: String, deviceId: String) = SessionData( + userId = userId, + deviceId = deviceId, + homeserverUrl = "example.com", + accessToken = "AA", + isTokenValid = true, + loginType = LoginType.DIRECT, + loginTimestamp = null, + oidcData = null, + refreshToken = null, + slidingSyncProxy = null, + passphrase = null + ) @Test fun `test sendBugReport error`() = runTest { val server = MockWebServer() From 984575d5c573219eb55474fca239619eb447754d Mon Sep 17 00:00:00 2001 From: Valere Date: Thu, 23 May 2024 17:15:02 +0200 Subject: [PATCH 5/7] fix test --- .../features/rageshake/impl/reporter/DefaultBugReporterTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterTest.kt b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterTest.kt index 0d03cce7cf..0babe87936 100755 --- a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterTest.kt +++ b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterTest.kt @@ -172,7 +172,7 @@ class DefaultBugReporterTest { server.start() val mockSessionStore = InMemorySessionStore().apply { - storeData(mockSessionData("@foo:eample.com", null)) + storeData(mockSessionData("@foo:eample.com", "ABCDEFGH")) } val buildMeta = aBuildMeta() From 74c25fd7178c09369976f694b30dc5b6abb228d0 Mon Sep 17 00:00:00 2001 From: Valere Date: Thu, 23 May 2024 17:47:53 +0200 Subject: [PATCH 6/7] More coverage --- .../impl/reporter/DefaultBugReporterTest.kt | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterTest.kt b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterTest.kt index 0babe87936..0115b0407d 100755 --- a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterTest.kt +++ b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterTest.kt @@ -209,6 +209,50 @@ class DefaultBugReporterTest { server.shutdown() } + @Test + fun `test sendBugReport no client provider no session data`() = runTest { + val server = MockWebServer() + server.enqueue( + MockResponse() + .setResponseCode(200) + ) + server.start() + + val buildMeta = aBuildMeta() + val fakeEncryptionService = FakeEncryptionService() + + fakeEncryptionService.givenDeviceKeys(null, null) + val sut = DefaultBugReporter( + context = RuntimeEnvironment.getApplication(), + screenshotHolder = FakeScreenshotHolder(), + crashDataStore = FakeCrashDataStore(), + coroutineDispatchers = testCoroutineDispatchers(), + okHttpClient = { OkHttpClient.Builder().build() }, + userAgentProvider = DefaultUserAgentProvider(buildMeta, FakeSdkMetadata("123456789")), + sessionStore = InMemorySessionStore(), + buildMeta = buildMeta, + bugReporterUrlProvider = { server.url("/") }, + sdkMetadata = FakeSdkMetadata("123456789"), + matrixClientProvider = FakeMatrixClientProvider(getClient = { Result.failure(Exception("Mock no client")) }) + ) + + sut.sendBugReport( + withDevicesLogs = true, + withCrashLogs = true, + withScreenshot = true, + theBugDescription = "a bug occurred", + canContact = true, + listener = null + ) + val request = server.takeRequest() + + val foundValues = collectValuesFromFormData(request) + assertThat(foundValues["device_keys"]).isNull() + assertThat(foundValues["device_id"]).isEqualTo("undefined") + assertThat(foundValues["user_id"]).isEqualTo("undefined") + server.shutdown() + } + private fun collectValuesFromFormData(request: RecordedRequest): HashMap { val boundary = request.headers["Content-Type"]!!.split("=").last() val foundValues = HashMap() From 141e835dbc78cec35f28bae3509409db34f1f58e Mon Sep 17 00:00:00 2001 From: Valere Date: Thu, 23 May 2024 18:28:39 +0200 Subject: [PATCH 7/7] more coverage --- .../rageshake/impl/reporter/DefaultBugReporterTest.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterTest.kt b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterTest.kt index 0115b0407d..cb28c973dc 100755 --- a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterTest.kt +++ b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterTest.kt @@ -225,7 +225,7 @@ class DefaultBugReporterTest { val sut = DefaultBugReporter( context = RuntimeEnvironment.getApplication(), screenshotHolder = FakeScreenshotHolder(), - crashDataStore = FakeCrashDataStore(), + crashDataStore = FakeCrashDataStore("I did crash", true), coroutineDispatchers = testCoroutineDispatchers(), okHttpClient = { OkHttpClient.Builder().build() }, userAgentProvider = DefaultUserAgentProvider(buildMeta, FakeSdkMetadata("123456789")), @@ -247,10 +247,11 @@ class DefaultBugReporterTest { val request = server.takeRequest() val foundValues = collectValuesFromFormData(request) + println("## FOUND VALUES $foundValues") assertThat(foundValues["device_keys"]).isNull() assertThat(foundValues["device_id"]).isEqualTo("undefined") assertThat(foundValues["user_id"]).isEqualTo("undefined") - server.shutdown() + assertThat(foundValues["label"]).isEqualTo("crash") } private fun collectValuesFromFormData(request: RecordedRequest): HashMap {