This is an automated email from the ASF dual-hosted git repository.

yu199195 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shenyu.git


The following commit(s) were added to refs/heads/master by this push:
     new 86e544b074 Stabilize Sentinel integrated rate-limit assertions (#6374)
86e544b074 is described below

commit 86e544b074b010eb787e1e062a4d7048461b10f3
Author: aias00 <[email protected]>
AuthorDate: Tue Jun 9 10:22:28 2026 +0800

    Stabilize Sentinel integrated rate-limit assertions (#6374)
    
    The HTTP integrated Sentinel tests used back-to-back requests to prove one 
successful pass and one rate-limited response. On slow CI, sequential requests 
can cross Sentinel's one-second QPS window, leaving the expected limited 
response absent even though the gateway behavior is correct.
    
    This changes the trigger to concurrent requests and asserts over the 
observed result set, preserving the same behavior coverage while removing 
timing dependence from the test.
    
    Constraint: Failure appeared in the master push for #6361 in build 
(shenyu-integrated-test-http), after Sentinel fallback coverage was added.
    
    Rejected: Increase sleeps or timeouts | would make the timing window larger 
without making the assertion deterministic.
    
    Rejected: Change Sentinel production cleanup | the failing surface is the 
test trigger timing, and production cleanup changes would broaden risk 
unnecessarily.
    
    Confidence: medium
    
    Scope-risk: narrow
    
    Directive: Keep Sentinel rate-limit integration assertions 
concurrency-based; sequential requests can cross QPS windows on loaded runners.
    
    Tested: ./mvnw -f shenyu-integrated-test/pom.xml -pl 
shenyu-integrated-test-http -am -DskipTests test-compile
    
    Tested: git diff --check
    
    Not-tested: Full Docker integrated-test-http run locally; required CI 
images and supporting services were not prebuilt locally.
---
 .../test/http/combination/SentinelPluginTest.java  | 60 +++++++++++++++++-----
 1 file changed, 46 insertions(+), 14 deletions(-)

diff --git 
a/shenyu-integrated-test/shenyu-integrated-test-http/src/test/java/org/apache/shenyu/integrated/test/http/combination/SentinelPluginTest.java
 
b/shenyu-integrated-test/shenyu-integrated-test-http/src/test/java/org/apache/shenyu/integrated/test/http/combination/SentinelPluginTest.java
index 210b3a2bc0..521e3adfd5 100644
--- 
a/shenyu-integrated-test/shenyu-integrated-test-http/src/test/java/org/apache/shenyu/integrated/test/http/combination/SentinelPluginTest.java
+++ 
b/shenyu-integrated-test/shenyu-integrated-test-http/src/test/java/org/apache/shenyu/integrated/test/http/combination/SentinelPluginTest.java
@@ -36,12 +36,15 @@ import org.junit.jupiter.api.Test;
 import java.io.IOException;
 import java.lang.reflect.Type;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
 
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 
 public final class SentinelPluginTest extends AbstractPluginDataInit {
@@ -57,34 +60,63 @@ public final class SentinelPluginTest extends 
AbstractPluginDataInit {
     }
 
     @Test
-    public void test() throws IOException {
+    public void test() throws IOException, ExecutionException, 
InterruptedException {
         String selectorAndRulesResult =
                 initSelectorAndRules(PluginEnum.SENTINEL.getName(), "", 
buildSelectorConditionList(), buildRuleLocalDataList(null));
         assertThat(selectorAndRulesResult, is("success"));
 
         Type returnType = new TypeToken<Map<String, Object>>() {
         }.getType();
-        Map<String, Object> result = 
HttpHelper.INSTANCE.postGateway(TEST_SENTINEL_PATH, returnType);
-        assertNotNull(result);
-        assertEquals("pass", result.get("msg"));
-        result = HttpHelper.INSTANCE.postGateway(TEST_SENTINEL_PATH, 
returnType);
-        assertEquals("You have been restricted, please try again later!", 
result.get("message"));
+        Future<Map<String, Object>> first = this.getService().submit(() -> 
HttpHelper.INSTANCE.postGateway(TEST_SENTINEL_PATH, returnType));
+        Future<Map<String, Object>> second = this.getService().submit(() -> 
HttpHelper.INSTANCE.postGateway(TEST_SENTINEL_PATH, returnType));
+        Map<String, Object> firstResult = first.get();
+        Map<String, Object> secondResult = second.get();
+        Set<String> messages = toMessages(firstResult, secondResult);
+        assertThat(messages.contains("pass"), is(true));
+        assertThat(messages.contains("You have been restricted, please try 
again later!"), is(true));
     }
 
     @Test
-    public void testFallbackUri() throws IOException {
+    public void testFallbackUri() throws IOException, ExecutionException, 
InterruptedException {
         String selectorAndRulesResult =
                 initSelectorAndRules(PluginEnum.SENTINEL.getName(), "", 
buildSelectorConditionList(), 
buildRuleLocalDataList(TEST_SENTINEL_FALLBACK_PATH));
         assertThat(selectorAndRulesResult, is("success"));
 
         Type returnType = new TypeToken<Map<String, Object>>() {
         }.getType();
-        Map<String, Object> result = 
HttpHelper.INSTANCE.postGateway(TEST_SENTINEL_PATH, returnType);
-        assertNotNull(result);
-        assertEquals("pass", result.get("msg"));
-        Map<String, Object> fallbackRet = 
HttpHelper.INSTANCE.postGateway(TEST_SENTINEL_PATH, returnType);
-        assertEquals(ShenyuResultEnum.SENTINEL_PLUGIN_FALLBACK.getCode(), 
((Number) fallbackRet.get("code")).intValue());
-        assertEquals(ShenyuResultEnum.SENTINEL_PLUGIN_FALLBACK.getMsg(), 
fallbackRet.get("message"));
+        Future<Map<String, Object>> first = this.getService().submit(() -> 
HttpHelper.INSTANCE.postGateway(TEST_SENTINEL_PATH, returnType));
+        Future<Map<String, Object>> second = this.getService().submit(() -> 
HttpHelper.INSTANCE.postGateway(TEST_SENTINEL_PATH, returnType));
+        Map<String, Object> firstResult = first.get();
+        Map<String, Object> secondResult = second.get();
+        Set<String> messages = toMessages(firstResult, secondResult);
+        Set<Integer> codes = toCodes(firstResult, secondResult);
+        assertThat(messages.contains("pass"), is(true));
+        
assertThat(codes.contains(ShenyuResultEnum.SENTINEL_PLUGIN_FALLBACK.getCode()), 
is(true));
+        
assertThat(messages.contains(ShenyuResultEnum.SENTINEL_PLUGIN_FALLBACK.getMsg()),
 is(true));
+    }
+
+    private static Set<String> toMessages(final Map<String, Object>... 
results) {
+        Set<String> messages = new HashSet<>();
+        for (Map<String, Object> result : results) {
+            assertNotNull(result);
+            Object msg = result.containsKey("msg") ? result.get("msg") : 
result.get("message");
+            if (msg instanceof String) {
+                messages.add((String) msg);
+            }
+        }
+        return messages;
+    }
+
+    private static Set<Integer> toCodes(final Map<String, Object>... results) {
+        Set<Integer> codes = new HashSet<>();
+        for (Map<String, Object> result : results) {
+            assertNotNull(result);
+            Object code = result.get("code");
+            if (code instanceof Number) {
+                codes.add(((Number) code).intValue());
+            }
+        }
+        return codes;
     }
 
     private static List<ConditionData> buildSelectorConditionList() {

Reply via email to