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]