From a73b6410a203d897aedf6305efe0e44166f06b5a Mon Sep 17 00:00:00 2001 From: Jason Ross Date: Wed, 1 Jul 2026 14:33:13 -0500 Subject: [PATCH] test(sync): tally rows offered to insertNew to prove no double-fetch The backfiller's "no message fetched twice" claim (full-history + resume tests) previously rested on the insertNew fake de-duping by id, so a re-fetched page was silently absorbed and `cached.size == TOTAL` could not fail on it. Count the rows offered to insertNew BEFORE de-dupe and assert it equals TOTAL - WINDOW, so any re-request of an already-cached page now fails the test. This isolates the real boundary-descent guarantee and, unlike a fetchOlderThan call-count, is independent of BACKFILL_BATCH_SIZE. Co-Authored-By: Claude Opus 4.8 --- .../org/libremail/data/sync/MailBackfillerTest.kt | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/app/src/test/kotlin/org/libremail/data/sync/MailBackfillerTest.kt b/app/src/test/kotlin/org/libremail/data/sync/MailBackfillerTest.kt index 3e032a4..a50feaf 100644 --- a/app/src/test/kotlin/org/libremail/data/sync/MailBackfillerTest.kt +++ b/app/src/test/kotlin/org/libremail/data/sync/MailBackfillerTest.kt @@ -65,6 +65,10 @@ class MailBackfillerTest { private val cached = mutableListOf() private val progress = mutableMapOf, BackfillProgressEntity>() + // Rows offered to insertNew, counted BEFORE de-dupe: a re-fetched page inflates this even + // though `cached` would silently absorb it. Isolates the "no message fetched twice" guarantee. + private var totalOffered = 0 + @Before fun setUp() { greenMail = GreenMail(ServerSetupTest.SMTP_IMAP) @@ -96,6 +100,7 @@ class MailBackfillerTest { while (backfiller.runBackfill() && guard++ < 10) { /* keep going until no more work */ } assertEquals(TOTAL, distinctCachedUids().size, "backfill must cache every message") + assertEquals(TOTAL - WINDOW, totalOffered, "each backfilled message fetched exactly once") assertTrue(cached.size > WINDOW, "that is strictly more than the foreground window") assertEquals(true, progress["acct" to "INBOX"]?.complete) assertNoDeletes() @@ -120,7 +125,8 @@ class MailBackfillerTest { while (resumed.runBackfill() && guard++ < 10) { /* finish */ } assertEquals(TOTAL, distinctCachedUids().size, "resume completes the full history") - assertEquals(TOTAL, cached.size, "no message is fetched twice") + assertEquals(TOTAL - WINDOW, totalOffered, "no re-fetch across the interruption") + assertEquals(TOTAL, cached.size, "and nothing is double-inserted") assertNoDeletes() } @@ -151,7 +157,9 @@ class MailBackfillerTest { val messageDao = mockk(relaxed = true) coEvery { messageDao.insertNew(any()) } answers { - firstArg>().forEach { e -> if (cached.none { it.id == e.id }) cached += e } + val batch = firstArg>() + totalOffered += batch.size // count BEFORE de-dupe (see field) + batch.forEach { e -> if (cached.none { it.id == e.id }) cached += e } } coEvery { messageDao.syncedFolders("acct") } answers { cached.filter { it.inInbox }.map { it.folder }.distinct()