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 f40fd90435a Harden shared fixture boundaries (#38852)
f40fd90435a is described below
commit f40fd90435ac28ce6eca996ebe7c29cbb3973673
Author: Liang Zhang <[email protected]>
AuthorDate: Tue Jun 16 09:12:22 2026 +0800
Harden shared fixture boundaries (#38852)
* Expose storage unit metadata resources
Expose Proxy-backed storage unit and single table metadata through MCP
resources,
search metadata, completion targets, and recovery guidance.
Add contract and Proxy-backed E2E coverage to verify that MCP clients can
read
storage unit information from a logical database.
* Expose storage unit metadata resources
Expose Proxy-backed storage unit and single table metadata through MCP
resources,
search metadata, completion targets, and recovery guidance.
Add contract and Proxy-backed E2E coverage to verify that MCP clients can
read
storage unit information from a logical database.
* Expose storage unit metadata resources
Expose Proxy-backed storage unit and single table metadata through MCP
resources,
search metadata, completion targets, and recovery guidance.
Add contract and Proxy-backed E2E coverage to verify that MCP clients can
read
storage unit information from a logical database.
* Expose storage unit metadata resources
Expose Proxy-backed storage unit and single table metadata through MCP
resources,
search metadata, completion targets, and recovery guidance.
Add contract and Proxy-backed E2E coverage to verify that MCP clients can
read
storage unit information from a logical database.
* Expose storage unit metadata resources
Expose Proxy-backed storage unit and single table metadata through MCP
resources,
search metadata, completion targets, and recovery guidance.
Add contract and Proxy-backed E2E coverage to verify that MCP clients can
read
storage unit information from a logical database.
* Expose storage unit metadata resources
Expose Proxy-backed storage unit and single table metadata through MCP
resources,
search metadata, completion targets, and recovery guidance.
Add contract and Proxy-backed E2E coverage to verify that MCP clients can
read
storage unit information from a logical database.
* Harden shared fixture boundaries
- Use transport-specific reusable homes for packaged distribution tests
- Rename Proxy shared fixture opt-in to make read-only scope explicit
- Keep shared fixture boundary decisions in test code and verification
evidence
---
.../e2e/mcp/llm/suite/smoke/LLMSmokeE2ETest.java | 8 +-
.../suite/usability/LLMUsabilitySuiteE2ETest.java | 22 +++-
.../AbstractProductionMySQLRuntimeE2ETest.java | 116 ++++++++++++++++++---
.../AbstractProductionProxyWorkflowE2ETest.java | 42 ++++++--
...sportParameterizedProductionRuntimeE2ETest.java | 6 ++
.../HttpProductionProxyEncryptWorkflowE2ETest.java | 1 +
...ductionProxyFeatureWorkflowContractE2ETest.java | 1 +
.../HttpProductionProxyMaskWorkflowE2ETest.java | 1 +
.../production/PackagedDistributionE2ETest.java | 24 +++--
.../production/ProductionMySQLRuntimeE2ETest.java | 33 +++---
.../ProductionMySQLSQLRuntimeE2ETest.java | 46 ++++----
.../ProductionRuntimeTransportSmokeE2ETest.java | 53 ++++++++++
.../AbstractHttpProgrammaticRuntimeE2ETest.java | 86 ++++++++++++---
...t.java => AbstractHttpProtocolOnlyE2ETest.java} | 99 ++++++------------
...tractSharedHttpProgrammaticRuntimeE2ETest.java} | 24 ++---
.../FeatureWorkflowContractE2ETest.java | 2 +-
.../HttpTransportApprovalSafetyE2ETest.java | 2 +-
.../HttpTransportBaselineContractE2ETest.java | 2 +-
.../HttpTransportCompletionE2ETest.java | 2 +-
.../programmatic/HttpTransportContractE2ETest.java | 2 +-
.../HttpTransportProtocolContractE2ETest.java | 9 +-
.../programmatic/HttpTransportRecoveryE2ETest.java | 2 +-
.../programmatic/HttpTransportSecurityE2ETest.java | 9 +-
.../HttpTransportSessionLifecycleE2ETest.java | 9 +-
.../programmatic/MetadataDiscoveryE2ETest.java | 8 +-
.../PackagedDistributionTestSupport.java | 26 +++++
26 files changed, 434 insertions(+), 201 deletions(-)
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/smoke/LLMSmokeE2ETest.java
index e16872f461e..01cf010c4d3 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/smoke/LLMSmokeE2ETest.java
@@ -33,6 +33,7 @@ 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.TestInstance;
import org.junit.jupiter.api.condition.EnabledIf;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
@@ -48,6 +49,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
@Tag("llm-e2e")
@EnabledIf("isEnabled")
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class LLMSmokeE2ETest extends AbstractConfigBackedRuntimeE2ETest {
private static final String DATABASE_NAME = "logic_db";
@@ -86,12 +88,16 @@ class LLMSmokeE2ETest extends
AbstractConfigBackedRuntimeE2ETest {
}
}
- @AfterEach
+ @AfterAll
void closeRuntimeFixture() {
if (null != currentRuntimeFixture) {
currentRuntimeFixture.close();
currentRuntimeFixture = null;
}
+ }
+
+ @AfterEach
+ void clearCurrentTransport() {
currentTransport = null;
}
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 f550e2f9018..6639df93a99 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
@@ -32,6 +32,7 @@ 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.TestInstance;
import org.junit.jupiter.api.condition.EnabledIf;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
@@ -44,6 +45,7 @@ import java.util.stream.Stream;
@Tag("llm-e2e")
@EnabledIf("isEnabled")
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class LLMUsabilitySuiteE2ETest extends AbstractConfigBackedRuntimeE2ETest {
private static final String RUNTIME_KIND = "mysql";
@@ -54,6 +56,8 @@ 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 LLMRuntimeSupport.ModelRuntime llmRuntime;
private final LLMRuntimeFixtureFactory runtimeFixtureFactory = new
LLMRuntimeFixtureFactory();
@@ -79,12 +83,16 @@ class LLMUsabilitySuiteE2ETest extends
AbstractConfigBackedRuntimeE2ETest {
}
}
- @AfterEach
+ @AfterAll
void closeRuntimeFixture() {
if (null != currentRuntimeFixture) {
currentRuntimeFixture.close();
currentRuntimeFixture = null;
}
+ }
+
+ @AfterEach
+ void clearCurrentTransport() {
currentTransport = null;
}
@@ -93,9 +101,15 @@ class LLMUsabilitySuiteE2ETest extends
AbstractConfigBackedRuntimeE2ETest {
}
static Stream<Arguments> getTestCases() {
- return Stream.of(
- Arguments.of("llm-usability-mysql-http",
RuntimeTransport.HTTP),
- Arguments.of("llm-usability-mysql-stdio",
RuntimeTransport.STDIO));
+ 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() {
+ return
Boolean.parseBoolean(System.getProperty(FULL_TRANSPORT_MATRIX_PROPERTY,
"false"));
}
@ParameterizedTest(name = "{0}")
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 24904c31217..325368d8077 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
@@ -17,11 +17,6 @@
package org.apache.shardingsphere.test.e2e.mcp.runtime.production;
-import static org.hamcrest.Matchers.containsInAnyOrder;
-import static org.hamcrest.Matchers.hasItems;
-import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.isA;
-
import io.modelcontextprotocol.client.McpClient;
import io.modelcontextprotocol.client.McpSyncClient;
import
io.modelcontextprotocol.client.transport.HttpClientStreamableHttpTransport;
@@ -42,8 +37,10 @@ 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.apache.shardingsphere.test.e2e.mcp.support.transport.client.MCPStdioLogbackConfiguration;
+import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assumptions;
+import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.condition.EnabledIf;
import org.junit.jupiter.params.provider.Arguments;
import org.testcontainers.containers.GenericContainer;
@@ -59,9 +56,14 @@ import java.util.Map;
import java.util.stream.Stream;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsInAnyOrder;
+import static org.hamcrest.Matchers.hasItems;
+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;
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@EnabledIf("isEnabled")
abstract class AbstractProductionMySQLRuntimeE2ETest extends
AbstractTransportParameterizedProductionRuntimeE2ETest {
@@ -77,6 +79,8 @@ abstract class AbstractProductionMySQLRuntimeE2ETest extends
AbstractTransportPa
private String physicalSchemaName;
+ private MySQLRuntimeFixture sharedRuntimeFixture;
+
protected GenericContainer<?> getContainer() {
return container;
}
@@ -87,6 +91,11 @@ abstract class AbstractProductionMySQLRuntimeE2ETest extends
AbstractTransportPa
@AfterEach
void tearDownContainer() {
+ if (useSharedRuntimeFixture()) {
+ container = null;
+ physicalSchemaName = null;
+ return;
+ }
if (null != container) {
container.stop();
container = null;
@@ -94,21 +103,59 @@ abstract class AbstractProductionMySQLRuntimeE2ETest
extends AbstractTransportPa
physicalSchemaName = null;
}
+ @AfterAll
+ void tearDownSharedContainer() {
+ if (null != sharedRuntimeFixture) {
+ sharedRuntimeFixture.close();
+ sharedRuntimeFixture = null;
+ }
+ }
+
@Override
protected void prepareRuntimeFixture() throws IOException {
Assumptions.assumeTrue(MySQLRuntimeTestSupport.isDockerAvailable(),
() ->
MySQLRuntimeTestSupport.createDockerRequiredMessage("Docker is required for the
MySQL-backed production runtime E2E test."));
- container = MySQLRuntimeTestSupport.createContainer();
- container.start();
+ if (useSharedRuntimeFixture()) {
+ prepareSharedRuntimeFixture();
+ return;
+ }
+ applyRuntimeFixture(createRuntimeFixture());
+ }
+
+ private void prepareSharedRuntimeFixture() throws IOException {
+ if (null == sharedRuntimeFixture) {
+ sharedRuntimeFixture = createRuntimeFixture();
+ }
+ applyRuntimeFixture(sharedRuntimeFixture);
+ }
+
+ private MySQLRuntimeFixture createRuntimeFixture() throws IOException {
+ GenericContainer<?> result = MySQLRuntimeTestSupport.createContainer();
+ boolean success = false;
try {
- MySQLRuntimeTestSupport.initializeDatabase(container);
- String detectedSchemaName =
MySQLRuntimeTestSupport.detectSchema(container);
- physicalSchemaName = detectedSchemaName.isEmpty() ?
PHYSICAL_DATABASE_NAME : detectedSchemaName;
+ result.start();
+ MySQLRuntimeTestSupport.initializeDatabase(result);
+ String detectedSchemaName =
MySQLRuntimeTestSupport.detectSchema(result);
+ success = true;
+ return new MySQLRuntimeFixture(result,
detectedSchemaName.isEmpty() ? PHYSICAL_DATABASE_NAME : detectedSchemaName);
} catch (final SQLException ex) {
throw new IOException(ex);
+ } finally {
+ if (!success) {
+ result.stop();
+ }
}
}
+ private void applyRuntimeFixture(final MySQLRuntimeFixture fixture) {
+ container = fixture.container();
+ physicalSchemaName = fixture.physicalSchemaName();
+ }
+
+ protected boolean useSharedRuntimeFixture() {
+ return false;
+ }
+
@Override
protected Map<String, RuntimeDatabaseConfiguration> getRuntimeDatabases() {
return MySQLRuntimeTestSupport.createRuntimeDatabases(container,
LOGICAL_DATABASE_NAME);
@@ -130,8 +177,16 @@ abstract class AbstractProductionMySQLRuntimeE2ETest
extends AbstractTransportPa
return runtimeTransports().map(each ->
Arguments.of(getTransportName(each), each));
}
+ protected static Stream<Arguments> dualTransports() {
+ return transports();
+ }
+
+ protected static Stream<Arguments> semanticPrimaryTransport() {
+ return semanticPrimaryRuntimeTransports().map(each ->
Arguments.of(getTransportName(each), each));
+ }
+
protected static Stream<Arguments> assertReadSingleMetadataResourceCases()
{
- return runtimeTransports().flatMap(each -> Stream.of(
+ return semanticPrimaryRuntimeTransports().flatMap(each -> Stream.of(
Arguments.of(getTransportName(each) + " database detail",
each, "shardingsphere://databases/logic_db", "database", LOGICAL_DATABASE_NAME),
Arguments.of(getTransportName(each) + " schema detail", each,
"shardingsphere://databases/logic_db/schemas/logic_db", "schema",
LOGICAL_DATABASE_NAME),
Arguments.of(getTransportName(each) + " table column detail",
each,
@@ -144,7 +199,7 @@ abstract class AbstractProductionMySQLRuntimeE2ETest
extends AbstractTransportPa
}
protected static Stream<Arguments>
assertReadCollectionMetadataResourceCases() {
- return runtimeTransports().flatMap(each -> Stream.of(
+ return semanticPrimaryRuntimeTransports().flatMap(each -> Stream.of(
Arguments.of(getTransportName(each) + " schemas list", each,
"shardingsphere://databases/logic_db/schemas", "schema",
List.of(LOGICAL_DATABASE_NAME)),
Arguments.of(getTransportName(each) + " tables list", each,
"shardingsphere://databases/logic_db/schemas/logic_db/tables", "table",
List.of("order_items", "orders")),
@@ -158,6 +213,10 @@ abstract class AbstractProductionMySQLRuntimeE2ETest
extends AbstractTransportPa
return Stream.of(RuntimeTransport.HTTP, RuntimeTransport.STDIO);
}
+ private static Stream<RuntimeTransport> semanticPrimaryRuntimeTransports()
{
+ return Stream.of(RuntimeTransport.HTTP);
+ }
+
protected static String getTransportName(final RuntimeTransport transport)
{
return RuntimeTransport.HTTP == transport ? "http" : "stdio";
}
@@ -274,7 +333,7 @@ abstract class AbstractProductionMySQLRuntimeE2ETest
extends AbstractTransportPa
assertFalse((Boolean) actual.get("would_execute"));
List<Map<String, Object>> nextActions =
getMapList(actual.get("next_actions"));
assertThat(nextActions.stream().map(each ->
String.valueOf(each.get("type"))).toList(), is(List.of("tool_call")));
- assertFalse(nextActions.get(0).containsKey("requires_user_approval"));
+
assertFalse(nextActions.getFirst().containsKey("requires_user_approval"));
}
protected void assertAiNativeSqlResult(final MCPInteractionClient
interactionClient) throws IOException, InterruptedException {
@@ -284,7 +343,7 @@ abstract class AbstractProductionMySQLRuntimeE2ETest
extends AbstractTransportPa
assertThat(String.valueOf(actual.get("row_object_status")),
is("available"));
assertThat(((List<?>) actual.get("row_objects")).size(), is(1));
assertThat(String.valueOf(actual.get("truncated")), is("true"));
-
assertThat(String.valueOf(getMapList(actual.get("next_actions")).get(0).get("type")),
is("ask_user"));
+
assertThat(String.valueOf(getMapList(actual.get("next_actions")).getFirst().get("type")),
is("ask_user"));
}
protected McpSyncClient createElicitationClient(final RuntimeTransport
transport, final List<McpSchema.ElicitRequest> elicitationRequests) throws
IOException {
@@ -322,13 +381,13 @@ abstract class AbstractProductionMySQLRuntimeE2ETest
extends AbstractTransportPa
elicitationRequests.add(request);
List<String> requiredFields =
getStringList(request.requestedSchema().get("required"));
return new
McpSchema.ElicitResult(McpSchema.ElicitResult.Action.ACCEPT, Map.of(
- requiredFields.get(0), "1",
+ requiredFields.getFirst(), "1",
requiredFields.get(1), "3"));
}
protected void assertElicitationRequest(final
List<McpSchema.ElicitRequest> actualRequests) {
assertThat(actualRequests.size(), is(1));
- McpSchema.ElicitRequest actual = actualRequests.get(0);
+ McpSchema.ElicitRequest actual = actualRequests.getFirst();
assertThat(actual.meta().get(MCPShardingSphereMetadataKeys.TOOL),
is(MASK_PLAN_TOOL_NAME));
assertFalse(String.valueOf(actual.meta().get(MCPShardingSphereMetadataKeys.PLAN_ID)).isBlank());
Map<String, Object> actualRequestedSchema = actual.requestedSchema();
@@ -376,4 +435,29 @@ abstract class AbstractProductionMySQLRuntimeE2ETest
extends AbstractTransportPa
return List.of(ProtocolVersions.MCP_2025_06_18,
ProtocolVersions.MCP_2025_11_25);
}
}
+
+ private static final class MySQLRuntimeFixture implements AutoCloseable {
+
+ private final GenericContainer<?> container;
+
+ private final String physicalSchemaName;
+
+ private MySQLRuntimeFixture(final GenericContainer<?> container, final
String physicalSchemaName) {
+ this.container = container;
+ this.physicalSchemaName = physicalSchemaName;
+ }
+
+ private GenericContainer<?> container() {
+ return container;
+ }
+
+ private String physicalSchemaName() {
+ return physicalSchemaName;
+ }
+
+ @Override
+ public void close() {
+ container.stop();
+ }
+ }
}
diff --git
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/AbstractProductionProxyWorkflowE2ETest.java
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/AbstractProductionProxyWorkflowE2ETest.java
index cd369e42afe..93a1d77d458 100644
---
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/AbstractProductionProxyWorkflowE2ETest.java
+++
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/AbstractProductionProxyWorkflowE2ETest.java
@@ -22,8 +22,10 @@ import
org.apache.shardingsphere.test.e2e.mcp.support.runtime.MySQLRuntimeTestSu
import
org.apache.shardingsphere.test.e2e.mcp.support.runtime.ProxyEncryptWorkflowRuntimeTestSupport;
import
org.apache.shardingsphere.test.e2e.mcp.support.runtime.ProxyEncryptWorkflowRuntimeTestSupport.ProxyEncryptWorkflowRuntimeFixture;
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.Assumptions;
+import org.junit.jupiter.api.TestInstance;
import java.io.IOException;
import java.sql.SQLException;
@@ -33,18 +35,36 @@ import java.util.Map;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
abstract class AbstractProductionProxyWorkflowE2ETest extends
AbstractProductionRuntimeE2ETest {
private ProxyEncryptWorkflowRuntimeFixture runtimeFixture;
+ private ProxyEncryptWorkflowRuntimeFixture sharedRuntimeFixture;
+
+ private boolean sharedRuntimeFixtureSelected;
+
@AfterEach
void tearDownFixture() {
+ if (sharedRuntimeFixtureSelected) {
+ runtimeFixture = null;
+ sharedRuntimeFixtureSelected = false;
+ return;
+ }
if (null != runtimeFixture) {
runtimeFixture.close();
runtimeFixture = null;
}
}
+ @AfterAll
+ void tearDownSharedFixture() {
+ if (null != sharedRuntimeFixture) {
+ sharedRuntimeFixture.close();
+ sharedRuntimeFixture = null;
+ }
+ }
+
@Override
protected final RuntimeTransport getTransport() {
return RuntimeTransport.HTTP;
@@ -55,12 +75,23 @@ abstract class AbstractProductionProxyWorkflowE2ETest
extends AbstractProduction
Assumptions.assumeTrue(MySQLRuntimeTestSupport.isDockerAvailable(),
() ->
MySQLRuntimeTestSupport.createDockerRequiredMessage("Docker is required for the
Proxy-backed workflow E2E tests."));
try {
+ if (sharedRuntimeFixtureSelected) {
+ prepareSharedRuntimeFixture();
+ return;
+ }
runtimeFixture =
ProxyEncryptWorkflowRuntimeTestSupport.createFixture();
} catch (final SQLException ex) {
throw new IOException(ex);
}
}
+ private void prepareSharedRuntimeFixture() throws SQLException {
+ if (null == sharedRuntimeFixture) {
+ sharedRuntimeFixture =
ProxyEncryptWorkflowRuntimeTestSupport.createFixture();
+ }
+ runtimeFixture = sharedRuntimeFixture;
+ }
+
@Override
protected final Map<String, RuntimeDatabaseConfiguration>
getRuntimeDatabases() {
return runtimeFixture.getRuntimeDatabases();
@@ -70,10 +101,8 @@ abstract class AbstractProductionProxyWorkflowE2ETest
extends AbstractProduction
return runtimeFixture.getLogicalDatabaseName();
}
- protected final int countPhysicalColumn(final String columnName) throws
SQLException {
- return
MySQLRuntimeTestSupport.querySingleInt(runtimeFixture.getStorageContainer(),
String.format(
- "SELECT COUNT(*) FROM information_schema.columns WHERE
table_schema = '%s' AND table_name = 'orders' AND column_name = '%s'",
- runtimeFixture.getPhysicalDatabaseName(), columnName));
+ protected final void useSharedReadOnlyRuntimeFixture() {
+ sharedRuntimeFixtureSelected = true;
}
protected final void assertValidationPassed(final Map<String, Object>
actualValidationResponse) {
@@ -83,11 +112,6 @@ abstract class AbstractProductionProxyWorkflowE2ETest
extends AbstractProduction
assertThat(actualValidationResponse.toString(),
getMapList(actualValidationResponse.get("mismatches")).size(), is(0));
}
- protected final void assertValidationFailed(final Map<String, Object>
actualValidationResponse) {
- assertThat(actualValidationResponse.toString(),
String.valueOf(actualValidationResponse.get("status")), is("failed"));
- assertThat(actualValidationResponse.toString(),
String.valueOf(actualValidationResponse.get("overall_status")), is("failed"));
- }
-
protected final void assertApplyCompleted(final Map<String, Object>
actualApplyResponse) {
assertThat(actualApplyResponse.toString(),
String.valueOf(actualApplyResponse.get("status")), is("completed"));
}
diff --git
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/AbstractTransportParameterizedProductionRuntimeE2ETest.java
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/AbstractTransportParameterizedProductionRuntimeE2ETest.java
index 7b6b66a1ebf..09c4245c98d 100644
---
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/AbstractTransportParameterizedProductionRuntimeE2ETest.java
+++
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/AbstractTransportParameterizedProductionRuntimeE2ETest.java
@@ -18,6 +18,7 @@
package org.apache.shardingsphere.test.e2e.mcp.runtime.production;
import org.apache.shardingsphere.test.e2e.mcp.support.runtime.RuntimeTransport;
+import org.junit.jupiter.api.AfterEach;
abstract class AbstractTransportParameterizedProductionRuntimeE2ETest extends
AbstractProductionRuntimeE2ETest {
@@ -27,6 +28,11 @@ abstract class
AbstractTransportParameterizedProductionRuntimeE2ETest extends Ab
this.transport = transport;
}
+ @AfterEach
+ void clearTransport() {
+ transport = null;
+ }
+
@Override
protected final RuntimeTransport getTransport() {
if (null == transport) {
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 d103a343556..76943ad51b0 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
@@ -63,6 +63,7 @@ class HttpProductionProxyEncryptWorkflowE2ETest extends
AbstractProductionProxyW
@Test
void assertCompleteEncryptAlgorithmThroughProxy() throws IOException,
InterruptedException {
+ useSharedReadOnlyRuntimeFixture();
try (MCPInteractionClient interactionClient =
createOpenedInteractionClient()) {
Map<String, Object> actual =
interactionClient.complete(Map.of("type", "ref/prompt", "name",
PLAN_PROMPT_NAME), "algorithm_type", "AE", Map.of());
assertThat(getStringList(getMap(actual.get("completion")).get("values")),
hasItem("AES"));
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 515ea25116c..15f3246bcc6 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
@@ -86,6 +86,7 @@ class HttpProductionProxyFeatureWorkflowContractE2ETest
extends AbstractProducti
@Test
void assertReadStorageUnitsThroughProxy() throws IOException,
InterruptedException {
+ useSharedReadOnlyRuntimeFixture();
try (MCPInteractionClient interactionClient =
createOpenedInteractionClient()) {
List<Map<String, Object>> actualStorageUnits =
getPayloadItems(interactionClient.readResource(
String.format("shardingsphere://databases/%s/storage-units",
getLogicalDatabaseName())));
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 1ef2ac6ae24..68279ef4a99 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
@@ -57,6 +57,7 @@ class HttpProductionProxyMaskWorkflowE2ETest extends
AbstractProductionProxyWork
@Test
void assertCompleteMaskAlgorithmThroughProxy() throws IOException,
InterruptedException {
+ useSharedReadOnlyRuntimeFixture();
try (MCPInteractionClient interactionClient =
createOpenedInteractionClient()) {
Map<String, Object> actual =
interactionClient.complete(Map.of("type", "ref/prompt", "name",
PLAN_PROMPT_NAME), "algorithm_type", "KEEP", Map.of());
assertThat(getStringList(getMap(actual.get("completion")).get("values")),
hasItem("KEEP_FIRST_N_LAST_M"));
diff --git
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/PackagedDistributionE2ETest.java
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/PackagedDistributionE2ETest.java
index 2120d3144f6..17073afad68 100644
---
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/PackagedDistributionE2ETest.java
+++
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/PackagedDistributionE2ETest.java
@@ -17,10 +17,6 @@
package org.apache.shardingsphere.test.e2e.mcp.runtime.production;
-import static org.hamcrest.Matchers.containsInAnyOrder;
-import static org.hamcrest.Matchers.hasItems;
-import static org.hamcrest.Matchers.is;
-
import
org.apache.shardingsphere.test.e2e.env.runtime.EnvironmentPropertiesLoader;
import org.apache.shardingsphere.test.e2e.mcp.env.MCPE2ECondition;
import org.apache.shardingsphere.test.e2e.mcp.support.OfficialMCPToolNames;
@@ -50,10 +46,14 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.sql.SQLException;
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.containsInAnyOrder;
+import static org.hamcrest.Matchers.hasItems;
+import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -105,7 +105,7 @@ class PackagedDistributionE2ETest {
@Test
void assertLaunchPackagedDistributionOverHttp() throws IOException,
InterruptedException, SQLException {
- PreparedPackagedDistribution distribution =
preparePackagedDistribution("http", RuntimeTransport.HTTP);
+ PreparedPackagedDistribution distribution =
prepareReusableOfficialPackagedDistribution(RuntimeTransport.HTTP);
try (
PackagedDistributionHttpRuntime runtime = new
PackagedDistributionHttpRuntime(distribution);
MCPInteractionClient interactionClient =
runtime.openInteractionClient()) {
@@ -117,7 +117,7 @@ class PackagedDistributionE2ETest {
@Test
void assertLaunchPackagedDistributionOverStdio() throws IOException,
InterruptedException, SQLException {
- PreparedPackagedDistribution distribution =
preparePackagedDistribution("stdio", RuntimeTransport.STDIO);
+ PreparedPackagedDistribution distribution =
prepareReusableOfficialPackagedDistribution(RuntimeTransport.STDIO);
try (MCPInteractionClient interactionClient = new
PackagedDistributionStdioInteractionClient(distribution.home(),
distribution.configFile())) {
interactionClient.open();
assertOfficialRuntime(distribution.home(), RuntimeTransport.STDIO,
interactionClient);
@@ -174,9 +174,15 @@ class PackagedDistributionE2ETest {
MySQLRuntimeTestSupport.createRuntimeDatabases(mysqlContainer,
LOGICAL_DATABASE_NAME));
}
+ private PreparedPackagedDistribution
prepareReusableOfficialPackagedDistribution(final RuntimeTransport transport)
throws IOException, SQLException {
+ prepareMySQLContainer();
+ return
PackagedDistributionTestSupport.prepareReusable(tempDir.resolve("official-distribution-home-"
+ transport.name().toLowerCase(Locale.ENGLISH)), transport,
+ MySQLRuntimeTestSupport.createRuntimeDatabases(mysqlContainer,
LOGICAL_DATABASE_NAME));
+ }
+
private Path createDockerConfigurationFile(final RuntimeTransport
transport) throws IOException, SQLException {
prepareMySQLContainer();
- return
PackagedDistributionTestSupport.createDockerConfigurationFile(tempDir.resolve("container-"
+ transport.name().toLowerCase() + ".yaml"), transport,
+ return
PackagedDistributionTestSupport.createDockerConfigurationFile(tempDir.resolve("container-"
+ transport.name().toLowerCase(Locale.ENGLISH) + ".yaml"), transport,
MySQLRuntimeTestSupport.createDockerHostRuntimeDatabases(mysqlContainer,
LOGICAL_DATABASE_NAME));
}
@@ -307,8 +313,8 @@ class PackagedDistributionE2ETest {
private void assertFixtureResource(final Map<String, Object> payload) {
List<Map<String, Object>> actualItems =
MCPInteractionPayloads.castToList(payload.get("items"));
assertThat(actualItems.size(), is(1));
- assertThat(actualItems.get(0).get("feature"), is("test-fixture"));
- assertThat(actualItems.get(0).get("status"), is("ready"));
+ assertThat(actualItems.getFirst().get("feature"), is("test-fixture"));
+ assertThat(actualItems.getFirst().get("status"), is("ready"));
}
private void assertCapabilities(final Map<String, Object> payload) {
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 165bcf7f0e0..f9ae572a6ba 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
@@ -40,8 +40,13 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
@EnabledIf("isEnabled")
class ProductionMySQLRuntimeE2ETest extends
AbstractProductionMySQLRuntimeE2ETest {
+ @Override
+ protected boolean useSharedRuntimeFixture() {
+ return true;
+ }
+
@ParameterizedTest(name = "{0}")
- @MethodSource("transports")
+ @MethodSource("semanticPrimaryTransport")
void assertReadCapabilitiesWithActualMySQLBackend(final String name, final
RuntimeTransport transport) throws IOException, InterruptedException {
useTransport(transport);
try (MCPInteractionClient interactionClient =
createOpenedInteractionClient()) {
@@ -52,7 +57,7 @@ class ProductionMySQLRuntimeE2ETest extends
AbstractProductionMySQLRuntimeE2ETes
}
@ParameterizedTest(name = "{0}")
- @MethodSource("transports")
+ @MethodSource("semanticPrimaryTransport")
void assertReadDatabasesResourceWithActualMySQLBackend(final String name,
final RuntimeTransport transport) throws IOException, InterruptedException {
useTransport(transport);
try (MCPInteractionClient interactionClient =
createOpenedInteractionClient()) {
@@ -61,7 +66,7 @@ class ProductionMySQLRuntimeE2ETest extends
AbstractProductionMySQLRuntimeE2ETes
}
@ParameterizedTest(name = "{0}")
- @MethodSource("transports")
+ @MethodSource("semanticPrimaryTransport")
void assertServiceCapabilitiesResourceWithActualMySQLBackend(final String
name, final RuntimeTransport transport) throws IOException,
InterruptedException {
useTransport(transport);
try (MCPInteractionClient interactionClient =
createOpenedInteractionClient()) {
@@ -70,7 +75,7 @@ class ProductionMySQLRuntimeE2ETest extends
AbstractProductionMySQLRuntimeE2ETes
}
@ParameterizedTest(name = "{0}")
- @MethodSource("transports")
+ @MethodSource("semanticPrimaryTransport")
void assertListToolsWithActualMySQLBackend(final String name, final
RuntimeTransport transport) throws IOException, InterruptedException {
useTransport(transport);
try (MCPInteractionClient interactionClient =
createOpenedInteractionClient()) {
@@ -84,7 +89,7 @@ class ProductionMySQLRuntimeE2ETest extends
AbstractProductionMySQLRuntimeE2ETes
}
@ParameterizedTest(name = "{0}")
- @MethodSource("transports")
+ @MethodSource("semanticPrimaryTransport")
void assertListResourcesWithActualMySQLBackend(final String name, final
RuntimeTransport transport) throws IOException, InterruptedException {
useTransport(transport);
try (MCPInteractionClient interactionClient =
createOpenedInteractionClient()) {
@@ -93,7 +98,7 @@ class ProductionMySQLRuntimeE2ETest extends
AbstractProductionMySQLRuntimeE2ETes
}
@ParameterizedTest(name = "{0}")
- @MethodSource("transports")
+ @MethodSource("semanticPrimaryTransport")
void assertListResourceTemplatesWithActualMySQLBackend(final String name,
final RuntimeTransport transport) throws IOException, InterruptedException {
useTransport(transport);
try (MCPInteractionClient interactionClient =
createOpenedInteractionClient()) {
@@ -106,7 +111,7 @@ class ProductionMySQLRuntimeE2ETest extends
AbstractProductionMySQLRuntimeE2ETes
}
@ParameterizedTest(name = "{0}")
- @MethodSource("transports")
+ @MethodSource("semanticPrimaryTransport")
void assertRejectUnsupportedResourceUriWithActualMySQLBackend(final String
name, final RuntimeTransport transport) throws IOException,
InterruptedException {
useTransport(transport);
try (MCPInteractionClient interactionClient =
createOpenedInteractionClient()) {
@@ -119,7 +124,7 @@ class ProductionMySQLRuntimeE2ETest extends
AbstractProductionMySQLRuntimeE2ETes
}
@ParameterizedTest(name = "{0}")
- @MethodSource("transports")
+ @MethodSource("semanticPrimaryTransport")
void assertRejectUnsupportedToolNameWithActualMySQLBackend(final String
name, final RuntimeTransport transport) throws IOException,
InterruptedException {
useTransport(transport);
try (MCPInteractionClient interactionClient =
createOpenedInteractionClient()) {
@@ -131,7 +136,7 @@ class ProductionMySQLRuntimeE2ETest extends
AbstractProductionMySQLRuntimeE2ETes
}
@ParameterizedTest(name = "{0}")
- @MethodSource("transports")
+ @MethodSource("semanticPrimaryTransport")
void
assertInitializeExposesMarkdownInstructionsWithActualMySQLBackend(final String
name, final RuntimeTransport transport) throws IOException,
InterruptedException {
useTransport(transport);
try (MCPInteractionClient interactionClient =
createOpenedInteractionClient()) {
@@ -143,7 +148,7 @@ class ProductionMySQLRuntimeE2ETest extends
AbstractProductionMySQLRuntimeE2ETes
}
@ParameterizedTest(name = "{0}")
- @MethodSource("transports")
+ @MethodSource("semanticPrimaryTransport")
void
assertServerInstructionsAreNotListedAsResourceWithActualMySQLBackend(final
String name, final RuntimeTransport transport) throws IOException,
InterruptedException {
useTransport(transport);
try (MCPInteractionClient interactionClient =
createOpenedInteractionClient()) {
@@ -173,7 +178,7 @@ class ProductionMySQLRuntimeE2ETest extends
AbstractProductionMySQLRuntimeE2ETes
}
@ParameterizedTest(name = "{0}")
- @MethodSource("transports")
+ @MethodSource("semanticPrimaryTransport")
void assertReadTableDetailWithActualMySQLBackend(final String name, final
RuntimeTransport transport) throws IOException, InterruptedException {
useTransport(transport);
try (MCPInteractionClient interactionClient =
createOpenedInteractionClient()) {
@@ -188,7 +193,7 @@ class ProductionMySQLRuntimeE2ETest extends
AbstractProductionMySQLRuntimeE2ETes
}
@ParameterizedTest(name = "{0}")
- @MethodSource("transports")
+ @MethodSource("semanticPrimaryTransport")
void assertSearchMetadataTablesAndViewsWithActualMySQLBackend(final String
name, final RuntimeTransport transport) throws IOException,
InterruptedException {
useTransport(transport);
try (MCPInteractionClient interactionClient =
createOpenedInteractionClient()) {
@@ -199,7 +204,7 @@ class ProductionMySQLRuntimeE2ETest extends
AbstractProductionMySQLRuntimeE2ETes
}
@ParameterizedTest(name = "{0}")
- @MethodSource("transports")
+ @MethodSource("semanticPrimaryTransport")
void assertReadViewsWithActualMySQLBackend(final String name, final
RuntimeTransport transport) throws IOException, InterruptedException {
useTransport(transport);
try (MCPInteractionClient interactionClient =
createOpenedInteractionClient()) {
@@ -211,7 +216,7 @@ class ProductionMySQLRuntimeE2ETest extends
AbstractProductionMySQLRuntimeE2ETes
}
@ParameterizedTest(name = "{0}")
- @MethodSource("transports")
+ @MethodSource("semanticPrimaryTransport")
void assertReadIndexesWithActualMySQLBackend(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/runtime/production/ProductionMySQLSQLRuntimeE2ETest.java
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/ProductionMySQLSQLRuntimeE2ETest.java
index f3b1f79528f..a25269bb910 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
@@ -46,7 +46,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
class ProductionMySQLSQLRuntimeE2ETest extends
AbstractProductionMySQLRuntimeE2ETest {
@ParameterizedTest(name = "{0}")
- @MethodSource("transports")
+ @MethodSource("semanticPrimaryTransport")
void assertExecuteSelectWithActualMySQLBackend(final String name, final
RuntimeTransport transport) throws IOException, InterruptedException {
useTransport(transport);
try (MCPInteractionClient interactionClient =
createOpenedInteractionClient()) {
@@ -57,7 +57,7 @@ class ProductionMySQLSQLRuntimeE2ETest extends
AbstractProductionMySQLRuntimeE2E
}
@ParameterizedTest(name = "{0}")
- @MethodSource("transports")
+ @MethodSource("semanticPrimaryTransport")
void assertExecuteSelectWithTruncationWithActualMySQLBackend(final String
name, final RuntimeTransport transport) throws IOException,
InterruptedException {
useTransport(transport);
try (MCPInteractionClient interactionClient =
createOpenedInteractionClient()) {
@@ -70,7 +70,7 @@ class ProductionMySQLSQLRuntimeE2ETest extends
AbstractProductionMySQLRuntimeE2E
}
@ParameterizedTest(name = "{0}")
- @MethodSource("transports")
+ @MethodSource("semanticPrimaryTransport")
void assertExecuteExplainAnalyzeSelectWithActualMySQLBackend(final String
name, final RuntimeTransport transport) throws IOException,
InterruptedException {
useTransport(transport);
try (MCPInteractionClient interactionClient =
createOpenedInteractionClient()) {
@@ -83,7 +83,7 @@ class ProductionMySQLSQLRuntimeE2ETest extends
AbstractProductionMySQLRuntimeE2E
}
@ParameterizedTest(name = "{0}")
- @MethodSource("transports")
+ @MethodSource("semanticPrimaryTransport")
void assertExecuteQueryTimeoutWithActualMySQLBackend(final String name,
final RuntimeTransport transport) throws IOException, InterruptedException {
useTransport(transport);
try (MCPInteractionClient interactionClient =
createOpenedInteractionClient()) {
@@ -94,7 +94,7 @@ class ProductionMySQLSQLRuntimeE2ETest extends
AbstractProductionMySQLRuntimeE2E
}
@ParameterizedTest(name = "{0}")
- @MethodSource("transports")
+ @MethodSource("semanticPrimaryTransport")
void assertRejectExecuteMultiStatementWithActualMySQLBackend(final String
name, final RuntimeTransport transport) throws IOException,
InterruptedException {
useTransport(transport);
try (MCPInteractionClient interactionClient =
createOpenedInteractionClient()) {
@@ -105,7 +105,7 @@ class ProductionMySQLSQLRuntimeE2ETest extends
AbstractProductionMySQLRuntimeE2E
}
@ParameterizedTest(name = "{0}")
- @MethodSource("transports")
+ @MethodSource("semanticPrimaryTransport")
void
assertRejectExplainAnalyzeUpdateFromReadOnlyToolWithActualMySQLBackend(final
String name, final RuntimeTransport transport) throws IOException,
InterruptedException {
useTransport(transport);
try (MCPInteractionClient interactionClient =
createOpenedInteractionClient()) {
@@ -119,7 +119,7 @@ class ProductionMySQLSQLRuntimeE2ETest extends
AbstractProductionMySQLRuntimeE2E
}
@ParameterizedTest(name = "{0}")
- @MethodSource("transports")
+ @MethodSource("semanticPrimaryTransport")
void assertRejectLockingReadFromReadOnlyToolWithActualMySQLBackend(final
String name, final RuntimeTransport transport) throws IOException,
InterruptedException {
useTransport(transport);
try (MCPInteractionClient interactionClient =
createOpenedInteractionClient()) {
@@ -130,7 +130,7 @@ class ProductionMySQLSQLRuntimeE2ETest extends
AbstractProductionMySQLRuntimeE2E
}
@ParameterizedTest(name = "{0}")
- @MethodSource("transports")
+ @MethodSource("semanticPrimaryTransport")
void assertRejectLockingReadFromUpdateToolWithActualMySQLBackend(final
String name, final RuntimeTransport transport) throws IOException,
InterruptedException {
useTransport(transport);
try (MCPInteractionClient interactionClient =
createOpenedInteractionClient()) {
@@ -141,7 +141,7 @@ class ProductionMySQLSQLRuntimeE2ETest extends
AbstractProductionMySQLRuntimeE2E
}
@ParameterizedTest(name = "{0}")
- @MethodSource("transports")
+ @MethodSource("dualTransports")
void assertElicitMaskPlanningWithActualMySQLBackend(final String name,
final RuntimeTransport transport) throws IOException {
useTransport(transport);
List<McpSchema.ElicitRequest> actualElicitationRequests = new
CopyOnWriteArrayList<>();
@@ -170,7 +170,7 @@ class ProductionMySQLSQLRuntimeE2ETest extends
AbstractProductionMySQLRuntimeE2E
}
@ParameterizedTest(name = "{0}")
- @MethodSource("transports")
+ @MethodSource("semanticPrimaryTransport")
void assertExecuteUpdateWithActualMySQLBackend(final String name, final
RuntimeTransport transport) throws SQLException, IOException,
InterruptedException {
useTransport(transport);
try (MCPInteractionClient interactionClient =
createOpenedInteractionClient()) {
@@ -183,7 +183,7 @@ class ProductionMySQLSQLRuntimeE2ETest extends
AbstractProductionMySQLRuntimeE2E
}
@ParameterizedTest(name = "{0}")
- @MethodSource("transports")
+ @MethodSource("semanticPrimaryTransport")
void
assertExecuteUpdateWithoutApprovalArgumentWithActualMySQLBackend(final String
name, final RuntimeTransport transport) throws IOException,
InterruptedException {
useTransport(transport);
try (MCPInteractionClient interactionClient =
createOpenedInteractionClient()) {
@@ -195,7 +195,7 @@ class ProductionMySQLSQLRuntimeE2ETest extends
AbstractProductionMySQLRuntimeE2E
}
@ParameterizedTest(name = "{0}")
- @MethodSource("transports")
+ @MethodSource("semanticPrimaryTransport")
void assertRejectSequenceResourceWithActualMySQLBackend(final String name,
final RuntimeTransport transport) throws IOException, InterruptedException {
useTransport(transport);
try (MCPInteractionClient interactionClient =
createOpenedInteractionClient()) {
@@ -207,7 +207,7 @@ class ProductionMySQLSQLRuntimeE2ETest extends
AbstractProductionMySQLRuntimeE2E
}
@ParameterizedTest(name = "{0}")
- @MethodSource("transports")
+ @MethodSource("semanticPrimaryTransport")
void assertExecuteRollbackWithActualMySQLBackend(final String name, final
RuntimeTransport transport) throws SQLException, IOException,
InterruptedException {
useTransport(transport);
try (MCPInteractionClient interactionClient =
createOpenedInteractionClient()) {
@@ -222,7 +222,7 @@ class ProductionMySQLSQLRuntimeE2ETest extends
AbstractProductionMySQLRuntimeE2E
}
@ParameterizedTest(name = "{0}")
- @MethodSource("transports")
+ @MethodSource("semanticPrimaryTransport")
void assertRejectBlankSavepointNameWithActualMySQLBackend(final String
name, final RuntimeTransport transport) throws IOException,
InterruptedException {
useTransport(transport);
try (MCPInteractionClient interactionClient =
createOpenedInteractionClient()) {
@@ -232,7 +232,7 @@ class ProductionMySQLSQLRuntimeE2ETest extends
AbstractProductionMySQLRuntimeE2E
}
@ParameterizedTest(name = "{0}")
- @MethodSource("transports")
+ @MethodSource("semanticPrimaryTransport")
void assertExecuteSavepointFlowWithActualMySQLBackend(final String name,
final RuntimeTransport transport) throws SQLException, IOException,
InterruptedException {
useTransport(transport);
try (MCPInteractionClient interactionClient =
createOpenedInteractionClient()) {
@@ -252,7 +252,7 @@ class ProductionMySQLSQLRuntimeE2ETest extends
AbstractProductionMySQLRuntimeE2E
}
@ParameterizedTest(name = "{0}")
- @MethodSource("transports")
+ @MethodSource("semanticPrimaryTransport")
void assertExecuteReleaseSavepointWithActualMySQLBackend(final String
name, final RuntimeTransport transport) throws IOException,
InterruptedException {
useTransport(transport);
try (MCPInteractionClient interactionClient =
createOpenedInteractionClient()) {
@@ -267,7 +267,7 @@ class ProductionMySQLSQLRuntimeE2ETest extends
AbstractProductionMySQLRuntimeE2E
}
@ParameterizedTest(name = "{0}")
- @MethodSource("transports")
+ @MethodSource("semanticPrimaryTransport")
void assertExecuteDdlRefreshesMetadataWithActualMySQLBackend(final String
name, final RuntimeTransport transport) throws IOException,
InterruptedException {
useTransport(transport);
try (MCPInteractionClient interactionClient =
createOpenedInteractionClient()) {
@@ -281,7 +281,7 @@ class ProductionMySQLSQLRuntimeE2ETest extends
AbstractProductionMySQLRuntimeE2E
}
@ParameterizedTest(name = "{0}")
- @MethodSource("transports")
+ @MethodSource("semanticPrimaryTransport")
void assertCloseRollsBackPendingTransactionWithActualMySQLBackend(final
String name, final RuntimeTransport transport) throws SQLException,
IOException, InterruptedException {
useTransport(transport);
try (MCPInteractionClient interactionClient =
createOpenedInteractionClient()) {
@@ -294,7 +294,7 @@ class ProductionMySQLSQLRuntimeE2ETest extends
AbstractProductionMySQLRuntimeE2E
}
@ParameterizedTest(name = "{0}")
- @MethodSource("transports")
+ @MethodSource("semanticPrimaryTransport")
void
assertAiNativeDeterministicInteractionLoopWithActualMySQLBackend(final String
name, final RuntimeTransport transport) throws IOException,
InterruptedException {
useTransport(transport);
try (MCPInteractionClient interactionClient =
createOpenedInteractionClient()) {
@@ -313,7 +313,7 @@ class ProductionMySQLSQLRuntimeE2ETest extends
AbstractProductionMySQLRuntimeE2E
}
@ParameterizedTest(name = "{0}")
- @MethodSource("transports")
+ @MethodSource("semanticPrimaryTransport")
void
assertListDatabasesWithMultipleRuntimeDatabasesWithActualMySQLBackend(final
String name, final RuntimeTransport transport) throws IOException,
InterruptedException {
useTransport(transport);
try (MCPInteractionClient interactionClient =
createOpenedInteractionClient(createPreparedProgrammaticRuntimeDatabases())) {
@@ -324,7 +324,7 @@ class ProductionMySQLSQLRuntimeE2ETest extends
AbstractProductionMySQLRuntimeE2E
}
@ParameterizedTest(name = "{0}")
- @MethodSource("transports")
+ @MethodSource("semanticPrimaryTransport")
void
assertRefreshMetadataVisibleForTargetDatabaseOnlyWithActualMySQLBackend(final
String name, final RuntimeTransport transport) throws IOException,
InterruptedException {
useTransport(transport);
try (MCPInteractionClient firstInteractionClient =
createOpenedInteractionClient(createPreparedProgrammaticRuntimeDatabases())) {
@@ -343,7 +343,7 @@ class ProductionMySQLSQLRuntimeE2ETest extends
AbstractProductionMySQLRuntimeE2E
}
@ParameterizedTest(name = "{0}")
- @MethodSource("transports")
+ @MethodSource("semanticPrimaryTransport")
void
assertRejectCrossDatabaseTransactionSwitchWithActualMySQLBackend(final String
name, final RuntimeTransport transport) throws IOException,
InterruptedException {
useTransport(transport);
try (MCPInteractionClient interactionClient =
createOpenedInteractionClient(createPreparedProgrammaticRuntimeDatabases())) {
@@ -355,7 +355,7 @@ class ProductionMySQLSQLRuntimeE2ETest extends
AbstractProductionMySQLRuntimeE2E
}
@ParameterizedTest(name = "{0}")
- @MethodSource("transports")
+ @MethodSource("dualTransports")
void assertRejectMismatchedDatabaseTypeWithActualMySQLBackend(final String
name, final RuntimeTransport transport) {
useTransport(transport);
if (RuntimeTransport.HTTP == transport) {
diff --git
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/ProductionRuntimeTransportSmokeE2ETest.java
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/ProductionRuntimeTransportSmokeE2ETest.java
new file mode 100644
index 00000000000..11f3920d6b5
--- /dev/null
+++
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/ProductionRuntimeTransportSmokeE2ETest.java
@@ -0,0 +1,53 @@
+/*
+ * 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.production;
+
+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;
+import org.junit.jupiter.api.condition.EnabledIf;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.io.IOException;
+import java.util.Map;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+
+@EnabledIf("isEnabled")
+class ProductionRuntimeTransportSmokeE2ETest extends
AbstractProductionMySQLRuntimeE2ETest {
+
+ @Override
+ protected boolean useSharedRuntimeFixture() {
+ return true;
+ }
+
+ @ParameterizedTest(name = "{0}")
+ @MethodSource("dualTransports")
+ void assertTransportLifecycleWithActualMySQLBackend(final String name,
final RuntimeTransport transport) throws IOException, InterruptedException {
+ useTransport(transport);
+ try (MCPInteractionClient interactionClient =
createOpenedInteractionClient()) {
+
assertOfficialToolNames(interactionClient.listTools().stream().map(each ->
String.valueOf(each.get("name"))).toList());
+
MCPPayloadAssertions.assertSingleItemValue(interactionClient.readResource("shardingsphere://databases"),
"database", LOGICAL_DATABASE_NAME);
+ Map<String, Object> actual =
interactionClient.call("database_gateway_execute_query",
+ Map.of("database", LOGICAL_DATABASE_NAME, "schema",
LOGICAL_DATABASE_NAME, "sql", "SELECT status FROM orders WHERE order_id = 1",
"max_rows", 1));
+ assertThat(String.valueOf(actual.get("result_kind")),
is("result_set"));
+ }
+ }
+}
diff --git
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/AbstractHttpProgrammaticRuntimeE2ETest.java
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/AbstractHttpProgrammaticRuntimeE2ETest.java
index f7584ad3eca..b73b2efc603 100644
---
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/AbstractHttpProgrammaticRuntimeE2ETest.java
+++
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/AbstractHttpProgrammaticRuntimeE2ETest.java
@@ -47,8 +47,15 @@ abstract class AbstractHttpProgrammaticRuntimeE2ETest
extends AbstractConfigBack
private Map<String, RuntimeDatabaseConfiguration> runtimeDatabases;
+ private ProgrammaticRuntimeFixture sharedRuntimeFixture;
+
@AfterEach
void tearDownContainer() {
+ if (useSharedDatabaseBackedRuntime()) {
+ container = null;
+ runtimeDatabases = null;
+ return;
+ }
if (null != container) {
container.stop();
container = null;
@@ -96,10 +103,6 @@ abstract class AbstractHttpProgrammaticRuntimeE2ETest
extends AbstractConfigBack
return sendJsonRpcRequest(httpClient, createSessionHeaders(sessionId),
"resource-1", "resources/read", Map.of("uri", resourceUri));
}
- protected final HttpResponse<String> sendCapabilitiesRequest(final
HttpClient httpClient, final Map<String, String> headers) throws IOException,
InterruptedException {
- return sendJsonRpcRequest(httpClient, headers, "resource-1",
"resources/read", Map.of("uri", "shardingsphere://capabilities"));
- }
-
protected final HttpResponse<String> sendDeleteRequest(final HttpClient
httpClient, final Map<String, String> headers) throws IOException,
InterruptedException {
HttpRequest.Builder requestBuilder =
HttpRequest.newBuilder(getEndpointUri()).DELETE();
applyHeaders(requestBuilder, headers);
@@ -114,12 +117,6 @@ abstract class AbstractHttpProgrammaticRuntimeE2ETest
extends AbstractConfigBack
return httpClient.send(requestBuilder.build(),
HttpResponse.BodyHandlers.ofString());
}
- protected final HttpResponse<String> openEventStream(final HttpClient
httpClient, final Map<String, String> headers) throws IOException,
InterruptedException {
- HttpRequest.Builder requestBuilder =
HttpRequest.newBuilder(getEndpointUri()).GET();
- applyHeaders(requestBuilder, headers);
- return httpClient.send(requestBuilder.build(),
HttpResponse.BodyHandlers.ofString());
- }
-
protected final Map<String, Object> getStructuredContent(final String
responseBody) {
Map<String, Object> payload =
MCPInteractionPayloads.parseJsonPayload(responseBody);
return MCPInteractionPayloads.hasJsonRpcError(payload) ?
MCPInteractionPayloads.getJsonRpcErrorPayload(payload) :
MCPInteractionPayloads.getStructuredContent(payload);
@@ -174,12 +171,50 @@ abstract class AbstractHttpProgrammaticRuntimeE2ETest
extends AbstractConfigBack
protected final void prepareRuntimeFixture() throws IOException {
Assumptions.assumeTrue(MySQLRuntimeTestSupport.isDockerAvailable(),
() ->
MySQLRuntimeTestSupport.createDockerRequiredMessage("Docker is required for the
MySQL-backed MCP programmatic contract E2E test."));
- container = MySQLRuntimeTestSupport.createContainer();
- container.start();
+ if (useSharedDatabaseBackedRuntime()) {
+ prepareSharedDatabaseBackedRuntime();
+ return;
+ }
+ applyRuntimeFixture(createRuntimeFixture());
+ }
+
+ private void prepareSharedDatabaseBackedRuntime() throws IOException {
+ if (null == sharedRuntimeFixture) {
+ sharedRuntimeFixture = createRuntimeFixture();
+ }
+ applyRuntimeFixture(sharedRuntimeFixture);
+ }
+
+ private ProgrammaticRuntimeFixture createRuntimeFixture() throws
IOException {
+ GenericContainer<?> result = MySQLRuntimeTestSupport.createContainer();
+ boolean success = false;
try {
- runtimeDatabases =
MySQLRuntimeTestSupport.createPreparedProgrammaticRuntimeDatabases(container);
+ result.start();
+ Map<String, RuntimeDatabaseConfiguration> actualRuntimeDatabases =
MySQLRuntimeTestSupport.createPreparedProgrammaticRuntimeDatabases(result);
+ success = true;
+ return new ProgrammaticRuntimeFixture(result,
actualRuntimeDatabases);
} catch (final SQLException ex) {
throw new IOException("Failed to initialize MCP E2E runtime
databases.", ex);
+ } finally {
+ if (!success) {
+ result.stop();
+ }
+ }
+ }
+
+ private void applyRuntimeFixture(final ProgrammaticRuntimeFixture fixture)
{
+ container = fixture.container();
+ runtimeDatabases = fixture.runtimeDatabases();
+ }
+
+ protected boolean useSharedDatabaseBackedRuntime() {
+ return false;
+ }
+
+ protected void closeSharedDatabaseBackedRuntime() {
+ if (null != sharedRuntimeFixture) {
+ sharedRuntimeFixture.close();
+ sharedRuntimeFixture = null;
}
}
@@ -191,4 +226,29 @@ abstract class AbstractHttpProgrammaticRuntimeE2ETest
extends AbstractConfigBack
private void applyHeaders(final HttpRequest.Builder requestBuilder, final
Map<String, String> headers) {
headers.forEach(requestBuilder::setHeader);
}
+
+ private static final class ProgrammaticRuntimeFixture implements
AutoCloseable {
+
+ private final GenericContainer<?> container;
+
+ private final Map<String, RuntimeDatabaseConfiguration>
runtimeDatabases;
+
+ private ProgrammaticRuntimeFixture(final GenericContainer<?>
container, final Map<String, RuntimeDatabaseConfiguration> runtimeDatabases) {
+ this.container = container;
+ this.runtimeDatabases = runtimeDatabases;
+ }
+
+ private GenericContainer<?> container() {
+ return container;
+ }
+
+ private Map<String, RuntimeDatabaseConfiguration> runtimeDatabases() {
+ return runtimeDatabases;
+ }
+
+ @Override
+ public void close() {
+ container.stop();
+ }
+ }
}
diff --git
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/AbstractHttpProgrammaticRuntimeE2ETest.java
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/AbstractHttpProtocolOnlyE2ETest.java
similarity index 62%
copy from
test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/AbstractHttpProgrammaticRuntimeE2ETest.java
copy to
test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/AbstractHttpProtocolOnlyE2ETest.java
index f7584ad3eca..2dc53a275bd 100644
---
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/AbstractHttpProgrammaticRuntimeE2ETest.java
+++
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/AbstractHttpProtocolOnlyE2ETest.java
@@ -17,47 +17,51 @@
package org.apache.shardingsphere.test.e2e.mcp.runtime.programmatic;
-import
org.apache.shardingsphere.mcp.support.database.metadata.jdbc.RuntimeDatabaseConfiguration;
-import
org.apache.shardingsphere.test.e2e.mcp.support.runtime.AbstractConfigBackedRuntimeE2ETest;
-import
org.apache.shardingsphere.test.e2e.mcp.support.runtime.MySQLRuntimeTestSupport;
-import org.apache.shardingsphere.test.e2e.mcp.support.runtime.RuntimeTransport;
+import
org.apache.shardingsphere.mcp.bootstrap.config.HttpTransportConfiguration;
+import
org.apache.shardingsphere.mcp.bootstrap.transport.server.http.StreamableHttpMCPServer;
+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.test.e2e.mcp.support.transport.MCPInteractionPayloads;
import
org.apache.shardingsphere.test.e2e.mcp.support.transport.client.MCPHttpTransportTestSupport;
import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.Assumptions;
-import org.testcontainers.containers.GenericContainer;
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
-import java.sql.SQLException;
+import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
-abstract class AbstractHttpProgrammaticRuntimeE2ETest extends
AbstractConfigBackedRuntimeE2ETest {
+abstract class AbstractHttpProtocolOnlyE2ETest {
- private static final String CLIENT_NAME = "mcp-e2e-programmatic";
+ private static final String LOOPBACK_BIND_HOST = "127.0.0.1";
+
+ private static final String ENDPOINT_PATH = "/gateway";
- private GenericContainer<?> container;
+ private static final String CLIENT_NAME = "mcp-e2e-programmatic";
- private Map<String, RuntimeDatabaseConfiguration> runtimeDatabases;
+ private StreamableHttpMCPServer httpServer;
@AfterEach
- void tearDownContainer() {
- if (null != container) {
- container.stop();
- container = null;
+ void tearDownHttpServer() {
+ if (null != httpServer) {
+ httpServer.stop();
+ httpServer = null;
}
- runtimeDatabases = null;
}
protected final void launchHttpTransport() throws IOException {
- prepareRuntime();
+ if (null != httpServer) {
+ return;
+ }
+ httpServer = new
StreamableHttpMCPServer(createHttpTransportConfiguration(),
createRuntimeContext());
+ httpServer.start();
}
protected final String initializeSession(final HttpClient httpClient)
throws IOException, InterruptedException {
@@ -86,16 +90,6 @@ abstract class AbstractHttpProgrammaticRuntimeE2ETest
extends AbstractConfigBack
MCPHttpTransportTestSupport.createJsonRpcNotificationBody("notifications/initialized",
Map.of()));
}
- protected final HttpResponse<String> sendToolCallRequest(final HttpClient
httpClient, final String sessionId,
- final String
toolName, final Map<String, Object> arguments) throws IOException,
InterruptedException {
- return sendJsonRpcRequest(httpClient, createSessionHeaders(sessionId),
toolName + "-1", "tools/call", Map.of("name", toolName, "arguments",
arguments));
- }
-
- protected final HttpResponse<String> sendResourceReadRequest(final
HttpClient httpClient, final String sessionId,
- final String
resourceUri) throws IOException, InterruptedException {
- return sendJsonRpcRequest(httpClient, createSessionHeaders(sessionId),
"resource-1", "resources/read", Map.of("uri", resourceUri));
- }
-
protected final HttpResponse<String> sendCapabilitiesRequest(final
HttpClient httpClient, final Map<String, String> headers) throws IOException,
InterruptedException {
return sendJsonRpcRequest(httpClient, headers, "resource-1",
"resources/read", Map.of("uri", "shardingsphere://capabilities"));
}
@@ -120,16 +114,6 @@ abstract class AbstractHttpProgrammaticRuntimeE2ETest
extends AbstractConfigBack
return httpClient.send(requestBuilder.build(),
HttpResponse.BodyHandlers.ofString());
}
- protected final Map<String, Object> getStructuredContent(final String
responseBody) {
- Map<String, Object> payload =
MCPInteractionPayloads.parseJsonPayload(responseBody);
- return MCPInteractionPayloads.hasJsonRpcError(payload) ?
MCPInteractionPayloads.getJsonRpcErrorPayload(payload) :
MCPInteractionPayloads.getStructuredContent(payload);
- }
-
- protected final Map<String, Object> getFirstResourcePayload(final String
responseBody) {
- Map<String, Object> payload =
MCPInteractionPayloads.parseJsonPayload(responseBody);
- return MCPInteractionPayloads.hasJsonRpcError(payload) ?
MCPInteractionPayloads.getJsonRpcErrorPayload(payload) :
MCPInteractionPayloads.getFirstResourcePayload(payload);
- }
-
protected final Map<String, Object> parseJsonBody(final String
responseBody) {
return MCPInteractionPayloads.parseJsonPayload(responseBody);
}
@@ -138,15 +122,11 @@ abstract class AbstractHttpProgrammaticRuntimeE2ETest
extends AbstractConfigBack
return MCPInteractionPayloads.castToMap(value);
}
- protected final Map<String, Object> getRecoveryPayload(final Map<String,
Object> payload, final String expectedRecoveryCategory) {
- assertThat(String.valueOf(payload.get("response_mode")),
is("recovery"));
- Map<String, Object> result = castToMap(payload.get("recovery"));
- assertThat(String.valueOf(result.get("recovery_category")),
is(expectedRecoveryCategory));
- return result;
- }
-
- protected final URI getEndpointUri() throws IOException {
- return getHttpEndpointUri();
+ protected final URI getEndpointUri() {
+ if (null == httpServer) {
+ throw new IllegalStateException("HTTP transport is not enabled for
current protocol E2E test.");
+ }
+ return URI.create(String.format("http://%s:%d%s", LOOPBACK_BIND_HOST,
httpServer.getLocalPort(), getHttpEndpointPath()));
}
protected final String getProtocolVersion() {
@@ -160,27 +140,12 @@ abstract class AbstractHttpProgrammaticRuntimeE2ETest
extends AbstractConfigBack
return result;
}
- @Override
- protected final RuntimeTransport getTransport() {
- return RuntimeTransport.HTTP;
+ protected HttpTransportConfiguration createHttpTransportConfiguration() {
+ return new HttpTransportConfiguration(LOOPBACK_BIND_HOST, 0,
getHttpEndpointPath());
}
- @Override
- protected final Map<String, RuntimeDatabaseConfiguration>
getRuntimeDatabases() {
- return runtimeDatabases;
- }
-
- @Override
- protected final void prepareRuntimeFixture() throws IOException {
- Assumptions.assumeTrue(MySQLRuntimeTestSupport.isDockerAvailable(),
- () ->
MySQLRuntimeTestSupport.createDockerRequiredMessage("Docker is required for the
MySQL-backed MCP programmatic contract E2E test."));
- container = MySQLRuntimeTestSupport.createContainer();
- container.start();
- try {
- runtimeDatabases =
MySQLRuntimeTestSupport.createPreparedProgrammaticRuntimeDatabases(container);
- } catch (final SQLException ex) {
- throw new IOException("Failed to initialize MCP E2E runtime
databases.", ex);
- }
+ protected String getHttpEndpointPath() {
+ return ENDPOINT_PATH;
}
private HttpResponse<String> sendJsonRpcRequest(final HttpClient
httpClient, final Map<String, String> headers, final String requestId,
@@ -191,4 +156,8 @@ abstract class AbstractHttpProgrammaticRuntimeE2ETest
extends AbstractConfigBack
private void applyHeaders(final HttpRequest.Builder requestBuilder, final
Map<String, String> headers) {
headers.forEach(requestBuilder::setHeader);
}
+
+ private MCPRuntimeContext createRuntimeContext() {
+ return new MCPRuntimeContext(new
MCPSessionManager(Collections.emptyMap()), new
MCPDatabaseCapabilityProvider(Collections.emptyMap()), "http");
+ }
}
diff --git
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/AbstractTransportParameterizedProductionRuntimeE2ETest.java
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/AbstractSharedHttpProgrammaticRuntimeE2ETest.java
similarity index 56%
copy from
test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/AbstractTransportParameterizedProductionRuntimeE2ETest.java
copy to
test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/AbstractSharedHttpProgrammaticRuntimeE2ETest.java
index 7b6b66a1ebf..a2728e221a5 100644
---
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/production/AbstractTransportParameterizedProductionRuntimeE2ETest.java
+++
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/AbstractSharedHttpProgrammaticRuntimeE2ETest.java
@@ -15,23 +15,21 @@
* limitations under the License.
*/
-package org.apache.shardingsphere.test.e2e.mcp.runtime.production;
+package org.apache.shardingsphere.test.e2e.mcp.runtime.programmatic;
-import org.apache.shardingsphere.test.e2e.mcp.support.runtime.RuntimeTransport;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.TestInstance;
-abstract class AbstractTransportParameterizedProductionRuntimeE2ETest extends
AbstractProductionRuntimeE2ETest {
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+abstract class AbstractSharedHttpProgrammaticRuntimeE2ETest extends
AbstractHttpProgrammaticRuntimeE2ETest {
- private RuntimeTransport transport;
-
- protected final void useTransport(final RuntimeTransport transport) {
- this.transport = transport;
+ @Override
+ protected boolean useSharedDatabaseBackedRuntime() {
+ return true;
}
- @Override
- protected final RuntimeTransport getTransport() {
- if (null == transport) {
- throw new IllegalStateException("Runtime transport is not selected
for current production E2E test.");
- }
- return transport;
+ @AfterAll
+ void tearDownSharedDatabaseBackedRuntime() {
+ closeSharedDatabaseBackedRuntime();
}
}
diff --git
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/FeatureWorkflowContractE2ETest.java
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/FeatureWorkflowContractE2ETest.java
index 8a59e4faae2..91513ca75c0 100644
---
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/FeatureWorkflowContractE2ETest.java
+++
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/FeatureWorkflowContractE2ETest.java
@@ -38,7 +38,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
@EnabledIf("isEnabled")
-class FeatureWorkflowContractE2ETest extends
AbstractHttpProgrammaticRuntimeE2ETest {
+class FeatureWorkflowContractE2ETest extends
AbstractSharedHttpProgrammaticRuntimeE2ETest {
private static final List<String> FORBIDDEN_ARTIFACT_TOKENS = List.of(
"create table", "alter table", "drop table", "create index", "drop
index", "migrate", "migration", "backfill", "data probe", "physical metadata",
diff --git
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/HttpTransportApprovalSafetyE2ETest.java
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/HttpTransportApprovalSafetyE2ETest.java
index 1020d66662d..2b97d688b4b 100644
---
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/HttpTransportApprovalSafetyE2ETest.java
+++
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/HttpTransportApprovalSafetyE2ETest.java
@@ -35,7 +35,7 @@ import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
@EnabledIf("isEnabled")
-class HttpTransportApprovalSafetyE2ETest extends
AbstractHttpProgrammaticRuntimeE2ETest {
+class HttpTransportApprovalSafetyE2ETest extends
AbstractSharedHttpProgrammaticRuntimeE2ETest {
private static boolean isEnabled() {
return MCPE2ECondition.isDockerEnabled();
diff --git
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/HttpTransportBaselineContractE2ETest.java
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/HttpTransportBaselineContractE2ETest.java
index fef006781dd..469d5e2322d 100644
---
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/HttpTransportBaselineContractE2ETest.java
+++
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/HttpTransportBaselineContractE2ETest.java
@@ -35,7 +35,7 @@ import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
@EnabledIf("isEnabled")
-class HttpTransportBaselineContractE2ETest extends
AbstractHttpProgrammaticRuntimeE2ETest {
+class HttpTransportBaselineContractE2ETest extends
AbstractSharedHttpProgrammaticRuntimeE2ETest {
private static final String BASELINE_RESOURCE_PATH =
"baseline-contract/model-contract/";
diff --git
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/HttpTransportCompletionE2ETest.java
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/HttpTransportCompletionE2ETest.java
index 55ef7d8cadc..038284dcf3e 100644
---
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/HttpTransportCompletionE2ETest.java
+++
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/HttpTransportCompletionE2ETest.java
@@ -36,7 +36,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
@EnabledIf("isEnabled")
-class HttpTransportCompletionE2ETest extends
AbstractHttpProgrammaticRuntimeE2ETest {
+class HttpTransportCompletionE2ETest extends
AbstractSharedHttpProgrammaticRuntimeE2ETest {
private static final String PLAN_MASK_PROMPT_NAME = "plan_mask_rule";
diff --git
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/HttpTransportContractE2ETest.java
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/HttpTransportContractE2ETest.java
index 341749ca36c..9c6dcc7282a 100644
---
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/HttpTransportContractE2ETest.java
+++
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/HttpTransportContractE2ETest.java
@@ -41,7 +41,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
@EnabledIf("isEnabled")
-class HttpTransportContractE2ETest extends
AbstractHttpProgrammaticRuntimeE2ETest {
+class HttpTransportContractE2ETest extends
AbstractSharedHttpProgrammaticRuntimeE2ETest {
private static final List<String> OFFICIAL_TOOL_NAMES =
OfficialMCPToolNames.getAll();
diff --git
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/HttpTransportProtocolContractE2ETest.java
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/HttpTransportProtocolContractE2ETest.java
index 10f27c01995..05db95835de 100644
---
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/HttpTransportProtocolContractE2ETest.java
+++
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/HttpTransportProtocolContractE2ETest.java
@@ -17,10 +17,8 @@
package org.apache.shardingsphere.test.e2e.mcp.runtime.programmatic;
-import org.apache.shardingsphere.test.e2e.mcp.env.MCPE2ECondition;
import
org.apache.shardingsphere.test.e2e.mcp.support.transport.client.MCPHttpTransportTestSupport;
import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.condition.EnabledIf;
import java.io.IOException;
import java.net.http.HttpClient;
@@ -34,12 +32,7 @@ import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
-@EnabledIf("isEnabled")
-class HttpTransportProtocolContractE2ETest extends
AbstractHttpProgrammaticRuntimeE2ETest {
-
- private static boolean isEnabled() {
- return MCPE2ECondition.isDockerEnabled();
- }
+class HttpTransportProtocolContractE2ETest extends
AbstractHttpProtocolOnlyE2ETest {
@Test
void assertInitializeSessionAndProtocolHeaders() throws IOException,
InterruptedException {
diff --git
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/HttpTransportRecoveryE2ETest.java
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/HttpTransportRecoveryE2ETest.java
index 833e7776d83..b5b1d10603d 100644
---
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/HttpTransportRecoveryE2ETest.java
+++
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/HttpTransportRecoveryE2ETest.java
@@ -38,7 +38,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
@EnabledIf("isEnabled")
-class HttpTransportRecoveryE2ETest extends
AbstractHttpProgrammaticRuntimeE2ETest {
+class HttpTransportRecoveryE2ETest extends
AbstractSharedHttpProgrammaticRuntimeE2ETest {
private static final String RECOVERY_SECRET = "recovery-secret-value";
diff --git
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/HttpTransportSecurityE2ETest.java
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/HttpTransportSecurityE2ETest.java
index 1f5124d3481..9d02d1fcb85 100644
---
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/HttpTransportSecurityE2ETest.java
+++
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/HttpTransportSecurityE2ETest.java
@@ -18,10 +18,8 @@
package org.apache.shardingsphere.test.e2e.mcp.runtime.programmatic;
import
org.apache.shardingsphere.mcp.bootstrap.config.HttpTransportConfiguration;
-import org.apache.shardingsphere.test.e2e.mcp.env.MCPE2ECondition;
import
org.apache.shardingsphere.test.e2e.mcp.support.transport.client.MCPHttpTransportTestSupport;
import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.condition.EnabledIf;
import java.io.IOException;
import java.net.http.HttpClient;
@@ -33,15 +31,10 @@ import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertFalse;
-@EnabledIf("isEnabled")
-class HttpTransportSecurityE2ETest extends
AbstractHttpProgrammaticRuntimeE2ETest {
+class HttpTransportSecurityE2ETest extends AbstractHttpProtocolOnlyE2ETest {
private boolean remoteBinding;
- private static boolean isEnabled() {
- return MCPE2ECondition.isDockerEnabled();
- }
-
@Override
protected HttpTransportConfiguration createHttpTransportConfiguration() {
return remoteBinding ? new HttpTransportConfiguration("0.0.0.0", 0,
getHttpEndpointPath()) : super.createHttpTransportConfiguration();
diff --git
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/HttpTransportSessionLifecycleE2ETest.java
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/HttpTransportSessionLifecycleE2ETest.java
index ff41ec73476..86671d5cf54 100644
---
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/HttpTransportSessionLifecycleE2ETest.java
+++
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/HttpTransportSessionLifecycleE2ETest.java
@@ -17,9 +17,7 @@
package org.apache.shardingsphere.test.e2e.mcp.runtime.programmatic;
-import org.apache.shardingsphere.test.e2e.mcp.env.MCPE2ECondition;
import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.condition.EnabledIf;
import java.io.IOException;
import java.net.http.HttpClient;
@@ -29,12 +27,7 @@ import java.util.Map;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
-@EnabledIf("isEnabled")
-class HttpTransportSessionLifecycleE2ETest extends
AbstractHttpProgrammaticRuntimeE2ETest {
-
- private static boolean isEnabled() {
- return MCPE2ECondition.isDockerEnabled();
- }
+class HttpTransportSessionLifecycleE2ETest extends
AbstractHttpProtocolOnlyE2ETest {
@Test
void assertRejectOpenStreamAfterDelete() throws IOException,
InterruptedException {
diff --git
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/MetadataDiscoveryE2ETest.java
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/MetadataDiscoveryE2ETest.java
index 85654d7847b..bb6a2e84005 100644
---
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/MetadataDiscoveryE2ETest.java
+++
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/runtime/programmatic/MetadataDiscoveryE2ETest.java
@@ -33,7 +33,7 @@ import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertFalse;
@EnabledIf("isEnabled")
-class MetadataDiscoveryE2ETest extends AbstractHttpProgrammaticRuntimeE2ETest {
+class MetadataDiscoveryE2ETest extends
AbstractSharedHttpProgrammaticRuntimeE2ETest {
private static boolean isEnabled() {
return MCPE2ECondition.isDockerEnabled();
@@ -61,7 +61,7 @@ class MetadataDiscoveryE2ETest extends
AbstractHttpProgrammaticRuntimeE2ETest {
"shardingsphere://databases/logic_db/schemas/logic_db/tables/orders/indexes")));
HttpResponse<String> tableResource =
sendResourceReadRequest(httpClient, sessionId,
String.valueOf(actualResource.get("uri")));
assertThat(tableResource.statusCode(), is(200));
-
assertThat(String.valueOf(MCPInteractionPayloads.castToList(getFirstResourcePayload(tableResource.body()).get("items")).get(0).get("table")),
is("orders"));
+
assertThat(String.valueOf(MCPInteractionPayloads.castToList(getFirstResourcePayload(tableResource.body()).get("items")).getFirst().get("table")),
is("orders"));
}
@Test
@@ -96,8 +96,8 @@ class MetadataDiscoveryE2ETest extends
AbstractHttpProgrammaticRuntimeE2ETest {
assertThat(actual.statusCode(), is(200));
List<Map<String, Object>> actualItems =
MCPInteractionPayloads.castToList(getFirstResourcePayload(actual.body()).get("items"));
assertThat(actualItems.size(), is(1));
- assertThat(String.valueOf(actualItems.get(0).get("table")),
is("facts"));
- assertThat(String.valueOf(actualItems.get(0).get("index")),
is("PRIMARY"));
+ assertThat(String.valueOf(actualItems.getFirst().get("table")),
is("facts"));
+ assertThat(String.valueOf(actualItems.getFirst().get("index")),
is("PRIMARY"));
}
@Test
diff --git
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/support/distribution/PackagedDistributionTestSupport.java
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/support/distribution/PackagedDistributionTestSupport.java
index 0b00365e19a..95343e05c36 100644
---
a/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/support/distribution/PackagedDistributionTestSupport.java
+++
b/test/e2e/mcp/src/test/java/org/apache/shardingsphere/test/e2e/mcp/support/distribution/PackagedDistributionTestSupport.java
@@ -89,6 +89,23 @@ public final class PackagedDistributionTestSupport {
return new PreparedPackagedDistribution(workingHome, configFile,
transport, httpPort);
}
+ /**
+ * Prepare a reusable packaged MCP distribution home with supplied runtime
databases.
+ *
+ * @param workingHome reusable working home
+ * @param transport runtime transport
+ * @param runtimeDatabases runtime databases
+ * @return prepared packaged distribution
+ * @throws IOException I/O exception
+ */
+ public static PreparedPackagedDistribution prepareReusable(final Path
workingHome, final RuntimeTransport transport,
+ final
Map<String, RuntimeDatabaseConfiguration> runtimeDatabases) throws IOException {
+ Path actualWorkingHome = prepareReusableWorkingHome(workingHome);
+ int httpPort = resolveHttpPort(transport);
+ Path configFile = rewriteConfiguration(actualWorkingHome, transport,
httpPort, runtimeDatabases);
+ return new PreparedPackagedDistribution(actualWorkingHome, configFile,
transport, httpPort);
+ }
+
/**
* Create a mounted configuration file for a Dockerized MCP distribution.
*
@@ -130,6 +147,15 @@ public final class PackagedDistributionTestSupport {
return workingHome;
}
+ private static Path prepareReusableWorkingHome(final Path workingHome)
throws IOException {
+ if (!Files.exists(workingHome)) {
+ copyDistributionHome(findRequiredDistributionHome(), workingHome);
+ }
+ resetRuntimeDirectories(workingHome);
+ makeStartScriptExecutable(workingHome);
+ return workingHome;
+ }
+
private static Path findRequiredDistributionHome() throws IOException {
return
findDistributionHome().orElseThrow(PackagedDistributionTestSupport::createMissingDistributionHomeException);
}