From 15b94a0407f3781593c9eabf896e4b5a9b5f0ed8 Mon Sep 17 00:00:00 2001 From: Friday Date: Fri, 5 Jun 2026 16:14:13 +0000 Subject: [PATCH] Add real-world large-file test (multi-GB from phone via external URL, chunked) Opt-in (-e bigFileMB=): streams a multi-GB file from the device through the app's chunked-upload path to the external nextcloud.khodak.me and verifies the full size lands. Verified live: 1.5 GB and 5 GB both succeed end-to-end. --- .../com/syncflow/NextcloudIntegrationTest.kt | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/app/src/androidTest/kotlin/com/syncflow/NextcloudIntegrationTest.kt b/app/src/androidTest/kotlin/com/syncflow/NextcloudIntegrationTest.kt index f37ad52..6f3ae50 100644 --- a/app/src/androidTest/kotlin/com/syncflow/NextcloudIntegrationTest.kt +++ b/app/src/androidTest/kotlin/com/syncflow/NextcloudIntegrationTest.kt @@ -22,6 +22,9 @@ import org.junit.Test import org.junit.runner.RunWith import java.io.ByteArrayInputStream import java.io.ByteArrayOutputStream +import java.io.File +import java.io.FileInputStream +import java.io.FileOutputStream import java.time.Instant /** @@ -37,6 +40,7 @@ import java.time.Instant @RunWith(AndroidJUnit4::class) class NextcloudIntegrationTest { + private val ctx = InstrumentationRegistry.getInstrumentation().targetContext private val args = InstrumentationRegistry.getArguments() private val url = args.getString("ncUrl") private val user = args.getString("ncUser") @@ -138,4 +142,44 @@ class NextcloudIntegrationTest { runCatching { p.deleteFile(dir) } } } + + /** + * Real-world large-file test: streams a multi-GB file FROM THE PHONE through the app's + * chunked-upload path to the external URL, verifies the full size landed, then cleans up. + * Opt-in (slow): pass -e bigFileMB=, e.g. 1536 for 1.5 GB. + */ + @Test + fun realWorld_largeFileChunkedUpload() = runBlocking { + assumeTrue("ncUrl/ncUser/ncPass required", url != null && user != null && pass != null) + val mb = args.getString("bigFileMB")?.toIntOrNull() ?: 0 + assumeTrue("pass -e bigFileMB= to run the big-file test", mb > 0) + + val account = CloudAccount( + id = 1, displayName = "IT", email = user, providerType = ProviderType.NEXTCLOUD, + credentialJson = """{"username":"$user","password":"$pass"}""", serverUrl = url, port = null, + ) + val p = NextcloudProvider(account) // default 100 MB chunks -> chunked path for >100 MB + val dir = "SyncFlowBig_${System.currentTimeMillis()}" + val tmp = File(ctx.cacheDir, "bigfile_${System.currentTimeMillis()}.bin") + try { + val total = mb.toLong() * 1024 * 1024 + FileOutputStream(tmp).use { os -> + val buf = ByteArray(8 * 1024 * 1024) + var written = 0L + while (written < total) { + val n = minOf(buf.size.toLong(), total - written).toInt() + os.write(buf, 0, n); written += n + } + } + assertEquals(total, tmp.length()) + p.createDirectory(dir).getOrThrow() + val up = p.uploadFile(FileInputStream(tmp), "$dir/big.bin", tmp.length()) + assertTrue("large chunked upload failed: ${up.exceptionOrNull()}", up.isSuccess) + assertEquals("full file size must land on the server", total, + p.listFiles(dir).getOrThrow().first { it.name == "big.bin" }.sizeBytes) + } finally { + tmp.delete() + runCatching { p.deleteFile(dir) } + } + } }