@@ -157,13 +157,13 @@ public void testFileChangeWatcher() throws IOException {
157157 var now = Instant .now ();
158158 int ticks = 0 ;
159159 var watcher = new JwkSetLoader .FileChangeWatcher (path );
160- assertThat (watcher .changed (), is (true ));
160+ assertThat (watcher .changedSinceLastCall (), is (true ));
161161 Files .setLastModifiedTime (path , FileTime .from (now .plusSeconds (++ticks )));
162- assertThat (watcher .changed (), is (true ));
163- assertThat (watcher .changed (), is (false ));
162+ assertThat (watcher .changedSinceLastCall (), is (true ));
163+ assertThat (watcher .changedSinceLastCall (), is (false ));
164164 Files .setLastModifiedTime (path , FileTime .from (now .plusSeconds (++ticks )));
165- assertThat (watcher .changed (), is (true ));
166- assertThat (watcher .changed (), is (false ));
165+ assertThat (watcher .changedSinceLastCall (), is (true ));
166+ assertThat (watcher .changedSinceLastCall (), is (false ));
167167 }
168168
169169 public void testFilePkcJwkSetLoader () throws IOException {
@@ -181,7 +181,9 @@ public void testFilePkcJwkSetLoader() throws IOException {
181181 ThreadPool threadPool = mock (ThreadPool .class );
182182 CountingCallback callback = new CountingCallback ();
183183
184- new JwkSetLoader .FilePkcJwkSetLoader (realmConfig , threadPool , path .toString (), callback ).start (); // schedules task
184+ var loader = new JwkSetLoader .FilePkcJwkSetLoader (realmConfig , threadPool , path .toString (), callback );
185+ loader .start (); // schedules task
186+
185187 ArgumentCaptor <Runnable > taskCaptor = ArgumentCaptor .forClass (Runnable .class );
186188 verify (threadPool , times (1 )).scheduleWithFixedDelay (taskCaptor .capture (), any (TimeValue .class ), isNull ());
187189
@@ -198,6 +200,15 @@ public void testFilePkcJwkSetLoader() throws IOException {
198200 taskCaptor .getValue ().run (); // run third time, change detected
199201 assertThat (callback .content , is (equalTo (goodbye )));
200202 assertThat (callback .count , is (2 ));
203+
204+ loader .stop ();
205+
206+ Files .writeString (path , "too late" );
207+ Files .setLastModifiedTime (path , FileTime .from (Instant .now ().plusSeconds (1 ))); // ensure mod time changes
208+
209+ taskCaptor .getValue ().run (); // run fourth time, callback not invoked due to closure
210+ assertThat (callback .content , is (equalTo (goodbye )));
211+ assertThat (callback .count , is (2 ));
201212 }
202213
203214 public void testUrlPkcJwkSetLoader () throws IOException {
@@ -209,12 +220,18 @@ public void testUrlPkcJwkSetLoader() throws IOException {
209220
210221 CountingCallback callback = new CountingCallback ();
211222
212- new JwkSetLoader .UrlPkcJwkSetLoader (realmConfig , threadPool , uri , httpClient , callback ).start (); // schedules task
223+ var loader = new JwkSetLoader .UrlPkcJwkSetLoader (realmConfig , threadPool , uri , httpClient , callback );
224+ loader .start (); // schedules task
213225
214226 int iterations = randomIntBetween (5 , 10 );
215227 for (int i = 0 ; i < iterations ; i ++) {
216228 verifySchedulingIteration (callback , threadPool , httpClient , i + 1 );
217229 }
230+
231+ loader .stop ();
232+ verify (httpClient , times (1 )).close ();
233+ verifySchedulingIteration (callback , threadPool , httpClient , iterations + 1 ); // last schedule call, no subsequent reschedule
234+ verify (threadPool , never ()).schedule (any (Runnable .class ), any (TimeValue .class ), isNull ());
218235 }
219236
220237 public void testUrlPkcJwkSetLoaderListenerException () throws IOException {
@@ -230,7 +247,7 @@ public void testUrlPkcJwkSetLoaderListenerException() throws IOException {
230247
231248 int iterations = randomIntBetween (5 , 10 );
232249 for (int i = 0 ; i < iterations ; i ++) {
233- verifySchedulingIterationWithListenerException (threadPool , httpClient , i + 1 );
250+ verifySchedulingIterationWithListenerException (threadPool , httpClient );
234251 }
235252 }
236253
@@ -263,16 +280,15 @@ private void verifySchedulingIteration(
263280 byte [] bytes = "x" .repeat (iteration ).getBytes (StandardCharsets .UTF_8 );
264281 HttpResponse response = makeHttpResponse (bytes , randomBoolean ());
265282
266- reset (threadPool );
267- reset (httpClient );
283+ reset (threadPool , httpClient );
268284
269285 // invoke response handler
270286 responseFn .getValue ().completed (response );
271287 assertThat (callback .content , is (equalTo (bytes )));
272288 assertThat (callback .count , is (iteration ));
273289 }
274290
275- private void verifySchedulingIterationWithListenerException (ThreadPool threadPool , CloseableHttpAsyncClient httpClient , int iteration )
291+ private void verifySchedulingIterationWithListenerException (ThreadPool threadPool , CloseableHttpAsyncClient httpClient )
276292 throws IOException {
277293 // capture scheduled task and delay
278294 ArgumentCaptor <Runnable > taskCaptor = ArgumentCaptor .forClass (Runnable .class );
@@ -289,8 +305,7 @@ private void verifySchedulingIterationWithListenerException(ThreadPool threadPoo
289305 verify (httpClient , times (1 )).execute (any (HttpGet .class ), responseFn .capture ());
290306 HttpResponse response = makeHttpResponse (new byte [0 ], randomBoolean ());
291307
292- reset (threadPool );
293- reset (httpClient );
308+ reset (threadPool , httpClient );
294309
295310 // invoke response handler
296311 responseFn .getValue ().completed (response );
0 commit comments