package mcp.server; import jakarta.servlet.ReadListener; import jakarta.servlet.ServletInputStream; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import mcp.registry.ToolRegistry; import mcp.tools.McpTool; import mcp.util.Err; import mcp.util.Ok; import mcp.util.Result; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.*; public class ToolRegistrationServletTest { private ToolRegistry toolRegistry; private HttpServletRequest request; private HttpServletResponse response; private StringWriter responseWriter; @BeforeEach void setUp() throws IOException { toolRegistry = new ToolRegistry(java.util.Set.of()); request = mock(HttpServletRequest.class); response = mock(HttpServletResponse.class); responseWriter = new StringWriter(); when(response.getWriter()).thenReturn(new PrintWriter(responseWriter)); } @Test void testDoPostSuccess() throws Exception { McpTool mockTool = mock(McpTool.class); when(mockTool.name()).thenReturn("EchoTool"); // Use anonymous subclass instead of spy to avoid instrumentation issues on Java 25 ToolRegistrationServlet servlet = new ToolRegistrationServlet(toolRegistry) { @Override public Result registerTool(String jarPath, String className) { toolRegistry.register(mockTool); return new Ok(mockTool); } }; String json = "{\"jarPath\": \"/tmp/test.jar\", \"className\": \"com.example.EchoTool\"}"; mockRequestInput(json); servlet.doPost(request, response); verify(response).setStatus(HttpServletResponse.SC_OK); assertTrue(responseWriter.toString().contains("Tool registered successfully: EchoTool")); assertTrue(toolRegistry.get("EchoTool") != null); } @Test void testDoPostMissingParams() throws Exception { ToolRegistrationServlet servlet = new ToolRegistrationServlet(toolRegistry); String json = "{\"jarPath\": \"/tmp/test.jar\"}"; // missing className mockRequestInput(json); servlet.doPost(request, response); verify(response).setStatus(HttpServletResponse.SC_BAD_REQUEST); assertTrue(responseWriter.toString().contains("Missing jarPath or className")); } @Test void testDoPostRegistrationError() throws Exception { ToolRegistrationServlet servlet = new ToolRegistrationServlet(toolRegistry) { @Override public Result registerTool(String jarPath, String className) { return new Err(new Exception("Loading failed")); } }; String json = "{\"jarPath\": \"/tmp/test.jar\", \"className\": \"com.example.FailTool\"}"; mockRequestInput(json); servlet.doPost(request, response); verify(response).setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); assertTrue(responseWriter.toString().contains("Error registering tool: Loading failed")); } private void mockRequestInput(String json) throws IOException { ByteArrayInputStream bais = new ByteArrayInputStream(json.getBytes()); ServletInputStream sis = new ServletInputStream() { @Override public int read() throws IOException { return bais.read(); } @Override public boolean isFinished() { return bais.available() == 0; } @Override public boolean isReady() { return true; } @Override public void setReadListener(ReadListener readListener) { } }; when(request.getInputStream()).thenReturn(sis); } }