From 4c24f45808a0ff00717f2d5a106fc169d7a0a71c Mon Sep 17 00:00:00 2001 From: Friday Date: Fri, 5 Jun 2026 16:02:57 +0000 Subject: [PATCH] Add live SFTPGo WebDAV test (real 2nd WebDAV server via dav.khodak.me) Tests the app's SFTPGo provider (WebDavProvider) end-to-end against a real SFTPGo server over its exposed WebDAV URL: connect, mkdir, atomic upload, list, download, overwrite, non-ASCII filename, delete. Validates the WebDAV code path against a non-Nextcloud server. Creds via -e davUrl/davUser/davPass. --- .../kotlin/com/syncflow/SftpgoWebDavTest.kt | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 app/src/androidTest/kotlin/com/syncflow/SftpgoWebDavTest.kt diff --git a/app/src/androidTest/kotlin/com/syncflow/SftpgoWebDavTest.kt b/app/src/androidTest/kotlin/com/syncflow/SftpgoWebDavTest.kt new file mode 100644 index 0000000..e3eb05e --- /dev/null +++ b/app/src/androidTest/kotlin/com/syncflow/SftpgoWebDavTest.kt @@ -0,0 +1,68 @@ +package com.syncflow + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import com.syncflow.data.providers.webdav.WebDavProvider +import com.syncflow.domain.model.CloudAccount +import com.syncflow.domain.model.ProviderType +import kotlinx.coroutines.runBlocking +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Assume.assumeTrue +import org.junit.Test +import org.junit.runner.RunWith +import java.io.ByteArrayInputStream +import java.io.ByteArrayOutputStream + +/** + * Live test of the app's SFTPGo provider (which is WebDavProvider) against a real SFTPGo + * server over its externally-exposed WebDAV URL. Validates the provider against a different + * WebDAV implementation than Nextcloud. Creds via -e davUrl/davUser/davPass; skips otherwise. + */ +@RunWith(AndroidJUnit4::class) +class SftpgoWebDavTest { + + private val args = InstrumentationRegistry.getArguments() + + private fun provider() = WebDavProvider( + CloudAccount( + id = 1, displayName = "sftpgo", email = null, providerType = ProviderType.SFTPGO, + credentialJson = """{"username":"${args.getString("davUser")}","password":"${args.getString("davPass")}"}""", + serverUrl = args.getString("davUrl"), port = null, + ), + ) + + @Test fun sftpgoWebDavRoundTrip() = runBlocking { + assumeTrue("davUrl/davUser/davPass required", args.getString("davUrl") != null) + val p = provider() + val dir = "SyncFlowDav_${System.currentTimeMillis()}" + try { + assertTrue("testConnection", p.testConnection().isSuccess) + assertTrue("mkdir", p.createDirectory(dir).isSuccess) + + // upload (atomic temp + MOVE), list, download — with a non-ASCII payload + val body = "sftpgo webdav round-trip ✓ café".toByteArray() + assertTrue("upload", p.uploadFile(ByteArrayInputStream(body), "$dir/f.txt", body.size.toLong()).isSuccess) + assertTrue("f.txt" in p.listFiles(dir).getOrThrow().map { it.name }) + val out = ByteArrayOutputStream(); p.downloadFile("$dir/f.txt", out).getOrThrow() + assertEquals("sftpgo webdav round-trip ✓ café", out.toString("UTF-8")) + + // overwrite via atomic temp+MOVE + val v2 = "updated-content".toByteArray() + assertTrue(p.uploadFile(ByteArrayInputStream(v2), "$dir/f.txt", v2.size.toLong()).isSuccess) + val out2 = ByteArrayOutputStream(); p.downloadFile("$dir/f.txt", out2).getOrThrow() + assertEquals("updated-content", out2.toString("UTF-8")) + + // non-ASCII / special filename (the URL/MOVE-header encoding fix) + val special = "café & rapport (1).txt" + assertTrue(p.uploadFile(ByteArrayInputStream("x".toByteArray()), "$dir/$special", 1).isSuccess) + assertTrue(special in p.listFiles(dir).getOrThrow().map { it.name }) + + // delete + assertTrue(p.deleteFile("$dir/f.txt").isSuccess) + assertTrue("f.txt" !in p.listFiles(dir).getOrThrow().map { it.name }) + } finally { + runCatching { p.deleteFile(dir) } + } + } +}