diff options
Diffstat (limited to 'app/src/test/java/invalid/lena')
| -rw-r--r-- | app/src/test/java/invalid/lena/rsend/ConfigTest.kt | 111 | ||||
| -rw-r--r-- | app/src/test/java/invalid/lena/rsend/RsyncRunnerTest.kt | 12 |
2 files changed, 114 insertions, 9 deletions
diff --git a/app/src/test/java/invalid/lena/rsend/ConfigTest.kt b/app/src/test/java/invalid/lena/rsend/ConfigTest.kt index a63582d..3449a8c 100644 --- a/app/src/test/java/invalid/lena/rsend/ConfigTest.kt +++ b/app/src/test/java/invalid/lena/rsend/ConfigTest.kt @@ -2,6 +2,8 @@ package invalid.lena.rsend import org.json.JSONObject import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue import org.junit.Test class ConfigTest { @@ -9,11 +11,14 @@ class ConfigTest { @Test fun roundTrip() { val c = Config( - remote = Remote("home", 2222, "backup"), + remotes = listOf( + Remote("nas", "home", 2222, "backup", "home ssh-ed25519 AAAA"), + Remote("reader", "kobo.lan", 22, "sync"), + ), schedule = Schedule(enabled = true, intervalMinutes = 30, wifiOnly = false, requireCharging = true), folders = listOf( - Folder("DCIM", "/storage/emulated/0/DCIM", "/b/DCIM", false, listOf(".thumbnails/")), - Folder("W", "/w", "/b/w", true, emptyList()), + Folder("DCIM", "/storage/emulated/0/DCIM", "nas", "/b/DCIM", false, listOf(".thumbnails/")), + Folder("Books", "/books", "reader", "/sd/books", true, emptyList()), ), ) assertEquals(c, Config.fromJson(c.toJson())) @@ -29,4 +34,104 @@ class ConfigTest { val json = JSONObject("""{"schedule":{"intervalMinutes":1}}""") assertEquals(Schedule.MIN_INTERVAL_MINUTES, Config.fromJson(json).schedule.intervalMinutes) } + + @Test + fun readsLegacySingleRemoteShape() { + val json = JSONObject( + """ + { + "remote": {"host": "home", "port": 2222, "user": "backup"}, + "folders": [ + {"name": "DCIM", "local": "/d", "remote": "/b/DCIM", "delete": true, "excludes": [".x/"]} + ] + } + """ + ) + val c = Config.fromJson(json, legacyPin = "home ssh-ed25519 AAAA") + assertEquals(listOf(Remote("home", "home", 2222, "backup", "home ssh-ed25519 AAAA")), c.remotes) + assertEquals( + listOf(Folder("DCIM", "/d", "home", "/b/DCIM", true, listOf(".x/"))), + c.folders, + ) + } + + @Test + fun legacyShapeWithoutHostStaysEmpty() { + val json = JSONObject("""{"remote": {"host": "", "port": 22, "user": ""}}""") + assertEquals(emptyList<Remote>(), Config.fromJson(json, legacyPin = "unused").remotes) + } + + // A 0.1.x config always wrote a "remote" object, even before the host was + // filled in. Such a config still has folders with real destination paths; + // migrating must keep them rather than drop them on the floor. + @Test + fun legacyShapeWithoutHostKeepsFolderPaths() { + val json = JSONObject( + """ + { + "remote": {"host": "", "port": 22, "user": ""}, + "folders": [ + {"name": "DCIM", "local": "/d", "remote": "/b/DCIM", "delete": false, "excludes": []} + ] + } + """ + ) + val c = Config.fromJson(json) + assertEquals(listOf(Folder("DCIM", "/d", "", "/b/DCIM", false, emptyList())), c.folders) + } + + // The migration must run exactly once. load() writes the converted config + // back, and the written shape must no longer look legacy, or a later read + // would migrate an already-migrated config and re-apply the legacy field + // mapping to fields that no longer carry those meanings. + @Test + fun migratingTwiceIsNotPossible() { + val legacy = JSONObject( + """ + { + "remote": {"host": "home", "port": 2222, "user": "backup"}, + "folders": [{"name": "DCIM", "local": "/d", "remote": "/b/DCIM", "delete": true, "excludes": []}] + } + """ + ) + assertTrue(Config.legacyShape(legacy)) + + val once = Config.fromJson(legacy, legacyPin = "home ssh-ed25519 AAAA") + val written = once.toJson() + assertFalse(Config.legacyShape(written)) + assertEquals(once, Config.fromJson(written)) + } + + @Test + fun freshAndCurrentConfigsAreNotLegacy() { + assertFalse(Config.legacyShape(JSONObject("{}"))) + assertFalse(Config.legacyShape(Config().toJson())) + assertFalse(Config.legacyShape(JSONObject("""{"remotes": [], "remote": {"host": "x"}}"""))) + } + + // An empty remotes array is still the new shape: a 0.2.x user who deleted + // their only remote must not be dragged back through the migration. + @Test + fun emptyRemotesArrayIsNotLegacy() { + val json = JSONObject( + """{"remotes": [], "folders": [{"name": "DCIM", "local": "/d", "remotePath": "/b", "remoteName": "gone"}]}""" + ) + assertFalse(Config.legacyShape(json)) + val c = Config.fromJson(json, legacyPin = "should be ignored") + assertEquals(emptyList<Remote>(), c.remotes) + assertEquals(listOf(Folder("DCIM", "/d", "gone", "/b", false, emptyList())), c.folders) + } + + @Test + fun syncReadyNeedsKeyFolderAndPinnedRemote() { + val pinned = Remote("nas", "home", 22, "backup", "line") + val folder = Folder("d", "/d", "nas", "/b") + assertTrue(Config(remotes = listOf(pinned), folders = listOf(folder)).syncReady(keyExists = true)) + assertFalse(Config(remotes = listOf(pinned), folders = listOf(folder)).syncReady(keyExists = false)) + assertFalse(Config(remotes = listOf(pinned)).syncReady(keyExists = true)) + assertFalse( + Config(remotes = listOf(pinned.copy(hostKey = "")), folders = listOf(folder)) + .syncReady(keyExists = true) + ) + } } diff --git a/app/src/test/java/invalid/lena/rsend/RsyncRunnerTest.kt b/app/src/test/java/invalid/lena/rsend/RsyncRunnerTest.kt index 8edf9fa..9729f62 100644 --- a/app/src/test/java/invalid/lena/rsend/RsyncRunnerTest.kt +++ b/app/src/test/java/invalid/lena/rsend/RsyncRunnerTest.kt @@ -6,11 +6,11 @@ import org.junit.Test class RsyncRunnerTest { - private val remote = Remote("host", 22, "user") + private val remote = Remote("nas", "host", 22, "user") @Test fun basicArgs() { - val a = RsyncRunner.args("RSH", remote, Folder(name = "n", local = "/a", remote = "/b")) + val a = RsyncRunner.args("RSH", remote, Folder(name = "n", local = "/a", remoteName = "nas", remotePath = "/b")) assertEquals( listOf( "-rt", "--partial", "--timeout=300", @@ -23,26 +23,26 @@ class RsyncRunnerTest { @Test fun mirrorAddsDelete() { - val a = RsyncRunner.args("RSH", remote, Folder(local = "/a", remote = "/b", delete = true)) + val a = RsyncRunner.args("RSH", remote, Folder(local = "/a", remotePath = "/b", delete = true)) assertTrue(a.contains("--delete")) } @Test fun additiveOmitsDelete() { - val a = RsyncRunner.args("RSH", remote, Folder(local = "/a", remote = "/b", delete = false)) + val a = RsyncRunner.args("RSH", remote, Folder(local = "/a", remotePath = "/b", delete = false)) assertTrue(!a.contains("--delete")) } @Test fun excludesBecomeFlags() { - val a = RsyncRunner.args("RSH", remote, Folder(local = "/a", remote = "/b", excludes = listOf(".x/", ".y"))) + val a = RsyncRunner.args("RSH", remote, Folder(local = "/a", remotePath = "/b", excludes = listOf(".x/", ".y"))) assertTrue(a.contains("--exclude=.x/")) assertTrue(a.contains("--exclude=.y")) } @Test fun trailingSlashIdempotent() { - val a = RsyncRunner.args("RSH", remote, Folder(local = "/a/", remote = "/b/")) + val a = RsyncRunner.args("RSH", remote, Folder(local = "/a/", remotePath = "/b/")) assertTrue(a.contains("/a/")) assertTrue(a.contains("user@host:/b/")) } |