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

Aias00 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 560db583be fix: avoid NPE when mock rule omits httpStatusCode (#6881) 
(#6953)
560db583be is described below

commit 560db583beb7c20093c9989a9aa9426db2e04fbd
Author: Southern <[email protected]>
AuthorDate: Sat Sep 5 18:05:55 2026 +0800

    fix: avoid NPE when mock rule omits httpStatusCode (#6881) (#6953)
    
    MockHandle.httpStatusCode has no default, so when a mock rule's handle
      JSON omits or nulls it, Gson leaves the field null and MockPlugin passes
      it to HttpStatus.valueOf(int), whose auto-unboxing throws NPE and makes
      every matching request return 500.
    
      Fall back to 200 (OK) via Optional.ofNullable(...).orElse(200) in
      MockPlugin.doExecute, so the mock response is returned instead.
    
      Add MockPluginTest covering: null httpStatusCode -> 200 mock response,
      an explicitly specified status code being honored, and a missing cached
      handle falling through to the plugin chain.
    
    Co-authored-by: aias00 <[email protected]>
---
 .../org/apache/shenyu/plugin/mock/MockPlugin.java  |   3 +-
 .../apache/shenyu/plugin/mock/MockPluginTest.java  | 143 +++++++++++++++++++++
 2 files changed, 145 insertions(+), 1 deletion(-)

diff --git 
a/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/MockPlugin.java
 
b/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/MockPlugin.java
index ce886acaa8..9a8eada8a8 100644
--- 
a/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/MockPlugin.java
+++ 
b/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/MockPlugin.java
@@ -38,6 +38,7 @@ import reactor.core.publisher.Mono;
 
 import java.nio.charset.StandardCharsets;
 import java.util.Objects;
+import java.util.Optional;
 
 /**
  * MockPlugin.
@@ -53,7 +54,7 @@ public class MockPlugin extends AbstractShenyuPlugin {
             return chain.execute(exchange);
         }
         
exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON);
-        
exchange.getResponse().setStatusCode(HttpStatus.valueOf(mockHandle.getHttpStatusCode()));
+        
exchange.getResponse().setStatusCode(HttpStatus.valueOf(Optional.ofNullable(mockHandle.getHttpStatusCode()).orElse(200)));
         
         return DataBufferUtils.join(exchange.getRequest().getBody())
                 
.switchIfEmpty(Mono.just(DefaultDataBufferFactory.sharedInstance.allocateBuffer(0)))
diff --git 
a/shenyu-plugin/shenyu-plugin-mock/src/test/java/org/apache/shenyu/plugin/mock/MockPluginTest.java
 
b/shenyu-plugin/shenyu-plugin-mock/src/test/java/org/apache/shenyu/plugin/mock/MockPluginTest.java
new file mode 100644
index 0000000000..06f0e89d8d
--- /dev/null
+++ 
b/shenyu-plugin/shenyu-plugin-mock/src/test/java/org/apache/shenyu/plugin/mock/MockPluginTest.java
@@ -0,0 +1,143 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.plugin.mock;
+
+import org.apache.shenyu.common.dto.RuleData;
+import org.apache.shenyu.common.dto.SelectorData;
+import org.apache.shenyu.common.dto.convert.rule.MockHandle;
+import org.apache.shenyu.common.enums.PluginEnum;
+import org.apache.shenyu.plugin.api.ShenyuPluginChain;
+import org.apache.shenyu.plugin.base.utils.CacheKeyUtils;
+import org.apache.shenyu.plugin.mock.handler.MockPluginHandler;
+import org.hamcrest.Matchers;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.http.HttpStatus;
+import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
+import org.springframework.mock.web.server.MockServerWebExchange;
+import reactor.core.publisher.Mono;
+import reactor.test.StepVerifier;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ * test case for {@link MockPlugin}.
+ */
+@ExtendWith(MockitoExtension.class)
+public final class MockPluginTest {
+
+    private static final String SELECTOR_ID = "mock-selector";
+
+    private static final String RULE_ID = "mock-rule";
+
+    private final MockPlugin mockPlugin = new MockPlugin();
+
+    @Mock
+    private ShenyuPluginChain chain;
+
+    private MockServerWebExchange exchange;
+
+    @BeforeEach
+    public void setUp() {
+        exchange = 
MockServerWebExchange.from(MockServerHttpRequest.get("localhost").build());
+        
MockPluginHandler.CACHED_HANDLE.get().removeHandle(CacheKeyUtils.INST.getKey(SELECTOR_ID,
 RULE_ID));
+    }
+
+    @AfterEach
+    public void clean() {
+        
MockPluginHandler.CACHED_HANDLE.get().removeHandle(CacheKeyUtils.INST.getKey(SELECTOR_ID,
 RULE_ID));
+    }
+
+    /**
+     * issue #6881: a mock rule whose handle omits {@code httpStatusCode} must 
not NPE
+     * and should fall back to a 200 response with the mock content.
+     */
+    @Test
+    public void testDoExecuteWithNullHttpStatusCode() {
+        RuleData ruleData = buildRuleData();
+        MockHandle mockHandle = new MockHandle();
+        mockHandle.setHttpStatusCode(null);
+        mockHandle.setResponseContent("{\"user\":\"test\"}");
+        
MockPluginHandler.CACHED_HANDLE.get().cachedHandle(CacheKeyUtils.INST.getKey(ruleData),
 mockHandle);
+
+        StepVerifier.create(mockPlugin.doExecute(exchange, chain, 
mock(SelectorData.class), ruleData))
+                .expectSubscription()
+                .verifyComplete();
+
+        assertThat(exchange.getResponse().getStatusCode(), is(HttpStatus.OK));
+        assertThat(exchange.getResponse().getBodyAsString().block(), 
is("{\"user\":\"test\"}"));
+        verify(chain, never()).execute(any());
+    }
+
+    @Test
+    public void testDoExecuteWithSpecifiedHttpStatusCode() {
+        RuleData ruleData = buildRuleData();
+        MockHandle mockHandle = new MockHandle();
+        mockHandle.setHttpStatusCode(HttpStatus.NOT_FOUND.value());
+        mockHandle.setResponseContent("not found");
+        
MockPluginHandler.CACHED_HANDLE.get().cachedHandle(CacheKeyUtils.INST.getKey(ruleData),
 mockHandle);
+
+        StepVerifier.create(mockPlugin.doExecute(exchange, chain, 
mock(SelectorData.class), ruleData))
+                .expectSubscription()
+                .verifyComplete();
+
+        assertThat(exchange.getResponse().getStatusCode(), 
is(HttpStatus.NOT_FOUND));
+        assertThat(exchange.getResponse().getBodyAsString().block(), is("not 
found"));
+        verify(chain, never()).execute(any());
+    }
+
+    @Test
+    public void testDoExecuteWithoutCachedHandle() {
+        RuleData ruleData = buildRuleData();
+        when(chain.execute(any())).thenReturn(Mono.empty());
+
+        StepVerifier.create(mockPlugin.doExecute(exchange, chain, 
mock(SelectorData.class), ruleData))
+                .expectSubscription()
+                .verifyComplete();
+
+        verify(chain).execute(any());
+    }
+
+    @Test
+    public void testGetOrder() {
+        final int result = mockPlugin.getOrder();
+        assertThat(PluginEnum.MOCK.getCode(), Matchers.is(result));
+    }
+
+    @Test
+    public void testNamed() {
+        final String result = mockPlugin.named();
+        assertThat(PluginEnum.MOCK.getName(), Matchers.is(result));
+    }
+
+    private RuleData buildRuleData() {
+        return new RuleData()
+                .setSelectorId(SELECTOR_ID)
+                .setId(RULE_ID);
+    }
+}

Reply via email to