|
| 1 | +package me.rosuh.easywatermark.data.db |
| 2 | + |
| 3 | +import kotlinx.cinterop.ExperimentalForeignApi |
| 4 | +import kotlinx.coroutines.Dispatchers |
| 5 | +import kotlinx.coroutines.flow.first |
| 6 | +import kotlinx.coroutines.runBlocking |
| 7 | +import me.rosuh.easywatermark.data.repo.TemplateRepository |
| 8 | +import me.rosuh.easywatermark.domain.TemplateEditor |
| 9 | +import platform.Foundation.NSFileManager |
| 10 | +import platform.Foundation.NSTemporaryDirectory |
| 11 | +import platform.Foundation.NSUUID |
| 12 | +import kotlin.test.Test |
| 13 | +import kotlin.test.assertEquals |
| 14 | +import kotlin.test.assertFalse |
| 15 | +import kotlin.test.assertTrue |
| 16 | +import kotlin.time.Clock |
| 17 | + |
| 18 | +/** |
| 19 | + * S4d-231: iOS runtime proof that the commonMain Room templates path RUNS on iOS via the new iosMain |
| 20 | + * [buildTemplateDatabase] (bundled SQLite driver). Empty store (no seeding). Exercises the existing |
| 21 | + * commonMain [TemplateRepository] (`checkIfIsDaoNull`/`getAllTemplate`) + [TemplateEditor] |
| 22 | + * (`isDaoNull`/`add`/`update`/`delete`), mirroring the desktopTest `TemplateRoundtripTest` with plain |
| 23 | + * `runBlocking` (no `kotlinx-coroutines-test`). RUNS on `iosSimulatorArm64Test`. |
| 24 | + * |
| 25 | + * A unique `NSUUID`-suffixed directory under `NSTemporaryDirectory()` avoids cross-run collisions in the |
| 26 | + * ephemeral simulator container (the builder hardcodes the DB file name `ewm-db`). |
| 27 | + */ |
| 28 | +class TemplateRoundtripTest { |
| 29 | + |
| 30 | + @OptIn(ExperimentalForeignApi::class) |
| 31 | + private fun newEmptyDb(): AppDatabase { |
| 32 | + val dir = NSTemporaryDirectory() + "s4d231_template_" + NSUUID().UUIDString() |
| 33 | + NSFileManager.defaultManager.createDirectoryAtPath( |
| 34 | + path = dir, |
| 35 | + withIntermediateDirectories = true, |
| 36 | + attributes = null, |
| 37 | + error = null, |
| 38 | + ) |
| 39 | + return buildTemplateDatabase(dir) |
| 40 | + } |
| 41 | + |
| 42 | + @Test |
| 43 | + fun ios_empty_store_add_list_update_delete_roundtrip() = runBlocking { |
| 44 | + val db = newEmptyDb() |
| 45 | + try { |
| 46 | + val repo = TemplateRepository(db.templateDao(), Dispatchers.Default) |
| 47 | + val editor = TemplateEditor(repo) |
| 48 | + |
| 49 | + // A real bundled-driver DB (not the null-DB fallback) that starts with zero templates. |
| 50 | + assertFalse(editor.isDaoNull(), "real DAO must be present (not the null-DB fallback)") |
| 51 | + assertTrue( |
| 52 | + repo.getAllTemplate().first().isEmpty(), |
| 53 | + "a freshly built empty store has no templates", |
| 54 | + ) |
| 55 | + |
| 56 | + editor.add("S4d-231 ios roundtrip") |
| 57 | + val afterAdd = repo.getAllTemplate().first() |
| 58 | + assertEquals(1, afterAdd.size, "exactly one template after add") |
| 59 | + assertEquals("S4d-231 ios roundtrip", afterAdd[0].content, "the inserted content round-trips") |
| 60 | + assertTrue(afterAdd[0].id != 0, "autoGenerate assigned a row id") |
| 61 | + |
| 62 | + val original = afterAdd[0] |
| 63 | + editor.update(original.copy(content = "updated content", lastModifiedDate = Clock.System.now())) |
| 64 | + val afterUpdate = repo.getAllTemplate().first() |
| 65 | + assertEquals(1, afterUpdate.size, "still exactly one template after update") |
| 66 | + assertEquals(original.id, afterUpdate[0].id, "update preserves the row id") |
| 67 | + assertEquals("updated content", afterUpdate[0].content, "update changes the content") |
| 68 | + assertEquals( |
| 69 | + original.creationDate, |
| 70 | + afterUpdate[0].creationDate, |
| 71 | + "update preserves the creation date", |
| 72 | + ) |
| 73 | + |
| 74 | + editor.delete(afterUpdate[0]) |
| 75 | + assertTrue( |
| 76 | + repo.getAllTemplate().first().isEmpty(), |
| 77 | + "store is empty again after delete", |
| 78 | + ) |
| 79 | + } finally { |
| 80 | + db.close() |
| 81 | + } |
| 82 | + } |
| 83 | +} |
0 commit comments