From 61ceaa728f7166124b8ab768ccc3d281ebb2b810 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 21 Sep 2026 16:28:59 +0000 Subject: [PATCH] test(amethyst): hold the DVM announcement fixtures against the weak cache MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit theUngatedAnnouncementScanKeepsDvmsTheGateWouldHide failed on CI at the "dvm-x must still be sourced" assertion. The consume and the scan are adjacent statements in one method, so nothing between them can have polluted the cache; what the failure says is that the whole addressables scan came back empty, which is how a LargeSoftCache reports its WeakReferences being cleared. The test consumed three announcements and held a strong reference to none of them. LargeCacheAddressableFilterTest already documents this exact trap for exactly this store. So the fixture now keeps the Notes it expects the scan to find, and a forced GC sits between the consume and the scan so that stays proven rather than assumed. Two locals, `alive` and `subscriptionApp`, were built and never consumed, which left "subscription apps are not content-discovery DVMs" asserting the absence of something that was never there. They are consumed now, so that assertion means what it says. I could not reproduce the clearing locally — a probe shows the note is still retained after two full GCs here, so the CI heap is what makes the difference. The fix removes the dependence either way. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01KkULS5SVq4GHDdoCzajKi8 --- .../amethyst/model/DvmHeartbeatTest.kt | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/amethyst/src/test/java/com/vitorpamplona/amethyst/model/DvmHeartbeatTest.kt b/amethyst/src/test/java/com/vitorpamplona/amethyst/model/DvmHeartbeatTest.kt index 22fbbb4e67..cb11cc64d2 100644 --- a/amethyst/src/test/java/com/vitorpamplona/amethyst/model/DvmHeartbeatTest.kt +++ b/amethyst/src/test/java/com/vitorpamplona/amethyst/model/DvmHeartbeatTest.kt @@ -160,9 +160,7 @@ class DvmHeartbeatTest { sig = "cc".repeat(64), ) - LocalCache.justConsume(appDef("dvm-x"), null, true) - LocalCache.justConsume(nonDiscoveryApp, null, true) - val consumed = + val secondSubscriptionApp = AppDefinitionEvent( id = "c2".repeat(32), pubKey = appDefPubKey, @@ -171,7 +169,18 @@ class DvmHeartbeatTest { content = """{"name":"Paid2","subscription":true}""", sig = "cc".repeat(64), ) - LocalCache.justConsume(consumed, null, true) + + // `addressables` holds notes by WeakReference, so a GC between consuming these and + // scanning for them clears the lot and the scan returns nothing — which is exactly how + // this failed on CI, where the heap is tighter than a dev box's. A fixture has to hold + // what it expects to find, the same discipline LargeCacheAddressableFilterTest spells + // out; the forced GC below is what keeps that honest rather than assumed. + val held = + listOf(alive, appDef("dvm-x"), subscriptionApp, nonDiscoveryApp, secondSubscriptionApp) + .onEach { LocalCache.justConsume(it, null, true) } + .map { LocalCache.getOrCreateNote(it) } + + System.gc() val scanned = LocalCache.cachedDvmAnnouncements() @@ -180,5 +189,6 @@ class DvmHeartbeatTest { assertFalse("k=9999 apps are not content-discovery DVMs", scanned.any { it.dTag() == "other" }) assertEquals("newest-first so the cap keeps the most relevant announcements", scanned.sortedByDescending { it.createdAt }, scanned) assertTrue("capped", scanned.size <= 100) + assertEquals("the fixture notes must stay reachable for the whole test", 5, held.size) } }