@@ -2,110 +2,59 @@ package com.powersync
22
33import app.cash.turbine.turbineScope
44import co.touchlab.kermit.ExperimentalKermitApi
5- import co.touchlab.kermit.Logger
6- import co.touchlab.kermit.Severity
7- import co.touchlab.kermit.TestConfig
8- import co.touchlab.kermit.TestLogWriter
95import com.powersync.db.ActiveDatabaseGroup
10- import com.powersync.db.getString
116import com.powersync.db.schema.Schema
127import com.powersync.testutils.UserRow
13- import com.powersync.testutils.generatePrintLogWriter
8+ import com.powersync.testutils.databaseTest
149import com.powersync.testutils.getTempDir
1510import com.powersync.testutils.waitFor
11+ import io.kotest.assertions.throwables.shouldThrow
12+ import io.kotest.matchers.collections.shouldHaveSize
13+ import io.kotest.matchers.shouldBe
14+ import io.kotest.matchers.string.shouldContain
1615import kotlinx.coroutines.CompletableDeferred
1716import kotlinx.coroutines.Dispatchers
1817import kotlinx.coroutines.async
1918import kotlinx.coroutines.delay
2019import kotlinx.coroutines.runBlocking
21- import kotlinx.coroutines.test.runTest
2220import kotlinx.coroutines.withContext
23- import kotlin.test.AfterTest
24- import kotlin.test.BeforeTest
2521import kotlin.test.Test
2622import kotlin.test.assertEquals
27- import kotlin.test.assertFailsWith
2823import kotlin.test.assertNotNull
29- import kotlin.test.assertTrue
3024
3125@OptIn(ExperimentalKermitApi ::class )
3226class DatabaseTest {
33- private val logWriter =
34- TestLogWriter (
35- loggable = Severity .Debug ,
36- )
37-
38- private val logger =
39- Logger (
40- TestConfig (
41- minSeverity = Severity .Debug ,
42- logWriterList = listOf (logWriter, generatePrintLogWriter()),
43- ),
44- )
45-
46- private lateinit var database: PowerSyncDatabase
47-
48- private fun openDB () =
49- PowerSyncDatabase (
50- factory = com.powersync.testutils.factory,
51- schema = Schema (UserRow .table),
52- dbFilename = " testdb" ,
53- logger = logger,
54- )
55-
56- @BeforeTest
57- fun setupDatabase () {
58- logWriter.reset()
59-
60- database = openDB()
61-
62- runBlocking {
63- database.disconnectAndClear(true )
64- }
65- }
66-
67- @AfterTest
68- fun tearDown () {
69- runBlocking {
70- if (! database.closed) {
71- database.disconnectAndClear(true )
72- database.close()
73- }
74- }
75- com.powersync.testutils.cleanup(" testdb" )
76- }
77-
7827 @Test
7928 fun testLinksPowerSync () =
80- runTest {
29+ databaseTest {
8130 database.get(" SELECT powersync_rs_version();" ) { it.getString(0 )!! }
8231 }
8332
8433 @Test
8534 fun testWAL () =
86- runTest {
35+ databaseTest {
8736 val mode =
8837 database.get(
8938 " PRAGMA journal_mode" ,
9039 mapper = { it.getString(0 )!! },
9140 )
92- assertEquals( mode, " wal" )
41+ mode shouldBe " wal"
9342 }
9443
9544 @Test
9645 fun testFTS () =
97- runTest {
46+ databaseTest {
9847 val mode =
9948 database.get(
10049 " SELECT sqlite_compileoption_used('ENABLE_FTS5');" ,
10150 mapper = { it.getLong(0 )!! },
10251 )
103- assertEquals( mode, 1 )
52+ mode shouldBe 1
10453 }
10554
10655 @Test
10756 fun testConcurrentReads () =
108- runTest {
57+ databaseTest {
10958 database.execute(
11059 " INSERT INTO users (id, name, email) VALUES (uuid(), ?, ?)" ,
11160 listOf (
@@ -118,7 +67,7 @@ class DatabaseTest {
11867 val transactionItemCreated = CompletableDeferred <Unit >()
11968 // Start a long running writeTransaction
12069 val transactionJob =
121- async {
70+ scope. async {
12271 database.writeTransaction { tx ->
12372 // Create another user
12473 // External readers should not see this user while the transaction is open
@@ -156,7 +105,7 @@ class DatabaseTest {
156105
157106 @Test
158107 fun testTransactionReads () =
159- runTest {
108+ databaseTest {
160109 database.execute(
161110 " INSERT INTO users (id, name, email) VALUES (uuid(), ?, ?)" ,
162111 listOf (
@@ -187,18 +136,18 @@ class DatabaseTest {
187136
188137 @Test
189138 fun testTableUpdates () =
190- runTest {
139+ databaseTest {
191140 turbineScope {
192141 val query = database.watch(" SELECT * FROM users" ) { UserRow .from(it) }.testIn(this )
193142
194143 // Wait for initial query
195- assertEquals( 0 , query.awaitItem().size)
144+ query.awaitItem() shouldHaveSize 0
196145
197146 database.execute(
198147 " INSERT INTO users (id, name, email) VALUES (uuid(), ?, ?)" ,
199148 listOf (" Test" , " test@example.org" ),
200149 )
201- assertEquals( 1 , query.awaitItem().size)
150+ query.awaitItem() shouldHaveSize 1
202151
203152 database.writeTransaction {
204153 it.execute(
@@ -211,7 +160,7 @@ class DatabaseTest {
211160 )
212161 }
213162
214- assertEquals( 3 , query.awaitItem().size)
163+ query.awaitItem() shouldHaveSize 3
215164
216165 try {
217166 database.writeTransaction {
@@ -226,7 +175,7 @@ class DatabaseTest {
226175 " INSERT INTO users (id, name, email) VALUES (uuid(), ?, ?)" ,
227176 listOf (" Test4" , " test4@example.org" ),
228177 )
229- assertEquals( 4 , query.awaitItem().size)
178+ query.awaitItem() shouldHaveSize 4
230179
231180 query.expectNoEvents()
232181 query.cancel()
@@ -235,12 +184,12 @@ class DatabaseTest {
235184
236185 @Test
237186 fun testClosingReadPool () =
238- runTest {
187+ databaseTest {
239188 val pausedLock = CompletableDeferred <Unit >()
240189 val inLock = CompletableDeferred <Unit >()
241190 // Request a lock
242191 val lockJob =
243- async {
192+ scope. async {
244193 database.readLock {
245194 inLock.complete(Unit )
246195 runBlocking {
@@ -255,60 +204,48 @@ class DatabaseTest {
255204 // Close the database. This should close the read pool
256205 // The pool should wait for jobs to complete before closing
257206 val closeJob =
258- async {
207+ scope. async {
259208 database.close()
260209 }
261210
262211 // Wait a little for testing
263- // Spawns in a different context for the delay to actually take affect
264- async { withContext(Dispatchers .Default ) { delay(500 ) } }.await()
212+ // Spawns in a different context for the delay to actually take effect
213+ scope. async { withContext(Dispatchers .Default ) { delay(500 ) } }.await()
265214
266215 // The database should not close yet
267216 assertEquals(actual = database.closed, expected = false )
268217
269218 // Any new readLocks should throw
270- val exception = assertFailsWith<PowerSyncException > { database.readLock {} }
271- assertEquals(
272- expected = " Cannot process connection pool request" ,
273- actual = exception.message,
274- )
219+ val exception = shouldThrow<PowerSyncException > { database.readLock {} }
220+ exception.message shouldBe " Cannot process connection pool request"
221+
275222 // Release the lock
276223 pausedLock.complete(Unit )
277224 lockJob.await()
278225 closeJob.await()
279226
280- assertEquals(actual = database.closed, expected = true )
227+ database.closed shouldBe true
281228 }
282229
283230 @Test
284231 fun openDBWithDirectory () =
285- runTest {
232+ databaseTest {
286233 val tempDir =
287234 getTempDir()
288235 ? : // SQLiteR, which is used on iOS, does not support opening dbs from directories
289- return @runTest
290-
291- val dbFilename = " testdb"
236+ return @databaseTest
292237
293- val db =
294- PowerSyncDatabase (
295- factory = com.powersync.testutils.factory,
296- schema = Schema (UserRow .table),
297- dbFilename = dbFilename,
298- dbDirectory = getTempDir(),
299- logger = logger,
300- )
301-
302- val path = db.get(" SELECT file FROM pragma_database_list;" ) { it.getString(0 )!! }
303- assertTrue { path.contains(tempDir) }
304- db.close()
238+ // On platforms that support it, openDatabase() from our test utils should use a temporary
239+ // location.
240+ val path = database.get(" SELECT file FROM pragma_database_list;" ) { it.getString(0 )!! }
241+ path shouldContain tempDir
305242 }
306243
307244 @Test
308245 fun warnsMultipleInstances () =
309- runTest {
246+ databaseTest {
310247 // Opens a second DB with the same database filename
311- val db2 = openDB ()
248+ val db2 = openDatabase ()
312249 waitFor {
313250 assertNotNull(
314251 logWriter.logs.find {
@@ -321,9 +258,9 @@ class DatabaseTest {
321258
322259 @Test
323260 fun readConnectionsReadOnly () =
324- runTest {
261+ databaseTest {
325262 val exception =
326- assertFailsWith <PowerSyncException > {
263+ shouldThrow <PowerSyncException > {
327264 database.getOptional(
328265 """
329266 INSERT INTO
@@ -335,23 +272,24 @@ class DatabaseTest {
335272 parameters = listOf (" steven" , " steven@journeyapps.com" ),
336273 ) {}
337274 }
275+
338276 // The exception messages differ slightly between drivers
339- assertTrue { exception.message!! .contains( " write a readonly database" ) }
277+ exception.message shouldContain " write a readonly database"
340278 }
341279
342280 @Test
343281 fun basicReadTransaction () =
344- runTest {
282+ databaseTest {
345283 val count =
346284 database.readTransaction { it ->
347285 it.get(" SELECT COUNT(*) from users" ) { it.getLong(0 )!! }
348286 }
349- assertEquals(expected = 0 , actual = count)
287+ count shouldBe 0
350288 }
351289
352290 @Test
353291 fun localOnlyCRUD () =
354- runTest {
292+ databaseTest {
355293 database.updateSchema(
356294 schema =
357295 Schema (
@@ -375,16 +313,16 @@ class DatabaseTest {
375313 )
376314
377315 val count = database.get(" SELECT COUNT(*) FROM local_users" ) { it.getLong(0 )!! }
378- assertEquals(actual = count, expected = 1 )
316+ count shouldBe 1
379317
380318 // No CRUD entries should be present for local only tables
381319 val crudItems = database.getAll(" SELECT id from ps_crud" ) { it.getLong(0 )!! }
382- assertEquals(actual = crudItems.size, expected = 0 )
320+ crudItems shouldHaveSize 0
383321 }
384322
385323 @Test
386324 fun insertOnlyCRUD () =
387- runTest {
325+ databaseTest {
388326 database.updateSchema(schema = Schema (UserRow .table.copy(insertOnly = true )))
389327
390328 database.execute(
@@ -397,15 +335,15 @@ class DatabaseTest {
397335 )
398336
399337 val crudItems = database.getAll(" SELECT id from ps_crud" ) { it.getLong(0 )!! }
400- assertEquals(actual = crudItems.size, expected = 1 )
338+ crudItems shouldHaveSize 1
401339
402340 val count = database.get(" SELECT COUNT(*) from users" ) { it.getLong(0 )!! }
403- assertEquals(actual = count, expected = 0 )
341+ count shouldBe 0
404342 }
405343
406344 @Test
407345 fun viewOverride () =
408- runTest {
346+ databaseTest {
409347 database.updateSchema(schema = Schema (UserRow .table.copy(viewNameOverride = " people" )))
410348
411349 database.execute(
@@ -418,9 +356,9 @@ class DatabaseTest {
418356 )
419357
420358 val crudItems = database.getAll(" SELECT id from ps_crud" ) { it.getLong(0 )!! }
421- assertEquals(actual = crudItems.size, expected = 1 )
359+ crudItems shouldHaveSize 1
422360
423361 val count = database.get(" SELECT COUNT(*) from people" ) { it.getLong(0 )!! }
424- assertEquals(actual = count, expected = 1 )
362+ count shouldBe 1
425363 }
426364}
0 commit comments