|
| 1 | +package me.rosuh.easywatermark.data.repo |
| 2 | + |
| 3 | +import kotlinx.cinterop.ExperimentalForeignApi |
| 4 | +import kotlinx.coroutines.Dispatchers |
| 5 | +import kotlinx.coroutines.runBlocking |
| 6 | +import me.rosuh.easywatermark.data.db.buildTemplateDatabase |
| 7 | +import me.rosuh.easywatermark.domain.TemplateEditor |
| 8 | +import okio.FileSystem |
| 9 | +import okio.Path.Companion.toPath |
| 10 | +import platform.Foundation.NSFileManager |
| 11 | +import platform.Foundation.NSTemporaryDirectory |
| 12 | +import platform.Foundation.NSUUID |
| 13 | +import kotlin.test.Test |
| 14 | +import kotlin.test.assertEquals |
| 15 | +import kotlin.test.assertTrue |
| 16 | + |
| 17 | +/** |
| 18 | + * S4d-233: iOS runtime proof of the Swift-facing [IosTemplateBridge] over the common templates store. |
| 19 | + * RUNS on `iosSimulatorArm64Test`. |
| 20 | + * |
| 21 | + * The bridge is exercised over test-controlled DBs built with the parameterized `buildTemplateDatabase` |
| 22 | + * overloads (empty-store and S4d-232 seed-bytes), NOT the no-arg `buildTemplateDatabase()` — that path |
| 23 | + * reads the seed from `NSBundle.mainBundle`, which a Kotlin/Native test executable's bundle does not carry |
| 24 | + * (see `IosFontLoaderTest`). The no-arg seeded path used by `defaultIosTemplateBridge()` is proven by the |
| 25 | + * S4d-232 packaging gate (the seed ships in `iosApp.app`) + the live app. |
| 26 | + */ |
| 27 | +class IosTemplateBridgeTest { |
| 28 | + |
| 29 | + @OptIn(ExperimentalForeignApi::class) |
| 30 | + private fun uniqueDir(tag: String): String { |
| 31 | + val dir = NSTemporaryDirectory() + "s4d233_${tag}_" + NSUUID().UUIDString() |
| 32 | + NSFileManager.defaultManager.createDirectoryAtPath( |
| 33 | + path = dir, |
| 34 | + withIntermediateDirectories = true, |
| 35 | + attributes = null, |
| 36 | + error = null, |
| 37 | + ) |
| 38 | + return dir |
| 39 | + } |
| 40 | + |
| 41 | + @Test |
| 42 | + fun bridge_add_list_delete_roundtrip() = runBlocking { |
| 43 | + val db = buildTemplateDatabase(uniqueDir("roundtrip")) |
| 44 | + try { |
| 45 | + val bridge = IosTemplateBridge(TemplateRepository(db.templateDao(), Dispatchers.Default)) |
| 46 | + assertTrue(bridge.currentTemplates().isEmpty(), "empty store starts empty") |
| 47 | + |
| 48 | + bridge.addTemplate("ios bridge A") |
| 49 | + bridge.addTemplate("ios bridge B") |
| 50 | + val after = bridge.currentTemplates() |
| 51 | + assertEquals(2, after.size, "two templates after add") |
| 52 | + assertTrue(after.any { it.content == "ios bridge A" }, "content A round-trips") |
| 53 | + assertTrue(after.any { it.content == "ios bridge B" }, "content B round-trips") |
| 54 | + assertTrue(after.all { it.id != 0 }, "rows carry autoGenerate ids") |
| 55 | + |
| 56 | + val target = after.first { it.content == "ios bridge A" } |
| 57 | + bridge.deleteTemplate(target.id) |
| 58 | + val afterDelete = bridge.currentTemplates() |
| 59 | + assertEquals(1, afterDelete.size, "one template after delete") |
| 60 | + assertTrue(afterDelete.none { it.id == target.id }, "the deleted id is gone") |
| 61 | + assertTrue(afterDelete.any { it.content == "ios bridge B" }, "the other template remains") |
| 62 | + } finally { |
| 63 | + db.close() |
| 64 | + } |
| 65 | + } |
| 66 | + |
| 67 | + @Test |
| 68 | + fun bridge_reads_seeded_rows() = runBlocking { |
| 69 | + // Produce a valid seed DB (mirrors S4d-232), then prove the bridge reads seeded content. |
| 70 | + val seedDir = uniqueDir("seedsrc") |
| 71 | + val seedDb = buildTemplateDatabase(seedDir) |
| 72 | + try { |
| 73 | + TemplateEditor(TemplateRepository(seedDb.templateDao(), Dispatchers.Default)).add("seeded template") |
| 74 | + } finally { |
| 75 | + seedDb.close() |
| 76 | + } |
| 77 | + val seedBytes = FileSystem.SYSTEM.read("$seedDir/ewm-db".toPath()) { readByteArray() } |
| 78 | + |
| 79 | + val db = buildTemplateDatabase(uniqueDir("seeded"), seedBytes = seedBytes) |
| 80 | + try { |
| 81 | + val bridge = IosTemplateBridge(TemplateRepository(db.templateDao(), Dispatchers.Default)) |
| 82 | + val rows = bridge.currentTemplates() |
| 83 | + assertEquals(1, rows.size, "the seeded row is visible through the bridge") |
| 84 | + assertTrue(rows.any { it.content == "seeded template" }, "seeded content reads back") |
| 85 | + } finally { |
| 86 | + db.close() |
| 87 | + } |
| 88 | + } |
| 89 | +} |
0 commit comments