feat(test): Use dynamic port allocation in integration tests (#133)
- Add TestUtil class with findAvailablePort() method to the mcp-test module - Add findAvailablePort() method to TomcatTestUtil classes - Replace hardcoded port numbers with dynamic port allocation Signed-off-by: Christian Tzolov <christian.tzolov@broadcom.com>
This commit is contained in:
parent
8068854227
commit
263e3741b2
12 changed files with 87 additions and 29 deletions
|
|
@ -18,6 +18,7 @@ import io.modelcontextprotocol.client.transport.HttpClientSseClientTransport;
|
|||
import io.modelcontextprotocol.client.transport.WebFluxSseClientTransport;
|
||||
import io.modelcontextprotocol.server.McpServer;
|
||||
import io.modelcontextprotocol.server.McpServerFeatures;
|
||||
import io.modelcontextprotocol.server.TestUtil;
|
||||
import io.modelcontextprotocol.server.transport.WebFluxSseServerTransportProvider;
|
||||
import io.modelcontextprotocol.spec.McpError;
|
||||
import io.modelcontextprotocol.spec.McpSchema;
|
||||
|
|
@ -50,9 +51,9 @@ import static org.assertj.core.api.Assertions.assertThat;
|
|||
import static org.awaitility.Awaitility.await;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
public class WebFluxSseIntegrationTests {
|
||||
class WebFluxSseIntegrationTests {
|
||||
|
||||
private static final int PORT = 8182;
|
||||
private static final int PORT = TestUtil.findAvailablePort();
|
||||
|
||||
private static final String CUSTOM_SSE_ENDPOINT = "/somePath/sse";
|
||||
|
||||
|
|
@ -133,7 +134,7 @@ public class WebFluxSseIntegrationTests {
|
|||
|
||||
@ParameterizedTest(name = "{0} : {displayName} ")
|
||||
@ValueSource(strings = { "httpclient", "webflux" })
|
||||
void testCreateMessageSuccess(String clientType) throws InterruptedException {
|
||||
void testCreateMessageSuccess(String clientType) {
|
||||
|
||||
var clientBuilder = clientBuilders.get(clientType);
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ import org.springframework.web.reactive.function.server.RouterFunctions;
|
|||
@Timeout(15) // Giving extra time beyond the client timeout
|
||||
class WebFluxSseMcpAsyncServerTests extends AbstractMcpAsyncServerTests {
|
||||
|
||||
private static final int PORT = 8181;
|
||||
private static final int PORT = TestUtil.findAvailablePort();
|
||||
|
||||
private static final String MESSAGE_ENDPOINT = "/mcp/message";
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ import org.springframework.web.reactive.function.server.RouterFunctions;
|
|||
@Timeout(15) // Giving extra time beyond the client timeout
|
||||
class WebFluxSseMcpSyncServerTests extends AbstractMcpSyncServerTests {
|
||||
|
||||
private static final int PORT = 8182;
|
||||
private static final int PORT = TestUtil.findAvailablePort();
|
||||
|
||||
private static final String MESSAGE_ENDPOINT = "/mcp/message";
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,10 @@
|
|||
*/
|
||||
package io.modelcontextprotocol.server;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.ServerSocket;
|
||||
|
||||
import org.apache.catalina.Context;
|
||||
import org.apache.catalina.startup.Tomcat;
|
||||
|
||||
|
|
@ -14,10 +18,14 @@ import org.springframework.web.servlet.DispatcherServlet;
|
|||
*/
|
||||
public class TomcatTestUtil {
|
||||
|
||||
TomcatTestUtil() {
|
||||
// Prevent instantiation
|
||||
}
|
||||
|
||||
public record TomcatServer(Tomcat tomcat, AnnotationConfigWebApplicationContext appContext) {
|
||||
}
|
||||
|
||||
public TomcatServer createTomcatServer(String contextPath, int port, Class<?> componentClass) {
|
||||
public static TomcatServer createTomcatServer(String contextPath, int port, Class<?> componentClass) {
|
||||
|
||||
// Set up Tomcat first
|
||||
var tomcat = new Tomcat();
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ class WebMvcSseAsyncServerTransportTests extends AbstractMcpAsyncServerTests {
|
|||
|
||||
private static final String MESSAGE_ENDPOINT = "/mcp/message";
|
||||
|
||||
private static final int PORT = 8181;
|
||||
private static final int PORT = TestUtil.findAvailablePort();
|
||||
|
||||
private Tomcat tomcat;
|
||||
|
||||
|
|
@ -73,7 +73,6 @@ class WebMvcSseAsyncServerTransportTests extends AbstractMcpAsyncServerTests {
|
|||
|
||||
// Create DispatcherServlet with our Spring context
|
||||
DispatcherServlet dispatcherServlet = new DispatcherServlet(appContext);
|
||||
// dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);
|
||||
|
||||
// Add servlet to Tomcat and get the wrapper
|
||||
var wrapper = Tomcat.addServlet(context, "dispatcherServlet", dispatcherServlet);
|
||||
|
|
|
|||
|
|
@ -22,11 +22,11 @@ import org.springframework.web.servlet.function.ServerResponse;
|
|||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class WebMvcSseCustomContextPathTests {
|
||||
class WebMvcSseCustomContextPathTests {
|
||||
|
||||
private static final String CUSTOM_CONTEXT_PATH = "/app/1";
|
||||
|
||||
private static final int PORT = 8183;
|
||||
private static final int PORT = TestUtil.findAvailablePort();
|
||||
|
||||
private static final String MESSAGE_ENDPOINT = "/mcp/message";
|
||||
|
||||
|
|
@ -39,11 +39,11 @@ public class WebMvcSseCustomContextPathTests {
|
|||
@BeforeEach
|
||||
public void before() {
|
||||
|
||||
tomcatServer = new TomcatTestUtil().createTomcatServer(CUSTOM_CONTEXT_PATH, PORT, TestConfig.class);
|
||||
tomcatServer = TomcatTestUtil.createTomcatServer(CUSTOM_CONTEXT_PATH, PORT, TestConfig.class);
|
||||
|
||||
try {
|
||||
tomcatServer.tomcat().start();
|
||||
assertThat(tomcatServer.tomcat().getServer().getState() == LifecycleState.STARTED);
|
||||
assertThat(tomcatServer.tomcat().getServer().getState()).isEqualTo(LifecycleState.STARTED);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException("Failed to start Tomcat", e);
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ import static org.mockito.Mockito.mock;
|
|||
|
||||
class WebMvcSseIntegrationTests {
|
||||
|
||||
private static final int PORT = 8183;
|
||||
private static final int PORT = TestUtil.findAvailablePort();
|
||||
|
||||
private static final String MESSAGE_ENDPOINT = "/mcp/message";
|
||||
|
||||
|
|
@ -75,7 +75,7 @@ class WebMvcSseIntegrationTests {
|
|||
@BeforeEach
|
||||
public void before() {
|
||||
|
||||
tomcatServer = new TomcatTestUtil().createTomcatServer("", PORT, TestConfig.class);
|
||||
tomcatServer = TomcatTestUtil.createTomcatServer("", PORT, TestConfig.class);
|
||||
|
||||
try {
|
||||
tomcatServer.tomcat().start();
|
||||
|
|
@ -151,7 +151,7 @@ class WebMvcSseIntegrationTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
void testCreateMessageSuccess() throws InterruptedException {
|
||||
void testCreateMessageSuccess() {
|
||||
|
||||
Function<CreateMessageRequest, CreateMessageResult> samplingHandler = request -> {
|
||||
assertThat(request.messages()).hasSize(1);
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ class WebMvcSseSyncServerTransportTests extends AbstractMcpSyncServerTests {
|
|||
|
||||
private static final String MESSAGE_ENDPOINT = "/mcp/message";
|
||||
|
||||
private static final int PORT = 8181;
|
||||
private static final int PORT = TestUtil.findAvailablePort();
|
||||
|
||||
private Tomcat tomcat;
|
||||
|
||||
|
|
@ -72,7 +72,6 @@ class WebMvcSseSyncServerTransportTests extends AbstractMcpSyncServerTests {
|
|||
|
||||
// Create DispatcherServlet with our Spring context
|
||||
DispatcherServlet dispatcherServlet = new DispatcherServlet(appContext);
|
||||
// dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);
|
||||
|
||||
// Add servlet to Tomcat and get the wrapper
|
||||
var wrapper = Tomcat.addServlet(context, "dispatcherServlet", dispatcherServlet);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright 2025 - 2025 the original author or authors.
|
||||
*/
|
||||
package io.modelcontextprotocol.server;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.ServerSocket;
|
||||
|
||||
public class TestUtil {
|
||||
|
||||
TestUtil() {
|
||||
// Prevent instantiation
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds an available port on the local machine.
|
||||
* @return an available port number
|
||||
* @throws IllegalStateException if no available port can be found
|
||||
*/
|
||||
public static int findAvailablePort() {
|
||||
try (final ServerSocket socket = new ServerSocket()) {
|
||||
socket.bind(new InetSocketAddress(0));
|
||||
return socket.getLocalPort();
|
||||
}
|
||||
catch (final IOException e) {
|
||||
throw new IllegalStateException("Cannot bind to an available port!", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@
|
|||
package io.modelcontextprotocol.server.transport;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import io.modelcontextprotocol.client.McpClient;
|
||||
import io.modelcontextprotocol.client.transport.HttpClientSseClientTransport;
|
||||
import io.modelcontextprotocol.server.McpServer;
|
||||
|
|
@ -17,9 +18,9 @@ import org.junit.jupiter.api.Test;
|
|||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class HttpServletSseServerCustomContextPathTests {
|
||||
class HttpServletSseServerCustomContextPathTests {
|
||||
|
||||
private static final int PORT = 8195;
|
||||
private static final int PORT = TomcatTestUtil.findAvailablePort();
|
||||
|
||||
private static final String CUSTOM_CONTEXT_PATH = "/api/v1";
|
||||
|
||||
|
|
@ -48,7 +49,7 @@ public class HttpServletSseServerCustomContextPathTests {
|
|||
|
||||
try {
|
||||
tomcat.start();
|
||||
assertThat(tomcat.getServer().getState() == LifecycleState.STARTED);
|
||||
assertThat(tomcat.getServer().getState()).isEqualTo(LifecycleState.STARTED);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException("Failed to start Tomcat", e);
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import java.util.function.Function;
|
|||
import java.util.stream.Collectors;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import io.modelcontextprotocol.client.McpClient;
|
||||
import io.modelcontextprotocol.client.transport.HttpClientSseClientTransport;
|
||||
import io.modelcontextprotocol.server.McpServer;
|
||||
|
|
@ -44,9 +45,9 @@ import static org.assertj.core.api.Assertions.assertThat;
|
|||
import static org.awaitility.Awaitility.await;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
public class HttpServletSseServerTransportProviderIntegrationTests {
|
||||
class HttpServletSseServerTransportProviderIntegrationTests {
|
||||
|
||||
private static final int PORT = 8189;
|
||||
private static final int PORT = TomcatTestUtil.findAvailablePort();
|
||||
|
||||
private static final String CUSTOM_SSE_ENDPOINT = "/somePath/sse";
|
||||
|
||||
|
|
@ -70,7 +71,7 @@ public class HttpServletSseServerTransportProviderIntegrationTests {
|
|||
tomcat = TomcatTestUtil.createTomcatServer("", PORT, mcpServerTransportProvider);
|
||||
try {
|
||||
tomcat.start();
|
||||
assertThat(tomcat.getServer().getState() == LifecycleState.STARTED);
|
||||
assertThat(tomcat.getServer().getState()).isEqualTo(LifecycleState.STARTED);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException("Failed to start Tomcat", e);
|
||||
|
|
@ -133,7 +134,7 @@ public class HttpServletSseServerTransportProviderIntegrationTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
void testCreateMessageSuccess() throws InterruptedException {
|
||||
void testCreateMessageSuccess() {
|
||||
|
||||
Function<CreateMessageRequest, CreateMessageResult> samplingHandler = request -> {
|
||||
assertThat(request.messages()).hasSize(1);
|
||||
|
|
|
|||
|
|
@ -3,19 +3,23 @@
|
|||
*/
|
||||
package io.modelcontextprotocol.server.transport;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.ServerSocket;
|
||||
|
||||
import jakarta.servlet.Servlet;
|
||||
import org.apache.catalina.Context;
|
||||
import org.apache.catalina.LifecycleState;
|
||||
import org.apache.catalina.startup.Tomcat;
|
||||
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
/**
|
||||
* @author Christian Tzolov
|
||||
*/
|
||||
public class TomcatTestUtil {
|
||||
|
||||
TomcatTestUtil() {
|
||||
// Prevent instantiation
|
||||
}
|
||||
|
||||
public static Tomcat createTomcatServer(String contextPath, int port, Servlet servlet) {
|
||||
|
||||
var tomcat = new Tomcat();
|
||||
|
|
@ -24,7 +28,6 @@ public class TomcatTestUtil {
|
|||
String baseDir = System.getProperty("java.io.tmpdir");
|
||||
tomcat.setBaseDir(baseDir);
|
||||
|
||||
// Context context = tomcat.addContext("", baseDir);
|
||||
Context context = tomcat.addContext(contextPath, baseDir);
|
||||
|
||||
// Add transport servlet to Tomcat
|
||||
|
|
@ -42,4 +45,19 @@ public class TomcatTestUtil {
|
|||
return tomcat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds an available port on the local machine.
|
||||
* @return an available port number
|
||||
* @throws IllegalStateException if no available port can be found
|
||||
*/
|
||||
public static int findAvailablePort() {
|
||||
try (final ServerSocket socket = new ServerSocket()) {
|
||||
socket.bind(new InetSocketAddress(0));
|
||||
return socket.getLocalPort();
|
||||
}
|
||||
catch (final IOException e) {
|
||||
throw new IllegalStateException("Cannot bind to an available port!", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue