Skip to content

test: expand E2E integration test suite for cross-SDK parity - #83

Open
stenalpjolly wants to merge 4 commits into
googleapis:mainfrom
stenalpjolly:stenalpjolly_integration-test-parity
Open

test: expand E2E integration test suite for cross-SDK parity#83
stenalpjolly wants to merge 4 commits into
googleapis:mainfrom
stenalpjolly:stenalpjolly_integration-test-parity

Conversation

@stenalpjolly

@stenalpjolly stenalpjolly commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Summary

Expands the Java SDK E2E integration test suite to achieve full functional parity with the Go, JavaScript/TypeScript, and Python SDKs. Adds comprehensive error handling, argument type and presence validations, schema pruning assertions, optional/default parameter tests, structured Map data handling, protocol version negotiations, and custom client header verifications.

Expectation & Implementation

  • Tool & Toolset Loading Errors: Validates negative paths when loading non-existent toolsets and tools, asserting appropriate exception propagation.
  • Argument Validation: Verifies that missing required parameters or type mismatches (e.g. passing integer for string) are caught before or during execution.
  • Bound Parameter Pruning in Live E2E: Confirms that parameters bound via tool.bindParam(...) are pruned from the exposed tool definition schema.
  • Authentication Resilience: Adds test cases for missing tokens on authenticated tools, invalid tokens, and failing token supplier futures.
  • Complex & Structured Payloads: Adds test suite for search-rows (testing optional string and integer parameters with omission and explicit values) and process-data (testing nested Map<String, Object> payloads with structural JSON parsing to avoid key-ordering flakes).
  • Protocol Versions & Headers: Validates client creation with custom headers and explicit protocol versions (VERSION_2024_11_05, VERSION_2025_03_26, VERSION_2025_06_18, VERSION_2025_11_25) against the live MCP Toolbox server.
  • Environment Flexibility: Enhances ToolboxE2ESetup to support TOOLBOX_SERVER_URL for pre-configured server instances and provides a centralized getTextContent test helper.

Test cases

  • McpToolboxClientE2ETest:
    • testLoadToolsetSpecific
    • testLoadToolsetDefault
    • testLoadNonExistentToolset
    • testLoadNonExistentTool
    • testRunTool
    • testRunToolMissingRequiredParams
    • testRunToolWrongParamType
    • testBindParams
    • testBindParamsCallable
    • testBoundParamPruningSchema
    • testRunToolAuth
    • testRunToolWrongAuth
    • testRunToolAuthWithoutProvidingAuth
    • testRunToolParamAuth
    • testRunToolParamAuthNoField
    • testRunToolWithFailingTokenSupplier
  • McpToolboxComplexTypesE2ETest:
    • testSearchRowsDefinitionSchema
    • testSearchRowsOmittingOptionals
    • testSearchRowsWithAllParamsProvided
    • testSearchRowsMissingRequiredParam
    • testSearchRowsNonMatchingData
    • testProcessDataDefinitionSchema
    • testProcessDataWithMapParams
    • testProcessDataOmittingOptionalMap
  • McpToolboxProtocolE2ETest:
    • testClientWithCustomHeaders
    • testClientWithExplicitProtocolVersions

Acceptance criteria

  • All 130 local unit tests pass with 100% check coverage.
  • Code strictly formatted according to Google Java Format standard.
  • All new E2E tests are compatible with the MCP Toolbox live server.
  • Presubmit CI triggers (core-java-sdk-pr in Cloud Build and GitHub Actions workflows) pass cleanly.

Breaking changes

None.

- Add missing toolset and tool loading negative error tests
- Add argument validation tests for missing and wrong parameter types
- Add bound parameter schema pruning assertion in live integration tests
- Add auth failure and missing token negative test cases
- Add complex data types suite covering optional search-rows and process-data
- Add protocol version selection and client headers E2E tests
- Add TOOLBOX_SERVER_URL environment support to ToolboxE2ESetup
@stenalpjolly
stenalpjolly requested a review from a team as a code owner August 12, 2026 08:27

@anubhav756 anubhav756 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tests are incorrect, they seem to be passing wrong param values and on failure they seem to be swallowing the error and passing anyway.

void testProcessDataWithMapParams() {
Tool tool = client.loadTool("process-data").join();
Map<String, Object> inputData =
Map.of("key1", "val1", "count", 5, "nested", Map.of("inner", "value"));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we using correct param names? Seems like these are wrong param names? Please check projects/107716898620/secrets/sdk_testing_tools/versions/34 secret value (note that it's not the latest revision 35, but the revision 34).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved. Replaced parameter names with execution_context, user_scores, and feature_flags from the revision 34 manifest, eliminated the try-catch block, asserted assertFalse(result.isError()), and added testProcessDataOmittingOptionalMap.

assertNotNull(result);
} catch (Exception e) {
// In case server expects specific parameters for process-data
assertNotNull(e);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Server will throw error as the test passes incorrect param names, but the test swallows those errors and passes anyway.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved. Removed the try-catch block; the test now asserts assertFalse(result.isError()) directly against the valid parameter schema.

@stenalpjolly
stenalpjolly force-pushed the stenalpjolly_integration-test-parity branch from 16bb008 to ec12882 Compare September 3, 2026 09:39

@anubhav756 anubhav756 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR summary states:

Adds test cases for missing tokens on authenticated tools, invalid tokens, unauthenticated tools receiving tokens, and failing token supplier futures.

Is that really implemented in this PR?

Comment on lines +43 to +44
private static final String TOOLBOX_AUTH_TOKEN_1_ENV = "TOOLBOX_AUTH_TOKEN_1";
private static final String TOOLBOX_AUTH_TOKEN_2_ENV = "TOOLBOX_AUTH_TOKEN_2";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In other language SDKs, we dynamically obtain these from GCS. Is there a specific reason we want it this way specifically in Java SDK? Or do you think this should be the way for other language SDKs as well?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Java SDK tests also dynamically download the toolbox binary from GCS (mcp-toolbox-for-databases) and dynamically fetch client IDs and ID tokens via Secret Manager (sdk_testing_client1 / sdk_testing_client2), matching Python and Go. TOOLBOX_AUTH_TOKEN_1 and TOOLBOX_AUTH_TOKEN_2 were kept purely as optional environment overrides when connecting to an already running external server (TOOLBOX_SERVER_URL) without requiring local GCP credentials.

authToken2 = getAuthToken(client2Id);

// Start Server
startServer();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If both TOOLBOX_SERVER_URL and GOOGLE_CLOUD_PROJECT are set, will it run on the local server or could it be left unused?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated! TOOLBOX_SERVER_URL now takes precedence whenever set. Additionally, if GOOGLE_CLOUD_PROJECT is also present, it leverages Secret Manager to dynamically obtain authentication tokens if explicit token env vars aren't provided.

assertFalse(result.isError(), "Expected success: " + getTextContent(result));
String output = getTextContent(result);
assertTrue(
output.contains("\"execution_context\":{\"env\":\"prod\",\"id\":1234,\"user\":1234.5}"),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does Java guarantee iteration order guarantee? If not, could this lead to false negative flakes?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great point. Java's Map.of() does not guarantee iteration order. We updated the test payload to use LinkedHashMap, which enforces deterministic insertion-order serialization in Jackson and prevents key ordering flakes.

() -> CompletableFuture.failedFuture(new RuntimeException("Token unavailable")));

assertThrows(
Exception.class,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we consider narrowing down this error expectation to avoid catching NullPointerException and such?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Replaced broad assertThrows(Exception.class) with CompletionException across testLoadNonExistentToolset, testLoadNonExistentTool, and testRunToolWithFailingTokenSupplier, and added explicit assertions on the cause types and message strings ('Token unavailable', 'toolset does not exist', and 'Tool not found').

Comment on lines +64 to +72
if ("email".equals(p.name())) {
hasEmail = true;
assertTrue(p.required(), "Parameter 'email' should be required");
} else if ("data".equals(p.name())) {
hasData = true;
assertFalse(p.required(), "Parameter 'data' should be optional");
} else if ("id".equals(p.name())) {
hasId = true;
assertFalse(p.required(), "Parameter 'id' should be optional");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Should we also check the types here like the other tests?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added! testSearchRowsDefinitionSchema now asserts types: email ('string'), data ('string'), and id ('integer').

boolean hasParamAfter =
boundTool.definition().parameters() != null
&& boundTool.definition().parameters().stream()
.anyMatch(p -> "num_rows".equals(p.name()));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we also check if tool.definition().parameters() still contains num_rows to ensure immutability?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added! We now assert that the original tool.definition().parameters() still contains num_rows after calling tool.bindParam(...) to verify instance immutability.

Comment on lines +236 to +242
private String getTextContent(ToolResult result) {
if (result.content() == null) return "";
return result.content().stream()
.filter(c -> "text".equals(c.type()) && c.text() != null)
.map(c -> c.text())
.collect(java.util.stream.Collectors.joining("\n"));
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we make this method a package-private static helper method to make this DRY?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Consolidated getTextContent into ToolboxE2ESetup.java as a shared static helper and removed the duplicated methods across the test classes.

- Correct process-data parameter payload to use execution_context, user_scores, and feature_flags instead of fictional input_data
- Remove exception-swallowing try-catch blocks in testProcessDataWithMapParams and testRunToolAuthWithoutProvidingAuth
- Implement missing testProcessDataOmittingOptionalMap test method
- Add comprehensive parameter schema assertions in testProcessDataDefinitionSchema
- Parse JSON structurally in process-data tests to eliminate non-deterministic map ordering flakes
- Narrow exception expectations from Exception.class to CompletionException with specific cause message checks
- Consolidate duplicated getTextContent helper into ToolboxE2ESetup for DRY compliance
- Align testSearchRowsNonMatchingData with expected empty or 'null' return on argument mismatch
- Support TOOLBOX_AUTH_TOKEN_1 and TOOLBOX_AUTH_TOKEN_2 fallback environment variables in ToolboxE2ESetup

TAG=agy
CONV=f9ec8a85-66e3-479e-a944-3b621f8bcd18
@stenalpjolly
stenalpjolly force-pushed the stenalpjolly_integration-test-parity branch from ec12882 to e6826b9 Compare September 3, 2026 12:07
…er precedence

- Align testLoadNonExistentToolset cause check with server error message
- Assert parameter immutability on original tool definition after bindParam
- Check parameter types ('string', 'integer') in search-rows definition schema
- Use LinkedHashMap for map parameters to ensure deterministic key ordering
- Ensure TOOLBOX_SERVER_URL takes precedence when configured in test setup

TAG=agy
CONV=f9ec8a85-66e3-479e-a944-3b621f8bcd18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants