content: /*
   * Copyright 1999-2024 Alibaba Group Holding Ltd.
   *
   * Licensed 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
   *
   *      https://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 com.alibaba.csp.sentinel.adapter.spring.webmvc_v6x;
  
  import static org.junit.Assert.assertEquals;
  import static org.junit.Assert.assertNotNull;
  import static org.junit.Assert.assertNull;
  import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
  import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
  import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
  
  import com.alibaba.csp.sentinel.context.ContextUtil;
  import com.alibaba.csp.sentinel.node.ClusterNode;
  import com.alibaba.csp.sentinel.slots.block.RuleConstant;
  import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
  import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
  import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot;
  import com.alibaba.csp.sentinel.util.StringUtil;
  
  import java.util.Collections;
  
  import org.junit.After;
  import org.junit.Test;
  import org.junit.runner.RunWith;
  import org.springframework.beans.factory.annotation.Autowired;
  import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
  import org.springframework.boot.test.context.SpringBootTest;
  import org.springframework.http.MediaType;
  import org.springframework.test.context.junit4.SpringRunner;
  import org.springframework.test.web.servlet.MockMvc;
  
  /**
   * @author kaizi2009
   */
  @RunWith(SpringRunner.class)
  @SpringBootTest(classes = TestApplication.class)
  @AutoConfigureMockMvc
  public class SentinelSpringMvcIntegrationTest {
  
      private static final String HELLO_STR = "Hello!";
      @Autowired
      private MockMvc mvc;
  
      @Test
      public void testBase() throws Exception {
          String url = "/hello";
          this.mvc.perform(get(url))
                  .andExpect(status().isOk())
                  .andExpect(content().string(HELLO_STR));
  
          ClusterNode cn = ClusterBuilderSlot.getClusterNode(url);
          assertNotNull(cn);
          assertEquals(1, cn.passQps(), 0.01);
      }
  
      @Test
      public void testAsync() throws Exception {
          String url = "/async";
          this.mvc.perform(get(url))
                  .andExpect(status().isOk());
  
          ClusterNode cn = ClusterBuilderSlot.getClusterNode(url);
          assertNotNull(cn);
          assertEquals(1, cn.passQps(), 0.01);
          assertNull(ContextUtil.getContext());
      }
  
      @Test
      public void testOriginParser() throws Exception {
          String springMvcPathVariableUrl = "/foo/{id}";
          String limitOrigin = "userA";
          final String headerName = "S-User";
          configureRulesFor(springMvcPathVariableUrl, 0, limitOrigin);
  
          // This will be passed since the caller is different: userB
          this.mvc.perform(get("/foo/1").accept(MediaType.TEXT_PLAIN).header(headerName, "userB"))
                  .andExpect(status().isOk())
                  .andExpect(content().string("foo 1"));
  
          // This will be blocked since the caller is same: userA
          this.mvc.perform(
                          get("/foo/2").accept(MediaType.APPLICATION_JSON).header(headerName, limitOrigin))
                  .andExpect(status().isOk())
                  .andExpect(content().json(ResultWrapper.blocked().toJsonString()));
  
          // This will be passed since the caller is different: ""
          this.mvc.perform(get("/foo/3").accept(MediaType.APPLICATION_JSON))
                  .andExpect(status().isOk())
                  .andExpect(content().string("foo 3"));
  
          FlowRuleManager.loadRules(null);
      }
  
      @Test
      public void testTotalInterceptor() throws Exception {
          String url = "/hello";
          String totalTarget = "my_spring_mvc_total_url_request";
          for (int i = 0; i < 3; i++) {
              this.mvc.perform(get(url))
                      .andExpect(status().isOk())
                      .andExpect(content().string(HELLO_STR));
          }
          ClusterNode cn = ClusterBuilderSlot.getClusterNode(totalTarget);
          assertNotNull(cn);
          assertEquals(3, cn.passQps(), 0.01);
      }
  
      @Test
      public void testRuntimeException() throws Exception {
          String url = "/runtimeException";
          configureExceptionRulesFor(url, 3, null);
          int repeat = 3;
          for (int i = 0; i < repeat; i++) {
              this.mvc.perform(get(url))
                      .andExpect(status().isOk())
                      .andExpect(content().string(ResultWrapper.error().toJsonString()));
              ClusterNode cn = ClusterBuilderSlot.getClusterNode(url);
              assertNotNull(cn);
              assertEquals(i + 1, cn.passQps(), 0.01);
          }
  
          // This will be blocked and response json.
          this.mvc.perform(get(url))
                  .andExpect(status().isOk())
                  .andExpect(content().string(ResultWrapper.blocked().toJsonString()));
          ClusterNode cn = ClusterBuilderSlot.getClusterNode(url);
          assertNotNull(cn);
          assertEquals(repeat, cn.passQps(), 0.01);
          assertEquals(1, cn.blockRequest(), 1);
      }
  
      private void configureRulesFor(String resource, int count, String limitApp) {
          FlowRule rule = new FlowRule()
                  .setCount(count)
                  .setGrade(RuleConstant.FLOW_GRADE_QPS);
          rule.setResource(resource);
          if (StringUtil.isNotBlank(limitApp)) {
              rule.setLimitApp(limitApp);
          }
          FlowRuleManager.loadRules(Collections.singletonList(rule));
      }
  
      private void configureExceptionRulesFor(String resource, int count, String limitApp) {
          FlowRule rule = new FlowRule()
                  .setCount(count)
                  .setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO);
          rule.setResource(resource);
          if (StringUtil.isNotBlank(limitApp)) {
              rule.setLimitApp(limitApp);
          }
          FlowRuleManager.loadRules(Collections.singletonList(rule));
      }
  
      @After
      public void cleanUp() {
          FlowRuleManager.loadRules(null);
          ClusterBuilderSlot.resetClusterNodes();
      }
  }
  