Skip to content

Commit b259db3

Browse files
committed
Add dynamic-client end-to-end test against real Connect model
Loads the production AWS Connect model (DescribeContact, GET /contacts/{InstanceId}/{ContactId}) and exercises the full DynamicClient -> RestJsonClientProtocol -> PathSerializer path via a capturing transport (no credentials or network), asserting the two @httpLabel path variables serialize correctly. This is an end-to-end smoke test for the dynamic-client path. Note: it does NOT reproduce the original ClassCastException on its own, because DocumentUtils.getMemberValue (since 4ec73e9) unwraps a string label via asObject() before PathSerializer sees it. The unit-level PathSerializerTest remains the regression guard for the cast bug; this test guards the dynamic-client integration end to end. Adds smithy-aws-endpoints as a test dependency so the real Connect model (endpointBdd / aws.partition) assembles.
1 parent 351f916 commit b259db3

3 files changed

Lines changed: 64812 additions & 0 deletions

File tree

client/dynamic-client/build.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,6 @@ dependencies {
1616
testImplementation(project(":aws:client:aws-client-restjson"))
1717
testImplementation(project(":aws:client:aws-client-awsjson"))
1818
testImplementation(project(":aws:aws-sigv4"))
19+
// Registers the aws.partition endpoint function used by real AWS service models (e.g. Connect's endpointBdd).
20+
testImplementation(libs.smithy.aws.endpoints)
1921
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package software.amazon.smithy.java.dynamicclient;
7+
8+
import static org.hamcrest.MatcherAssert.assertThat;
9+
import static org.hamcrest.Matchers.equalTo;
10+
11+
import java.util.Map;
12+
import java.util.concurrent.atomic.AtomicReference;
13+
import org.junit.jupiter.api.BeforeAll;
14+
import org.junit.jupiter.api.Test;
15+
import software.amazon.smithy.java.aws.client.restjson.RestJsonClientProtocol;
16+
import software.amazon.smithy.java.client.core.ClientTransport;
17+
import software.amazon.smithy.java.client.core.MessageExchange;
18+
import software.amazon.smithy.java.client.core.auth.scheme.AuthSchemeResolver;
19+
import software.amazon.smithy.java.client.http.HttpMessageExchange;
20+
import software.amazon.smithy.java.context.Context;
21+
import software.amazon.smithy.java.core.serde.document.Document;
22+
import software.amazon.smithy.java.endpoints.EndpointResolver;
23+
import software.amazon.smithy.java.http.api.HttpRequest;
24+
import software.amazon.smithy.java.http.api.HttpResponse;
25+
import software.amazon.smithy.java.http.api.HttpVersion;
26+
import software.amazon.smithy.java.io.datastream.DataStream;
27+
import software.amazon.smithy.model.Model;
28+
import software.amazon.smithy.model.shapes.ShapeId;
29+
30+
/**
31+
* Reproduces the AWS Connect failure that motivated the PathSerializer fix, using the real production
32+
* Connect model from api-models-aws. {@code DescribeContact} is a restJson1 GET with two {@code @httpLabel}
33+
* path variables ({@code /contacts/{InstanceId}/{ContactId}}). Before the fix, serializing these labels from
34+
* a document-backed DynamicClient struct threw
35+
* {@code ClassCastException: ContentDocument cannot be cast to String} at PathSerializer.formatLabelValue.
36+
*
37+
* <p>This is the sibling of {@link DynamicClientHttpBindingTest}, but against a live service model rather than a
38+
* hand-written one. It uses a capturing transport so no credentials or network are needed: we intercept the
39+
* fully-serialized HTTP request and assert the path was built correctly.
40+
*/
41+
public class ConnectDescribeContactPathTest {
42+
43+
private static final ShapeId SERVICE = ShapeId.from("com.amazonaws.connect#AmazonConnectService");
44+
45+
private static Model model;
46+
47+
@BeforeAll
48+
public static void setup() {
49+
// discoverModels() loads AWS trait definitions (restJson1, sigv4, endpoint rules functions like
50+
// aws.partition) off the classpath so the real Connect model resolves. We only need the path-label
51+
// serialization to work, so unknown traits are tolerated rather than failing assembly.
52+
model = Model.assembler()
53+
.discoverModels(ConnectDescribeContactPathTest.class.getClassLoader())
54+
.putProperty(software.amazon.smithy.model.loader.ModelAssembler.ALLOW_UNKNOWN_TRAITS, true)
55+
.addImport(ConnectDescribeContactPathTest.class.getResource("/connect-2017-08-08.json"))
56+
.assemble()
57+
.unwrap();
58+
}
59+
60+
@Test
61+
public void serializesPathLabelsFromDocumentInput() {
62+
var captured = new AtomicReference<HttpRequest>();
63+
var client = DynamicClient.builder()
64+
.serviceId(SERVICE)
65+
.model(model)
66+
.protocol(new RestJsonClientProtocol(SERVICE))
67+
.authSchemeResolver(AuthSchemeResolver.NO_AUTH)
68+
.transport(capturingTransport(captured))
69+
.endpointResolver(EndpointResolver.staticEndpoint("https://connect.us-east-1.amazonaws.com"))
70+
.build();
71+
72+
// Document-backed input: this is exactly the shape AWS Connect's ProxyService supplies via MCP.
73+
client.call("DescribeContact",
74+
Document.ofObject(Map.of(
75+
"InstanceId", "inst-1234567890abcdef",
76+
"ContactId", "contact-0987654321fedcba")));
77+
78+
var request = captured.get();
79+
// The two @httpLabel path variables must serialize into the URI, not throw ClassCastException.
80+
assertThat(
81+
request.uri().getPath(),
82+
equalTo("/contacts/inst-1234567890abcdef/contact-0987654321fedcba"));
83+
}
84+
85+
private static ClientTransport<HttpRequest, HttpResponse> capturingTransport(AtomicReference<HttpRequest> sink) {
86+
return new ClientTransport<>() {
87+
@Override
88+
public MessageExchange<HttpRequest, HttpResponse> messageExchange() {
89+
return HttpMessageExchange.INSTANCE;
90+
}
91+
92+
@Override
93+
public HttpResponse send(Context context, HttpRequest request) {
94+
sink.set(request);
95+
return HttpResponse.create()
96+
.setHttpVersion(HttpVersion.HTTP_1_1)
97+
.setStatusCode(200)
98+
.setBody(DataStream.ofString("{}"))
99+
.toUnmodifiable();
100+
}
101+
};
102+
}
103+
}

0 commit comments

Comments
 (0)