From 4e0ee420535b7b940f3ddc5f327f595d9f2ae81d Mon Sep 17 00:00:00 2001 From: kazusa <409053122@qq.com> Date: Wed, 23 Jul 2025 16:46:31 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B5=8B=E8=AF=95RestController=E6=8B=A6?= =?UTF-8?q?=E6=88=AA,=E5=90=8E=E7=BB=AD=E6=94=B9=E4=B8=BA=E5=A7=94?= =?UTF-8?q?=E6=89=98=E6=96=B9=E5=BC=8F=E8=BF=9B=E8=A1=8C=E6=96=B9=E6=B3=95?= =?UTF-8?q?=E5=A2=9E=E5=BC=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 15 -- .../spring-plugins/mvc-plugin/pom.xml | 11 + .../plugins/web/RestControllerPlugin.java | 22 +- ...essmonster.common.plugin.PluginDefinition} | 0 press-monster-plugins/spring-plugins/pom.xml | 31 ++- press-monster-test/demo/README.md | 4 +- press-monster-test/demo/pom.xml | 26 +-- .../pressmonster/demo/SampleController.java | 10 +- .../pressmonster/demo/agent/SimpleAgent.java | 4 +- press-monster-test/plugins-tests/pom.xml | 2 + .../github/kazusa/pressmonster/mvc/Main.java | 14 ++ .../mvc/web/RestControllerPluginTest.java | 199 ++++++++++++++++++ .../mvc/web/SampleController.java | 80 +++++++ .../plugins/web/RestControllerPluginTest.java | 195 ----------------- 14 files changed, 364 insertions(+), 249 deletions(-) rename press-monster-plugins/spring-plugins/mvc-plugin/src/main/resources/META-INF/services/{io.github.kazusa.pressmonster.common.plugin.TransformerPlugin => io.github.kazusa.pressmonster.common.plugin.PluginDefinition} (100%) create mode 100644 press-monster-test/plugins-tests/src/test/java/io/github/kazusa/pressmonster/mvc/Main.java create mode 100644 press-monster-test/plugins-tests/src/test/java/io/github/kazusa/pressmonster/mvc/web/RestControllerPluginTest.java create mode 100644 press-monster-test/plugins-tests/src/test/java/io/github/kazusa/pressmonster/mvc/web/SampleController.java delete mode 100644 press-monster-test/plugins-tests/src/test/java/io/github/kazusa/pressmonster/plugins/web/RestControllerPluginTest.java diff --git a/pom.xml b/pom.xml index 309a345..d8331d5 100644 --- a/pom.xml +++ b/pom.xml @@ -25,7 +25,6 @@ 5.10.0 1.5.13 2.0.16 - 5.3.37 2.17.3 @@ -71,20 +70,6 @@ ${logback.version} - - - org.springframework - spring-web - ${spring.version} - provided - - - org.springframework - spring-webmvc - ${spring.version} - provided - - com.fasterxml.jackson.core diff --git a/press-monster-plugins/spring-plugins/mvc-plugin/pom.xml b/press-monster-plugins/spring-plugins/mvc-plugin/pom.xml index 5337088..2b335d2 100644 --- a/press-monster-plugins/spring-plugins/mvc-plugin/pom.xml +++ b/press-monster-plugins/spring-plugins/mvc-plugin/pom.xml @@ -18,5 +18,16 @@ Spring MVC Plugin Spring MVC interceptor plugin for Press Monster + + + org.springframework + spring-web + + + org.springframework + spring-webmvc + + + \ No newline at end of file diff --git a/press-monster-plugins/spring-plugins/mvc-plugin/src/main/java/io/github/kazusa/pressmonster/plugins/web/RestControllerPlugin.java b/press-monster-plugins/spring-plugins/mvc-plugin/src/main/java/io/github/kazusa/pressmonster/plugins/web/RestControllerPlugin.java index d6519d0..02e6b5e 100644 --- a/press-monster-plugins/spring-plugins/mvc-plugin/src/main/java/io/github/kazusa/pressmonster/plugins/web/RestControllerPlugin.java +++ b/press-monster-plugins/spring-plugins/mvc-plugin/src/main/java/io/github/kazusa/pressmonster/plugins/web/RestControllerPlugin.java @@ -1,17 +1,14 @@ package io.github.kazusa.pressmonster.plugins.web; -import java.lang.annotation.Annotation; -import java.lang.reflect.Method; - -import net.bytebuddy.description.annotation.AnnotationDescription; -import org.slf4j.Logger; - import io.github.kazusa.pressmonster.common.logging.LoggerFactory; import io.github.kazusa.pressmonster.common.plugin.PluginInfo; import io.github.kazusa.pressmonster.common.plugin.TransformerPlugin; import net.bytebuddy.agent.builder.AgentBuilder; import net.bytebuddy.asm.Advice; import net.bytebuddy.matcher.ElementMatchers; +import org.slf4j.Logger; + +import java.lang.reflect.Method; /** * RestController拦截插件 @@ -69,10 +66,11 @@ public class RestControllerPlugin implements TransformerPlugin { logger.info("配置RestController字节码转换器"); return agentBuilder - // 临时方案:先用包名匹配Controller类,运行时验证注解 .type(ElementMatchers.nameContains("Controller") - .and(ElementMatchers.not(ElementMatchers.isInterface())) - .and(ElementMatchers.not(ElementMatchers.isAbstract()))) + //ElementMatchers.isAnnotatedWith(RestController.class) + //.and(ElementMatchers.not(ElementMatchers.isInterface())) + //.and(ElementMatchers.not(ElementMatchers.isAbstract())) + ) .transform((builder, typeDescription, classLoader, @@ -120,7 +118,7 @@ public class RestControllerPlugin implements TransformerPlugin { .method(ElementMatchers.isPublic() .and(ElementMatchers.not(ElementMatchers.isStatic())) .and(ElementMatchers.not(ElementMatchers.isConstructor())) - .and(ElementMatchers.not(ElementMatchers.nameStartsWith("get").and(ElementMatchers.takesArguments(0)))) + //.and(ElementMatchers.not(ElementMatchers.nameStartsWith("get").and(ElementMatchers.takesArguments(0)))) .and(ElementMatchers.not(ElementMatchers.nameStartsWith("set").and(ElementMatchers.takesArguments(1)))) .and(ElementMatchers.not(ElementMatchers.nameStartsWith("is").and(ElementMatchers.takesArguments(0))))) .intercept(Advice.to(RestControllerAdvice.class)); @@ -148,7 +146,7 @@ public class RestControllerPlugin implements TransformerPlugin { private static final RestControllerInterceptor INTERCEPTOR = new RestControllerInterceptor(); - @Advice.OnMethodEnter + @Advice.OnMethodEnter(suppress = Throwable.class) public static long onEnter(@Advice.This Object target, @Advice.Origin Method method, @Advice.AllArguments Object[] args) { @@ -163,7 +161,7 @@ public class RestControllerPlugin implements TransformerPlugin { } } - @Advice.OnMethodExit(onThrowable = Throwable.class) + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static void onExit(@Advice.This Object target, @Advice.Origin Method method, @Advice.AllArguments Object[] args, diff --git a/press-monster-plugins/spring-plugins/mvc-plugin/src/main/resources/META-INF/services/io.github.kazusa.pressmonster.common.plugin.TransformerPlugin b/press-monster-plugins/spring-plugins/mvc-plugin/src/main/resources/META-INF/services/io.github.kazusa.pressmonster.common.plugin.PluginDefinition similarity index 100% rename from press-monster-plugins/spring-plugins/mvc-plugin/src/main/resources/META-INF/services/io.github.kazusa.pressmonster.common.plugin.TransformerPlugin rename to press-monster-plugins/spring-plugins/mvc-plugin/src/main/resources/META-INF/services/io.github.kazusa.pressmonster.common.plugin.PluginDefinition diff --git a/press-monster-plugins/spring-plugins/pom.xml b/press-monster-plugins/spring-plugins/pom.xml index 39c08bd..a6e9ce7 100644 --- a/press-monster-plugins/spring-plugins/pom.xml +++ b/press-monster-plugins/spring-plugins/pom.xml @@ -22,22 +22,33 @@ mvc-plugin - - - - org.springframework - spring-web - - - org.springframework - spring-webmvc - + + 5.3.37 + + com.fasterxml.jackson.core jackson-databind + + + + + org.springframework + spring-web + ${spring.version} + provided + + + org.springframework + spring-webmvc + ${spring.version} + provided + + + \ No newline at end of file diff --git a/press-monster-test/demo/README.md b/press-monster-test/demo/README.md index a9ca31c..fcef629 100644 --- a/press-monster-test/demo/README.md +++ b/press-monster-test/demo/README.md @@ -36,7 +36,7 @@ src/main/java/io/github/kazusa/pressmonster/demo/ ### 1. 精确类名匹配 ```java -named("io.github.kazusa.pressmonster.demo.SampleController") +named("io.github.kazusa.pressmonster.plugins.web.SampleController") ``` ### 2. Spring注解匹配 @@ -112,7 +112,7 @@ java -javaagent:target/demo-1.0.0-SNAPSHOT.jar -jar target/demo-1.0.0-SNAPSHOT.j Simple ByteBuddy Agent 启动中... Agent参数: 无 ======================================== -[AGENT] 成功增强类: io.github.kazusa.pressmonster.demo.SampleController (匹配方式: SPRING_CONTROLLER) +[AGENT] 成功增强类: io.github.kazusa.pressmonster.plugins.web.SampleController (匹配方式: SPRING_CONTROLLER) [AGENT] 成功增强类: io.github.kazusa.pressmonster.demo.service.UserService (匹配方式: SERVICE_PACKAGE) [AGENT] 成功增强类: io.github.kazusa.pressmonster.demo.service.OrderService (匹配方式: SERVICE_PACKAGE) [AGENT] ByteBuddy Agent 安装完成! diff --git a/press-monster-test/demo/pom.xml b/press-monster-test/demo/pom.xml index f09d622..6bd2640 100644 --- a/press-monster-test/demo/pom.xml +++ b/press-monster-test/demo/pom.xml @@ -36,22 +36,12 @@ net.bytebuddy byte-buddy - 1.14.9 + net.bytebuddy byte-buddy-agent - 1.14.9 - - - - - org.springframework - spring-web - - - org.springframework - spring-webmvc + @@ -69,6 +59,18 @@ ch.qos.logback logback-classic + + + org.springframework + spring-web + 5.3.37 + + + org.springframework + spring-webmvc + 5.3.37 + diff --git a/press-monster-test/demo/src/main/java/io/github/kazusa/pressmonster/demo/SampleController.java b/press-monster-test/demo/src/main/java/io/github/kazusa/pressmonster/demo/SampleController.java index 693b2d7..051a7b3 100644 --- a/press-monster-test/demo/src/main/java/io/github/kazusa/pressmonster/demo/SampleController.java +++ b/press-monster-test/demo/src/main/java/io/github/kazusa/pressmonster/demo/SampleController.java @@ -1,6 +1,14 @@ package io.github.kazusa.pressmonster.demo; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; import java.util.HashMap; import java.util.Map; diff --git a/press-monster-test/demo/src/main/java/io/github/kazusa/pressmonster/demo/agent/SimpleAgent.java b/press-monster-test/demo/src/main/java/io/github/kazusa/pressmonster/demo/agent/SimpleAgent.java index d02584d..da7f366 100644 --- a/press-monster-test/demo/src/main/java/io/github/kazusa/pressmonster/demo/agent/SimpleAgent.java +++ b/press-monster-test/demo/src/main/java/io/github/kazusa/pressmonster/demo/agent/SimpleAgent.java @@ -93,7 +93,7 @@ public class SimpleAgent { private static net.bytebuddy.matcher.ElementMatcher.Junction buildTypeMatchers() { return // 方式1:精确类名匹配 - named("io.github.kazusa.pressmonster.demo.SampleController") + named("io.github.kazusa.pressmonster.plugins.web.SampleController") // 方式2:Spring @RestController注解匹配(使用字符串名称避免类加载问题) .or(isAnnotatedWith(named("org.springframework.web.bind.annotation.RestController"))) @@ -132,7 +132,7 @@ public class SimpleAgent { * 用于在日志中显示是通过什么方式匹配到的目标类 */ private static String determineMatchType(String className) { - if ("io.github.kazusa.pressmonster.demo.SampleController".equals(className)) { + if ("io.github.kazusa.pressmonster.plugins.web.SampleController".equals(className)) { return "EXACT_NAME"; } else if (className.contains("Controller")) { return "SPRING_CONTROLLER"; diff --git a/press-monster-test/plugins-tests/pom.xml b/press-monster-test/plugins-tests/pom.xml index 158cfc3..3cb5f22 100644 --- a/press-monster-test/plugins-tests/pom.xml +++ b/press-monster-test/plugins-tests/pom.xml @@ -36,10 +36,12 @@ org.springframework spring-web + 5.3.37 org.springframework spring-webmvc + 5.3.37 diff --git a/press-monster-test/plugins-tests/src/test/java/io/github/kazusa/pressmonster/mvc/Main.java b/press-monster-test/plugins-tests/src/test/java/io/github/kazusa/pressmonster/mvc/Main.java new file mode 100644 index 0000000..4fbec22 --- /dev/null +++ b/press-monster-test/plugins-tests/src/test/java/io/github/kazusa/pressmonster/mvc/Main.java @@ -0,0 +1,14 @@ +package io.github.kazusa.pressmonster.mvc; + +import io.github.kazusa.pressmonster.mvc.web.SampleController; + +/** + * @author Wen + * @date 2025/7/23 15:56 + */ +public class Main { + public static void main(String[] args) { + SampleController controller = new SampleController(); + controller.getUsers(); + } +} diff --git a/press-monster-test/plugins-tests/src/test/java/io/github/kazusa/pressmonster/mvc/web/RestControllerPluginTest.java b/press-monster-test/plugins-tests/src/test/java/io/github/kazusa/pressmonster/mvc/web/RestControllerPluginTest.java new file mode 100644 index 0000000..86d43c2 --- /dev/null +++ b/press-monster-test/plugins-tests/src/test/java/io/github/kazusa/pressmonster/mvc/web/RestControllerPluginTest.java @@ -0,0 +1,199 @@ +//package io.github.kazusa.pressmonster.mvc.web; +// +//import io.github.kazusa.pressmonster.common.plugin.PluginRegistry; +//import io.github.kazusa.pressmonster.plugins.web.ApiEndpoint; +//import io.github.kazusa.pressmonster.plugins.web.EndpointRegistry; +//import io.github.kazusa.pressmonster.plugins.web.RestControllerInterceptor; +//import io.github.kazusa.pressmonster.plugins.web.RestControllerPlugin; +//import org.junit.jupiter.api.BeforeEach; +//import org.junit.jupiter.api.Test; +//import org.springframework.web.bind.annotation.GetMapping; +//import org.springframework.web.bind.annotation.PostMapping; +//import org.springframework.web.bind.annotation.RestController; +// +//import java.lang.reflect.Method; +// +//import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +//import static org.junit.jupiter.api.Assertions.assertEquals; +//import static org.junit.jupiter.api.Assertions.assertFalse; +//import static org.junit.jupiter.api.Assertions.assertNotNull; +//import static org.junit.jupiter.api.Assertions.assertTrue; +// +///** +// * RestController插件测试 +// */ +//public class RestControllerPluginTest { +// +// private RestControllerPlugin plugin; +// private RestControllerInterceptor interceptor; +// private EndpointRegistry endpointRegistry; +// +// @BeforeEach +// public void setUp() { +// plugin = new RestControllerPlugin(); +// interceptor = new RestControllerInterceptor(); +// endpointRegistry = EndpointRegistry.getInstance(); +// endpointRegistry.clear(); // 清空之前的数据 +// } +// +// @Test +// public void testPluginInfo() { +// assertEquals("RestControllerPlugin", plugin.getName()); +// assertEquals("1.0.0", plugin.getVersion()); +// assertTrue(plugin.getDescription().contains("RestController")); +// assertTrue(plugin.isEnabled()); +// assertEquals(10, plugin.getPriority()); +// } +// +// @Test +// public void testPluginLifecycle() { +// // 测试初始化 +// assertDoesNotThrow(() -> plugin.initialize()); +// +// // 测试销毁 +// assertDoesNotThrow(() -> plugin.destroy()); +// assertFalse(plugin.isEnabled()); +// } +// +// @Test +// public void testSupports() { +// assertTrue(plugin.supports("com.example.UserController")); +// assertTrue(plugin.supports("RestController")); +// assertTrue(plugin.supports("org.springframework.SomeClass")); +// assertFalse(plugin.supports("com.example.Service")); +// } +// +// @Test +// public void testEndpointExtraction() throws Exception { +// // 创建测试Controller实例 +// TestController controller = new TestController(); +// +// // 获取方法 +// Method getUsersMethod = TestController.class.getMethod("getUsers"); +// Method createUserMethod = TestController.class.getMethod("createUser"); +// +// // 测试方法拦截 +// assertTrue(interceptor.beforeMethod(controller, getUsersMethod, new Object[]{})); +// assertEquals("result", interceptor.afterMethod(controller, getUsersMethod, new Object[]{}, "result")); +// +// // 验证端点注册 +// assertEquals(1, endpointRegistry.getAllControllerClasses().size()); +// assertTrue(endpointRegistry.getAllControllerClasses().contains(TestController.class.getName())); +// } +// +// @Test +// public void testEndpointRegistry() { +// // 创建测试端点 +// ApiEndpoint endpoint1 = new ApiEndpoint( +// "com.example.UserController", +// "getUsers", +// "GET", +// "/api/users" +// ); +// +// ApiEndpoint endpoint2 = new ApiEndpoint( +// "com.example.UserController", +// "createUser", +// "POST", +// "/api/users" +// ); +// +// // 注册端点 +// endpointRegistry.registerEndpoint(endpoint1); +// endpointRegistry.registerEndpoint(endpoint2); +// +// // 验证注册结果 +// assertEquals(2, endpointRegistry.getAllEndpoints().size()); +// assertEquals(1, endpointRegistry.getAllControllerClasses().size()); +// +// // 验证统计信息 +// EndpointRegistry.EndpointStats stats = endpointRegistry.getStats(); +// assertEquals(1, stats.getControllerCount()); +// assertEquals(2, stats.getEndpointCount()); +// assertEquals(1, stats.getMethodCounts().get("GET").intValue()); +// assertEquals(1, stats.getMethodCounts().get("POST").intValue()); +// } +// +// @Test +// public void testEndpointSearch() { +// // 创建测试端点 +// ApiEndpoint endpoint1 = new ApiEndpoint( +// "com.example.UserController", +// "getUsers", +// "GET", +// "/api/users" +// ); +// +// ApiEndpoint endpoint2 = new ApiEndpoint( +// "com.example.OrderController", +// "getOrders", +// "GET", +// "/api/orders" +// ); +// +// endpointRegistry.registerEndpoint(endpoint1); +// endpointRegistry.registerEndpoint(endpoint2); +// +// // 测试搜索 +// assertEquals(2, endpointRegistry.searchEndpoints("api").size()); +// assertEquals(1, endpointRegistry.searchEndpoints("user").size()); +// assertEquals(1, endpointRegistry.searchEndpoints("order").size()); +// assertEquals(0, endpointRegistry.searchEndpoints("nonexistent").size()); +// } +// +// @Test +// public void testJsonExport() { +// // 创建测试端点 +// ApiEndpoint endpoint = new ApiEndpoint( +// "com.example.TestController", +// "test", +// "GET", +// "/test" +// ); +// +// endpointRegistry.registerEndpoint(endpoint); +// +// // 测试JSON导出 +// String json = endpointRegistry.exportToJson(); +// assertNotNull(json); +// assertTrue(json.contains("TestController")); +// assertTrue(json.contains("/test")); +// assertTrue(json.contains("GET")); +// } +// +// @Test +// public void testPluginRegistry() { +// PluginRegistry registry = PluginRegistry.getInstance(); +// +// // 注册插件 +// registry.registerPlugin(plugin); +// +// // 验证注册 +// assertTrue(registry.isRegistered("RestControllerPlugin")); +// assertNotNull(registry.getPlugin("RestControllerPlugin")); +// +// // 测试类型过滤 +// assertEquals(1, registry.getPluginsByType(RestControllerPlugin.class).size()); +// +// // 清理 +// registry.unregisterPlugin("RestControllerPlugin"); +// assertFalse(registry.isRegistered("RestControllerPlugin")); +// } +// +// /** +// * 测试用的Controller类 +// */ +// @RestController +// public static class TestController { +// +// @GetMapping("/users") +// public String getUsers() { +// return "users"; +// } +// +// @PostMapping("/users") +// public String createUser() { +// return "created"; +// } +// } +//} \ No newline at end of file diff --git a/press-monster-test/plugins-tests/src/test/java/io/github/kazusa/pressmonster/mvc/web/SampleController.java b/press-monster-test/plugins-tests/src/test/java/io/github/kazusa/pressmonster/mvc/web/SampleController.java new file mode 100644 index 0000000..95ba151 --- /dev/null +++ b/press-monster-test/plugins-tests/src/test/java/io/github/kazusa/pressmonster/mvc/web/SampleController.java @@ -0,0 +1,80 @@ +package io.github.kazusa.pressmonster.mvc.web; + +import org.springframework.web.bind.annotation.*; + +import java.util.HashMap; +import java.util.Map; + +/** + * 示例Controller + * 用于演示RestController插件的拦截功能 + */ +@RestController +@RequestMapping("/api") +public class SampleController { + + @GetMapping("/users") + public Map getUsers() { + Map result = new HashMap<>(); + result.put("users", new String[]{"user1", "user2", "user3"}); + result.put("total", 3); + return result; + } + + //@GetMapping("/users/{id}") + //public Map getUserById(@PathVariable String id) { + // Map result = new HashMap<>(); + // result.put("id", id); + // result.put("name", "User " + id); + // result.put("email", "user" + id + "@example.com"); + // return result; + //} + // + //@PostMapping("/users") + //public Map createUser(@RequestBody Map user) { + // Map result = new HashMap<>(); + // result.put("id", "new-user-id"); + // result.put("name", user.get("name")); + // result.put("email", user.get("email")); + // result.put("created", System.currentTimeMillis()); + // return result; + //} + // + //@PutMapping("/users/{id}") + //public Map updateUser(@PathVariable String id, @RequestBody Map user) { + // Map result = new HashMap<>(); + // result.put("id", id); + // result.put("name", user.get("name")); + // result.put("email", user.get("email")); + // result.put("updated", System.currentTimeMillis()); + // return result; + //} + // + //@DeleteMapping("/users/{id}") + //public Map deleteUser(@PathVariable String id) { + // Map result = new HashMap<>(); + // result.put("deleted", true); + // result.put("id", id); + // result.put("timestamp", System.currentTimeMillis()); + // return result; + //} + // + //@GetMapping("/orders") + //public Map getOrders(@RequestParam(required = false) String status) { + // Map result = new HashMap<>(); + // result.put("orders", new String[]{"order1", "order2"}); + // result.put("status", status != null ? status : "all"); + // result.put("total", 2); + // return result; + //} + // + //@PostMapping("/orders/{userId}") + //public Map createOrder(@PathVariable String userId, @RequestBody Map order) { + // Map result = new HashMap<>(); + // result.put("orderId", "new-order-id"); + // result.put("userId", userId); + // result.put("amount", order.get("amount")); + // result.put("created", System.currentTimeMillis()); + // return result; + //} +} \ No newline at end of file diff --git a/press-monster-test/plugins-tests/src/test/java/io/github/kazusa/pressmonster/plugins/web/RestControllerPluginTest.java b/press-monster-test/plugins-tests/src/test/java/io/github/kazusa/pressmonster/plugins/web/RestControllerPluginTest.java deleted file mode 100644 index 7acb11c..0000000 --- a/press-monster-test/plugins-tests/src/test/java/io/github/kazusa/pressmonster/plugins/web/RestControllerPluginTest.java +++ /dev/null @@ -1,195 +0,0 @@ -package io.github.kazusa.pressmonster.plugins.web; - -import io.github.kazusa.pressmonster.common.plugin.PluginRegistry; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RestController; - -import java.lang.reflect.Method; - -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -/** - * RestController插件测试 - */ -public class RestControllerPluginTest { - - private RestControllerPlugin plugin; - private RestControllerInterceptor interceptor; - private EndpointRegistry endpointRegistry; - - @BeforeEach - public void setUp() { - plugin = new RestControllerPlugin(); - interceptor = new RestControllerInterceptor(); - endpointRegistry = EndpointRegistry.getInstance(); - endpointRegistry.clear(); // 清空之前的数据 - } - - @Test - public void testPluginInfo() { - assertEquals("RestControllerPlugin", plugin.getName()); - assertEquals("1.0.0", plugin.getVersion()); - assertTrue(plugin.getDescription().contains("RestController")); - assertTrue(plugin.isEnabled()); - assertEquals(10, plugin.getPriority()); - } - - @Test - public void testPluginLifecycle() { - // 测试初始化 - assertDoesNotThrow(() -> plugin.initialize()); - - // 测试销毁 - assertDoesNotThrow(() -> plugin.destroy()); - assertFalse(plugin.isEnabled()); - } - - @Test - public void testSupports() { - assertTrue(plugin.supports("com.example.UserController")); - assertTrue(plugin.supports("RestController")); - assertTrue(plugin.supports("org.springframework.SomeClass")); - assertFalse(plugin.supports("com.example.Service")); - } - - @Test - public void testEndpointExtraction() throws Exception { - // 创建测试Controller实例 - TestController controller = new TestController(); - - // 获取方法 - Method getUsersMethod = TestController.class.getMethod("getUsers"); - Method createUserMethod = TestController.class.getMethod("createUser"); - - // 测试方法拦截 - assertTrue(interceptor.beforeMethod(controller, getUsersMethod, new Object[]{})); - assertEquals("result", interceptor.afterMethod(controller, getUsersMethod, new Object[]{}, "result")); - - // 验证端点注册 - assertEquals(1, endpointRegistry.getAllControllerClasses().size()); - assertTrue(endpointRegistry.getAllControllerClasses().contains(TestController.class.getName())); - } - - @Test - public void testEndpointRegistry() { - // 创建测试端点 - ApiEndpoint endpoint1 = new ApiEndpoint( - "com.example.UserController", - "getUsers", - "GET", - "/api/users" - ); - - ApiEndpoint endpoint2 = new ApiEndpoint( - "com.example.UserController", - "createUser", - "POST", - "/api/users" - ); - - // 注册端点 - endpointRegistry.registerEndpoint(endpoint1); - endpointRegistry.registerEndpoint(endpoint2); - - // 验证注册结果 - assertEquals(2, endpointRegistry.getAllEndpoints().size()); - assertEquals(1, endpointRegistry.getAllControllerClasses().size()); - - // 验证统计信息 - EndpointRegistry.EndpointStats stats = endpointRegistry.getStats(); - assertEquals(1, stats.getControllerCount()); - assertEquals(2, stats.getEndpointCount()); - assertEquals(1, stats.getMethodCounts().get("GET").intValue()); - assertEquals(1, stats.getMethodCounts().get("POST").intValue()); - } - - @Test - public void testEndpointSearch() { - // 创建测试端点 - ApiEndpoint endpoint1 = new ApiEndpoint( - "com.example.UserController", - "getUsers", - "GET", - "/api/users" - ); - - ApiEndpoint endpoint2 = new ApiEndpoint( - "com.example.OrderController", - "getOrders", - "GET", - "/api/orders" - ); - - endpointRegistry.registerEndpoint(endpoint1); - endpointRegistry.registerEndpoint(endpoint2); - - // 测试搜索 - assertEquals(2, endpointRegistry.searchEndpoints("api").size()); - assertEquals(1, endpointRegistry.searchEndpoints("user").size()); - assertEquals(1, endpointRegistry.searchEndpoints("order").size()); - assertEquals(0, endpointRegistry.searchEndpoints("nonexistent").size()); - } - - @Test - public void testJsonExport() { - // 创建测试端点 - ApiEndpoint endpoint = new ApiEndpoint( - "com.example.TestController", - "test", - "GET", - "/test" - ); - - endpointRegistry.registerEndpoint(endpoint); - - // 测试JSON导出 - String json = endpointRegistry.exportToJson(); - assertNotNull(json); - assertTrue(json.contains("TestController")); - assertTrue(json.contains("/test")); - assertTrue(json.contains("GET")); - } - - @Test - public void testPluginRegistry() { - PluginRegistry registry = PluginRegistry.getInstance(); - - // 注册插件 - registry.registerPlugin(plugin); - - // 验证注册 - assertTrue(registry.isRegistered("RestControllerPlugin")); - assertNotNull(registry.getPlugin("RestControllerPlugin")); - - // 测试类型过滤 - assertEquals(1, registry.getPluginsByType(RestControllerPlugin.class).size()); - - // 清理 - registry.unregisterPlugin("RestControllerPlugin"); - assertFalse(registry.isRegistered("RestControllerPlugin")); - } - - /** - * 测试用的Controller类 - */ - @RestController - public static class TestController { - - @GetMapping("/users") - public String getUsers() { - return "users"; - } - - @PostMapping("/users") - public String createUser() { - return "created"; - } - } -} \ No newline at end of file