oneby-wang commented on code in PR #25949:
URL: https://github.com/apache/pulsar/pull/25949#discussion_r3371811037


##########
pulsar-broker-auth-sasl/src/test/java/org/apache/pulsar/broker/authentication/SaslAuthenticateTest.java:
##########
@@ -310,49 +310,55 @@ public void testSaslServerAndClientAuth() throws 
Exception {
 
     @Test
     @SuppressWarnings("unchecked")
-    public void testSaslOnlyAuthFirstStage() throws Exception {
+    public void testSaslOnlyAuthFirstStageKeepsInflightContextsBeforeExpiry() 
throws Exception {
         @Cleanup
         AuthenticationProviderSasl saslServer = new 
AuthenticationProviderSasl();
-        // The cache expiration time is set to 500ms. Residual auth info 
should be cleaned up
-        conf.setInflightSaslContextExpiryMs(500);
+        conf.setInflightSaslContextExpiryMs(Integer.MAX_VALUE);
         
saslServer.initialize(AuthenticationProvider.Context.builder().config(conf).build());
 
         HttpServletRequest servletRequest = mock(HttpServletRequest.class);
-        doReturn("Init").when(servletRequest).getHeader("State");
-        // 10 clients only do one-stage verification, resulting in 10 auth 
info remaining in memory
+        
doReturn(SaslConstants.SASL_STATE_CLIENT_INIT).when(servletRequest).getHeader(SaslConstants.SASL_HEADER_STATE);
         for (int i = 0; i < 10; i++) {
-            AuthenticationDataProvider dataProvider =  
authSasl.getAuthData("localhost");
+            AuthenticationDataProvider dataProvider =  
authSasl.getAuthData(localHostname);
             AuthData initData1 = 
dataProvider.authenticate(AuthData.INIT_AUTH_DATA);
             
doReturn(Base64.getEncoder().encodeToString(initData1.getBytes())).when(
-                    servletRequest).getHeader("SASL-Token");
-            
doReturn(String.valueOf(i)).when(servletRequest).getHeader("SASL-Server-ID");
+                    servletRequest).getHeader(SaslConstants.SASL_AUTH_TOKEN);
+            
doReturn(String.valueOf(i)).when(servletRequest).getHeader(SaslConstants.SASL_STATE_SERVER);
             saslServer.authenticateHttpRequest(servletRequest, 
mock(HttpServletResponse.class));
         }
+
         Field field = 
AuthenticationProviderSasl.class.getDeclaredField("authStates");
         field.setAccessible(true);
         Cache<Long, AuthenticationState> cache = (Cache<Long, 
AuthenticationState>) field.get(saslServer);
         assertEquals(cache.asMap().size(), 10);
-        // Add more auth info into memory
+    }
+
+    @Test
+    @SuppressWarnings("unchecked")
+    public void testSaslOnlyAuthFirstStageExpiresResidualContexts() throws 
Exception {
+        @Cleanup
+        AuthenticationProviderSasl saslServer = new 
AuthenticationProviderSasl();
+        conf.setInflightSaslContextExpiryMs(50);
+        
saslServer.initialize(AuthenticationProvider.Context.builder().config(conf).build());
+
+        HttpServletRequest servletRequest = mock(HttpServletRequest.class);
+        
doReturn(SaslConstants.SASL_STATE_CLIENT_INIT).when(servletRequest).getHeader(SaslConstants.SASL_HEADER_STATE);
         for (int i = 0; i < 10; i++) {
-            AuthenticationDataProvider dataProvider =  
authSasl.getAuthData("localhost");
+            AuthenticationDataProvider dataProvider =  
authSasl.getAuthData(localHostname);
             AuthData initData1 = 
dataProvider.authenticate(AuthData.INIT_AUTH_DATA);
             
doReturn(Base64.getEncoder().encodeToString(initData1.getBytes())).when(
-                    servletRequest).getHeader("SASL-Token");
-            doReturn(String.valueOf(10 + 
i)).when(servletRequest).getHeader("SASL-Server-ID");
+                    servletRequest).getHeader(SaslConstants.SASL_AUTH_TOKEN);
+            
doReturn(String.valueOf(i)).when(servletRequest).getHeader(SaslConstants.SASL_STATE_SERVER);
             saslServer.authenticateHttpRequest(servletRequest, 
mock(HttpServletResponse.class));
         }
-        long start = System.currentTimeMillis();
-        while (true) {
-            if (System.currentTimeMillis() - start > 5000) {
-                fail();
-            }
-            cache = (Cache<Long, AuthenticationState>) field.get(saslServer);
-            // Residual auth info should be cleaned up
-            if (CollectionUtils.hasElements(cache.asMap())) {
-                break;
-            }
-            Thread.sleep(5);
-        }
+
+        Field field = 
AuthenticationProviderSasl.class.getDeclaredField("authStates");
+        field.setAccessible(true);
+        Cache<Long, AuthenticationState> cache = (Cache<Long, 
AuthenticationState>) field.get(saslServer);
+        Awaitility.await().atMost(3, TimeUnit.SECONDS).pollDelay(100, 
TimeUnit.MILLISECONDS).untilAsserted(() -> {
+            cache.cleanUp();
+            assertEquals(cache.asMap().size(), 0);

Review Comment:
   I agree. I think it would be clearer to verify the behavior that the test 
actually cares about: the expired authentication contexts are no longer 
retrievable from the cache.
   
   Instead of asserting the internal cache size after cleanUp(), we can 
directly check:
   
   ```java
   Awaitility.await().atMost(5, TimeUnit.SECONDS).pollDelay(100, 
TimeUnit.MILLISECONDS).untilAsserted(() -> {
       for (int i = 0; i < 10; i++) {
           AuthenticationState authenticationState = cache.getIfPresent(((long) 
i));
           log.info("authenticationState: " + authenticationState);
           assertNull(cache.getIfPresent(((long) i)));
       }
       // assertEquals(cache.asMap().size(), 0);
   });
   ```
   
   This makes the test focus on the observable behavior rather than the cache's 
internal state, and avoids relying on asMap().size() for validation.
   
   Another interesting thing is: if I uncomment the 
`assertEquals(cache.asMap().size(), 0)` assertion, the test may still fail even 
though all `getIfPresent(...)` checks return `null`.
   
   ```
   2026-06-08T16:35:00,952 - INFO  - [awaitility-thread:SaslAuthenticateTest] - 
authenticationState: null {}
   2026-06-08T16:35:00,952 - INFO  - [awaitility-thread:SaslAuthenticateTest] - 
authenticationState: null {}
   2026-06-08T16:35:01,057 - INFO  - [awaitility-thread:SaslAuthenticateTest] - 
authenticationState: null {}
   2026-06-08T16:35:01,057 - INFO  - [awaitility-thread:SaslAuthenticateTest] - 
authenticationState: null {}
   2026-06-08T16:35:01,057 - INFO  - [awaitility-thread:SaslAuthenticateTest] - 
authenticationState: null {}
   2026-06-08T16:35:01,057 - INFO  - [awaitility-thread:SaslAuthenticateTest] - 
authenticationState: null {}
   2026-06-08T16:35:01,057 - INFO  - [awaitility-thread:SaslAuthenticateTest] - 
authenticationState: null {}
   2026-06-08T16:35:01,057 - INFO  - [awaitility-thread:SaslAuthenticateTest] - 
authenticationState: null {}
   2026-06-08T16:35:01,057 - INFO  - [awaitility-thread:SaslAuthenticateTest] - 
authenticationState: null {}
   2026-06-08T16:35:01,057 - INFO  - [awaitility-thread:SaslAuthenticateTest] - 
authenticationState: null {}
   2026-06-08T16:35:01,057 - INFO  - [awaitility-thread:SaslAuthenticateTest] - 
authenticationState: null {}
   2026-06-08T16:35:01,057 - INFO  - [awaitility-thread:SaslAuthenticateTest] - 
authenticationState: null {}
   2026-06-08T16:35:01,165 - WARN  - 
[pulsar-tgt-refresh-thread:TGTRefreshThread] - TGT renewal thread has been 
interrupted and will exit {}
   
   Assertion condition expected [0] but found [10] within 10 seconds.
   org.awaitility.core.ConditionTimeoutException: Assertion condition expected 
[0] but found [10] within 10 seconds.
        at org.awaitility.core.ConditionAwaiter.await(ConditionAwaiter.java:167)
        at 
org.awaitility.core.AssertionCondition.await(AssertionCondition.java:119)
   ```
   
   Also, do you think a similar change would make sense in 
https://github.com/apache/pulsar/pull/25948/changes as well? It seems that 
asserting getIfPresent(...) == null could make the test intent clearer there 
too.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to