Add tests for completion requests targeting missing prompt or resource
Polish gh-996 Signed-off-by: Daniel Garnier-Moiroux <git@garnier.wf>
This commit is contained in:
parent
301f8b06c7
commit
d015b4e97c
2 changed files with 118 additions and 4 deletions
|
|
@ -4,8 +4,6 @@
|
|||
|
||||
package io.modelcontextprotocol.server;
|
||||
|
||||
import static io.modelcontextprotocol.util.ToolsUtils.EMPTY_JSON_SCHEMA;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
|
@ -19,6 +17,7 @@ import io.modelcontextprotocol.common.McpTransportContext;
|
|||
import io.modelcontextprotocol.server.transport.HttpServletStatelessServerTransport;
|
||||
import io.modelcontextprotocol.server.transport.TomcatTestUtil;
|
||||
import io.modelcontextprotocol.spec.HttpHeaders;
|
||||
import io.modelcontextprotocol.spec.McpError;
|
||||
import io.modelcontextprotocol.spec.McpSchema;
|
||||
import io.modelcontextprotocol.spec.McpSchema.CallToolResult;
|
||||
import io.modelcontextprotocol.spec.McpSchema.CompleteRequest;
|
||||
|
|
@ -53,9 +52,12 @@ import org.springframework.web.client.RestClient;
|
|||
import static io.modelcontextprotocol.server.transport.HttpServletStatelessServerTransport.APPLICATION_JSON;
|
||||
import static io.modelcontextprotocol.server.transport.HttpServletStatelessServerTransport.TEXT_EVENT_STREAM;
|
||||
import static io.modelcontextprotocol.util.McpJsonMapperUtils.JSON_MAPPER;
|
||||
import static io.modelcontextprotocol.util.ToolsUtils.EMPTY_JSON_SCHEMA;
|
||||
import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson;
|
||||
import static net.javacrumbs.jsonunit.assertj.JsonAssertions.json;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.assertj.core.api.InstanceOfAssertFactories.type;
|
||||
import static org.awaitility.Awaitility.await;
|
||||
|
||||
@Timeout(15)
|
||||
|
|
@ -337,6 +339,63 @@ class HttpServletStatelessIntegrationTests {
|
|||
}
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "{0} : Completion call for non-existent prompt")
|
||||
@ValueSource(strings = { "httpclient" })
|
||||
void testCompletionForNonExistentPromptReturnsInvalidParams(String clientType) {
|
||||
var clientBuilder = clientBuilders.get(clientType);
|
||||
|
||||
var mcpServer = McpServer.sync(mcpStatelessServerTransport)
|
||||
.capabilities(ServerCapabilities.builder().completions().build())
|
||||
.build();
|
||||
|
||||
try (var mcpClient = clientBuilder.build()) {
|
||||
InitializeResult initResult = mcpClient.initialize();
|
||||
assertThat(initResult).isNotNull();
|
||||
|
||||
CompleteRequest request = CompleteRequest
|
||||
.builder(new PromptReference("nonexistent-prompt"), new CompleteRequest.CompleteArgument("arg", "val"))
|
||||
.build();
|
||||
|
||||
assertThatThrownBy(() -> mcpClient.completeCompletion(request)).isInstanceOf(McpError.class)
|
||||
.asInstanceOf(type(McpError.class))
|
||||
.extracting(McpError::getJsonRpcError)
|
||||
.extracting(McpSchema.JSONRPCResponse.JSONRPCError::code)
|
||||
.isEqualTo(McpSchema.ErrorCodes.RESOURCE_NOT_FOUND);
|
||||
}
|
||||
finally {
|
||||
mcpServer.close();
|
||||
}
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "{0} : Completion call for non-existent resource")
|
||||
@ValueSource(strings = { "httpclient" })
|
||||
void testCompletionForNonExistentResourceReturnsResourceNotFound(String clientType) {
|
||||
var clientBuilder = clientBuilders.get(clientType);
|
||||
|
||||
var mcpServer = McpServer.sync(mcpStatelessServerTransport)
|
||||
.capabilities(ServerCapabilities.builder().completions().build())
|
||||
.build();
|
||||
|
||||
try (var mcpClient = clientBuilder.build()) {
|
||||
InitializeResult initResult = mcpClient.initialize();
|
||||
assertThat(initResult).isNotNull();
|
||||
|
||||
CompleteRequest request = CompleteRequest
|
||||
.builder(new ResourceReference("test://nonexistent/{param}"),
|
||||
new CompleteRequest.CompleteArgument("param", "val"))
|
||||
.build();
|
||||
|
||||
assertThatThrownBy(() -> mcpClient.completeCompletion(request)).isInstanceOf(McpError.class)
|
||||
.asInstanceOf(type(McpError.class))
|
||||
.extracting(McpError::getJsonRpcError)
|
||||
.extracting(McpSchema.JSONRPCResponse.JSONRPCError::code)
|
||||
.isEqualTo(McpSchema.ErrorCodes.RESOURCE_NOT_FOUND);
|
||||
}
|
||||
finally {
|
||||
mcpServer.close();
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------
|
||||
// Tool Structured Output Schema Tests
|
||||
// ---------------------------------------
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import io.modelcontextprotocol.client.McpClient;
|
|||
import io.modelcontextprotocol.client.transport.HttpClientSseClientTransport;
|
||||
import io.modelcontextprotocol.server.transport.HttpServletSseServerTransportProvider;
|
||||
import io.modelcontextprotocol.server.transport.TomcatTestUtil;
|
||||
import io.modelcontextprotocol.spec.McpError;
|
||||
import io.modelcontextprotocol.spec.McpSchema;
|
||||
import io.modelcontextprotocol.spec.McpSchema.CompleteRequest;
|
||||
import io.modelcontextprotocol.spec.McpSchema.CompleteResult;
|
||||
|
|
@ -28,16 +29,17 @@ import io.modelcontextprotocol.spec.McpSchema.ErrorCodes;
|
|||
import io.modelcontextprotocol.spec.McpSchema.InitializeResult;
|
||||
import io.modelcontextprotocol.spec.McpSchema.Prompt;
|
||||
import io.modelcontextprotocol.spec.McpSchema.PromptArgument;
|
||||
import io.modelcontextprotocol.spec.McpSchema.PromptReference;
|
||||
import io.modelcontextprotocol.spec.McpSchema.ReadResourceResult;
|
||||
import io.modelcontextprotocol.spec.McpSchema.Resource;
|
||||
import io.modelcontextprotocol.spec.McpSchema.ResourceReference;
|
||||
import io.modelcontextprotocol.spec.McpSchema.ResourceTemplate;
|
||||
import io.modelcontextprotocol.spec.McpSchema.PromptReference;
|
||||
import io.modelcontextprotocol.spec.McpSchema.ServerCapabilities;
|
||||
import io.modelcontextprotocol.spec.McpError;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.assertj.core.api.InstanceOfAssertFactories.type;
|
||||
|
||||
/**
|
||||
* Tests for completion functionality with context support.
|
||||
|
|
@ -273,6 +275,59 @@ class McpCompletionTests {
|
|||
mcpServer.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCompletionForNonExistentPromptReturnsInvalidParams() {
|
||||
var mcpServer = McpServer.sync(mcpServerTransportProvider)
|
||||
.capabilities(ServerCapabilities.builder().completions().build())
|
||||
.build();
|
||||
|
||||
try (var mcpClient = clientBuilder
|
||||
.clientInfo(McpSchema.Implementation.builder("Sample " + "client", "0.0.0").build())
|
||||
.build()) {
|
||||
InitializeResult initResult = mcpClient.initialize();
|
||||
assertThat(initResult).isNotNull();
|
||||
|
||||
CompleteRequest request = CompleteRequest
|
||||
.builder(new PromptReference("nonexistent-prompt"), new CompleteRequest.CompleteArgument("arg", "val"))
|
||||
.build();
|
||||
|
||||
assertThatThrownBy(() -> mcpClient.completeCompletion(request)).isInstanceOf(McpError.class)
|
||||
.asInstanceOf(type(McpError.class))
|
||||
.extracting(McpError::getJsonRpcError)
|
||||
.extracting(McpSchema.JSONRPCResponse.JSONRPCError::code)
|
||||
.isEqualTo(ErrorCodes.INVALID_PARAMS);
|
||||
}
|
||||
|
||||
mcpServer.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCompletionForNonExistentResourceReturnsResourceNotFound() {
|
||||
var mcpServer = McpServer.sync(mcpServerTransportProvider)
|
||||
.capabilities(ServerCapabilities.builder().completions().build())
|
||||
.build();
|
||||
|
||||
try (var mcpClient = clientBuilder
|
||||
.clientInfo(McpSchema.Implementation.builder("Sample " + "client", "0.0.0").build())
|
||||
.build()) {
|
||||
InitializeResult initResult = mcpClient.initialize();
|
||||
assertThat(initResult).isNotNull();
|
||||
|
||||
CompleteRequest request = CompleteRequest
|
||||
.builder(new ResourceReference("test://nonexistent/{param}"),
|
||||
new CompleteRequest.CompleteArgument("param", "val"))
|
||||
.build();
|
||||
|
||||
assertThatThrownBy(() -> mcpClient.completeCompletion(request)).isInstanceOf(McpError.class)
|
||||
.asInstanceOf(type(McpError.class))
|
||||
.extracting(McpError::getJsonRpcError)
|
||||
.extracting(McpSchema.JSONRPCResponse.JSONRPCError::code)
|
||||
.isEqualTo(McpSchema.ErrorCodes.RESOURCE_NOT_FOUND);
|
||||
}
|
||||
|
||||
mcpServer.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDependentCompletionScenario() {
|
||||
BiFunction<McpSyncServerExchange, CompleteRequest, CompleteResult> completionHandler = (exchange, request) -> {
|
||||
|
|
|
|||
Loading…
Reference in a new issue