|
1 | 1 | package com.powersync |
2 | 2 |
|
| 3 | +import com.powersync.testutils.IntegrationTestHelpers |
3 | 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 |
4 | 5 | import androidx.test.platform.app.InstrumentationRegistry |
5 | | -import app.cash.turbine.turbineScope |
6 | | -import com.powersync.db.schema.Schema |
7 | | -import com.powersync.testutils.UserRow |
8 | | -import kotlinx.coroutines.* |
9 | | -import kotlinx.coroutines.runBlocking |
10 | | -import kotlinx.coroutines.test.runTest |
11 | 6 | import org.junit.After |
12 | | -import org.junit.Assert.* |
13 | 7 | import org.junit.Before |
14 | 8 | import org.junit.Test |
15 | 9 | import org.junit.runner.RunWith |
16 | 10 |
|
17 | 11 | @RunWith(AndroidJUnit4::class) |
18 | 12 | class AndroidDatabaseTest { |
19 | | - private lateinit var database: PowerSyncDatabase |
| 13 | + private val helpers = IntegrationTestHelpers( |
| 14 | + InstrumentationRegistry.getInstrumentation().targetContext |
| 15 | + ) |
20 | 16 |
|
21 | 17 | @Before |
22 | 18 | fun setupDatabase() { |
23 | | - database = |
24 | | - PowerSyncDatabase( |
25 | | - factory = DatabaseDriverFactory(InstrumentationRegistry.getInstrumentation().targetContext), |
26 | | - schema = Schema(UserRow.table), |
27 | | - dbFilename = "testdb", |
28 | | - ) |
29 | | - |
30 | | - runBlocking { |
31 | | - database.disconnectAndClear(true) |
32 | | - } |
| 19 | + helpers.setup() |
33 | 20 | } |
34 | 21 |
|
35 | 22 | @After |
36 | 23 | fun tearDown() { |
37 | | - runBlocking { |
38 | | - database.disconnectAndClear(true) |
39 | | - database.close() |
40 | | - } |
| 24 | + helpers.tearDown() |
41 | 25 | } |
42 | 26 |
|
43 | 27 | @Test |
44 | | - fun testLinksPowerSync() = |
45 | | - runTest { |
46 | | - database.get("SELECT powersync_rs_version() AS r;") { it.getString(0)!! } |
47 | | - } |
| 28 | + fun testLinksPowerSync() = helpers.testLinksPowerSync() |
48 | 29 |
|
49 | 30 | @Test |
50 | | - fun testTableUpdates() = |
51 | | - runTest { |
52 | | - turbineScope { |
53 | | - val query = database.watch("SELECT * FROM users") { UserRow.from(it) }.testIn(this) |
54 | | - |
55 | | - // Wait for initial query |
56 | | - assertEquals(0, query.awaitItem().size) |
57 | | - |
58 | | - database.execute( |
59 | | - "INSERT INTO users (id, name, email) VALUES (uuid(), ?, ?)", |
60 | | - listOf("Test", "test@example.org"), |
61 | | - ) |
62 | | - assertEquals(1, query.awaitItem().size) |
63 | | - |
64 | | - database.writeTransaction { |
65 | | - it.execute( |
66 | | - "INSERT INTO users (id, name, email) VALUES (uuid(), ?, ?)", |
67 | | - listOf("Test2", "test2@example.org"), |
68 | | - ) |
69 | | - it.execute( |
70 | | - "INSERT INTO users (id, name, email) VALUES (uuid(), ?, ?)", |
71 | | - listOf("Test3", "test3@example.org"), |
72 | | - ) |
73 | | - } |
74 | | - |
75 | | - assertEquals(3, query.awaitItem().size) |
76 | | - |
77 | | - try { |
78 | | - database.writeTransaction { |
79 | | - it.execute("DELETE FROM users;") |
80 | | - it.execute("syntax error, revert please") |
81 | | - } |
82 | | - } catch (e: Exception) { |
83 | | - // Ignore |
84 | | - } |
85 | | - |
86 | | - database.execute( |
87 | | - "INSERT INTO users (id, name, email) VALUES (uuid(), ?, ?)", |
88 | | - listOf("Test4", "test4@example.org"), |
89 | | - ) |
90 | | - assertEquals(4, query.awaitItem().size) |
91 | | - |
92 | | - query.expectNoEvents() |
93 | | - query.cancel() |
94 | | - } |
95 | | - } |
| 31 | + fun testTableUpdates() = helpers.testTableUpdates() |
96 | 32 |
|
97 | 33 | @Test |
98 | | - fun testConcurrentReads() = |
99 | | - runTest { |
100 | | - database.execute( |
101 | | - "INSERT INTO users (id, name, email) VALUES (uuid(), ?, ?)", |
102 | | - listOf( |
103 | | - "steven", |
104 | | - "s@journeyapps.com", |
105 | | - ), |
106 | | - ) |
107 | | - |
108 | | - val pausedTransaction = CompletableDeferred<Unit>() |
109 | | - val transactionItemCreated = CompletableDeferred<Unit>() |
110 | | - // Start a long running writeTransaction |
111 | | - val transactionJob = |
112 | | - async { |
113 | | - database.writeTransaction { tx -> |
114 | | - // Create another user |
115 | | - // External readers should not see this user while the transaction is open |
116 | | - tx.execute( |
117 | | - "INSERT INTO users (id, name, email) VALUES (uuid(), ?, ?)", |
118 | | - listOf( |
119 | | - "steven", |
120 | | - "s@journeyapps.com", |
121 | | - ), |
122 | | - ) |
123 | | - |
124 | | - transactionItemCreated.complete(Unit) |
125 | | - |
126 | | - // Block this transaction until we free it |
127 | | - runBlocking { |
128 | | - pausedTransaction.await() |
129 | | - } |
130 | | - } |
131 | | - } |
132 | | - |
133 | | - // Make sure to wait for the item to have been created in the transaction |
134 | | - transactionItemCreated.await() |
135 | | - // Try and read while the write transaction is busy |
136 | | - val result = database.getAll("SELECT * FROM users") { UserRow.from(it) } |
137 | | - // The transaction is not commited yet, we should only read 1 user |
138 | | - assertEquals(result.size, 1) |
139 | | - |
140 | | - // Let the transaction complete |
141 | | - pausedTransaction.complete(Unit) |
142 | | - transactionJob.await() |
143 | | - |
144 | | - val afterTx = database.getAll("SELECT * FROM users") { UserRow.from(it) } |
145 | | - assertEquals(afterTx.size, 2) |
146 | | - } |
| 34 | + fun testConcurrentReads() = helpers.testConcurrentReads() |
147 | 35 |
|
148 | 36 | @Test |
149 | | - fun transactionReads() = |
150 | | - runTest { |
151 | | - database.execute( |
152 | | - "INSERT INTO users (id, name, email) VALUES (uuid(), ?, ?)", |
153 | | - listOf( |
154 | | - "steven", |
155 | | - "s@journeyapps.com", |
156 | | - ), |
157 | | - ) |
158 | | - |
159 | | - database.writeTransaction { tx -> |
160 | | - val userCount = |
161 | | - tx.getAll("SELECT COUNT(*) as count FROM users") { cursor -> cursor.getLong(0)!! } |
162 | | - assertEquals(userCount[0], 1) |
163 | | - |
164 | | - tx.execute( |
165 | | - "INSERT INTO users (id, name, email) VALUES (uuid(), ?, ?)", |
166 | | - listOf( |
167 | | - "steven", |
168 | | - "s@journeyapps.com", |
169 | | - ), |
170 | | - ) |
171 | | - |
172 | | - // Getters inside the transaction should be able to see the latest update |
173 | | - val userCount2 = |
174 | | - tx.getAll("SELECT COUNT(*) as count FROM users") { cursor -> cursor.getLong(0)!! } |
175 | | - assertEquals(userCount2[0], 2) |
176 | | - } |
177 | | - } |
| 37 | + fun transactionReads() = helpers.transactionReads() |
178 | 38 |
|
179 | 39 | @Test |
180 | | - fun openDBWithDirectory() = |
181 | | - runTest { |
182 | | - val tempDir = |
183 | | - InstrumentationRegistry |
184 | | - .getInstrumentation() |
185 | | - .targetContext.cacheDir.canonicalPath |
186 | | - val dbFilename = "testdb" |
187 | | - |
188 | | - val db = |
189 | | - PowerSyncDatabase( |
190 | | - factory = DatabaseDriverFactory(InstrumentationRegistry.getInstrumentation().targetContext), |
191 | | - schema = Schema(UserRow.table), |
192 | | - dbDirectory = tempDir, |
193 | | - dbFilename = dbFilename, |
194 | | - ) |
195 | | - |
196 | | - val path = db.get("SELECT file FROM pragma_database_list;") { it.getString(0)!! } |
197 | | - |
198 | | - assertEquals(path.contains(tempDir), true) |
199 | | - |
200 | | - db.close() |
201 | | - } |
| 40 | + fun openDBWithDirectory() = helpers.openDBWithDirectory() |
202 | 41 |
|
203 | 42 | @Test |
204 | | - fun readConnectionsReadOnly() = |
205 | | - runTest { |
206 | | - val exception = |
207 | | - assertThrows(PowerSyncException::class.java) { |
208 | | - // This version of assertThrows does not support suspending functions |
209 | | - runBlocking { |
210 | | - database.getOptional( |
211 | | - """ |
212 | | - INSERT INTO |
213 | | - users (id, name, email) |
214 | | - VALUES |
215 | | - (uuid(), ?, ?) |
216 | | - RETURNING * |
217 | | - """.trimIndent(), |
218 | | - parameters = listOf("steven", "steven@journeyapps.com"), |
219 | | - ) {} |
220 | | - } |
221 | | - } |
222 | | - // The exception messages differ slightly between drivers |
223 | | - assertEquals(exception.message!!.contains("write a readonly database"), true) |
224 | | - } |
| 43 | + fun readConnectionsReadOnly() = helpers.readConnectionsReadOnly() |
225 | 44 |
|
226 | 45 | @Test |
227 | | - fun canUseTempStore() = runTest { |
228 | | - database.execute("PRAGMA temp_store = 1;") // Store temporary data as files |
229 | | - database.execute("CREATE TEMP TABLE foo (bar TEXT);") |
230 | | - val data = "new row".repeat(100); |
231 | | - for (i in 0..10000) { |
232 | | - database.execute("INSERT INTO foo VALUES (?)", parameters = listOf(data)) |
233 | | - } |
234 | | - } |
| 46 | + fun canUseTempStore() = helpers.canUseTempStore() |
235 | 47 | } |
0 commit comments