From 9bb1d3aaf29bc6c75bc4b6913434ac97ecf645f9 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 4 Jul 2026 17:48:18 +0000 Subject: [PATCH] fix: stabilize flaky LocalRelayStoreHydrationTest against GC eviction DesktopLocalCache stores Users in a WeakReference-backed LargeSoftCache. The followee User in kind3IsHydratedBeforeKind0SoMetadataLoadsForFollowedAuthors is created only during hydrate's kind:0 phase and has no Note referencing it, so it is only weakly reachable once hydrate returns. A GC landing between hydrate() and the assertions evicted it, flaking the test (reproduced deterministically by forcing System.gc()). Pin a strong reference to the followee's User for the duration of the test so the cache cannot evict it, mirroring how followed users stay reachable via live account/UI state in the running app. The ordering invariant the test asserts is unaffected. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01NHQ3g7wD9WbDvj7NspiAWW --- .../desktop/relay/LocalRelayStoreHydrationTest.kt | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/desktopApp/src/jvmTest/kotlin/com/vitorpamplona/amethyst/desktop/relay/LocalRelayStoreHydrationTest.kt b/desktopApp/src/jvmTest/kotlin/com/vitorpamplona/amethyst/desktop/relay/LocalRelayStoreHydrationTest.kt index eadaf362e9..3bd92f908a 100644 --- a/desktopApp/src/jvmTest/kotlin/com/vitorpamplona/amethyst/desktop/relay/LocalRelayStoreHydrationTest.kt +++ b/desktopApp/src/jvmTest/kotlin/com/vitorpamplona/amethyst/desktop/relay/LocalRelayStoreHydrationTest.kt @@ -167,6 +167,16 @@ class LocalRelayStoreHydrationTest { val cache = DesktopLocalCache() val store = newStore() + + // Pin a strong reference to the followee's User for the duration of the + // test. DesktopLocalCache stores Users in a WeakReference-backed cache + // (LargeSoftCache), and a metadata-only followed user has no Note pointing + // at it, so nothing else keeps it strongly reachable. Without this pin a GC + // landing between hydrate() and the assertions below could evict the user + // and flake the test. In the running app followed users stay reachable via + // live account/UI state. + val pinnedFollowee = cache.getOrCreateUser(followee.pubKey.toHexKey()) + try { store.hydrate(cache) } finally { @@ -184,6 +194,9 @@ class LocalRelayStoreHydrationTest { actual = followeeUser.toBestDisplayName(), message = "Phase 2 (kind:0) must run after phase 1 so metadata is applied to followed users", ) + // Keep the pin alive past the assertions and confirm hydrate updated the + // same instance rather than a second copy. + assertEquals(pinnedFollowee, followeeUser, "hydrate must update the cached User in place") } @Test