@@ -99,6 +99,42 @@ public void concurrentCallersOnlyRefreshOnce() throws Exception {
9999 }
100100 }
101101
102+ /**
103+ * Tests that the {@code isExpired} predicate alone can force a cache refresh even when the cached value is still
104+ * current within the same second. This exercises the branch where the time-check condition ({@code A}) evaluates to
105+ * {@code false} but the {@code isExpired} predicate ({@code B}) evaluates to {@code true}, covering the
106+ * {@code A=false, B=true} path in both the read-lock check and the write-lock double-check inside
107+ * {@code GitHubSanityCachedValue}.
108+ *
109+ * @throws Exception
110+ * if the test fails
111+ */
112+ @ Test
113+ public void isExpiredPredicateTriggersRefreshWithinSameSecond () throws Exception {
114+ alignToStartOfSecond ();
115+ GitHubSanityCachedValue <String > cachedValue = new GitHubSanityCachedValue <>();
116+ AtomicInteger calls = new AtomicInteger ();
117+
118+ // Populate the cache within the current second using an isExpired predicate that never
119+ // expires on its own.
120+ String first = cachedValue .get (result -> false , () -> {
121+ calls .incrementAndGet ();
122+ return "stale" ;
123+ });
124+
125+ // Within the same second, pass an isExpired predicate that always returns true. This forces
126+ // re-evaluation through the write lock even though the time has not elapsed, covering the
127+ // A=false, B=true branch in both compound conditions.
128+ String second = cachedValue .get (result -> true , () -> {
129+ calls .incrementAndGet ();
130+ return "fresh" ;
131+ });
132+
133+ assertThat (first , equalTo ("stale" ));
134+ assertThat (second , equalTo ("fresh" ));
135+ assertThat (calls .get (), equalTo (2 ));
136+ }
137+
102138 /**
103139 * Tests that the cache is refreshed after one second has elapsed, triggering a new query to retrieve the updated
104140 * value.
0 commit comments