This is an automated email from the ASF dual-hosted git repository.
terrymanu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shardingsphere.git
The following commit(s) were added to refs/heads/master by this push:
new b084c233514 Refactor MCP E2E test coverage (#39240)
b084c233514 is described below
commit b084c23351448396c4c9e22b927268abd5748742
Author: Liang Zhang <[email protected]>
AuthorDate: Sat Jul 25 15:33:50 2026 +0800
Refactor MCP E2E test coverage (#39240)
Move protocol contract checks to HTTP integration tests, remove redundant
rule-level E2E scenarios, and retain dedicated HTTP and stdio LLM coverage.
---
.github/workflows/e2e-mcp.yml | 4 +-
.../server/http/StreamableHttpMCPServerIT.java | 58 ++++++++++++-
.../LLMStdioTransportE2ETest.java} | 53 ++++--------
.../suite/usability/LLMUsabilitySuiteE2ETest.java | 47 ++--------
.../scenario/LLMUsabilityScenarioCatalog.java | 11 ---
.../scenario/LLMUsabilityScenarioCatalogTest.java | 7 +-
...uidanceResourceHintProviderIntegrationTest.java | 77 -----------------
.../AbstractProductionMySQLRuntimeE2ETest.java | 46 +---------
...AbstractProductionPostgreSQLRuntimeE2ETest.java | 4 +-
.../HttpProductionProxyEncryptWorkflowE2ETest.java | 64 --------------
...ductionProxyFeatureWorkflowContractE2ETest.java | 61 -------------
.../HttpProductionProxyMaskWorkflowE2ETest.java | 11 +--
.../ProductionMySQLReadOnlySQLRuntimeE2ETest.java | 74 ----------------
.../production/ProductionMySQLRuntimeE2ETest.java | 99 ----------------------
.../ProductionMySQLSQLRuntimeE2ETest.java | 22 -----
.../ProductionPostgreSQLRuntimeE2ETest.java | 2 +-
.../support/transport/MCPPayloadAssertions.java | 42 +--------
.../transport/MCPPayloadAssertionsTest.java | 22 -----
18 files changed, 90 insertions(+), 614 deletions(-)
diff --git a/.github/workflows/e2e-mcp.yml b/.github/workflows/e2e-mcp.yml
index 80787c098a6..cedfa423afc 100644
--- a/.github/workflows/e2e-mcp.yml
+++ b/.github/workflows/e2e-mcp.yml
@@ -271,8 +271,8 @@ jobs:
max-parallel: 3
matrix:
include:
- - suite-id: smoke
- test-class: LLMSmokeE2ETest
+ - suite-id: stdio-transport
+ test-class: LLMStdioTransportE2ETest
- suite-id: usability-core
test-class: LLMUsabilitySuiteE2ETest
test-property: mcp.e2e.llm.usability-suite=core
diff --git
a/mcp/bootstrap/src/test/java/org/apache/shardingsphere/mcp/bootstrap/transport/server/http/StreamableHttpMCPServerIT.java
b/mcp/bootstrap/src/test/java/org/apache/shardingsphere/mcp/bootstrap/transport/server/http/StreamableHttpMCPServerIT.java
index 78a41d062eb..7145ec9149b 100644
---
a/mcp/bootstrap/src/test/java/org/apache/shardingsphere/mcp/bootstrap/transport/server/http/StreamableHttpMCPServerIT.java
+++
b/mcp/bootstrap/src/test/java/org/apache/shardingsphere/mcp/bootstrap/transport/server/http/StreamableHttpMCPServerIT.java
@@ -25,6 +25,7 @@ import
org.apache.shardingsphere.mcp.bootstrap.transport.MCPTransportConstants;
import org.apache.shardingsphere.mcp.core.context.MCPRuntimeContext;
import org.apache.shardingsphere.mcp.core.session.MCPSessionManager;
import
org.apache.shardingsphere.mcp.support.database.capability.MCPDatabaseCapabilityProvider;
+import
org.apache.shardingsphere.mcp.support.markdown.MCPMarkdownResourceLoader;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
@@ -84,6 +85,7 @@ class StreamableHttpMCPServerIT {
assertThat(initializePayload.get("jsonrpc"), is("2.0"));
Map<?, ?> result = (Map<?, ?>) initializePayload.get("result");
assertThat(result.get("protocolVersion"),
is(MCPTransportConstants.PROTOCOL_VERSION));
+ assertThat(result.get("instructions"),
is(MCPMarkdownResourceLoader.load(MCPTransportConstants.SERVER_INSTRUCTIONS_RESOURCE,
"server instruction")));
assertThat(sendInitializedNotification(sessionId,
Collections.emptyMap()).statusCode(), is(202));
HttpResponse<String> capabilitiesResponse =
sendPost(createCapabilitiesPayload(), Map.of(
CONTENT_TYPE_HEADER, JSON_CONTENT_TYPE,
@@ -93,6 +95,24 @@ class StreamableHttpMCPServerIT {
assertThat(capabilitiesResponse.statusCode(), is(200));
}
+ @Test
+ void assertRejectUnsupportedResource() throws IOException,
InterruptedException {
+ startServer();
+ String sessionId = initializeSession(Collections.emptyMap());
+ HttpResponse<String> actual = sendRequest(sessionId,
"resource-unsupported", "resources/read", Map.of("uri",
"unsupported://resource"));
+ assertThat(actual.statusCode(), is(200));
+ assertThat(assertJsonRpcError(actual,
"resource-unsupported").get("message"), is("Resource not found"));
+ }
+
+ @Test
+ void assertRejectUnsupportedTool() throws IOException,
InterruptedException {
+ startServer();
+ String sessionId = initializeSession(Collections.emptyMap());
+ HttpResponse<String> actual = sendRequest(sessionId,
"tool-unsupported", "tools/call", Map.of("name", "unsupported_tool",
"arguments", Map.of()));
+ assertThat(actual.statusCode(), is(200));
+ assertFalse(String.valueOf(assertJsonRpcError(actual,
"tool-unsupported").get("message")).isBlank());
+ }
+
@Test
void assertRejectUnsupportedContentType() throws IOException,
InterruptedException {
startServer();
@@ -193,6 +213,11 @@ class StreamableHttpMCPServerIT {
return sendPost(createCapabilitiesPayload(),
createSessionHeaders(sessionId));
}
+ private HttpResponse<String> sendRequest(final String sessionId, final
String requestId, final String method,
+ final Map<String, Object> params)
throws IOException, InterruptedException {
+ return sendPost(Map.of("jsonrpc", "2.0", "id", requestId, "method",
method, "params", params), createSessionHeaders(sessionId));
+ }
+
private HttpResponse<String> sendDelete(final String sessionId) throws
IOException, InterruptedException {
HttpRequest.Builder requestBuilder =
HttpRequest.newBuilder(endpoint).DELETE();
createSessionHeaders(sessionId).forEach(requestBuilder::header);
@@ -243,7 +268,38 @@ class StreamableHttpMCPServerIT {
}
private Map<?, ?> parseBody(final HttpResponse<String> response) throws
IOException {
- return OBJECT_MAPPER.readValue(response.body(), Map.class);
+ return OBJECT_MAPPER.readValue(normalizeJsonBody(response.body()),
Map.class);
+ }
+
+ private String normalizeJsonBody(final String responseBody) {
+ String result = responseBody.trim();
+ if (result.startsWith("{")) {
+ return result;
+ }
+ StringBuilder stringBuilder = new StringBuilder();
+ boolean hasDataLine = false;
+ for (String each : result.split("\\R")) {
+ String line = each.trim();
+ if (!line.startsWith("data:")) {
+ continue;
+ }
+ if (hasDataLine) {
+ stringBuilder.append(System.lineSeparator());
+ }
+ stringBuilder.append(line.substring("data:".length()).trim());
+ hasDataLine = true;
+ }
+ return hasDataLine ? stringBuilder.toString() : result;
+ }
+
+ private Map<?, ?> assertJsonRpcError(final HttpResponse<String> response,
final String requestId) throws IOException {
+ Map<?, ?> actual = parseBody(response);
+ assertThat(actual.get("jsonrpc"), is("2.0"));
+ assertThat(actual.get("id"), is(requestId));
+ assertFalse(actual.containsKey("result"));
+ Map<?, ?> error = (Map<?, ?>) actual.get("error");
+ assertTrue(error.get("code") instanceof Number);
+ return error;
}
private String getRecoveryCategory(final HttpResponse<String> response)
throws IOException {
diff --git
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/suite/smoke/LLMSmokeE2ETest.java
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/suite/transport/LLMStdioTransportE2ETest.java
similarity index 77%
rename from
test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/suite/smoke/LLMSmokeE2ETest.java
rename to
test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/suite/transport/LLMStdioTransportE2ETest.java
index 93c57013427..94a15b6b5db 100644
---
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/suite/smoke/LLMSmokeE2ETest.java
+++
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/suite/transport/LLMStdioTransportE2ETest.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-package org.apache.shardingsphere.test.e2e.mcp.llm.suite.smoke;
+package org.apache.shardingsphere.test.e2e.mcp.llm.suite.transport;
import
org.apache.shardingsphere.mcp.support.database.metadata.jdbc.RuntimeDatabaseConfiguration;
import org.apache.shardingsphere.test.e2e.mcp.llm.config.LLME2EConfiguration;
@@ -29,19 +29,15 @@ import
org.apache.shardingsphere.test.e2e.mcp.llm.scenario.LLMStructuredAnswer;
import
org.apache.shardingsphere.test.e2e.mcp.support.runtime.AbstractConfigBackedRuntimeE2ETest;
import org.apache.shardingsphere.test.e2e.mcp.support.runtime.RuntimeTransport;
import org.junit.jupiter.api.AfterAll;
-import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.condition.EnabledIf;
-import org.junit.jupiter.params.ParameterizedTest;
-import org.junit.jupiter.params.provider.Arguments;
-import org.junit.jupiter.params.provider.MethodSource;
import java.io.IOException;
import java.util.List;
import java.util.Map;
-import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -49,7 +45,9 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
@Tag("llm-e2e")
@EnabledIf("org.apache.shardingsphere.test.e2e.mcp.env.MCPE2ECondition#isDockerEnabled")
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
-class LLMSmokeE2ETest extends AbstractConfigBackedRuntimeE2ETest {
+class LLMStdioTransportE2ETest extends AbstractConfigBackedRuntimeE2ETest {
+
+ private static final String SUITE_ID = "llm-stdio-transport";
private static final String DATABASE_NAME = "logic_db";
@@ -70,8 +68,6 @@ class LLMSmokeE2ETest extends
AbstractConfigBackedRuntimeE2ETest {
private final LLMRuntimeFixtureFactory runtimeFixtureFactory = new
LLMRuntimeFixtureFactory();
- private RuntimeTransport currentTransport;
-
private Fixture currentRuntimeFixture;
@BeforeAll
@@ -95,31 +91,18 @@ class LLMSmokeE2ETest extends
AbstractConfigBackedRuntimeE2ETest {
}
}
- @AfterEach
- void clearCurrentTransport() {
- currentTransport = null;
- }
-
- static Stream<Arguments> getTestCases() {
- return Stream.of(
- Arguments.of("llm-smoke-mysql-http", RuntimeTransport.HTTP),
- Arguments.of("llm-smoke-mysql-stdio", RuntimeTransport.STDIO));
- }
-
- @ParameterizedTest(name = "{0}")
- @MethodSource("getTestCases")
- void assertSmoke(final String suiteId, final RuntimeTransport transport)
throws IOException, InterruptedException {
- currentTransport = transport;
+ @Test
+ void assertStdioTransport() throws IOException, InterruptedException {
LLMConversationExecutor conversationExecutor = new
LLMConversationExecutor(getRequiredLLMConfiguration(),
getRequiredLLMRuntimeEvidence());
conversationExecutor.assertModelReady();
prepareRuntimeFixture();
- LLMConversationExecutor.ConversationResult actualResult =
conversationExecutor.runConversation(suiteId, createScenario(suiteId),
createInteractionClient());
+ LLMConversationExecutor.ConversationResult actualResult =
conversationExecutor.runConversation(SUITE_ID, createScenario(),
createInteractionClient());
assertSuccess(actualResult);
}
- private LLME2EScenario createScenario(final String scenarioId) {
+ private LLME2EScenario createScenario() {
Fixture fixture = getRequiredRuntimeFixture();
- return new LLME2EScenario(scenarioId, SYSTEM_PROMPT,
+ return new LLME2EScenario(SUITE_ID, SYSTEM_PROMPT,
"A user asks how many rows are in `" + TABLE_NAME + "` right
now. Use logical database `" + DATABASE_NAME + "`, schema `"
+ fixture.schemaName() + "`, and SQL `" +
COUNT_ORDERS_SQL + "`.",
new LLMStructuredAnswer(DATABASE_NAME, fixture.schemaName(),
TABLE_NAME, COUNT_ORDERS_SQL, fixture.totalOrders(), List.of()),
@@ -129,8 +112,8 @@ class LLMSmokeE2ETest extends
AbstractConfigBackedRuntimeE2ETest {
private void assertSuccess(final
LLMConversationExecutor.ConversationResult actualResult) {
LLME2EAssertionReport actualReport =
actualResult.artifactBundle().getAssertionReport();
assertTrue(actualReport.isSuccess(),
- () -> String.format("LLM smoke scenario failed: %s - %s",
actualReport.getFailureType(), actualReport.getMessage()));
-
assertFalse(actualResult.artifactBundle().getInteractionTrace().isEmpty(), "LLM
smoke scenario must record at least one MCP interaction.");
+ () -> String.format("LLM stdio transport scenario failed: %s -
%s", actualReport.getFailureType(), actualReport.getMessage()));
+
assertFalse(actualResult.artifactBundle().getInteractionTrace().isEmpty(), "LLM
stdio transport scenario must record at least one MCP interaction.");
}
private static LLME2EConfiguration getRequiredLLMConfiguration() {
@@ -150,7 +133,7 @@ class LLMSmokeE2ETest extends
AbstractConfigBackedRuntimeE2ETest {
@Override
protected RuntimeTransport getTransport() {
- return getRequiredTransport();
+ return RuntimeTransport.STDIO;
}
@Override
@@ -163,20 +146,14 @@ class LLMSmokeE2ETest extends
AbstractConfigBackedRuntimeE2ETest {
if (null != currentRuntimeFixture) {
return;
}
- currentRuntimeFixture =
runtimeFixtureFactory.createMySQLFixture(DATABASE_NAME, "Docker is required for
the MySQL-backed LLM smoke E2E test.");
+ currentRuntimeFixture =
runtimeFixtureFactory.createMySQLFixture(DATABASE_NAME, "Docker is required for
the MySQL-backed LLM stdio transport E2E test.");
}
private Fixture getRequiredRuntimeFixture() {
if (null == currentRuntimeFixture) {
- throw new IllegalStateException("LLM smoke runtime fixture was not
initialized.");
+ throw new IllegalStateException("LLM stdio transport runtime
fixture was not initialized.");
}
return currentRuntimeFixture;
}
- private RuntimeTransport getRequiredTransport() {
- if (null == currentTransport) {
- throw new IllegalStateException("LLM smoke test case was not
initialized.");
- }
- return currentTransport;
- }
}
diff --git
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/suite/usability/LLMUsabilitySuiteE2ETest.java
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/suite/usability/LLMUsabilitySuiteE2ETest.java
index d142e3bc818..6f8f7a0eb5f 100644
---
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/suite/usability/LLMUsabilitySuiteE2ETest.java
+++
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/suite/usability/LLMUsabilitySuiteE2ETest.java
@@ -28,19 +28,15 @@ import
org.apache.shardingsphere.test.e2e.mcp.llm.suite.usability.scenario.LLMUs
import
org.apache.shardingsphere.test.e2e.mcp.support.runtime.AbstractConfigBackedRuntimeE2ETest;
import org.apache.shardingsphere.test.e2e.mcp.support.runtime.RuntimeTransport;
import org.junit.jupiter.api.AfterAll;
-import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.condition.EnabledIf;
-import org.junit.jupiter.params.ParameterizedTest;
-import org.junit.jupiter.params.provider.Arguments;
-import org.junit.jupiter.params.provider.MethodSource;
import java.io.IOException;
import java.util.List;
import java.util.Map;
-import java.util.stream.Stream;
@Tag("llm-e2e")
@EnabledIf("org.apache.shardingsphere.test.e2e.mcp.env.MCPE2ECondition#isDockerEnabled")
@@ -55,8 +51,6 @@ class LLMUsabilitySuiteE2ETest extends
AbstractConfigBackedRuntimeE2ETest {
private static final String COUNT_ORDERS_SQL = "SELECT COUNT(*) AS
total_orders FROM orders";
- private static final String FULL_TRANSPORT_MATRIX_PROPERTY =
"mcp.e2e.llm.full-transport-matrix";
-
private static final String SUITE_PART_PROPERTY =
"mcp.e2e.llm.usability-suite";
private static LLMRuntimeSupport.ModelRuntime llmRuntime;
@@ -67,8 +61,6 @@ class LLMUsabilitySuiteE2ETest extends
AbstractConfigBackedRuntimeE2ETest {
private final LLMUsabilityScenarioCatalog scenarioCatalog = new
LLMUsabilityScenarioCatalog();
- private RuntimeTransport currentTransport;
-
private Fixture currentRuntimeFixture;
@BeforeAll
@@ -92,32 +84,9 @@ class LLMUsabilitySuiteE2ETest extends
AbstractConfigBackedRuntimeE2ETest {
}
}
- @AfterEach
- void clearCurrentTransport() {
- currentTransport = null;
- }
-
- static Stream<Arguments> getTestCases() {
- return isFullTransportMatrixEnabled()
- ? Stream.of(
- Arguments.of("llm-usability-mysql-http",
RuntimeTransport.HTTP),
- Arguments.of("llm-usability-mysql-stdio",
RuntimeTransport.STDIO))
- : Stream.of(Arguments.of("llm-usability-mysql-http",
RuntimeTransport.HTTP));
- }
-
- private static boolean isFullTransportMatrixEnabled() {
- String configuredValue =
System.getProperty(FULL_TRANSPORT_MATRIX_PROPERTY, "false").trim();
- if (!"true".equalsIgnoreCase(configuredValue) &&
!"false".equalsIgnoreCase(configuredValue)) {
- throw new IllegalArgumentException(String.format("MCP LLM E2E
property `%s` must be `true` or `false`, but was `%s`.",
- FULL_TRANSPORT_MATRIX_PROPERTY, configuredValue));
- }
- return Boolean.parseBoolean(configuredValue);
- }
-
- @ParameterizedTest(name = "{0}")
- @MethodSource("getTestCases")
- void assertUsabilityBaseline(final String suiteId, final RuntimeTransport
transport) throws IOException, InterruptedException {
- currentTransport = transport;
+ @Test
+ void assertUsabilityBaseline() throws IOException, InterruptedException {
+ String suiteId = "llm-usability-mysql-http";
LLMConversationExecutor conversationExecutor = new
LLMConversationExecutor(getRequiredLLMConfiguration(),
getRequiredLLMRuntimeEvidence());
conversationExecutor.assertModelReady();
prepareRuntimeFixture();
@@ -168,7 +137,7 @@ class LLMUsabilitySuiteE2ETest extends
AbstractConfigBackedRuntimeE2ETest {
@Override
protected RuntimeTransport getTransport() {
- return getRequiredTransport();
+ return RuntimeTransport.HTTP;
}
@Override
@@ -191,10 +160,4 @@ class LLMUsabilitySuiteE2ETest extends
AbstractConfigBackedRuntimeE2ETest {
return currentRuntimeFixture;
}
- private RuntimeTransport getRequiredTransport() {
- if (null == currentTransport) {
- throw new IllegalStateException("LLM usability test case was not
initialized.");
- }
- return currentTransport;
- }
}
diff --git
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/suite/usability/scenario/LLMUsabilityScenarioCatalog.java
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/suite/usability/scenario/LLMUsabilityScenarioCatalog.java
index 4fbba68b38e..f1e171207ca 100644
---
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/suite/usability/scenario/LLMUsabilityScenarioCatalog.java
+++
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/suite/usability/scenario/LLMUsabilityScenarioCatalog.java
@@ -80,17 +80,6 @@ public final class LLMUsabilityScenarioCatalog {
List.of(MCPInteractionActionNames.READ_RESOURCE,
"database_gateway_execute_update", "database_gateway_execute_query"),
List.of("database_gateway_execute_update",
"database_gateway_execute_query")),
List.of(MCPInteractionActionNames.READ_RESOURCE,
"database_gateway_execute_update"), List.of(), false, false));
- List<String> workflowActions =
List.of(MCPInteractionActionNames.READ_RESOURCE,
"database_gateway_plan_mask_rule", "database_gateway_apply_workflow",
"database_gateway_execute_query");
- List<String> workflowRequiredActions =
List.of("database_gateway_plan_mask_rule", "database_gateway_apply_workflow",
"database_gateway_execute_query");
- result.add(createScenario("natural-workflow-manual-export-" +
runtimeKind, LLMUsabilityDimension.TOOL, runtimeKind,
- List.of(LLMUsabilityScenario.NATURAL_TASK_TAG, "natural",
"workflow"),
- new LLME2EScenario("natural-workflow-manual-export-" +
runtimeKind, SYSTEM_PROMPT,
- "Prepare a mask-rule workflow for logical database `"
+ databaseName + "`, schema `" + schemaName + "`, table `" + tableName
- + "`, and column `status` using MD5. Do not
send plan_id to the planning tool; use the plan_id returned by the planning
response for follow-up workflow calls. "
- + "Keep runtime side effects out of MCP,
export reviewable artifacts for manual execution, and finish by verifying `" +
query + "`.",
- createAnswer(databaseName, schemaName, tableName,
query, totalOrders),
- workflowActions, workflowRequiredActions),
- List.of(MCPInteractionActionNames.READ_RESOURCE,
"database_gateway_plan_mask_rule"), List.of(), false, false));
result.add(createScenario("natural-mask-rule-md5-" + runtimeKind,
LLMUsabilityDimension.TOOL, runtimeKind,
List.of(LLMUsabilityScenario.NATURAL_TASK_TAG, "natural",
"workflow", "mask"),
new LLME2EScenario("natural-mask-rule-md5-" + runtimeKind,
SYSTEM_PROMPT,
diff --git
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/suite/usability/scenario/LLMUsabilityScenarioCatalogTest.java
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/suite/usability/scenario/LLMUsabilityScenarioCatalogTest.java
index 77969f7445d..d0cc1abd82e 100644
---
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/suite/usability/scenario/LLMUsabilityScenarioCatalogTest.java
+++
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/llm/suite/usability/scenario/LLMUsabilityScenarioCatalogTest.java
@@ -39,15 +39,10 @@ class LLMUsabilityScenarioCatalogTest {
"SELECT COUNT(*) AS total_orders FROM orders", 2);
Map<String, LLMUsabilityScenario> actualScenarios =
actual.stream().collect(Collectors.toMap(LLMUsabilityScenario::getScenarioId,
each -> each));
assertThat(actualScenarios.keySet(),
hasItems("natural-metadata-lookup-mysql", "natural-read-only-sql-mysql",
"natural-side-effect-preview-mysql",
- "natural-workflow-manual-export-mysql",
"natural-mask-rule-md5-mysql", "natural-encrypt-rule-md5-mysql",
"natural-table-resource-mysql"));
+ "natural-mask-rule-md5-mysql",
"natural-encrypt-rule-md5-mysql", "natural-table-resource-mysql"));
assertThat(actualScenarios.get("natural-side-effect-preview-mysql").getLlmScenario().getRequiredToolNames(),
is(List.of("database_gateway_execute_update",
"database_gateway_execute_query")));
assertThat(actualScenarios.get("natural-table-resource-mysql").getExpectedResourceUris(),
is(List.of("shardingsphere://databases/logic_db/schemas/logic_db/tables/orders")));
-
assertThat(actualScenarios.get("natural-workflow-manual-export-mysql").getLlmScenario().getRequiredToolNames(),
- is(List.of("database_gateway_plan_mask_rule",
"database_gateway_apply_workflow", "database_gateway_execute_query")));
-
assertThat(actualScenarios.get("natural-workflow-manual-export-mysql").getExpectedResourceUris(),
is(List.of()));
-
assertThat(actualScenarios.get("natural-workflow-manual-export-mysql").getLlmScenario().getUserPrompt(),
- containsString("table `orders`, and column `status`"));
assertThat(actualScenarios.get("natural-mask-rule-md5-mysql").getLlmScenario().getRequiredToolNames(),
is(List.of("database_gateway_plan_mask_rule",
"database_gateway_apply_workflow", "database_gateway_validate_workflow",
"database_gateway_execute_query")));
assertThat(actualScenarios.get("natural-mask-rule-md5-mysql").getLlmScenario().getUserPrompt(),
containsString("create a mask rule"));
diff --git
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/WorkflowGuidanceResourceHintProviderIntegrationTest.java
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/WorkflowGuidanceResourceHintProviderIntegrationTest.java
deleted file mode 100644
index ace647a59c2..00000000000
---
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/WorkflowGuidanceResourceHintProviderIntegrationTest.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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
- *
- * http://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 org.apache.shardingsphere.test.e2e.mcp.runtime;
-
-import
org.apache.shardingsphere.mcp.feature.readwritesplitting.ReadwriteSplittingFeatureDefinition;
-import org.apache.shardingsphere.mcp.feature.shadow.ShadowFeatureDefinition;
-import
org.apache.shardingsphere.mcp.feature.sharding.ShardingFeatureDefinition;
-import
org.apache.shardingsphere.mcp.support.workflow.model.WorkflowContextSnapshot;
-import org.apache.shardingsphere.mcp.support.workflow.model.WorkflowKind;
-import org.apache.shardingsphere.mcp.support.workflow.model.WorkflowRequest;
-import
org.apache.shardingsphere.mcp.support.workflow.service.WorkflowGuidanceResourceHintProvider;
-import org.junit.jupiter.params.ParameterizedTest;
-import org.junit.jupiter.params.provider.Arguments;
-import org.junit.jupiter.params.provider.MethodSource;
-
-import java.util.List;
-import java.util.stream.Stream;
-
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-class WorkflowGuidanceResourceHintProviderIntegrationTest {
-
- @ParameterizedTest(name = "{0}")
- @MethodSource("workflowResourceHints")
- void assertResolveCrossModuleResourceHints(final String scenarioName,
final WorkflowKind workflowKind,
- final List<String>
resourceUriTemplates, final List<String> expectedUris) {
- WorkflowContextSnapshot snapshot = createSnapshot(workflowKind);
- snapshot.getResourceUriTemplates().addAll(resourceUriTemplates);
- List<String> actual = new
WorkflowGuidanceResourceHintProvider().createResourcesToRead(snapshot).stream()
- .map(each -> String.valueOf(each.get("uri"))).toList();
- assertTrue(actual.containsAll(expectedUris));
- }
-
- private static Stream<Arguments> workflowResourceHints() {
- List<String> tableResourceUriTemplates = List.of(
- ShadowFeatureDefinition.STORAGE_UNITS_RESOURCE_URI,
- ShadowFeatureDefinition.SINGLE_TABLES_RESOURCE_URI,
- ShadowFeatureDefinition.SINGLE_TABLE_RESOURCE_URI);
- List<String> expectedTableResourceUris = List.of(
- "shardingsphere://databases/logic_db/storage-units",
- "shardingsphere://databases/logic_db/single-tables",
- "shardingsphere://databases/logic_db/single-tables/orders");
- return Stream.of(
- Arguments.of("readwrite-splitting",
ReadwriteSplittingFeatureDefinition.RULE_WORKFLOW_KIND,
-
List.of(ReadwriteSplittingFeatureDefinition.STORAGE_UNITS_RESOURCE_URI),
List.of("shardingsphere://databases/logic_db/storage-units")),
- Arguments.of("shadow",
ShadowFeatureDefinition.RULE_WORKFLOW_KIND, tableResourceUriTemplates,
expectedTableResourceUris),
- Arguments.of("sharding",
ShardingFeatureDefinition.TABLE_RULE_WORKFLOW_KIND,
-
List.of(ShardingFeatureDefinition.STORAGE_UNITS_RESOURCE_URI,
ShardingFeatureDefinition.SINGLE_TABLES_RESOURCE_URI,
-
ShardingFeatureDefinition.SINGLE_TABLE_RESOURCE_URI),
- expectedTableResourceUris));
- }
-
- private WorkflowContextSnapshot createSnapshot(final WorkflowKind
workflowKind) {
- WorkflowContextSnapshot result = new WorkflowContextSnapshot();
- result.setWorkflowKind(workflowKind);
- WorkflowRequest request = new WorkflowRequest();
- request.setDatabase("logic_db");
- request.setTable("orders");
- result.setRequest(request);
- return result;
- }
-}
diff --git
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/AbstractProductionMySQLRuntimeE2ETest.java
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/AbstractProductionMySQLRuntimeE2ETest.java
index b98844c0279..09c57d7af9d 100644
---
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/AbstractProductionMySQLRuntimeE2ETest.java
+++
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/AbstractProductionMySQLRuntimeE2ETest.java
@@ -24,11 +24,9 @@ import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import
org.apache.shardingsphere.mcp.support.database.metadata.jdbc.RuntimeDatabaseConfiguration;
-import org.apache.shardingsphere.test.e2e.mcp.support.OfficialMCPToolNames;
import
org.apache.shardingsphere.test.e2e.mcp.support.runtime.MySQLRuntimeTestSupport;
import org.apache.shardingsphere.test.e2e.mcp.support.runtime.RuntimeTransport;
import
org.apache.shardingsphere.test.e2e.mcp.support.transport.MCPInteractionPayloads;
-import
org.apache.shardingsphere.test.e2e.mcp.support.transport.MCPPayloadAssertions;
import
org.apache.shardingsphere.test.e2e.mcp.support.transport.client.MCPInteractionClient;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
@@ -41,13 +39,10 @@ import java.io.IOException;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
-import java.util.function.BiFunction;
import java.util.stream.Stream;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.isA;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -151,12 +146,8 @@ abstract class AbstractProductionMySQLRuntimeE2ETest
extends AbstractTransportPa
return Map.of("database", databaseName, "schema", databaseName, "sql",
sql, "execution_mode", "execute");
}
- protected static Stream<Arguments> transports() {
- return ProductionRuntimeTransportCases.transports();
- }
-
protected static Stream<Arguments> dualTransports() {
- return transports();
+ return ProductionRuntimeTransportCases.transports();
}
protected static Stream<Arguments> semanticPrimaryTransport() {
@@ -185,25 +176,6 @@ abstract class AbstractProductionMySQLRuntimeE2ETest
extends AbstractTransportPa
.stream().map(each ->
String.valueOf(each.get("table"))).toList();
}
- protected void assertOfficialToolNames(final List<String> actualToolNames)
{
- assertThat(actualToolNames,
containsInAnyOrder(OfficialMCPToolNames.getAll().toArray()));
- }
-
- protected void assertToolDefinition(final List<Map<String, Object>> tools,
final String toolName, final String expectedTitle,
- final String expectedRequiredField,
final String expectedPropertyField, final String expectedPropertyType) {
- MCPPayloadAssertions.assertToolDefinition(tools, toolName,
expectedTitle, expectedRequiredField, expectedPropertyField,
expectedPropertyType);
- }
-
- protected void assertJsonRpcErrorWithoutResult(final Map<String, Object>
actual, final String requestId) {
- assertThat(String.valueOf(actual.get("jsonrpc")), is("2.0"));
- assertThat(String.valueOf(actual.get("id")), is(requestId));
- assertTrue(MCPInteractionPayloads.hasJsonRpcError(actual));
- assertFalse(actual.containsKey("result"));
- Map<String, Object> error = getObjectOrEmpty(actual.get("error"));
- assertThat(error.get("code"), isA(Number.class));
- assertFalse(String.valueOf(error.get("message")).isBlank());
- }
-
protected void assertRecoveryResponse(final Map<String, Object> actual) {
assertThat(String.valueOf(actual.get("response_mode")),
is("recovery"));
assertFalse(String.valueOf(actual.get("summary")).isBlank());
@@ -214,11 +186,6 @@ abstract class AbstractProductionMySQLRuntimeE2ETest
extends AbstractTransportPa
assertThat(String.valueOf(actual.get("summary")), is(expectedMessage));
}
- protected void assertRecoveryResponse(final Map<String, Object> actual,
final String expectedMessage, final String expectedCategory) {
- assertRecoveryResponse(actual, expectedMessage);
-
assertThat(String.valueOf(getObjectOrEmpty(actual.get("recovery")).get("category")),
is(expectedCategory));
- }
-
protected void assertAiNativeGuidance(final Map<String, Object> guidance) {
assertTrue(guidance.containsKey("discovery"));
assertTrue(guidance.containsKey("model_contract"));
@@ -285,12 +252,7 @@ abstract class AbstractProductionMySQLRuntimeE2ETest
extends AbstractTransportPa
}
protected McpSyncClient createElicitationClient(final RuntimeTransport
transport, final List<McpSchema.ElicitRequest> elicitationRequests) throws
IOException {
- return createElicitationClient(transport, elicitationRequests,
this::createElicitationResult);
- }
-
- protected McpSyncClient createElicitationClient(final RuntimeTransport
transport, final List<McpSchema.ElicitRequest> elicitationRequests,
- final
BiFunction<List<McpSchema.ElicitRequest>, McpSchema.ElicitRequest,
McpSchema.ElicitResult> handler) throws IOException {
- return
ProductionMCPClientTransportFactory.createElicitationClient(createClientTransport(transport),
elicitationRequests, handler);
+ return
ProductionMCPClientTransportFactory.createElicitationClient(createClientTransport(transport),
elicitationRequests, this::createElicitationResult);
}
private McpClientTransport createClientTransport(final RuntimeTransport
transport) throws IOException {
@@ -299,8 +261,8 @@ abstract class AbstractProductionMySQLRuntimeE2ETest
extends AbstractTransportPa
:
ProductionMCPClientTransportFactory.createStdioClientTransport(getConfigFile());
}
- protected McpSchema.ElicitResult createElicitationResult(final
List<McpSchema.ElicitRequest> elicitationRequests,
- final
McpSchema.ElicitRequest request) {
+ private McpSchema.ElicitResult createElicitationResult(final
List<McpSchema.ElicitRequest> elicitationRequests,
+ final
McpSchema.ElicitRequest request) {
elicitationRequests.add(request);
List<String> requiredFields =
getRequiredStringList(request.requestedSchema().get("required"));
return new
McpSchema.ElicitResult(McpSchema.ElicitResult.Action.ACCEPT, Map.of(
diff --git
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/AbstractProductionPostgreSQLRuntimeE2ETest.java
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/AbstractProductionPostgreSQLRuntimeE2ETest.java
index dca50e0c5c1..f3e1251a430 100644
---
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/AbstractProductionPostgreSQLRuntimeE2ETest.java
+++
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/AbstractProductionPostgreSQLRuntimeE2ETest.java
@@ -77,7 +77,7 @@ abstract class AbstractProductionPostgreSQLRuntimeE2ETest
extends AbstractTransp
return Map.of("database", LOGICAL_DATABASE_NAME, "schema", schema,
"sql", sql, "execution_mode", "execute");
}
- protected static Stream<Arguments> dualTransports() {
- return ProductionRuntimeTransportCases.transports();
+ protected static Stream<Arguments> semanticPrimaryTransport() {
+ return ProductionRuntimeTransportCases.semanticPrimaryTransport();
}
}
diff --git
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/HttpProductionProxyEncryptWorkflowE2ETest.java
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/HttpProductionProxyEncryptWorkflowE2ETest.java
index bdbd47a7271..3aa237873a3 100644
---
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/HttpProductionProxyEncryptWorkflowE2ETest.java
+++
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/HttpProductionProxyEncryptWorkflowE2ETest.java
@@ -144,35 +144,6 @@ class HttpProductionProxyEncryptWorkflowE2ETest extends
AbstractProductionProxyW
}
}
- @Test
- void assertPlanReportsLikeQueryCapabilityConflictThroughProxy() throws
IOException, InterruptedException {
- try (MCPInteractionClient interactionClient =
createOpenedInteractionClient()) {
- Map<String, Object> actualPlanResponse =
interactionClient.call(PLAN_TOOL_NAME,
- Map.of("database", getLogicalDatabaseName(), "table",
"orders", "column", "status",
- "natural_language_intent", "encrypt status with
reversible encryption, requires equality and LIKE query"));
- assertThat(String.valueOf(actualPlanResponse.get("status")),
is("clarifying"));
- List<String> actualIssueCodes = getIssueCodes(actualPlanResponse);
- assertThat(actualIssueCodes,
hasItem(WorkflowIssueCode.ALGORITHM_CAPABILITY_CONFLICT));
- List<Map<String, Object>> actualRecommendations =
getObjectListOrEmpty(actualPlanResponse.get("algorithm_recommendations"));
- assertThat(actualRecommendations.size(), is(2));
- assertFalse(actualRecommendations.stream().anyMatch(each ->
"like_query".equals(each.get("algorithm_role"))));
- }
- }
-
- @Test
- void assertPlanRequiresExplicitCipherColumnThroughProxy() throws
IOException, InterruptedException {
- try (MCPInteractionClient interactionClient =
createOpenedInteractionClient()) {
- Map<String, Object> actualPlannedResponse =
interactionClient.call(PLAN_TOOL_NAME,
- Map.of("database", getLogicalDatabaseName(), "table",
"orders", "column", "status",
- "natural_language_intent", "encrypt status with
reversible encryption, no equality, no like", "algorithm_type", "AES",
- "primary_algorithm_properties",
Map.of("aes-key-value", "explicit-secret")));
- assertThat(String.valueOf(actualPlannedResponse.get("status")),
is("clarifying"));
- assertSecretRedacted(actualPlannedResponse, "explicit-secret");
- assertThat(getIssueCodes(actualPlannedResponse),
hasItem(WorkflowIssueCode.RULE_INPUT_REQUIRED));
-
assertThat(getStringListOrEmpty(actualPlannedResponse.get("missing_required_inputs")),
hasItem("cipher_column_name"));
- }
- }
-
@Test
void assertPlanRejectsUnsupportedSecondEncryptColumnThroughProxy() throws
IOException, InterruptedException {
try (MCPInteractionClient interactionClient =
createOpenedInteractionClient()) {
@@ -219,41 +190,6 @@ class HttpProductionProxyEncryptWorkflowE2ETest extends
AbstractProductionProxyW
}
}
- @Test
- void assertApplySupportsRuleApprovedStepThroughProxy() throws IOException,
InterruptedException {
- try (MCPInteractionClient interactionClient =
createOpenedInteractionClient()) {
- Map<String, Object> actualPlannedResponse =
interactionClient.call(PLAN_TOOL_NAME,
- Map.of("database", getLogicalDatabaseName(), "table",
"orders", "column", "status",
- "natural_language_intent", "encrypt status with
reversible encryption, no equality, no like", "algorithm_type", "AES",
- "cipher_column_name", "status_cipher",
"primary_algorithm_properties", Map.of("aes-key-value", "approved-secret")));
- assertThat(String.valueOf(actualPlannedResponse.get("status")),
is("planned"));
- String planId =
String.valueOf(actualPlannedResponse.get("plan_id"));
- Map<String, Object> actualPreviewResponse =
previewWorkflow(interactionClient, planId);
- List<Map<String, Object>> actualPreviewArtifacts =
getObjectListOrEmpty(actualPreviewResponse.get("preview_artifacts"));
- assertThat(actualPreviewArtifacts.size(), is(1));
-
assertThat(String.valueOf(actualPreviewArtifacts.getFirst().get("approval_step")),
is("rule_distsql"));
- Map<String, Object> actualRuleApplyResponse =
interactionClient.call(APPLY_TOOL_NAME,
createReviewThenExecuteArguments(planId, List.of("rule_distsql")));
- assertThat(String.valueOf(actualRuleApplyResponse.get("status")),
is("completed"));
-
assertThat(getObjectListOrEmpty(actualRuleApplyResponse.get("step_results")).size(),
is(1));
- assertValidationPassed(interactionClient.call(VALIDATE_TOOL_NAME,
Map.of("plan_id", planId)));
- }
- }
-
- @Test
- void assertPlanRejectsUnsupportedEncryptOperationThroughProxy() throws
IOException, InterruptedException {
- try (MCPInteractionClient interactionClient =
createOpenedInteractionClient()) {
- createEncryptRuleWithoutEquality(interactionClient);
- Map<String, Object> actualUnsupportedPlanResponse =
interactionClient.call(PLAN_TOOL_NAME,
- Map.of("database", getLogicalDatabaseName(), "table",
"orders", "column", "status",
- "natural_language_intent", "encrypt status with
reversible encryption, update to require equality and no like",
- "primary_algorithm_properties",
Map.of("aes-key-value", "unsupported-secret")));
-
assertThat(String.valueOf(actualUnsupportedPlanResponse.get("status")),
is("failed"));
- assertThat(getIssueCodes(actualUnsupportedPlanResponse),
hasItem(WorkflowIssueCode.WORKFLOW_STATUS_INVALID));
-
assertFalse(String.valueOf(actualUnsupportedPlanResponse).toLowerCase(Locale.ENGLISH).contains("alter"));
-
assertThat(getObjectListOrEmpty(actualUnsupportedPlanResponse.get("distsql_artifacts")).size(),
is(0));
- }
- }
-
@Test
void assertPlanApplyAndValidateEncryptDropWorkflowThroughProxy() throws
IOException, InterruptedException {
try (MCPInteractionClient interactionClient =
createOpenedInteractionClient()) {
diff --git
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/HttpProductionProxyFeatureWorkflowContractE2ETest.java
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/HttpProductionProxyFeatureWorkflowContractE2ETest.java
index 6160a41b531..8d16757b881 100644
---
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/HttpProductionProxyFeatureWorkflowContractE2ETest.java
+++
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/HttpProductionProxyFeatureWorkflowContractE2ETest.java
@@ -22,16 +22,12 @@ import
org.apache.shardingsphere.mcp.support.workflow.model.WorkflowIssueCode;
import
org.apache.shardingsphere.test.e2e.mcp.support.transport.client.MCPInteractionClient;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledIf;
-import org.junit.jupiter.params.ParameterizedTest;
-import org.junit.jupiter.params.provider.Arguments;
-import org.junit.jupiter.params.provider.MethodSource;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
-import java.util.stream.Stream;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
@@ -86,33 +82,6 @@ class HttpProductionProxyFeatureWorkflowContractE2ETest
extends AbstractProducti
"create table", "alter table", "drop table", "create index", "drop
index", "migrate", "migration", "backfill", "data probe", "physical metadata",
"register storage unit", "alter storage unit", "unregister storage
unit");
- @ParameterizedTest(name = "{0}")
- @MethodSource("featureWorkflowScenarios")
- void assertPlanManualApplyAndValidateFeatureWorkflowThroughProxy(final
String scenarioName, final FeatureWorkflowScenario scenario) throws
IOException, InterruptedException {
- try (MCPInteractionClient interactionClient =
createOpenedInteractionClient()) {
- assertThat(interactionClient.listTools().stream().map(each ->
String.valueOf(each.get("name"))).toList(), hasItem(scenario.toolName()));
- Map<String, Object> actualPlanResponse =
planWorkflow(interactionClient, scenario.toolName(),
createPlanArguments(scenario));
- assertThat(String.valueOf(actualPlanResponse.get("current_step")),
is("review"));
-
assertTrue(String.valueOf(getObjectListOrEmpty(actualPlanResponse.get("distsql_artifacts"))).contains(scenario.expectedDistSQLToken()));
- List<String> actualResourceUris =
getObjectListOrEmpty(actualPlanResponse.get("resources_to_read")).stream()
- .map(each -> String.valueOf(each.get("uri"))).toList();
- List<String> expectedResourceUris =
scenario.resourceUriTemplates().stream()
- .map(each -> String.format(each,
getLogicalDatabaseName())).toList();
- assertTrue(actualResourceUris.containsAll(expectedResourceUris));
- String planId = String.valueOf(actualPlanResponse.get("plan_id"));
- Map<String, Object> actualManualApplyResponse =
interactionClient.call(APPLY_TOOL_NAME, Map.of("plan_id", planId,
"execution_mode", "manual-only"));
-
assertThat(String.valueOf(actualManualApplyResponse.get("status")),
is("awaiting-manual-execution"));
-
assertThat(getObjectListOrEmpty(actualManualApplyResponse.get("step_results")).size(),
is(0));
- assertNoForbiddenArtifacts(actualManualApplyResponse);
- assertModelFacingPayloadContract(actualManualApplyResponse);
- Map<String, Object> actualValidationResponse =
interactionClient.call(VALIDATE_TOOL_NAME, Map.of("plan_id", planId));
- assertThat(String.valueOf(actualValidationResponse.get("status")),
is("failed"));
-
assertThat(String.valueOf(actualValidationResponse.get("overall_status")),
is("failed"));
-
assertFalse(getObjectListOrEmpty(actualValidationResponse.get("issues")).isEmpty());
- assertModelFacingPayloadContract(actualValidationResponse);
- }
- }
-
@Test
void assertBroadcastWorkflowCanBeAppliedAndValidatedThroughProxy() throws
IOException, InterruptedException {
try (MCPInteractionClient interactionClient =
createOpenedInteractionClient()) {
@@ -367,34 +336,6 @@ class HttpProductionProxyFeatureWorkflowContractE2ETest
extends AbstractProducti
assertThat(String.valueOf(actual.get("status")).toUpperCase(Locale.ENGLISH),
is(expectedStatus));
}
- private static Stream<Arguments> featureWorkflowScenarios() {
- return Stream.of(
- Arguments.of("broadcast", new
FeatureWorkflowScenario(BROADCAST_PLAN_TOOL_NAME,
- Map.of("operation_type", "create", "tables",
"orders"), "CREATE BROADCAST TABLE RULE", List.of())),
- Arguments.of("readwrite-splitting", new
FeatureWorkflowScenario(READWRITE_SPLITTING_PLAN_TOOL_NAME,
- Map.of("operation_type", "create", "rule",
"readwrite_ds", "write_storage_unit", "ds_0",
- "read_storage_units", "ds_1",
"transactional_read_query_strategy", "DYNAMIC"),
- "CREATE READWRITE_SPLITTING RULE",
List.of("shardingsphere://databases/%s/storage-units"))),
- Arguments.of("shadow", new
FeatureWorkflowScenario(SHADOW_PLAN_TOOL_NAME,
- Map.of("operation_type", "create", "rule",
"shadow_rule", "source_storage_unit", "ds_0",
- "shadow_storage_unit", "ds_shadow", "table",
"orders", "algorithm_type", "VALUE_MATCH",
- "algorithm_properties", Map.of("operation",
"insert", "column", "order_id", "value", "1")),
- "CREATE SHADOW RULE",
List.of("shardingsphere://databases/%s/storage-units",
"shardingsphere://databases/%s/single-tables",
-
"shardingsphere://databases/%s/single-tables/orders"))),
- Arguments.of("sharding", new
FeatureWorkflowScenario(SHARDING_PLAN_TOOL_NAME,
- Map.of("operation_type", "create", "table", "orders",
"column", "order_id",
- "data_nodes", "ds_0.orders", "strategy_type",
"standard", "algorithm_type", "INLINE",
- "algorithm_properties",
Map.of("algorithm-expression", "orders")),
- "CREATE SHARDING TABLE RULE",
List.of("shardingsphere://databases/%s/storage-units",
"shardingsphere://databases/%s/single-tables",
-
"shardingsphere://databases/%s/single-tables/orders"))));
- }
-
- private Map<String, Object> createPlanArguments(final
FeatureWorkflowScenario scenario) {
- Map<String, Object> result = new
LinkedHashMap<>(scenario.planArguments());
- result.put("database", getLogicalDatabaseName());
- return result;
- }
-
private void assertNoForbiddenArtifacts(final Map<String, Object> payload)
{
String actualPayload =
String.valueOf(payload).toLowerCase(Locale.ENGLISH);
FORBIDDEN_ARTIFACT_TOKENS.forEach(each ->
assertFalse(actualPayload.contains(each)));
@@ -443,6 +384,4 @@ class HttpProductionProxyFeatureWorkflowContractE2ETest
extends AbstractProducti
assertModelFacingPayloadContract(actual);
}
- private record FeatureWorkflowScenario(String toolName, Map<String,
Object> planArguments, String expectedDistSQLToken, List<String>
resourceUriTemplates) {
- }
}
diff --git
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/HttpProductionProxyMaskWorkflowE2ETest.java
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/HttpProductionProxyMaskWorkflowE2ETest.java
index 8a26254cae5..7dd64625a2a 100644
---
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/HttpProductionProxyMaskWorkflowE2ETest.java
+++
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/HttpProductionProxyMaskWorkflowE2ETest.java
@@ -32,7 +32,6 @@ import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.is;
-import static org.junit.jupiter.api.Assertions.assertFalse;
@EnabledIf("org.apache.shardingsphere.test.e2e.mcp.env.MCPE2ECondition#isDockerEnabled")
class HttpProductionProxyMaskWorkflowE2ETest extends
AbstractProductionProxyWorkflowE2ETest {
@@ -59,7 +58,7 @@ class HttpProductionProxyMaskWorkflowE2ETest extends
AbstractProductionProxyWork
}
@Test
- void assertPlanApplyValidateAndRejectUnsupportedMaskWorkflowThroughProxy()
throws IOException, InterruptedException {
+ void assertPlanApplyAndValidateMaskWorkflowThroughProxy() throws
IOException, InterruptedException {
try (MCPInteractionClient interactionClient =
createOpenedInteractionClient()) {
Map<String, Object> actualCreatePlanResponse =
interactionClient.call(PLAN_TOOL_NAME,
Map.of("database", getLogicalDatabaseName(), "table",
"orders", "column", "status",
@@ -75,14 +74,6 @@ class HttpProductionProxyMaskWorkflowE2ETest extends
AbstractProductionProxyWork
String.valueOf(getObjectListOrEmpty(getValidationSection(actualCreateValidationResponse,
"rule").get("evidence")).getFirst().get("algorithm_type"))
.toUpperCase(Locale.ENGLISH),
is("KEEP_FIRST_N_LAST_M"));
- Map<String, Object> actualUnsupportedPlanResponse =
interactionClient.call(PLAN_TOOL_NAME,
- Map.of("database", getLogicalDatabaseName(), "table",
"orders", "column", "status",
- "natural_language_intent", "update status mask
rule", "algorithm_type", "KEEP_FIRST_N_LAST_M",
- "primary_algorithm_properties", Map.of("first-n",
"2", "last-m", "2", "replace-char", "#")));
-
assertThat(String.valueOf(actualUnsupportedPlanResponse.get("status")),
is("failed"));
- assertThat(getIssueCodes(actualUnsupportedPlanResponse),
hasItem(WorkflowIssueCode.WORKFLOW_STATUS_INVALID));
-
assertFalse(String.valueOf(actualUnsupportedPlanResponse).toLowerCase(Locale.ENGLISH).contains("alter"));
-
assertThat(getObjectListOrEmpty(actualUnsupportedPlanResponse.get("distsql_artifacts")).size(),
is(0));
}
}
diff --git
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/ProductionMySQLReadOnlySQLRuntimeE2ETest.java
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/ProductionMySQLReadOnlySQLRuntimeE2ETest.java
index e5608bb12e9..fb099222a58 100644
---
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/ProductionMySQLReadOnlySQLRuntimeE2ETest.java
+++
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/ProductionMySQLReadOnlySQLRuntimeE2ETest.java
@@ -24,7 +24,6 @@ import
org.apache.shardingsphere.test.e2e.mcp.support.transport.MCPInteractionPa
import
org.apache.shardingsphere.test.e2e.mcp.support.transport.MCPPayloadAssertions;
import
org.apache.shardingsphere.test.e2e.mcp.support.transport.client.MCPInteractionClient;
import org.junit.jupiter.api.condition.EnabledIf;
-import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
@@ -41,8 +40,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
@EnabledIf("org.apache.shardingsphere.test.e2e.mcp.env.MCPE2ECondition#isDockerEnabled")
class ProductionMySQLReadOnlySQLRuntimeE2ETest extends
AbstractProductionMySQLRuntimeE2ETest {
- private static final long SLOW_ELICITATION_MILLIS = 11_000L;
-
@Override
protected boolean useSharedRuntimeFixture() {
return true;
@@ -97,51 +94,6 @@ class ProductionMySQLReadOnlySQLRuntimeE2ETest extends
AbstractProductionMySQLRu
}
}
- @ParameterizedTest(name = "{0}")
- @MethodSource("semanticPrimaryTransport")
- void assertRejectExecuteMultiStatementWithActualMySQLBackend(final String
name, final RuntimeTransport transport) throws IOException,
InterruptedException {
- useTransport(transport);
- try (MCPInteractionClient interactionClient =
createOpenedInteractionClient()) {
- Map<String, Object> actual =
interactionClient.call("database_gateway_execute_query",
- Map.of("database", LOGICAL_DATABASE_NAME, "schema",
LOGICAL_DATABASE_NAME, "sql", "SELECT 1; SELECT 2"));
- assertRecoveryResponse(actual, "Only one SQL statement is
allowed.", "multiple_sql_statements");
- }
- }
-
- @ParameterizedTest(name = "{0}")
- @MethodSource("semanticPrimaryTransport")
- void assertRejectExplainUpdateWithActualMySQLBackend(final String name,
final RuntimeTransport transport) throws IOException, InterruptedException {
- useTransport(transport);
- try (MCPInteractionClient interactionClient =
createOpenedInteractionClient()) {
- Map<String, Object> actual =
interactionClient.call("database_gateway_execute_explain_query",
- Map.of("database", LOGICAL_DATABASE_NAME, "schema",
LOGICAL_DATABASE_NAME, "sql", "UPDATE orders SET status = 'DONE' WHERE order_id
= 1",
- "explain_sql", "EXPLAIN UPDATE orders SET status =
'DONE' WHERE order_id = 1"));
- assertRecoveryResponse(actual,
"database_gateway_execute_explain_query only supports QUERY statements as the
explained SQL.");
- }
- }
-
- @ParameterizedTest(name = "{0}")
- @MethodSource("semanticPrimaryTransport")
- void assertRejectLockingReadFromReadOnlyToolWithActualMySQLBackend(final
String name, final RuntimeTransport transport) throws IOException,
InterruptedException {
- useTransport(transport);
- try (MCPInteractionClient interactionClient =
createOpenedInteractionClient()) {
- Map<String, Object> actual =
interactionClient.call("database_gateway_execute_query",
- Map.of("database", LOGICAL_DATABASE_NAME, "schema",
LOGICAL_DATABASE_NAME, "sql", "SELECT * FROM orders FOR UPDATE"));
- assertRecoveryResponse(actual, "Locking read statements such as
SELECT ... FOR UPDATE are not supported by the MCP read-only contract.");
- }
- }
-
- @ParameterizedTest(name = "{0}")
- @MethodSource("semanticPrimaryTransport")
- void assertRejectLockingReadFromUpdateToolWithActualMySQLBackend(final
String name, final RuntimeTransport transport) throws IOException,
InterruptedException {
- useTransport(transport);
- try (MCPInteractionClient interactionClient =
createOpenedInteractionClient()) {
- Map<String, Object> actual =
interactionClient.call("database_gateway_execute_update",
- createExecuteUpdateArguments("SELECT * FROM orders FOR
UPDATE"));
- assertRecoveryResponse(actual, "Locking read statements such as
SELECT ... FOR UPDATE are not supported by the MCP read-only contract.");
- }
- }
-
@ParameterizedTest(name = "{0}")
@MethodSource("dualTransports")
void assertElicitMaskPlanningWithActualMySQLBackend(final String name,
final RuntimeTransport transport) throws IOException {
@@ -173,32 +125,6 @@ class ProductionMySQLReadOnlySQLRuntimeE2ETest extends
AbstractProductionMySQLRu
}
}
- @Test
- void assertElicitationCanExceedSdkDefaultRequestTimeout() throws
IOException {
- useTransport(RuntimeTransport.STDIO);
- List<McpSchema.ElicitRequest> actualElicitationRequests = new
CopyOnWriteArrayList<>();
- try (McpSyncClient client =
createElicitationClient(RuntimeTransport.STDIO, actualElicitationRequests,
(requests, request) -> {
- try {
- Thread.sleep(SLOW_ELICITATION_MILLIS);
- } catch (final InterruptedException ex) {
- Thread.currentThread().interrupt();
- throw new IllegalStateException(ex);
- }
- return createElicitationResult(requests, request);
- })) {
- client.initialize();
- McpSchema.CallToolResult actual = client.callTool(new
McpSchema.CallToolRequest(MASK_PLAN_TOOL_NAME, Map.of(
- "database", LOGICAL_DATABASE_NAME,
- "schema", LOGICAL_DATABASE_NAME,
- "table", "orders",
- "column", "status",
- "operation_type", "create",
- "algorithm_type", "MASK_FROM_X_TO_Y")));
-
assertThat(String.valueOf(MCPInteractionPayloads.getRequiredObjectValue(actual.structuredContent(),
"structuredContent").get("status")), is("planned"));
- assertElicitationRequest(actualElicitationRequests);
- }
- }
-
@ParameterizedTest(name = "{0}")
@MethodSource("semanticPrimaryTransport")
void assertRejectSequenceResourceWithActualMySQLBackend(final String name,
final RuntimeTransport transport) throws IOException, InterruptedException {
diff --git
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/ProductionMySQLRuntimeE2ETest.java
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/ProductionMySQLRuntimeE2ETest.java
index 2cafe425b7c..e7d38238591 100644
---
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/ProductionMySQLRuntimeE2ETest.java
+++
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/ProductionMySQLRuntimeE2ETest.java
@@ -17,8 +17,6 @@
package org.apache.shardingsphere.test.e2e.mcp.runtime.production;
-import org.apache.shardingsphere.mcp.bootstrap.transport.MCPTransportConstants;
-import
org.apache.shardingsphere.mcp.support.markdown.MCPMarkdownResourceLoader;
import org.apache.shardingsphere.test.e2e.mcp.support.runtime.RuntimeTransport;
import
org.apache.shardingsphere.test.e2e.mcp.support.transport.MCPPayloadAssertions;
import
org.apache.shardingsphere.test.e2e.mcp.support.transport.client.MCPInteractionClient;
@@ -27,7 +25,6 @@ import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import java.io.IOException;
-import java.util.Collection;
import java.util.List;
import java.util.Map;
@@ -66,102 +63,6 @@ class ProductionMySQLRuntimeE2ETest extends
AbstractProductionMySQLRuntimeE2ETes
}
}
- @ParameterizedTest(name = "{0}")
- @MethodSource("semanticPrimaryTransport")
- void assertServiceCapabilitiesResourceWithActualMySQLBackend(final String
name, final RuntimeTransport transport) throws IOException,
InterruptedException {
- useTransport(transport);
- try (MCPInteractionClient interactionClient =
createOpenedInteractionClient()) {
- Map<String, Object> actual =
interactionClient.readResource("shardingsphere://capabilities");
- assertFalse(((Collection<?>)
actual.get("supportedStatementClasses")).isEmpty());
- assertFalse(((List<?>) actual.get("completionTargets")).isEmpty());
- assertFalse(((List<?>)
actual.get("resourceNavigation")).isEmpty());
- assertFalse(actual.containsKey("supportedTools"));
- }
- }
-
- @ParameterizedTest(name = "{0}")
- @MethodSource("semanticPrimaryTransport")
- void assertListToolsWithActualMySQLBackend(final String name, final
RuntimeTransport transport) throws IOException, InterruptedException {
- useTransport(transport);
- try (MCPInteractionClient interactionClient =
createOpenedInteractionClient()) {
- List<Map<String, Object>> actual = interactionClient.listTools();
- assertOfficialToolNames(actual.stream().map(each ->
String.valueOf(each.get("name"))).toList());
- assertToolDefinition(actual, "database_gateway_search_metadata",
"Search Metadata", "", "object_types", "array");
- assertToolDefinition(actual,
"database_gateway_validate_runtime_database", "Validate Runtime Database",
"database", "database", "string");
- assertToolDefinition(actual, "database_gateway_execute_query",
"Execute Query SQL", "sql", "timeout_ms", "integer");
- assertToolDefinition(actual, "database_gateway_execute_update",
"Preview or Execute Side-Effecting SQL", "sql", "timeout_ms", "integer");
- }
- }
-
- @ParameterizedTest(name = "{0}")
- @MethodSource("semanticPrimaryTransport")
- void assertListResourcesWithActualMySQLBackend(final String name, final
RuntimeTransport transport) throws IOException, InterruptedException {
- useTransport(transport);
- try (MCPInteractionClient interactionClient =
createOpenedInteractionClient()) {
-
assertTrue(getResources(interactionClient.listResources()).stream().anyMatch(each
-> "shardingsphere://capabilities".equals(each.get("uri"))));
- }
- }
-
- @ParameterizedTest(name = "{0}")
- @MethodSource("semanticPrimaryTransport")
- void assertListResourceTemplatesWithActualMySQLBackend(final String name,
final RuntimeTransport transport) throws IOException, InterruptedException {
- useTransport(transport);
- try (MCPInteractionClient interactionClient =
createOpenedInteractionClient()) {
- List<String> actualTemplates =
getResourceTemplates(interactionClient.listResourceTemplates()).stream()
- .map(each ->
String.valueOf(each.get("uriTemplate"))).toList();
-
assertTrue(actualTemplates.contains("shardingsphere://databases/{database}"));
-
assertTrue(actualTemplates.contains("shardingsphere://databases/{database}/schemas/{schema}"));
-
assertTrue(actualTemplates.contains("shardingsphere://databases/{database}/schemas/{schema}/tables/{table}/columns/{column}"));
- }
- }
-
- @ParameterizedTest(name = "{0}")
- @MethodSource("semanticPrimaryTransport")
- void assertRejectUnsupportedResourceUriWithActualMySQLBackend(final String
name, final RuntimeTransport transport) throws IOException,
InterruptedException {
- useTransport(transport);
- try (MCPInteractionClient interactionClient =
createOpenedInteractionClient()) {
- String requestId = "resources-read-unsupported-1";
- Map<String, Object> actual =
interactionClient.sendRawRequest(requestId, "resources/read", Map.of("uri",
"unsupported://resource"));
- assertJsonRpcErrorWithoutResult(actual, requestId);
-
assertFalse(getObjectOrEmpty(actual.get("result")).containsKey("contents"));
-
assertThat(String.valueOf(getObjectOrEmpty(actual.get("error")).get("message")),
is("Resource not found"));
- }
- }
-
- @ParameterizedTest(name = "{0}")
- @MethodSource("semanticPrimaryTransport")
- void assertRejectUnsupportedToolNameWithActualMySQLBackend(final String
name, final RuntimeTransport transport) throws IOException,
InterruptedException {
- useTransport(transport);
- try (MCPInteractionClient interactionClient =
createOpenedInteractionClient()) {
- String requestId = "tools-call-unsupported-1";
- Map<String, Object> actual =
interactionClient.sendRawRequest(requestId, "tools/call", Map.of("name",
"unsupported_tool", "arguments", Map.of()));
- assertJsonRpcErrorWithoutResult(actual, requestId);
-
assertFalse(getObjectOrEmpty(actual.get("result")).containsKey("isError"));
- }
- }
-
- @ParameterizedTest(name = "{0}")
- @MethodSource("semanticPrimaryTransport")
- void
assertInitializeExposesMarkdownInstructionsWithActualMySQLBackend(final String
name, final RuntimeTransport transport) throws IOException,
InterruptedException {
- useTransport(transport);
- try (MCPInteractionClient interactionClient =
createOpenedInteractionClient()) {
- Map<String, Object> actualResult =
getObjectOrEmpty(interactionClient.getInitializePayload().get("result"));
- String actualInstructions =
String.valueOf(actualResult.get("instructions"));
- assertThat(actualInstructions,
is(MCPMarkdownResourceLoader.load(MCPTransportConstants.SERVER_INSTRUCTIONS_RESOURCE,
"server instruction")));
- assertThat(actualInstructions.lines().findFirst().orElse(""),
is("Apache ShardingSphere MCP."));
- }
- }
-
- @ParameterizedTest(name = "{0}")
- @MethodSource("semanticPrimaryTransport")
- void
assertServerInstructionsAreNotListedAsResourceWithActualMySQLBackend(final
String name, final RuntimeTransport transport) throws IOException,
InterruptedException {
- useTransport(transport);
- try (MCPInteractionClient interactionClient =
createOpenedInteractionClient()) {
-
assertFalse(getResources(interactionClient.listResources()).stream()
- .anyMatch(each ->
MCPTransportConstants.SERVER_INSTRUCTIONS_RESOURCE.equals(String.valueOf(each.get("uri")))));
- }
- }
-
@ParameterizedTest(name = "{0}")
@MethodSource("assertReadSingleMetadataResourceCases")
void assertReadSingleMetadataResourceWithActualMySQLBackend(final String
name, final RuntimeTransport transport,
diff --git
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/ProductionMySQLSQLRuntimeE2ETest.java
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/ProductionMySQLSQLRuntimeE2ETest.java
index 403a5c898c0..1e784c097be 100644
---
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/ProductionMySQLSQLRuntimeE2ETest.java
+++
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/ProductionMySQLSQLRuntimeE2ETest.java
@@ -52,18 +52,6 @@ class ProductionMySQLSQLRuntimeE2ETest extends
AbstractProductionMySQLRuntimeE2E
}
}
- @ParameterizedTest(name = "{0}")
- @MethodSource("semanticPrimaryTransport")
- void
assertExecuteUpdateWithoutApprovalArgumentWithActualMySQLBackend(final String
name, final RuntimeTransport transport) throws IOException,
InterruptedException {
- useTransport(transport);
- try (MCPInteractionClient interactionClient =
createOpenedInteractionClient()) {
- Map<String, Object> actual =
interactionClient.call("database_gateway_execute_update",
- createExecuteUpdateArguments("UPDATE orders SET status =
status WHERE order_id = -1"));
- assertThat(String.valueOf(actual.get("result_kind")),
is("update_count"));
- assertThat(String.valueOf(actual.get("affected_rows")), is("0"));
- }
- }
-
@ParameterizedTest(name = "{0}")
@MethodSource("semanticPrimaryTransport")
void assertExecuteRollbackWithActualMySQLBackend(final String name, final
RuntimeTransport transport) throws SQLException, IOException,
InterruptedException {
@@ -79,16 +67,6 @@ class ProductionMySQLSQLRuntimeE2ETest extends
AbstractProductionMySQLRuntimeE2E
}
}
- @ParameterizedTest(name = "{0}")
- @MethodSource("semanticPrimaryTransport")
- void assertRejectBlankSavepointNameWithActualMySQLBackend(final String
name, final RuntimeTransport transport) throws IOException,
InterruptedException {
- useTransport(transport);
- try (MCPInteractionClient interactionClient =
createOpenedInteractionClient()) {
- Map<String, Object> actual =
interactionClient.call("database_gateway_execute_update",
createExecuteUpdateArguments("SAVEPOINT"));
- assertRecoveryResponse(actual, "Savepoint name is required.");
- }
- }
-
@ParameterizedTest(name = "{0}")
@MethodSource("semanticPrimaryTransport")
void assertExecuteSavepointFlowWithActualMySQLBackend(final String name,
final RuntimeTransport transport) throws SQLException, IOException,
InterruptedException {
diff --git
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/ProductionPostgreSQLRuntimeE2ETest.java
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/ProductionPostgreSQLRuntimeE2ETest.java
index b9fcde1eb7b..bdaae66514d 100644
---
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/ProductionPostgreSQLRuntimeE2ETest.java
+++
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/ProductionPostgreSQLRuntimeE2ETest.java
@@ -39,7 +39,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
class ProductionPostgreSQLRuntimeE2ETest extends
AbstractProductionPostgreSQLRuntimeE2ETest {
@ParameterizedTest(name = "{0}")
- @MethodSource("dualTransports")
+ @MethodSource("semanticPrimaryTransport")
void assertPostgreSQLRuntimeContract(final String name, final
RuntimeTransport transport) throws IOException, InterruptedException {
useTransport(transport);
try (MCPInteractionClient interactionClient =
createOpenedInteractionClient()) {
diff --git
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/support/transport/MCPPayloadAssertions.java
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/support/transport/MCPPayloadAssertions.java
index 6e9798c71f6..b0c21ee6bf0 100644
---
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/support/transport/MCPPayloadAssertions.java
+++
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/support/transport/MCPPayloadAssertions.java
@@ -25,7 +25,6 @@ import java.util.Map;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
-import static org.junit.jupiter.api.Assertions.assertTrue;
/**
* MCP payload assertions.
@@ -79,49 +78,12 @@ public final class MCPPayloadAssertions {
return getItems(payload).stream().filter(each ->
expectedValue.equals(each.get(key))).findFirst().orElseThrow();
}
- /**
- * Get item values.
- *
- * @param payload MCP payload
- * @param key item key
- * @return item values
- */
- public static List<String> getItemValues(final Map<String, Object>
payload, final String key) {
+ private static List<String> getItemValues(final Map<String, Object>
payload, final String key) {
return getItems(payload).stream().map(each ->
String.valueOf(each.get(key))).toList();
}
- /**
- * Get items.
- *
- * @param payload MCP payload
- * @return items
- */
- public static List<Map<String, Object>> getItems(final Map<String, Object>
payload) {
+ private static List<Map<String, Object>> getItems(final Map<String,
Object> payload) {
return MCPInteractionPayloads.getRequiredObjectList(payload, "items");
}
- /**
- * Assert tool definition.
- *
- * @param tools tool definitions
- * @param toolName tool name
- * @param expectedTitle expected title
- * @param expectedRequiredField expected required field
- * @param expectedPropertyField expected property field
- * @param expectedPropertyType expected property type
- */
- public static void assertToolDefinition(final List<Map<String, Object>>
tools, final String toolName, final String expectedTitle,
- final String
expectedRequiredField, final String expectedPropertyField, final String
expectedPropertyType) {
- Map<String, Object> actualTool = tools.stream().filter(each ->
toolName.equals(each.get("name"))).findFirst().orElseThrow(IllegalStateException::new);
- assertThat(String.valueOf(actualTool.get("title")), is(expectedTitle));
- Map<String, Object> actualInputSchema =
MCPInteractionPayloads.getRequiredObject(actualTool, "inputSchema");
- List<String> actualRequiredFields = ((List<?>)
actualInputSchema.get("required")).stream().map(String::valueOf).toList();
- Map<String, Object> actualProperties =
MCPInteractionPayloads.getRequiredObject(actualInputSchema, "properties");
- Map<String, Object> actualProperty =
MCPInteractionPayloads.getRequiredObject(actualProperties,
expectedPropertyField);
- if (!expectedRequiredField.isEmpty()) {
- assertTrue(actualRequiredFields.contains(expectedRequiredField));
- }
- assertThat(String.valueOf(actualProperty.get("type")),
is(expectedPropertyType));
- }
-
}
diff --git
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/support/transport/MCPPayloadAssertionsTest.java
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/support/transport/MCPPayloadAssertionsTest.java
index dd6bc55db5f..dbf091aca5a 100644
---
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/support/transport/MCPPayloadAssertionsTest.java
+++
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/support/transport/MCPPayloadAssertionsTest.java
@@ -47,28 +47,6 @@ class MCPPayloadAssertionsTest {
assertThat(MCPPayloadAssertions.findItem(createPayload(), "name",
"users"), is(Map.of("name", "users", "type", "table")));
}
- @Test
- void assertGetItemValues() {
- assertThat(MCPPayloadAssertions.getItemValues(createPayload(),
"name"), is(List.of("orders", "users")));
- }
-
- @Test
- void assertGetItems() {
- assertThat(MCPPayloadAssertions.getItems(createPayload()),
is(List.of(Map.of("name", "orders", "type", "table"), Map.of("name", "users",
"type", "table"))));
- }
-
- @Test
- void assertAssertToolDefinition() {
- MCPPayloadAssertions.assertToolDefinition(List.of(Map.of(
- "name", "database_gateway_execute_query",
- "title", "Execute Query",
- "inputSchema", Map.of(
- "type", "object",
- "required", List.of("sql"),
- "properties", Map.of("sql", Map.of("type",
"string"))))),
- "database_gateway_execute_query", "Execute Query", "sql",
"sql", "string");
- }
-
private Map<String, Object> createPayload() {
return Map.of("items", List.of(Map.of("name", "orders", "type",
"table"), Map.of("name", "users", "type", "table")));
}