package org.meshbay.client import org.junit.Assert.assertEquals import org.junit.Assert.assertNull import org.junit.Assert.assertTrue import org.junit.Rule import org.junit.Test import org.junit.rules.TemporaryFolder import org.meshbay.client.drive.DriveFile import org.meshbay.client.drive.DriveLedger import org.meshbay.client.drive.DrivePlan import org.meshbay.client.photos.PhotoPlan import org.meshbay.client.photos.PhotoSource import java.util.TimeZone class DriveTest { @get:Rule val tmp = TemporaryFolder() private val utc = TimeZone.getTimeZone("UTC") private val tree = "content://tree/primary%3ADocuments" private val names = mapOf(tree to "Documents") private fun file(id: String, sub: String = "", size: Long = 10, modified: Long = 1000) = DriveFile(tree, "primary:Documents/" + (if (sub.isEmpty()) "" else "$sub/") + id, sub, id, size, modified, "application/pdf") private fun plan(files: List, sent: Map = emptyMap(), skip: Set = emptySet(), same: Boolean = false) = DrivePlan.plan(files, "Backups/bob-drive", names, { sent[it] }, skip, utc) { _, _ -> same } private fun entry(f: DriveFile) = DriveLedger.Entry(f.docId, f.modified, f.size, "h", "d", f.name, 1) @Test fun `media folders and a whole volume cannot be chosen`() { assertEquals("whole_storage", DrivePlan.refusal("primary:")) assertEquals("whole_storage", DrivePlan.refusal("1234-ABCD:")) assertEquals("media_folder", DrivePlan.refusal("primary:DCIM")) assertEquals("media_folder", DrivePlan.refusal("primary:DCIM/Camera")) assertEquals("media_folder", DrivePlan.refusal("primary:Pictures/Screenshots")) assertEquals("media_folder", DrivePlan.refusal("1234-ABCD:movies")) assertNull(DrivePlan.refusal("primary:Documents")) assertNull(DrivePlan.refusal("primary:Download/Invoices")) assertNull(DrivePlan.refusal("primary:Documents/Pictures")) } @Test fun `files go under the chosen folder's name, sub-folders kept`() { val out = plan(listOf(file("a.pdf"), file("b.pdf", sub = "Tax/2026"))) assertEquals(setOf("Backups/bob-drive/Documents", "Backups/bob-drive/Documents/Tax/2026"), out.map { it.dir }.toSet()) } @Test fun `smallest first, hidden files and photo-backed files left out`() { val out = plan(listOf(file("big.pdf", size = 900), file("small.pdf", size = 5), file(".secret"), file("scan.jpg", sub = "Scans")), skip = setOf("documents/scans/scan.jpg")) assertEquals(listOf("small.pdf", "big.pdf"), out.map { it.name }) } @Test fun `a file already sent is not sent again, a changed one goes beside it`() { val f = file("a.pdf") assertTrue(plan(listOf(f), sent = mapOf(f.docId to entry(f))).isEmpty()) val changed = f.copy(size = 20, modified = 86_400_000) val out = plan(listOf(changed), sent = mapOf(f.docId to entry(f))) assertEquals("a-modified-19700102-000000.pdf", out.single().name) assertTrue(out.single().edited) val touched = f.copy(modified = 5000) assertTrue(plan(listOf(touched), sent = mapOf(f.docId to entry(f)), same = true).isEmpty()) } @Test fun `names the node would refuse are mended, not dropped`() { assertEquals("report.pdf", DrivePlan.safeName("report.pdf")) assertEquals("a_b_c.txt", DrivePlan.safeName("a:b?c.txt")) assertEquals("file-.env", DrivePlan.safeName(".env")) assertEquals("file-_draft", DrivePlan.safeName("_draft")) assertTrue(PhotoPlan.UPLOAD_NAME.matches(DrivePlan.safeName("x".repeat(300) + ".pdf"))) assertTrue(PhotoPlan.UPLOAD_NAME.matches(DrivePlan.safeName("end. "))) } @Test fun `the ledger remembers across a restart`() { val path = tmp.newFile("d.jsonl") DriveLedger(path).apply { record(entry(file("a.pdf"))); record(entry(file("a.pdf")).copy(sha256 = "z")) } val again = DriveLedger(path) assertEquals(1, again.size) assertEquals("z", again["primary:Documents/a.pdf"]!!.sha256) } @Test fun `a path from before Android 10 is read from the top of its volume`() { assertEquals("Documents/Scans/a.jpg", PhotoSource.volumeRelative("/storage/emulated/0/Documents/Scans/a.jpg")) assertEquals("x/a.jpg", PhotoSource.volumeRelative("/storage/1234-ABCD/x/a.jpg")) } }