From 2b333ae41db9f0d67809e8b594735f8a7d732cfe Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 17 Oct 2026 14:36:41 +0000 Subject: [PATCH 1/3] fix(deps): update okhttpversion to v5 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- app/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 5965f04f1c1..6be7e4bf1b8 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -30,7 +30,7 @@ val emojiVersion = "1.7.0" val fidoVersion = "4.1.0-patch2" val lifecycleVersion = "2.11.0" val maplibreComposeVersion = "0.20.0" -val okhttpVersion = "4.12.0" +val okhttpVersion = "5.5.0" val markwonVersion = "4.6.2" val materialDialogsVersion = "3.3.0" val parcelerVersion = "1.1.13" From 0864f616de13406fc5af0dee328598982062594f Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Sat, 17 Oct 2026 19:06:02 +0200 Subject: [PATCH 2/3] fix: resolve okhttp 5 compiler warnings - Response.body is non-null in okhttp 5, drop the unneeded safe calls - replace the deprecated RequestBody.create(MediaType?, ByteArray) with the toRequestBody extension Assisted-by: Claude Code:claude-opus-5-5 Signed-off-by: Andy Scherzinger --- .../talk/account/data/network/NetworkLoginDataSource.kt | 8 ++++---- .../java/com/nextcloud/talk/upload/normal/FileUploader.kt | 3 ++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/account/data/network/NetworkLoginDataSource.kt b/app/src/main/java/com/nextcloud/talk/account/data/network/NetworkLoginDataSource.kt index aa1b5aa104a..48479d6cbe1 100644 --- a/app/src/main/java/com/nextcloud/talk/account/data/network/NetworkLoginDataSource.kt +++ b/app/src/main/java/com/nextcloud/talk/account/data/network/NetworkLoginDataSource.kt @@ -88,7 +88,7 @@ class NetworkLoginDataSource(val okHttpClient: OkHttpClient) { if (!response.isSuccessful) { throw IOException("Unexpected code $response") } - return response.body?.string() + return response.body.string() } } @@ -129,7 +129,7 @@ class NetworkLoginDataSource(val okHttpClient: OkHttpClient) { if (!response.isSuccessful) { throw IOException("Unexpected code $response") } - return response.body?.string() + return response.body.string() } } @@ -157,9 +157,9 @@ class NetworkLoginDataSource(val okHttpClient: OkHttpClient) { client.newCall(request).execute() .use { response -> val status: Int = response.code - val responseBody = response.body?.string() + val responseBody = response.body.string() - result = if (response.isSuccessful && responseBody?.isNotEmpty() == true) { + result = if (response.isSuccessful && responseBody.isNotEmpty()) { val jsonObject = JsonParser.parseString(responseBody).asJsonObject val server: String = jsonObject.get("server").asString val loginName: String = jsonObject.get("loginName").asString diff --git a/app/src/main/java/com/nextcloud/talk/upload/normal/FileUploader.kt b/app/src/main/java/com/nextcloud/talk/upload/normal/FileUploader.kt index 144b872dd01..cbcb9191016 100644 --- a/app/src/main/java/com/nextcloud/talk/upload/normal/FileUploader.kt +++ b/app/src/main/java/com/nextcloud/talk/upload/normal/FileUploader.kt @@ -26,6 +26,7 @@ import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.OkHttpClient import okhttp3.Protocol import okhttp3.RequestBody +import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.Response import java.io.File import java.io.IOException @@ -125,7 +126,7 @@ class FileUploader( private fun createRequestBody(sourceFileUri: Uri): RequestBody? = try { val bytes = context.contentResolver.openInputStream(sourceFileUri)!!.use { it.readBytes() } - RequestBody.create("application/octet-stream".toMediaTypeOrNull(), bytes) + bytes.toRequestBody("application/octet-stream".toMediaTypeOrNull()) } catch (e: Exception) { Log.e(TAG, "failed to create RequestBody for $sourceFileUri", e) null From ae31dd688022847cbc750aea473300027f98b20f Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Sat, 17 Oct 2026 19:16:03 +0200 Subject: [PATCH 3/3] test: migrate MockWebServer tests to mockwebserver3 okhttp 5 keeps the okhttp3.mockwebserver package only as a compatibility layer over mockwebserver3. Use the new artifact and API directly: - MockResponse is immutable, build it with MockResponse.Builder or the code/body constructor - SocketPolicy.DISCONNECT_AFTER_REQUEST becomes onResponseStart(SocketEffect.CloseSocket()), the same mapping the compatibility layer used - shutdown() becomes close(), getHeader() becomes headers[] Assisted-by: Claude Code:claude-opus-5-5 Signed-off-by: Andy Scherzinger --- app/build.gradle.kts | 2 +- .../network/NetworkLoginDataSourceTest.kt | 36 ++++++++++--------- .../chunked/ChunkedFileUploaderAbortTest.kt | 26 +++++++------- .../upload/chunked/ChunkedFileUploaderTest.kt | 28 +++++++-------- .../utils/AccountCookieInterceptorTest.kt | 30 ++++++++-------- 5 files changed, 63 insertions(+), 59 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 6be7e4bf1b8..1d9b04b0e2b 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -386,7 +386,7 @@ dependencies { // no version: consistent resolution aligns it with the tink that org.unifiedpush.android:connector ships testImplementation("com.google.crypto.tink:tink") testImplementation("androidx.room:room-testing:$roomVersion") - testImplementation("com.squareup.okhttp3:mockwebserver:$okhttpVersion") + testImplementation("com.squareup.okhttp3:mockwebserver3:$okhttpVersion") testImplementation("com.squareup.okhttp3:okhttp-tls:$okhttpVersion") testImplementation("com.google.dagger:hilt-android-testing:2.60.1") testImplementation("org.robolectric:robolectric:4.17") diff --git a/app/src/test/java/com/nextcloud/talk/login/data/network/NetworkLoginDataSourceTest.kt b/app/src/test/java/com/nextcloud/talk/login/data/network/NetworkLoginDataSourceTest.kt index 9bd6737980c..27d116800a1 100644 --- a/app/src/test/java/com/nextcloud/talk/login/data/network/NetworkLoginDataSourceTest.kt +++ b/app/src/test/java/com/nextcloud/talk/login/data/network/NetworkLoginDataSourceTest.kt @@ -14,8 +14,8 @@ import junit.framework.TestCase.assertNotNull import junit.framework.TestCase.assertNull import okhttp3.Credentials import okhttp3.OkHttpClient -import okhttp3.mockwebserver.MockResponse -import okhttp3.mockwebserver.MockWebServer +import mockwebserver3.MockResponse +import mockwebserver3.MockWebServer import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -51,7 +51,7 @@ class NetworkLoginDataSourceTest { "login":"https:\/\/cloud.example.com\/login\/v2\/flow\/guyjGtcKPTKCi4epIRIupIexgJ8wNInMFSfHabACRPZUkmEaWZSM54bFkFuzWksbps7jmTFQjeskLpyJXyhpHlgK8sZBn9HXLXjohIx5iXgJKdOkkZTYCzUWHlsg3YFg" } """.trimIndent() - val mockResponse = MockResponse().setBody(validResponse) + val mockResponse = MockResponse(body = validResponse) server.enqueue(mockResponse) val loginResponse = network.anonymouslyPostLoginRequest(httpUrl.toString()) @@ -61,11 +61,12 @@ class NetworkLoginDataSourceTest { @Test fun `testing anonymouslyPostLoginRequest error path`() { val server = MockWebServer() - val invalidResponse = MockResponse() + val invalidResponse = MockResponse.Builder() .addHeader("Content-Type", "application/json; charset=utf-8") .addHeader("Cache-Control", "no-cache") - .setResponseCode(404) - .setBody("{}") + .code(404) + .body("{}") + .build() server.start() server.enqueue(invalidResponse) @@ -87,7 +88,7 @@ class NetworkLoginDataSourceTest { } """.trimIndent() - val mockResponse = MockResponse().setBody(validResponse) + val mockResponse = MockResponse(body = validResponse) server.enqueue(mockResponse) server.start() val httpUrl = server.url("index.php/login/v2") @@ -107,8 +108,9 @@ class NetworkLoginDataSourceTest { } """.trimIndent() - val validResponse = MockResponse() - .setBody(validBody) + val validResponse = MockResponse.Builder() + .body(validBody) + .build() server.enqueue(validResponse) server.start() @@ -127,11 +129,12 @@ class NetworkLoginDataSourceTest { fun `testing performLoginFlowV2 error path`() { val server = MockWebServer() - val invalidResponse = MockResponse() + val invalidResponse = MockResponse.Builder() .addHeader("Content-Type", "application/json; charset=utf-8") .addHeader("Cache-Control", "no-cache") - .setResponseCode(404) - .setBody("{}") + .code(404) + .body("{}") + .build() server.enqueue(invalidResponse) server.start() @@ -156,8 +159,9 @@ class NetworkLoginDataSourceTest { } """.trimIndent() - val validResponse = MockResponse() - .setBody(validBody) + val validResponse = MockResponse.Builder() + .body(validBody) + .build() server.enqueue(validResponse) server.start() @@ -175,7 +179,7 @@ class NetworkLoginDataSourceTest { @Test(expected = NetworkLoginDataSource.TooManyLoginAttemptsException::class) fun `testing oneTimePasswordRequest refused because of too many failed logins`() { val server = MockWebServer() - server.enqueue(MockResponse().setResponseCode(429)) + server.enqueue(MockResponse(code = 429)) server.start() network.oneTimePasswordRequest(server.url("").toString(), Credentials.basic("username", "oneTimePassword")) @@ -184,7 +188,7 @@ class NetworkLoginDataSourceTest { @Test fun `testing oneTimePasswordRequest error path`() { val server = MockWebServer() - server.enqueue(MockResponse().setResponseCode(401)) + server.enqueue(MockResponse(code = 401)) server.start() val appPassword = diff --git a/app/src/test/java/com/nextcloud/talk/upload/chunked/ChunkedFileUploaderAbortTest.kt b/app/src/test/java/com/nextcloud/talk/upload/chunked/ChunkedFileUploaderAbortTest.kt index 6bfbbd747e9..b5f233a4de3 100644 --- a/app/src/test/java/com/nextcloud/talk/upload/chunked/ChunkedFileUploaderAbortTest.kt +++ b/app/src/test/java/com/nextcloud/talk/upload/chunked/ChunkedFileUploaderAbortTest.kt @@ -11,9 +11,9 @@ import com.nextcloud.talk.api.NcApiCoroutines import com.nextcloud.talk.data.user.model.User import okhttp3.MediaType.Companion.toMediaType import okhttp3.OkHttpClient -import okhttp3.mockwebserver.MockResponse -import okhttp3.mockwebserver.MockWebServer -import okhttp3.mockwebserver.SocketPolicy +import mockwebserver3.MockResponse +import mockwebserver3.MockWebServer +import mockwebserver3.SocketEffect import org.junit.After import org.junit.Assert.assertEquals import org.junit.Before @@ -63,7 +63,7 @@ class ChunkedFileUploaderAbortTest { @After fun tearDown() { - server.shutdown() + server.close() } @Test @@ -77,7 +77,7 @@ class ChunkedFileUploaderAbortTest { @Test fun `does not throw and does not report success when the connection drops`() { val uploader = startedUploader() - server.enqueue(MockResponse().apply { socketPolicy = SocketPolicy.DISCONNECT_AFTER_REQUEST }) + server.enqueue(MockResponse.Builder().onResponseStart(SocketEffect.CloseSocket()).build()) uploader.abortUpload { successCalls++ } @@ -87,7 +87,7 @@ class ChunkedFileUploaderAbortTest { @Test fun `does not throw and does not report success on a server error`() { val uploader = startedUploader() - server.enqueue(MockResponse().setResponseCode(SERVER_ERROR)) + server.enqueue(MockResponse(code = SERVER_ERROR)) uploader.abortUpload { successCalls++ } @@ -97,7 +97,7 @@ class ChunkedFileUploaderAbortTest { @Test fun `reports success when the upload folder is already gone`() { val uploader = startedUploader() - server.enqueue(MockResponse().setResponseCode(NOT_FOUND)) + server.enqueue(MockResponse(code = NOT_FOUND)) uploader.abortUpload { successCalls++ } @@ -107,7 +107,7 @@ class ChunkedFileUploaderAbortTest { @Test fun `reports success when the upload folder is deleted`() { val uploader = startedUploader() - server.enqueue(MockResponse().setResponseCode(NO_CONTENT)) + server.enqueue(MockResponse(code = NO_CONTENT)) uploader.abortUpload { successCalls++ } @@ -117,7 +117,7 @@ class ChunkedFileUploaderAbortTest { @Test fun `does not throw when the success callback throws`() { val uploader = startedUploader() - server.enqueue(MockResponse().setResponseCode(NO_CONTENT)) + server.enqueue(MockResponse(code = NO_CONTENT)) uploader.abortUpload { throw IllegalStateException("callback failed") } } @@ -125,7 +125,7 @@ class ChunkedFileUploaderAbortTest { @Test fun `does not throw when the success callback throws for a folder that is already gone`() { val uploader = startedUploader() - server.enqueue(MockResponse().setResponseCode(NOT_FOUND)) + server.enqueue(MockResponse(code = NOT_FOUND)) uploader.abortUpload { throw IllegalStateException("callback failed") } } @@ -135,9 +135,9 @@ class ChunkedFileUploaderAbortTest { /** Runs a rejected upload so the uploader knows its upload folder, then drops the recorded requests. */ private fun startedUploader(): ChunkedFileUploader { val file = tempFolder.newFile("test.txt").apply { writeBytes(ByteArray(FILE_SIZE)) } - server.enqueue(MockResponse().setResponseCode(CREATED)) - server.enqueue(MockResponse().setResponseCode(NOT_FOUND)) - server.enqueue(MockResponse().setResponseCode(FORBIDDEN)) + server.enqueue(MockResponse(code = CREATED)) + server.enqueue(MockResponse(code = NOT_FOUND)) + server.enqueue(MockResponse(code = FORBIDDEN)) val uploader = uploader() uploader.upload(file, "text/plain".toMediaType(), "/Talk/test.txt") repeat(server.requestCount) { server.takeRequest(1, TimeUnit.SECONDS) } diff --git a/app/src/test/java/com/nextcloud/talk/upload/chunked/ChunkedFileUploaderTest.kt b/app/src/test/java/com/nextcloud/talk/upload/chunked/ChunkedFileUploaderTest.kt index 0e2e0613d50..c10b7e597de 100644 --- a/app/src/test/java/com/nextcloud/talk/upload/chunked/ChunkedFileUploaderTest.kt +++ b/app/src/test/java/com/nextcloud/talk/upload/chunked/ChunkedFileUploaderTest.kt @@ -11,9 +11,9 @@ import com.nextcloud.talk.api.NcApiCoroutines import com.nextcloud.talk.data.user.model.User import okhttp3.MediaType.Companion.toMediaType import okhttp3.OkHttpClient -import okhttp3.mockwebserver.MockResponse -import okhttp3.mockwebserver.MockWebServer -import okhttp3.mockwebserver.SocketPolicy +import mockwebserver3.MockResponse +import mockwebserver3.MockWebServer +import mockwebserver3.SocketEffect import org.junit.After import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse @@ -68,7 +68,7 @@ class ChunkedFileUploaderTest { @After fun tearDown() { - server.shutdown() + server.close() } @Test @@ -76,9 +76,9 @@ class ChunkedFileUploaderTest { val file = localFile(SMALL_FILE_SIZE) enqueueFolderCreated() enqueueNoChunksOnServer() - server.enqueue(MockResponse().setResponseCode(CREATED)) - server.enqueue(MockResponse().setResponseCode(CREATED)) - server.enqueue(MockResponse().setResponseCode(CREATED)) + server.enqueue(MockResponse(code = CREATED)) + server.enqueue(MockResponse(code = CREATED)) + server.enqueue(MockResponse(code = CREATED)) val result = uploader().upload(file, MIME_TYPE, TARGET_PATH) @@ -91,7 +91,7 @@ class ChunkedFileUploaderTest { val file = localFile(SMALL_FILE_SIZE) enqueueFolderCreated() enqueueNoChunksOnServer() - server.enqueue(MockResponse().apply { socketPolicy = SocketPolicy.DISCONNECT_AFTER_REQUEST }) + server.enqueue(MockResponse.Builder().onResponseStart(SocketEffect.CloseSocket()).build()) assertThrows(IOException::class.java) { uploader().upload(file, MIME_TYPE, TARGET_PATH) @@ -105,7 +105,7 @@ class ChunkedFileUploaderTest { val file = localFile(SMALL_FILE_SIZE) enqueueFolderCreated() enqueueNoChunksOnServer() - server.enqueue(MockResponse().setResponseCode(FORBIDDEN)) + server.enqueue(MockResponse(code = FORBIDDEN)) val result = uploader().upload(file, MIME_TYPE, TARGET_PATH) @@ -118,9 +118,9 @@ class ChunkedFileUploaderTest { val file = localFile(SMALL_FILE_SIZE) enqueueFolderCreated() enqueueNoChunksOnServer() - server.enqueue(MockResponse().setResponseCode(CREATED)) - server.enqueue(MockResponse().setResponseCode(CREATED)) - server.enqueue(MockResponse().setResponseCode(FORBIDDEN)) + server.enqueue(MockResponse(code = CREATED)) + server.enqueue(MockResponse(code = CREATED)) + server.enqueue(MockResponse(code = FORBIDDEN)) val result = uploader().upload(file, MIME_TYPE, TARGET_PATH) @@ -133,11 +133,11 @@ class ChunkedFileUploaderTest { private fun localFile(size: Int): File = tempFolder.newFile("test.txt").apply { writeBytes(ByteArray(size)) } private fun enqueueFolderCreated() { - server.enqueue(MockResponse().setResponseCode(CREATED)) + server.enqueue(MockResponse(code = CREATED)) } private fun enqueueNoChunksOnServer() { - server.enqueue(MockResponse().setResponseCode(NOT_FOUND)) + server.enqueue(MockResponse(code = NOT_FOUND)) } private fun takeRequests() = (0 until server.requestCount).map { server.takeRequest(1, TimeUnit.SECONDS)!! } diff --git a/app/src/test/java/com/nextcloud/talk/utils/AccountCookieInterceptorTest.kt b/app/src/test/java/com/nextcloud/talk/utils/AccountCookieInterceptorTest.kt index 9c600c25fbe..2722065cbee 100644 --- a/app/src/test/java/com/nextcloud/talk/utils/AccountCookieInterceptorTest.kt +++ b/app/src/test/java/com/nextcloud/talk/utils/AccountCookieInterceptorTest.kt @@ -11,8 +11,8 @@ import okhttp3.Credentials import okhttp3.JavaNetCookieJar import okhttp3.OkHttpClient import okhttp3.Request -import okhttp3.mockwebserver.MockResponse -import okhttp3.mockwebserver.MockWebServer +import mockwebserver3.MockResponse +import mockwebserver3.MockWebServer import org.junit.After import org.junit.Assert.assertEquals import org.junit.Assert.assertNull @@ -44,36 +44,36 @@ class AccountCookieInterceptorTest { @After fun tearDown() { - server.shutdown() + server.close() } @Test fun `an account keeps its server session between requests`() { - server.enqueue(MockResponse().addHeader("Set-Cookie", "$SESSION_A; Path=/")) + server.enqueue(MockResponse.Builder().addHeader("Set-Cookie", "$SESSION_A; Path=/").build()) server.enqueue(MockResponse()) execute(CREDENTIALS_A) execute(CREDENTIALS_A) server.takeRequest() - assertEquals(SESSION_A, server.takeRequest().getHeader("Cookie")) + assertEquals(SESSION_A, server.takeRequest().headers["Cookie"]) } @Test fun `another account on the same server doesn't get the session`() { - server.enqueue(MockResponse().addHeader("Set-Cookie", "$SESSION_A; Path=/")) + server.enqueue(MockResponse.Builder().addHeader("Set-Cookie", "$SESSION_A; Path=/").build()) server.enqueue(MockResponse()) execute(CREDENTIALS_A) execute(CREDENTIALS_B) server.takeRequest() - assertNull(server.takeRequest().getHeader("Cookie")) + assertNull(server.takeRequest().headers["Cookie"]) } @Test fun `a removed account's session isn't sent anymore`() { - server.enqueue(MockResponse().addHeader("Set-Cookie", "$SESSION_A; Path=/")) + server.enqueue(MockResponse.Builder().addHeader("Set-Cookie", "$SESSION_A; Path=/").build()) server.enqueue(MockResponse()) execute(CREDENTIALS_A) @@ -81,12 +81,12 @@ class AccountCookieInterceptorTest { execute(CREDENTIALS_A) server.takeRequest() - assertNull(server.takeRequest().getHeader("Cookie")) + assertNull(server.takeRequest().headers["Cookie"]) } @Test fun `cookies of an account are kept out of the shared cookie store`() { - server.enqueue(MockResponse().addHeader("Set-Cookie", "$SESSION_A; Path=/")) + server.enqueue(MockResponse.Builder().addHeader("Set-Cookie", "$SESSION_A; Path=/").build()) server.enqueue(MockResponse()) execute(CREDENTIALS_A) @@ -94,31 +94,31 @@ class AccountCookieInterceptorTest { assertTrue(sharedCookieManager.cookieStore.cookies.isEmpty()) server.takeRequest() - assertNull(server.takeRequest().getHeader("Cookie")) + assertNull(server.takeRequest().headers["Cookie"]) } @Test fun `a session cookie of the shared store isn't sent with credentials`() { - server.enqueue(MockResponse().addHeader("Set-Cookie", "$SESSION_SHARED; Path=/")) + server.enqueue(MockResponse.Builder().addHeader("Set-Cookie", "$SESSION_SHARED; Path=/").build()) server.enqueue(MockResponse()) execute(authorization = null) execute(CREDENTIALS_A) server.takeRequest() - assertNull(server.takeRequest().getHeader("Cookie")) + assertNull(server.takeRequest().headers["Cookie"]) } @Test fun `requests without credentials keep using the shared cookie store`() { - server.enqueue(MockResponse().addHeader("Set-Cookie", "$SESSION_SHARED; Path=/")) + server.enqueue(MockResponse.Builder().addHeader("Set-Cookie", "$SESSION_SHARED; Path=/").build()) server.enqueue(MockResponse()) execute(authorization = null) execute(authorization = null) server.takeRequest() - assertEquals(SESSION_SHARED, server.takeRequest().getHeader("Cookie")) + assertEquals(SESSION_SHARED, server.takeRequest().headers["Cookie"]) } private fun execute(authorization: String?) {