fix(sync): re-enqueue periodic work with UPDATE so upgrades re-apply the schedule

Periodic sync, backfill, and prune were enqueued with
ExistingPeriodicWorkPolicy.KEEP, so a newer app version's interval or
constraint change never reached already-installed devices: KEEP pins the job
to the spec from whichever version first scheduled it.

Switch the three periodic schedulers to UPDATE (WorkManager 2.8+; 2.11.2 in
use), which re-applies the current spec on each app-start re-enqueue while
preserving the running period's progress. An unchanged spec is effectively a
no-op, so this never resets the schedule on launch the way REPLACE (cancel +
re-enqueue) would. The one-shot kicks (syncNow/backfillNow/pruneNow) keep
their existing policies -- they are a separate concern from #96.

SyncScheduler now injects Provider<WorkManager> (via a new WorkManagerModule)
instead of calling the WorkManager.getInstance() static directly, so the
policy is unit-testable with MockK; the Provider keeps resolution lazy to
preserve the previous initialization timing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-02 02:27:33 -05:00
co-authored by Claude Fable 5
parent 45edb792a9
commit e8c4fc1ea1
6 changed files with 160 additions and 12 deletions
@@ -10,6 +10,7 @@ import androidx.lifecycle.SavedStateHandle
import androidx.room.Room
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.work.WorkManager
import kotlinx.coroutines.runBlocking
import org.junit.Rule
import org.junit.Test
@@ -27,6 +28,7 @@ import org.libremail.domain.model.ServerConfig
import org.libremail.ui.FakeAccountRepository
import org.libremail.ui.navigation.Routes
import org.libremail.ui.theme.LibreMailTheme
import javax.inject.Provider
/**
* End-to-end test for the per-account settings screen: editing the signature and toggling the
@@ -68,7 +70,7 @@ class AccountSettingsScreenTest {
accountRepository = FakeAccountRepository(accounts = listOf(account)),
accountSettingsRepository = repository,
signatureRepository = SignatureRepository(db.signatureDao()),
syncScheduler = SyncScheduler(context),
syncScheduler = SyncScheduler(Provider { WorkManager.getInstance(context) }),
)
composeTestRule.setContent {
LibreMailTheme(darkTheme = false, dynamicColor = false) {
@@ -8,6 +8,7 @@ import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performScrollTo
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import androidx.work.WorkManager
import kotlinx.coroutines.runBlocking
import org.junit.Rule
import org.junit.Test
@@ -25,6 +26,7 @@ import org.libremail.data.sync.SyncScheduler
import org.libremail.push.BatteryOptimizationManager
import org.libremail.ui.FakeAccountRepository
import org.libremail.ui.theme.LibreMailTheme
import javax.inject.Provider
/**
* End-to-end test for the top-level message-downloading setting: tapping a policy must round-trip
@@ -54,7 +56,7 @@ class SettingsScreenTest {
appLockManager,
keyStore,
BatteryOptimizationManager(context),
SyncScheduler(context),
SyncScheduler(Provider { WorkManager.getInstance(context) }),
)
composeTestRule.setContent {