Skip to content

Commit 8e0a26d

Browse files
Merge pull request #104 from kirill-markin/codex/split-appdatabase-contract-tests
[codex] Split AppDatabase instrumentation contracts
2 parents 89012cc + c1b1e8f commit 8e0a26d

10 files changed

Lines changed: 1870 additions & 1590 deletions

apps/android/data/local/src/androidTest/java/com/flashcardsopensourceapp/data/local/AppDatabaseTest.kt

Lines changed: 0 additions & 1590 deletions
This file was deleted.
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
package com.flashcardsopensourceapp.data.local
2+
3+
import androidx.test.ext.junit.runners.AndroidJUnit4
4+
import com.flashcardsopensourceapp.data.local.database.AppDatabase
5+
import com.flashcardsopensourceapp.data.local.database.CardTagEntity
6+
import com.flashcardsopensourceapp.data.local.database.TagEntity
7+
import com.flashcardsopensourceapp.data.local.model.EffortLevel
8+
import kotlinx.coroutines.flow.first
9+
import kotlinx.coroutines.runBlocking
10+
import org.junit.After
11+
import org.junit.Assert.assertEquals
12+
import org.junit.Assert.assertNull
13+
import org.junit.Before
14+
import org.junit.Test
15+
import org.junit.runner.RunWith
16+
17+
@RunWith(AndroidJUnit4::class)
18+
class CardDaoReviewQueueContractTest {
19+
private lateinit var runtime: LocalDatabaseTestRuntime
20+
private val database: AppDatabase
21+
get() = runtime.database
22+
23+
@Before
24+
fun setUp() = runBlocking {
25+
runtime = createLocalDatabaseTestRuntime()
26+
}
27+
28+
@After
29+
fun tearDown() {
30+
if (::runtime.isInitialized) {
31+
closeLocalDatabaseTestRuntime(runtime = runtime)
32+
}
33+
}
34+
35+
@Test
36+
fun topReviewCardQueriesUseRecentDueBoundariesAndFilters(): Unit = runBlocking {
37+
val nowMillis = 12 * 60 * 60 * 1_000L
38+
val oneHourMillis = 60 * 60 * 1_000L
39+
val workspaceId = bootstrapTestWorkspace(runtime = runtime, currentTimeMillis = nowMillis)
40+
val priorityTag = TagEntity(
41+
tagId = "tag-priority",
42+
workspaceId = workspaceId,
43+
name = "Priority"
44+
)
45+
val futureTag = TagEntity(
46+
tagId = "tag-future-only",
47+
workspaceId = workspaceId,
48+
name = "Future Only"
49+
)
50+
51+
database.cardDao().insertCards(
52+
listOf(
53+
makeDueReviewOrderingCardEntity(
54+
cardId = "recent-cutoff-a",
55+
workspaceId = workspaceId,
56+
effortLevel = EffortLevel.FAST,
57+
dueAtMillis = nowMillis - oneHourMillis,
58+
createdAtMillis = 300L,
59+
updatedAtMillis = 300L
60+
),
61+
makeDueReviewOrderingCardEntity(
62+
cardId = "recent-cutoff-b",
63+
workspaceId = workspaceId,
64+
effortLevel = EffortLevel.FAST,
65+
dueAtMillis = nowMillis - oneHourMillis,
66+
createdAtMillis = 300L,
67+
updatedAtMillis = 300L
68+
),
69+
makeDueReviewOrderingCardEntity(
70+
cardId = "recent-cutoff-older-created",
71+
workspaceId = workspaceId,
72+
effortLevel = EffortLevel.FAST,
73+
dueAtMillis = nowMillis - oneHourMillis,
74+
createdAtMillis = 200L,
75+
updatedAtMillis = 200L
76+
),
77+
makeDueReviewOrderingCardEntity(
78+
cardId = "old-boundary-card",
79+
workspaceId = workspaceId,
80+
effortLevel = EffortLevel.LONG,
81+
dueAtMillis = nowMillis - oneHourMillis - 1L,
82+
createdAtMillis = 400L,
83+
updatedAtMillis = 400L
84+
),
85+
makeDueReviewOrderingCardEntity(
86+
cardId = "due-now-card",
87+
workspaceId = workspaceId,
88+
effortLevel = EffortLevel.MEDIUM,
89+
dueAtMillis = nowMillis,
90+
createdAtMillis = 500L,
91+
updatedAtMillis = 500L
92+
),
93+
makeNewReviewOrderingCardEntity(
94+
cardId = "new-medium-card",
95+
workspaceId = workspaceId,
96+
effortLevel = EffortLevel.MEDIUM,
97+
createdAtMillis = 600L,
98+
updatedAtMillis = 600L
99+
),
100+
makeDueReviewOrderingCardEntity(
101+
cardId = "future-card",
102+
workspaceId = workspaceId,
103+
effortLevel = EffortLevel.FAST,
104+
dueAtMillis = nowMillis + 1L,
105+
createdAtMillis = 700L,
106+
updatedAtMillis = 700L
107+
)
108+
)
109+
)
110+
database.tagDao().insertTags(tags = listOf(priorityTag, futureTag))
111+
database.tagDao().insertCardTags(
112+
cardTags = listOf(
113+
CardTagEntity(cardId = "recent-cutoff-a", tagId = priorityTag.tagId),
114+
CardTagEntity(cardId = "recent-cutoff-b", tagId = priorityTag.tagId),
115+
CardTagEntity(cardId = "recent-cutoff-older-created", tagId = priorityTag.tagId),
116+
CardTagEntity(cardId = "old-boundary-card", tagId = priorityTag.tagId),
117+
CardTagEntity(cardId = "due-now-card", tagId = priorityTag.tagId),
118+
CardTagEntity(cardId = "new-medium-card", tagId = priorityTag.tagId),
119+
CardTagEntity(cardId = "future-card", tagId = futureTag.tagId)
120+
)
121+
)
122+
123+
val allCardsTop = database.cardDao().loadTopReviewCard(
124+
workspaceId = workspaceId,
125+
nowMillis = nowMillis
126+
)
127+
val effortTop = database.cardDao().loadTopReviewCardByEffortLevels(
128+
workspaceId = workspaceId,
129+
nowMillis = nowMillis,
130+
effortLevels = listOf(EffortLevel.FAST)
131+
)
132+
val tagTop = database.cardDao().loadTopReviewCardByAnyTags(
133+
workspaceId = workspaceId,
134+
nowMillis = nowMillis,
135+
tagNames = listOf("Priority")
136+
)
137+
val effortAndTagTop = database.cardDao().loadTopReviewCardByEffortLevelsAndAnyTags(
138+
workspaceId = workspaceId,
139+
nowMillis = nowMillis,
140+
effortLevels = listOf(EffortLevel.MEDIUM),
141+
tagNames = listOf("Priority")
142+
)
143+
val futureOnlyTagTop = database.cardDao().loadTopReviewCardByAnyTags(
144+
workspaceId = workspaceId,
145+
nowMillis = nowMillis,
146+
tagNames = listOf("Future Only")
147+
)
148+
val boundedQueue = database.cardDao().observeActiveReviewQueue(
149+
workspaceId = workspaceId,
150+
nowMillis = nowMillis,
151+
limit = 4
152+
).first().map { card ->
153+
card.card.cardId
154+
}
155+
val effortAndTagQueue = database.cardDao().observeActiveReviewQueueByEffortLevelsAndAnyTags(
156+
workspaceId = workspaceId,
157+
nowMillis = nowMillis,
158+
effortLevels = listOf(EffortLevel.MEDIUM),
159+
tagNames = listOf("Priority"),
160+
limit = 10
161+
).first().map { card ->
162+
card.card.cardId
163+
}
164+
val priorityDueCount = database.cardDao().observeReviewDueCountByAnyTags(
165+
workspaceId = workspaceId,
166+
nowMillis = nowMillis,
167+
tagNames = listOf("Priority")
168+
).first()
169+
val priorityTotalCount = database.cardDao().observeReviewTotalCountByAnyTags(
170+
workspaceId = workspaceId,
171+
tagNames = listOf("Priority")
172+
).first()
173+
val futureOnlyDueCount = database.cardDao().observeReviewDueCountByAnyTags(
174+
workspaceId = workspaceId,
175+
nowMillis = nowMillis,
176+
tagNames = listOf("Future Only")
177+
).first()
178+
val futureOnlyTotalCount = database.cardDao().observeReviewTotalCountByAnyTags(
179+
workspaceId = workspaceId,
180+
tagNames = listOf("Future Only")
181+
).first()
182+
183+
assertEquals("recent-cutoff-a", allCardsTop?.cardId)
184+
assertEquals("recent-cutoff-a", effortTop?.cardId)
185+
assertEquals("recent-cutoff-a", tagTop?.cardId)
186+
assertEquals("due-now-card", effortAndTagTop?.cardId)
187+
assertNull(futureOnlyTagTop)
188+
assertEquals(
189+
listOf("recent-cutoff-a", "recent-cutoff-b", "recent-cutoff-older-created", "due-now-card"),
190+
boundedQueue
191+
)
192+
assertEquals(listOf("due-now-card", "new-medium-card"), effortAndTagQueue)
193+
assertEquals(6, priorityDueCount)
194+
assertEquals(6, priorityTotalCount)
195+
assertEquals(0, futureOnlyDueCount)
196+
assertEquals(1, futureOnlyTotalCount)
197+
}
198+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package com.flashcardsopensourceapp.data.local
2+
3+
import android.content.Context
4+
import androidx.test.core.app.ApplicationProvider
5+
import androidx.test.ext.junit.runners.AndroidJUnit4
6+
import com.flashcardsopensourceapp.data.local.cloud.CloudPreferencesStore
7+
import com.flashcardsopensourceapp.data.local.database.AppDatabase
8+
import kotlinx.coroutines.runBlocking
9+
import org.junit.After
10+
import org.junit.Assert.assertEquals
11+
import org.junit.Before
12+
import org.junit.Test
13+
import org.junit.runner.RunWith
14+
15+
@RunWith(AndroidJUnit4::class)
16+
class CloudPreferencesStoreMigrationContractTest {
17+
private lateinit var context: Context
18+
private lateinit var database: AppDatabase
19+
20+
@Before
21+
fun setUp() {
22+
context = ApplicationProvider.getApplicationContext()
23+
clearLocalDatabaseSharedPreferences(context = context)
24+
database = createInMemoryAppDatabase(context = context)
25+
}
26+
27+
@After
28+
fun tearDown() {
29+
if (::database.isInitialized) {
30+
database.close()
31+
}
32+
if (::context.isInitialized) {
33+
clearLocalDatabaseSharedPreferences(context = context)
34+
}
35+
}
36+
37+
@Test
38+
fun cloudPreferencesStoreMigratesLegacyIdentityFromPreferencesIntoDatabaseSettings() = runBlocking {
39+
database.close()
40+
clearLocalDatabaseSharedPreferences(context = context)
41+
42+
val legacyPreferences = context.getSharedPreferences("flashcards-cloud-metadata", Context.MODE_PRIVATE)
43+
legacyPreferences.edit()
44+
.putString("installation-id", "legacy-installation-id")
45+
.putString("cloud-state", "LINKED")
46+
.putString("linked-user-id", "legacy-user")
47+
.putString("linked-workspace-id", "legacy-workspace")
48+
.putString("linked-email", "legacy@example.com")
49+
.putString("active-workspace-id", "legacy-workspace")
50+
.putLong("updated-at-millis", 456L)
51+
.commit()
52+
53+
database = createInMemoryAppDatabase(context = context)
54+
val migratedStore = CloudPreferencesStore(context = context, database = database)
55+
migratedStore.hydrateCloudSettingsFromDatabase()
56+
57+
val migratedSettings = migratedStore.currentCloudSettings()
58+
val storedSettings = requireNotNull(database.appLocalSettingsDao().loadSettings()) {
59+
"Expected app_local_settings after legacy migration."
60+
}
61+
62+
assertEquals("legacy-installation-id", migratedSettings.installationId)
63+
assertEquals("legacy-workspace", migratedSettings.activeWorkspaceId)
64+
assertEquals("legacy-installation-id", storedSettings.installationId)
65+
assertEquals("LINKED", storedSettings.cloudState)
66+
assertEquals("legacy-user", storedSettings.linkedUserId)
67+
assertEquals("legacy-workspace", storedSettings.linkedWorkspaceId)
68+
assertEquals("legacy@example.com", storedSettings.linkedEmail)
69+
assertEquals("legacy-workspace", storedSettings.activeWorkspaceId)
70+
assertEquals(456L, storedSettings.updatedAtMillis)
71+
}
72+
}

0 commit comments

Comments
 (0)