Skip to content

Improve local only sync #87

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 21 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
1ae94dd
More readable TaskRepository.toggleTaskCompletionState
opatry Oct 30, 2024
a052598
Fix local only tasks not being synced when task list is local only
opatry Oct 30, 2024
cf5c4e9
Sync local only tasks & subtask in proper order to preserve hierarchy…
opatry Oct 30, 2024
6e06ab1
Use upsertAll after sync instead of several individual inserts
opatry Oct 30, 2024
373445e
Prepare inversion of task entity mapper arguments
opatry May 31, 2025
d5bc104
Invert task entity mapper arguments to align with other similar APIs
opatry May 31, 2025
0c194bd
Avoid querying remote tasks for local only task list
opatry May 31, 2025
9217564
Refactor TaskRepository.sync() as split sub sync routines
opatry May 31, 2025
0fd02e7
Further refine sync
opatry May 31, 2025
bc9d2f4
Isolate cleanup of stale local data in its own function (to be called…
opatry Jun 1, 2025
ea4848e
Store (in memory for now) last sync and leverage it when listing task…
opatry Jun 1, 2025
7a144dd
Trigger cleanup of stale tasks after first sync (simple approach to b…
opatry Jun 1, 2025
5b48db5
Maximize the use of upsertAll whenever possible during sync
opatry Jun 1, 2025
c1bbdd6
Improve sync test names
opatry Jun 1, 2025
e96ec33
Add mockito-kotlin to mockito bundle
opatry Jun 1, 2025
27273cb
Add test dependencies to tasks-core for sync test using mocks
opatry Jun 1, 2025
1e19e24
Revise TaskRepository.sync to minimize mock requirements
opatry Jun 1, 2025
0db463d
Honor local only list update date when pushing remotely
opatry Jun 1, 2025
ab01b88
Reuse same task list to clean stale tasks from initial sync
opatry Jun 1, 2025
7aab903
Allow syncing local only subtask even when parent task is already synced
opatry Jun 2, 2025
6b0836b
Add TaskRepositorySyncTest in `:tasks-core` with Mockito
opatry Jun 1, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -22,6 +22,7 @@ koin = "4.0.4"
coil = "3.2.0"
about-libraries = "12.2.0"
mockito = "5.18.0"
mockito-kotlin = "5.4.0"
kover = "0.9.1"
androidx-test-runner = "1.6.2"

@@ -95,6 +96,7 @@ androidx-ui-test-junit4 = { module = "androidx.compose.ui:ui-test-junit4", versi
androidx-test-runner = { module = "androidx.test:runner", version.ref = "androidx-test-runner" }

mockito-core = { module = "org.mockito:mockito-core", version.ref = "mockito" }
mockito-kotlin = { module = "org.mockito.kotlin:mockito-kotlin", version.ref = "mockito-kotlin" }

androidx-ui-tooling-preview-android = { module = "androidx.compose.ui:ui-tooling-preview-android", version.ref = "compose" }
androidx-ui-tooling = { module = "androidx.compose.ui:ui-tooling", version.ref = "compose" }
@@ -115,6 +117,7 @@ coil = ["coil-compose", "coil-network"]

mockito = [
"mockito-core",
"mockito-kotlin",
]

[plugins]
Original file line number Diff line number Diff line change
@@ -33,21 +33,19 @@ import kotlin.test.assertEquals
import kotlin.test.assertNotNull


class TaskRepositorySyncTest {
class TaskRepositorySyncIntegrationTest {
@Test
fun `sync remote task lists`() {
fun `when remote task lists with tasks then sync should store data locally`() {
val taskListsApi = InMemoryTaskListsApi("My tasks", "Other tasks")
val tasksApi = InMemoryTasksApi("1" to listOf("First task TODO"), "2" to listOf("Another task"))

runTaskRepositoryTest(taskListsApi, tasksApi) { repository ->
val initialTaskLists = repository.getTaskLists().firstOrNull()
assertEquals(0, initialTaskLists?.size, "There shouldn't be any task list at start")

repository.sync()
repository.sync(cleanStaleTasks = false)

assertEquals(1, taskListsApi.requestCount)
assertContentEquals(listOf("list"), taskListsApi.requests)
assertEquals(2, tasksApi.requestCount)
assertContentEquals(listOf("list", "list"), tasksApi.requests)

val taskLists = repository.getTaskLists().firstOrNull()
@@ -66,7 +64,7 @@ class TaskRepositorySyncTest {
}

@Test
fun `task list CRUD without network should create a local only task list`() {
fun `when network is OFF then created task list should be local only`() {
val taskListsApi = InMemoryTaskListsApi()

runTaskRepositoryTest(taskListsApi) { repository ->
@@ -80,10 +78,11 @@ class TaskRepositorySyncTest {
}

@Test
fun `local only task lists are synced at next sync`() {
val taskListsApi = InMemoryTaskListsApi()
fun `when there are local only task lists then sync should upload them`() {
val taskListsApi = InMemoryTaskListsApi("Task list")
val tasksApi = InMemoryTasksApi()

runTaskRepositoryTest(taskListsApi) { repository ->
runTaskRepositoryTest(taskListsApi, tasksApi) { repository ->
// for first request, no network
taskListsApi.isNetworkAvailable = false
repository.createTaskList("Task list")
@@ -93,9 +92,9 @@ class TaskRepositorySyncTest {

// network is considered back, sync should trigger fetch & push requests
taskListsApi.isNetworkAvailable = true
repository.sync()
assertEquals(2, taskListsApi.requestCount)
repository.sync(cleanStaleTasks = false)
assertContentEquals(listOf("list", "insert"), taskListsApi.requests)
assertContentEquals(listOf("list"), tasksApi.requests)
}
}
}
Original file line number Diff line number Diff line change
@@ -154,7 +154,7 @@ class InMemoryTasksApi(
return handleRequest("list") {
// TODO maxResults & token handling
val tasks = synchronized(this) {
storage[taskListId] ?: error("Task list ($taskListId) not found")
storage[taskListId] ?: emptyList()
}
val filteredTasks = tasks.filter { task ->
// Check if completed tasks should be shown
4 changes: 4 additions & 0 deletions tasks-core/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -45,6 +45,10 @@ kotlin {

commonTest.dependencies {
implementation(kotlin("test"))

implementation(libs.kotlinx.coroutines.test)

implementation(libs.bundles.mockito)
}
}
}

Large diffs are not rendered by default.

Large diffs are not rendered by default.