Skip to content

Commit 1ace9b1

Browse files
cenedhrynL-Applin
authored andcommitted
Generate client endpoint tests if test cases have operationInputs or otherwise are viable (#4073)
* Updating codegen for endpoint client tests to ignore test cases without operationInputs unless explicitly instructed to include them * Only generate client test spec class if there are any matching test cases
1 parent 317aaa5 commit 1ace9b1

File tree

20 files changed

+111
-189
lines changed

20 files changed

+111
-189
lines changed

codegen/src/main/java/software/amazon/awssdk/codegen/emitters/tasks/EndpointProviderTasks.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,11 @@ protected List<GeneratorTask> createTasks() throws Exception {
5252
tasks.add(generateDefaultProvider());
5353
tasks.addAll(generateInterceptors());
5454
if (shouldGenerateEndpointTests()) {
55-
tasks.add(generateClientTests());
5655
tasks.add(generateProviderTests());
5756
}
57+
if (shouldGenerateEndpointTests() && shouldGenerateClientEndpointTests()) {
58+
tasks.add(generateClientTests());
59+
}
5860
if (hasClientContextParams()) {
5961
tasks.add(generateClientContextParams());
6062
}
@@ -118,6 +120,13 @@ private boolean shouldGenerateEndpointTests() {
118120
!generatorTaskParams.getModel().getEndpointTestSuiteModel().getTestCases().isEmpty();
119121
}
120122

123+
private boolean shouldGenerateClientEndpointTests() {
124+
CustomizationConfig customizationConfig = generatorTaskParams.getModel().getCustomizationConfig();
125+
boolean noTestCasesHaveOperationInputs = model.getEndpointTestSuiteModel().getTestCases().stream()
126+
.noneMatch(t -> t.getOperationInputs() != null);
127+
return noTestCasesHaveOperationInputs && Boolean.TRUE.equals(customizationConfig.isGenerateEndpointClientTests());
128+
}
129+
121130
private boolean hasClientContextParams() {
122131
Map<String, ClientContextParam> clientContextParams = model.getClientContextParams();
123132
return clientContextParams != null && !clientContextParams.isEmpty();

codegen/src/main/java/software/amazon/awssdk/codegen/model/config/customization/CustomizationConfig.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,11 @@ public class CustomizationConfig {
217217
*/
218218
private boolean skipEndpointTestGeneration;
219219

220+
/**
221+
* Whether to generate client-level endpoint tests; overrides test case criteria such as operation inputs.
222+
*/
223+
private boolean generateEndpointClientTests;
224+
220225
/**
221226
* A mapping from the skipped test's description to the reason why it's being skipped.
222227
*/
@@ -577,6 +582,14 @@ public void setSkipEndpointTestGeneration(boolean skipEndpointTestGeneration) {
577582
this.skipEndpointTestGeneration = skipEndpointTestGeneration;
578583
}
579584

585+
public boolean isGenerateEndpointClientTests() {
586+
return generateEndpointClientTests;
587+
}
588+
589+
public void setGenerateEndpointClientTests(boolean generateEndpointClientTests) {
590+
this.generateEndpointClientTests = generateEndpointClientTests;
591+
}
592+
580593
public boolean useGlobalEndpoint() {
581594
return useGlobalEndpoint;
582595
}

codegen/src/main/java/software/amazon/awssdk/codegen/poet/rules/EndpointRulesClientTestSpec.java

Lines changed: 50 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,10 @@ public TypeSpec poetSpec() {
111111
b.addField(s3RegionEndpointSystemPropertySaveValueField());
112112
}
113113

114+
if (serviceHasNoMatchingTestCases()) {
115+
return b.build();
116+
}
117+
114118
b.addMethod(methodSetupMethod());
115119
b.addMethod(teardownMethod());
116120

@@ -215,16 +219,22 @@ private MethodSpec syncTestsSourceMethod() {
215219
.addModifiers(Modifier.PRIVATE, Modifier.STATIC)
216220
.returns(ParameterizedTypeName.get(List.class, SyncTestCase.class));
217221

222+
223+
224+
218225
b.addCode("return $T.asList(", Arrays.class);
219226

220227
EndpointTestSuiteModel endpointTestSuiteModel = model.getEndpointTestSuiteModel();
221228
Iterator<EndpointTestModel> testIter = endpointTestSuiteModel.getTestCases().iterator();
222-
229+
boolean isFirst = true;
223230
while (testIter.hasNext()) {
224231
EndpointTestModel test = testIter.next();
225-
226-
if (test.getOperationInputs() != null) {
232+
if (testCaseHasOperationInputs(test)) {
227233
Iterator<OperationInput> operationInputsIter = test.getOperationInputs().iterator();
234+
if (!isFirst) {
235+
b.addCode(", ");
236+
}
237+
isFirst = false;
228238
while (operationInputsIter.hasNext()) {
229239
OperationInput opInput = operationInputsIter.next();
230240
OperationModel opModel = model.getOperation(opInput.getOperationName());
@@ -240,18 +250,18 @@ private MethodSpec syncTestsSourceMethod() {
240250
b.addCode(",");
241251
}
242252
}
243-
} else {
253+
} else if (shouldGenerateClientTestsOverride()) {
254+
if (!isFirst) {
255+
b.addCode(", ");
256+
}
257+
isFirst = false;
244258
b.addCode("new $T($S, $L, $L$L)",
245259
SyncTestCase.class,
246260
test.getDocumentation(),
247261
syncOperationCallLambda(defaultOpModel, test.getParams(), Collections.emptyMap()),
248262
TestGeneratorUtils.createExpect(test.getExpect(), defaultOpModel, null),
249263
getSkipReasonBlock(test.getDocumentation()));
250264
}
251-
252-
if (testIter.hasNext()) {
253-
b.addCode(",");
254-
}
255265
}
256266

257267
b.addStatement(")");
@@ -365,12 +375,16 @@ private MethodSpec asyncTestsSourceMethod() {
365375

366376
EndpointTestSuiteModel endpointTestSuiteModel = model.getEndpointTestSuiteModel();
367377
Iterator<EndpointTestModel> testIter = endpointTestSuiteModel.getTestCases().iterator();
368-
378+
boolean isFirst = true;
369379
while (testIter.hasNext()) {
370380
EndpointTestModel test = testIter.next();
371381

372-
if (test.getOperationInputs() != null) {
382+
if (testCaseHasOperationInputs(test)) {
373383
Iterator<OperationInput> operationInputsIter = test.getOperationInputs().iterator();
384+
if (!isFirst) {
385+
b.addCode(", ");
386+
}
387+
isFirst = false;
374388
while (operationInputsIter.hasNext()) {
375389
OperationInput opInput = operationInputsIter.next();
376390
OperationModel opModel = model.getOperation(opInput.getOperationName());
@@ -386,18 +400,18 @@ private MethodSpec asyncTestsSourceMethod() {
386400
b.addCode(",");
387401
}
388402
}
389-
} else {
403+
} else if (shouldGenerateClientTestsOverride()) {
404+
if (!isFirst) {
405+
b.addCode(", ");
406+
}
407+
isFirst = false;
390408
b.addCode("new $T($S, $L, $L$L)",
391409
AsyncTestCase.class,
392410
test.getDocumentation(),
393411
asyncOperationCallLambda(defaultOpModel, test.getParams(), Collections.emptyMap()),
394412
TestGeneratorUtils.createExpect(test.getExpect(), defaultOpModel, null),
395413
getSkipReasonBlock(test.getDocumentation()));
396414
}
397-
398-
if (testIter.hasNext()) {
399-
b.addCode(",");
400-
}
401415
}
402416

403417
b.addStatement(")");
@@ -649,6 +663,27 @@ private Map<String, String> getSkippedTests() {
649663
return skippedTests;
650664
}
651665

666+
private boolean serviceHasNoMatchingTestCases() {
667+
boolean noTestCasesHaveOperationInputs = model.getEndpointTestSuiteModel().getTestCases().stream()
668+
.noneMatch(EndpointRulesClientTestSpec::testCaseHasOperationInputs);
669+
return noTestCasesHaveOperationInputs && !shouldGenerateClientTestsOverride();
670+
}
671+
672+
/**
673+
* Always generate client endpoint tests if the test case has operation inputs
674+
*/
675+
private static boolean testCaseHasOperationInputs(EndpointTestModel test) {
676+
return test.getOperationInputs() != null;
677+
}
678+
679+
/**
680+
* Some services can run tests without operation inputs if there are other conditions that allow
681+
* codegen to create a functioning test case
682+
*/
683+
private boolean shouldGenerateClientTestsOverride() {
684+
return model.getCustomizationConfig().isGenerateEndpointClientTests();
685+
}
686+
652687
private CodeBlock getSkipReasonBlock(String testName) {
653688
if (getSkippedTests().containsKey(testName)) {
654689
Validate.notNull(getSkippedTests().get(testName), "Test %s must have a reason for skipping", testName);

codegen/src/test/resources/software/amazon/awssdk/codegen/poet/rules/endpoint-rules-test-class.java

Lines changed: 2 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
import software.amazon.awssdk.services.query.QueryAsyncClientBuilder;
1919
import software.amazon.awssdk.services.query.QueryClient;
2020
import software.amazon.awssdk.services.query.QueryClientBuilder;
21-
import software.amazon.awssdk.services.query.model.APostOperationRequest;
2221
import software.amazon.awssdk.services.query.model.ChecksumStructure;
2322
import software.amazon.awssdk.services.query.model.OperationWithContextParamRequest;
2423

@@ -47,26 +46,6 @@ public void asyncClient_usesCorrectEndpoint(AsyncTestCase tc) {
4746

4847
private static List<SyncTestCase> syncTestCases() {
4948
return Arrays.asList(
50-
new SyncTestCase("test case 1", () -> {
51-
QueryClientBuilder builder = QueryClient.builder();
52-
builder.credentialsProvider(BaseRuleSetClientTest.CREDENTIALS_PROVIDER);
53-
builder.tokenProvider(BaseRuleSetClientTest.TOKEN_PROVIDER);
54-
builder.httpClient(getSyncHttpClient());
55-
builder.region(Region.of("us-east-1"));
56-
APostOperationRequest request = APostOperationRequest.builder().build();
57-
builder.build().aPostOperation(request);
58-
}, Expect.builder().endpoint(Endpoint.builder().url(URI.create("https://foo-myservice.aws")).build()).build()),
59-
new SyncTestCase("test case 2", () -> {
60-
QueryClientBuilder builder = QueryClient.builder();
61-
builder.credentialsProvider(BaseRuleSetClientTest.CREDENTIALS_PROVIDER);
62-
builder.tokenProvider(BaseRuleSetClientTest.TOKEN_PROVIDER);
63-
builder.httpClient(getSyncHttpClient());
64-
builder.region(Region.of("us-east-1"));
65-
builder.booleanContextParam(true);
66-
builder.stringContextParam("this is a test");
67-
APostOperationRequest request = APostOperationRequest.builder().build();
68-
builder.build().aPostOperation(request);
69-
}, Expect.builder().endpoint(Endpoint.builder().url(URI.create("https://foo-myservice.aws")).build()).build()),
7049
new SyncTestCase("test case 3", () -> {
7150
QueryClientBuilder builder = QueryClient.builder();
7251
builder.credentialsProvider(BaseRuleSetClientTest.CREDENTIALS_PROVIDER);
@@ -88,14 +67,6 @@ private static List<SyncTestCase> syncTestCases() {
8867
builder.build().operationWithContextParam(request);
8968
}, Expect.builder().endpoint(Endpoint.builder().url(URI.create("https://myservice.aws")).build()).build(),
9069
"Does not work"),
91-
new SyncTestCase("For region us-iso-west-1 with FIPS enabled and DualStack enabled", () -> {
92-
QueryClientBuilder builder = QueryClient.builder();
93-
builder.credentialsProvider(BaseRuleSetClientTest.CREDENTIALS_PROVIDER);
94-
builder.tokenProvider(BaseRuleSetClientTest.TOKEN_PROVIDER);
95-
builder.httpClient(getSyncHttpClient());
96-
APostOperationRequest request = APostOperationRequest.builder().build();
97-
builder.build().aPostOperation(request);
98-
}, Expect.builder().error("Should have been skipped!").build(), "Client builder does the validation"),
9970
new SyncTestCase("Has complex operation input", () -> {
10071
QueryClientBuilder builder = QueryClient.builder();
10172
builder.credentialsProvider(BaseRuleSetClientTest.CREDENTIALS_PROVIDER);
@@ -104,39 +75,11 @@ private static List<SyncTestCase> syncTestCases() {
10475
OperationWithContextParamRequest request = OperationWithContextParamRequest.builder()
10576
.nestedMember(ChecksumStructure.builder().checksumMode("foo").build()).build();
10677
builder.build().operationWithContextParam(request);
107-
}, Expect.builder().error("Missing info").build()), new SyncTestCase("Has has undeclared input parameter",
108-
() -> {
109-
QueryClientBuilder builder = QueryClient.builder();
110-
builder.credentialsProvider(BaseRuleSetClientTest.CREDENTIALS_PROVIDER);
111-
builder.tokenProvider(BaseRuleSetClientTest.TOKEN_PROVIDER);
112-
builder.httpClient(getSyncHttpClient());
113-
APostOperationRequest request = APostOperationRequest.builder().build();
114-
builder.build().aPostOperation(request);
115-
}, Expect.builder().error("Missing info").build()));
78+
}, Expect.builder().error("Missing info").build()));
11679
}
11780

11881
private static List<AsyncTestCase> asyncTestCases() {
11982
return Arrays.asList(
120-
new AsyncTestCase("test case 1", () -> {
121-
QueryAsyncClientBuilder builder = QueryAsyncClient.builder();
122-
builder.credentialsProvider(BaseRuleSetClientTest.CREDENTIALS_PROVIDER);
123-
builder.tokenProvider(BaseRuleSetClientTest.TOKEN_PROVIDER);
124-
builder.httpClient(getAsyncHttpClient());
125-
builder.region(Region.of("us-east-1"));
126-
APostOperationRequest request = APostOperationRequest.builder().build();
127-
return builder.build().aPostOperation(request);
128-
}, Expect.builder().endpoint(Endpoint.builder().url(URI.create("https://foo-myservice.aws")).build()).build()),
129-
new AsyncTestCase("test case 2", () -> {
130-
QueryAsyncClientBuilder builder = QueryAsyncClient.builder();
131-
builder.credentialsProvider(BaseRuleSetClientTest.CREDENTIALS_PROVIDER);
132-
builder.tokenProvider(BaseRuleSetClientTest.TOKEN_PROVIDER);
133-
builder.httpClient(getAsyncHttpClient());
134-
builder.region(Region.of("us-east-1"));
135-
builder.booleanContextParam(true);
136-
builder.stringContextParam("this is a test");
137-
APostOperationRequest request = APostOperationRequest.builder().build();
138-
return builder.build().aPostOperation(request);
139-
}, Expect.builder().endpoint(Endpoint.builder().url(URI.create("https://foo-myservice.aws")).build()).build()),
14083
new AsyncTestCase("test case 3", () -> {
14184
QueryAsyncClientBuilder builder = QueryAsyncClient.builder();
14285
builder.credentialsProvider(BaseRuleSetClientTest.CREDENTIALS_PROVIDER);
@@ -158,14 +101,6 @@ private static List<AsyncTestCase> asyncTestCases() {
158101
return builder.build().operationWithContextParam(request);
159102
}, Expect.builder().endpoint(Endpoint.builder().url(URI.create("https://myservice.aws")).build()).build(),
160103
"Does not work"),
161-
new AsyncTestCase("For region us-iso-west-1 with FIPS enabled and DualStack enabled", () -> {
162-
QueryAsyncClientBuilder builder = QueryAsyncClient.builder();
163-
builder.credentialsProvider(BaseRuleSetClientTest.CREDENTIALS_PROVIDER);
164-
builder.tokenProvider(BaseRuleSetClientTest.TOKEN_PROVIDER);
165-
builder.httpClient(getAsyncHttpClient());
166-
APostOperationRequest request = APostOperationRequest.builder().build();
167-
return builder.build().aPostOperation(request);
168-
}, Expect.builder().error("Should have been skipped!").build(), "Client builder does the validation"),
169104
new AsyncTestCase("Has complex operation input", () -> {
170105
QueryAsyncClientBuilder builder = QueryAsyncClient.builder();
171106
builder.credentialsProvider(BaseRuleSetClientTest.CREDENTIALS_PROVIDER);
@@ -174,14 +109,6 @@ private static List<AsyncTestCase> asyncTestCases() {
174109
OperationWithContextParamRequest request = OperationWithContextParamRequest.builder()
175110
.nestedMember(ChecksumStructure.builder().checksumMode("foo").build()).build();
176111
return builder.build().operationWithContextParam(request);
177-
}, Expect.builder().error("Missing info").build()), new AsyncTestCase("Has has undeclared input parameter",
178-
() -> {
179-
QueryAsyncClientBuilder builder = QueryAsyncClient.builder();
180-
builder.credentialsProvider(BaseRuleSetClientTest.CREDENTIALS_PROVIDER);
181-
builder.tokenProvider(BaseRuleSetClientTest.TOKEN_PROVIDER);
182-
builder.httpClient(getAsyncHttpClient());
183-
APostOperationRequest request = APostOperationRequest.builder().build();
184-
return builder.build().aPostOperation(request);
185-
}, Expect.builder().error("Missing info").build()));
112+
}, Expect.builder().error("Missing info").build()));
186113
}
187114
}
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"verifiedSimpleMethods" : [
33
"listAccounts"
4-
]
4+
],
5+
"generateEndpointClientTests": true
56
}

services/dataexchange/src/main/resources/codegen-resources/customization.config

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@
33
"SendApiAsset": {
44
"exclude": true
55
}
6-
}
6+
},
7+
"generateEndpointClientTests": true
78
}

services/datasync/src/main/resources/codegen-resources/customization.config

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@
44
"listLocations",
55
"listTaskExecutions",
66
"listTasks"
7-
]
7+
],
8+
"generateEndpointClientTests": true
89
}

services/eventbridge/src/main/resources/codegen-resources/customization.config

Lines changed: 0 additions & 10 deletions
This file was deleted.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"generateEndpointClientTests": true
3+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"generateEndpointClientTests": true
3+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"generateEndpointClientTests": true
3+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"generateEndpointClientTests": true
3+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"generateEndpointClientTests": true
3+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"generateEndpointClientTests": true
3+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"generateEndpointClientTests": true
3+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"generateEndpointClientTests": true
3+
}

0 commit comments

Comments
 (0)