1 /*
2  * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License").
5  * You may not use this file except in compliance with the License.
6  * A copy of the License is located at
7  *
8  *  http://aws.amazon.com/apache2.0
9  *
10  * or in the "license" file accompanying this file. This file is distributed
11  * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12  * express or implied. See the License for the specific language governing
13  * permissions and limitations under the License.
14  */
15 
16 package software.amazon.awssdk.core.client.builder;
17 
18 import static org.assertj.core.api.Assertions.assertThat;
19 import static org.assertj.core.api.Assertions.assertThatThrownBy;
20 import static org.mockito.ArgumentMatchers.any;
21 import static org.mockito.Mockito.mock;
22 import static org.mockito.Mockito.never;
23 import static org.mockito.Mockito.times;
24 import static org.mockito.Mockito.verify;
25 import static org.mockito.Mockito.when;
26 import static software.amazon.awssdk.core.client.config.SdkAdvancedClientOption.DISABLE_HOST_PREFIX_INJECTION;
27 import static software.amazon.awssdk.core.client.config.SdkAdvancedClientOption.SIGNER;
28 import static software.amazon.awssdk.core.client.config.SdkAdvancedClientOption.USER_AGENT_PREFIX;
29 import static software.amazon.awssdk.core.client.config.SdkAdvancedClientOption.USER_AGENT_SUFFIX;
30 import static software.amazon.awssdk.core.client.config.SdkClientOption.ADDITIONAL_HTTP_HEADERS;
31 import static software.amazon.awssdk.core.client.config.SdkClientOption.API_CALL_ATTEMPT_TIMEOUT;
32 import static software.amazon.awssdk.core.client.config.SdkClientOption.API_CALL_TIMEOUT;
33 import static software.amazon.awssdk.core.client.config.SdkClientOption.ENDPOINT_OVERRIDDEN;
34 import static software.amazon.awssdk.core.client.config.SdkClientOption.EXECUTION_ATTRIBUTES;
35 import static software.amazon.awssdk.core.client.config.SdkClientOption.EXECUTION_INTERCEPTORS;
36 import static software.amazon.awssdk.core.client.config.SdkClientOption.METRIC_PUBLISHERS;
37 import static software.amazon.awssdk.core.client.config.SdkClientOption.PROFILE_FILE;
38 import static software.amazon.awssdk.core.client.config.SdkClientOption.PROFILE_FILE_SUPPLIER;
39 import static software.amazon.awssdk.core.client.config.SdkClientOption.PROFILE_NAME;
40 import static software.amazon.awssdk.core.client.config.SdkClientOption.RETRY_POLICY;
41 import static software.amazon.awssdk.core.client.config.SdkClientOption.SCHEDULED_EXECUTOR_SERVICE;
42 import static software.amazon.awssdk.core.internal.SdkInternalTestAdvancedClientOption.ENDPOINT_OVERRIDDEN_OVERRIDE;
43 
44 import com.google.common.collect.ImmutableSet;
45 import java.beans.BeanInfo;
46 import java.beans.Introspector;
47 import java.beans.PropertyDescriptor;
48 import java.lang.reflect.Method;
49 import java.net.URI;
50 import java.time.Duration;
51 import java.util.ArrayList;
52 import java.util.Arrays;
53 import java.util.HashMap;
54 import java.util.List;
55 import java.util.Map;
56 import java.util.Optional;
57 import java.util.Set;
58 import java.util.concurrent.Executors;
59 import java.util.concurrent.ScheduledExecutorService;
60 import java.util.function.Supplier;
61 import org.assertj.core.api.Assertions;
62 import org.junit.Before;
63 import org.junit.Test;
64 import org.junit.runner.RunWith;
65 import org.mockito.Mock;
66 import org.mockito.Mockito;
67 import org.mockito.junit.MockitoJUnitRunner;
68 import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration;
69 import software.amazon.awssdk.core.client.config.SdkAdvancedClientOption;
70 import software.amazon.awssdk.core.client.config.SdkClientConfiguration;
71 import software.amazon.awssdk.core.client.config.SdkClientOption;
72 import software.amazon.awssdk.core.interceptor.ExecutionAttribute;
73 import software.amazon.awssdk.core.interceptor.ExecutionAttributes;
74 import software.amazon.awssdk.core.interceptor.ExecutionInterceptor;
75 import software.amazon.awssdk.core.retry.RetryPolicy;
76 import software.amazon.awssdk.core.signer.NoOpSigner;
77 import software.amazon.awssdk.core.signer.Signer;
78 import software.amazon.awssdk.http.SdkHttpClient;
79 import software.amazon.awssdk.http.SdkHttpConfigurationOption;
80 import software.amazon.awssdk.http.async.SdkAsyncHttpClient;
81 import software.amazon.awssdk.metrics.MetricCollection;
82 import software.amazon.awssdk.metrics.MetricPublisher;
83 import software.amazon.awssdk.profiles.ProfileFile;
84 import software.amazon.awssdk.utils.AttributeMap;
85 import software.amazon.awssdk.utils.StringInputStream;
86 
87 /**
88  * Validate the functionality of the {@link SdkDefaultClientBuilder}.
89  */
90 @RunWith(MockitoJUnitRunner.class)
91 public class DefaultClientBuilderTest {
92 
93     private static final AttributeMap MOCK_DEFAULTS = AttributeMap
94             .builder()
95             .put(SdkHttpConfigurationOption.READ_TIMEOUT, Duration.ofSeconds(10))
96             .build();
97 
98     private static final String ENDPOINT_PREFIX = "prefix";
99     private static final URI DEFAULT_ENDPOINT = URI.create("https://defaultendpoint.com");
100     private static final URI ENDPOINT = URI.create("https://example.com");
101     private static final NoOpSigner TEST_SIGNER = new NoOpSigner();
102 
103     @Mock
104     private SdkHttpClient.Builder defaultHttpClientFactory;
105 
106     @Mock
107     private SdkAsyncHttpClient.Builder defaultAsyncHttpClientFactory;
108 
109     @Before
setup()110     public void setup() {
111         when(defaultHttpClientFactory.buildWithDefaults(any())).thenReturn(mock(SdkHttpClient.class));
112         when(defaultAsyncHttpClientFactory.buildWithDefaults(any())).thenReturn(mock(SdkAsyncHttpClient.class));
113     }
114 
115     @Test
overrideConfigurationIsNeverNull()116     public void overrideConfigurationIsNeverNull() {
117         ClientOverrideConfiguration config = testClientBuilder().overrideConfiguration();
118         assertThat(config).isNotNull();
119 
120         config = testClientBuilder().overrideConfiguration((ClientOverrideConfiguration) null).overrideConfiguration();
121         assertThat(config).isNotNull();
122     }
123 
124     @Test
overrideConfigurationReturnsSetValues()125     public void overrideConfigurationReturnsSetValues() {
126         List<ExecutionInterceptor> interceptors = new ArrayList<>();
127         RetryPolicy retryPolicy = RetryPolicy.builder().build();
128         Map<String, List<String>> headers = new HashMap<>();
129         List<MetricPublisher> metricPublishers = new ArrayList<>();
130         ExecutionAttributes executionAttributes = new ExecutionAttributes();
131         Signer signer = (request, execAttributes) -> request;
132         String suffix = "suffix";
133         String prefix = "prefix";
134         Duration apiCallTimeout = Duration.ofMillis(42);
135         Duration apiCallAttemptTimeout = Duration.ofMillis(43);
136         ProfileFile profileFile = ProfileFile.builder()
137                                              .content(new StringInputStream(""))
138                                              .type(ProfileFile.Type.CONFIGURATION)
139                                              .build();
140         Supplier<ProfileFile> profileFileSupplier = () -> profileFile;
141         String profileName = "name";
142         ScheduledExecutorService scheduledExecutorService = mock(ScheduledExecutorService.class);
143 
144         ClientOverrideConfiguration overrideConfig = ClientOverrideConfiguration.builder()
145             .executionInterceptors(interceptors)
146             .retryPolicy(retryPolicy)
147             .headers(headers)
148             .putAdvancedOption(SIGNER, signer)
149             .putAdvancedOption(USER_AGENT_SUFFIX, suffix)
150             .putAdvancedOption(USER_AGENT_PREFIX, prefix)
151             .apiCallTimeout(apiCallTimeout)
152             .apiCallAttemptTimeout(apiCallAttemptTimeout)
153             .putAdvancedOption(DISABLE_HOST_PREFIX_INJECTION, Boolean.TRUE)
154             .defaultProfileFileSupplier(profileFileSupplier)
155             .defaultProfileName(profileName)
156             .metricPublishers(metricPublishers)
157             .executionAttributes(executionAttributes)
158             .putAdvancedOption(ENDPOINT_OVERRIDDEN_OVERRIDE, Boolean.TRUE)
159             .scheduledExecutorService(scheduledExecutorService)
160             .build();
161 
162         TestClientBuilder builder = testClientBuilder().overrideConfiguration(overrideConfig);
163         ClientOverrideConfiguration builderOverrideConfig = builder.overrideConfiguration();
164 
165         assertThat(builderOverrideConfig.executionInterceptors()).isEqualTo(interceptors);
166         assertThat(builderOverrideConfig.retryPolicy()).isEqualTo(Optional.of(retryPolicy));
167         assertThat(builderOverrideConfig.headers()).isEqualTo(headers);
168         assertThat(builderOverrideConfig.advancedOption(SIGNER)).isEqualTo(Optional.of(signer));
169         assertThat(builderOverrideConfig.advancedOption(USER_AGENT_SUFFIX)).isEqualTo(Optional.of(suffix));
170         assertThat(builderOverrideConfig.apiCallTimeout()).isEqualTo(Optional.of(apiCallTimeout));
171         assertThat(builderOverrideConfig.apiCallAttemptTimeout()).isEqualTo(Optional.of(apiCallAttemptTimeout));
172         assertThat(builderOverrideConfig.advancedOption(DISABLE_HOST_PREFIX_INJECTION)).isEqualTo(Optional.of(Boolean.TRUE));
173         assertThat(builderOverrideConfig.defaultProfileFileSupplier()).hasValue(profileFileSupplier);
174         assertThat(builderOverrideConfig.defaultProfileFile()).isEqualTo(Optional.of(profileFile));
175         assertThat(builderOverrideConfig.defaultProfileName()).isEqualTo(Optional.of(profileName));
176         assertThat(builderOverrideConfig.metricPublishers()).isEqualTo(metricPublishers);
177         assertThat(builderOverrideConfig.executionAttributes().getAttributes()).isEqualTo(executionAttributes.getAttributes());
178         assertThat(builderOverrideConfig.advancedOption(ENDPOINT_OVERRIDDEN_OVERRIDE)).isEqualTo(Optional.of(Boolean.TRUE));
179 
180         Runnable runnable = () -> {
181         };
182         builderOverrideConfig.scheduledExecutorService().get().submit(runnable);
183         verify(scheduledExecutorService).submit(runnable);
184     }
185 
186     @Test
overrideConfigurationOmitsUnsetValues()187     public void overrideConfigurationOmitsUnsetValues() {
188         ClientOverrideConfiguration overrideConfig = ClientOverrideConfiguration.builder()
189                                                                                 .build();
190 
191         TestClientBuilder builder = testClientBuilder().overrideConfiguration(overrideConfig);
192         ClientOverrideConfiguration builderOverrideConfig = builder.overrideConfiguration();
193 
194         assertThat(builderOverrideConfig.executionInterceptors()).isEmpty();
195         assertThat(builderOverrideConfig.retryPolicy()).isEmpty();
196         assertThat(builderOverrideConfig.headers()).isEmpty();
197         assertThat(builderOverrideConfig.advancedOption(SIGNER)).isEmpty();
198         assertThat(builderOverrideConfig.advancedOption(USER_AGENT_SUFFIX)).isEmpty();
199         assertThat(builderOverrideConfig.apiCallTimeout()).isEmpty();
200         assertThat(builderOverrideConfig.apiCallAttemptTimeout()).isEmpty();
201         assertThat(builderOverrideConfig.advancedOption(DISABLE_HOST_PREFIX_INJECTION)).isEmpty();
202         assertThat(builderOverrideConfig.defaultProfileFileSupplier()).isEmpty();
203         assertThat(builderOverrideConfig.defaultProfileFile()).isEmpty();
204         assertThat(builderOverrideConfig.defaultProfileName()).isEmpty();
205         assertThat(builderOverrideConfig.metricPublishers()).isEmpty();
206         assertThat(builderOverrideConfig.executionAttributes().getAttributes()).isEmpty();
207         assertThat(builderOverrideConfig.advancedOption(ENDPOINT_OVERRIDDEN_OVERRIDE)).isEmpty();
208         assertThat(builderOverrideConfig.scheduledExecutorService()).isEmpty();
209     }
210 
211     @Test
buildIncludesClientOverrides()212     public void buildIncludesClientOverrides() {
213         List<ExecutionInterceptor> interceptors = new ArrayList<>();
214         ExecutionInterceptor interceptor = new ExecutionInterceptor() {};
215         interceptors.add(interceptor);
216 
217         RetryPolicy retryPolicy = RetryPolicy.builder().build();
218         ScheduledExecutorService scheduledExecutorService = Mockito.spy(Executors.newScheduledThreadPool(1));
219 
220         Map<String, List<String>> headers = new HashMap<>();
221         List<String> headerValues = new ArrayList<>();
222         headerValues.add("value");
223         headers.put("client-override-test", headerValues);
224 
225         List<MetricPublisher> metricPublishers = new ArrayList<>();
226         MetricPublisher metricPublisher = new MetricPublisher() {
227             @Override
228             public void publish(MetricCollection metricCollection) {
229 
230             }
231 
232             @Override
233             public void close() {
234 
235             }
236         };
237         metricPublishers.add(metricPublisher);
238 
239         ExecutionAttribute<String> execAttribute = new ExecutionAttribute<>("test");
240         ExecutionAttributes executionAttributes = ExecutionAttributes.builder().put(execAttribute, "value").build();
241 
242         Signer signer = (request, execAttributes) -> request;
243         String suffix = "suffix";
244         String prefix = "prefix";
245         Duration apiCallTimeout = Duration.ofMillis(42);
246         Duration apiCallAttemptTimeout = Duration.ofMillis(43);
247         ProfileFile profileFile = ProfileFile.builder()
248                                              .content(new StringInputStream(""))
249                                              .type(ProfileFile.Type.CONFIGURATION)
250                                              .build();
251         String profileName = "name";
252 
253         ClientOverrideConfiguration overrideConfig = ClientOverrideConfiguration.builder()
254             .executionInterceptors(interceptors)
255             .retryPolicy(retryPolicy)
256             .headers(headers)
257             .putAdvancedOption(SIGNER, signer)
258             .putAdvancedOption(USER_AGENT_SUFFIX, suffix)
259             .putAdvancedOption(USER_AGENT_PREFIX, prefix)
260             .apiCallTimeout(apiCallTimeout)
261             .apiCallAttemptTimeout(apiCallAttemptTimeout)
262             .putAdvancedOption(DISABLE_HOST_PREFIX_INJECTION, Boolean.TRUE)
263             .defaultProfileFile(profileFile)
264             .defaultProfileName(profileName)
265             .metricPublishers(metricPublishers)
266             .executionAttributes(executionAttributes)
267             .putAdvancedOption(ENDPOINT_OVERRIDDEN_OVERRIDE, Boolean.TRUE)
268             .scheduledExecutorService(scheduledExecutorService)
269             .build();
270 
271         SdkClientConfiguration config =
272             testClientBuilder().overrideConfiguration(overrideConfig).build().clientConfiguration;
273 
274         assertThat(config.option(EXECUTION_INTERCEPTORS)).contains(interceptor);
275         assertThat(config.option(RETRY_POLICY)).isEqualTo(retryPolicy);
276         assertThat(config.option(ADDITIONAL_HTTP_HEADERS).get("client-override-test")).isEqualTo(headerValues);
277         assertThat(config.option(SIGNER)).isEqualTo(signer);
278         assertThat(config.option(USER_AGENT_SUFFIX)).isEqualTo(suffix);
279         assertThat(config.option(USER_AGENT_PREFIX)).isEqualTo(prefix);
280         assertThat(config.option(API_CALL_TIMEOUT)).isEqualTo(apiCallTimeout);
281         assertThat(config.option(API_CALL_ATTEMPT_TIMEOUT)).isEqualTo(apiCallAttemptTimeout);
282         assertThat(config.option(DISABLE_HOST_PREFIX_INJECTION)).isEqualTo(Boolean.TRUE);
283         assertThat(config.option(PROFILE_FILE)).isEqualTo(profileFile);
284         assertThat(config.option(PROFILE_FILE_SUPPLIER).get()).isEqualTo(profileFile);
285         assertThat(config.option(PROFILE_NAME)).isEqualTo(profileName);
286         assertThat(config.option(METRIC_PUBLISHERS)).contains(metricPublisher);
287         assertThat(config.option(EXECUTION_ATTRIBUTES).getAttribute(execAttribute)).isEqualTo("value");
288         assertThat(config.option(ENDPOINT_OVERRIDDEN)).isEqualTo(Boolean.TRUE);
289 
290         // Ensure that the SDK won't close the scheduled executor service we provided.
291         config.close();
292         config.option(SCHEDULED_EXECUTOR_SERVICE).shutdownNow();
293         config.option(SCHEDULED_EXECUTOR_SERVICE).shutdown();
294         Mockito.verify(scheduledExecutorService, never()).shutdown();
295         Mockito.verify(scheduledExecutorService, never()).shutdownNow();
296     }
297 
298     @Test
buildIncludesServiceDefaults()299     public void buildIncludesServiceDefaults() {
300         TestClient client = testClientBuilder().build();
301         assertThat(client.clientConfiguration.option(SIGNER))
302                 .isEqualTo(TEST_SIGNER);
303     }
304 
305     @Test
buildWithEndpointShouldHaveCorrectEndpointAndSigningRegion()306     public void buildWithEndpointShouldHaveCorrectEndpointAndSigningRegion() {
307         TestClient client = testClientBuilder().endpointOverride(ENDPOINT).build();
308 
309         assertThat(client.clientConfiguration.option(SdkClientOption.ENDPOINT)).isEqualTo(ENDPOINT);
310     }
311 
312     @Test
buildWithEndpointWithoutScheme_shouldThrowException()313     public void buildWithEndpointWithoutScheme_shouldThrowException() {
314         assertThatThrownBy(() -> testClientBuilder().endpointOverride(URI.create("localhost")).build())
315             .hasMessageContaining("The URI scheme of endpointOverride must not be null");
316 
317     }
318 
319     @Test
noClientProvided_DefaultHttpClientIsManagedBySdk()320     public void noClientProvided_DefaultHttpClientIsManagedBySdk() {
321         TestClient client = testClientBuilder().build();
322         assertThat(client.clientConfiguration.option(SdkClientOption.SYNC_HTTP_CLIENT))
323                 .isNotInstanceOf(SdkDefaultClientBuilder.NonManagedSdkHttpClient.class);
324         verify(defaultHttpClientFactory, times(1)).buildWithDefaults(any());
325     }
326 
327     @Test
noAsyncClientProvided_DefaultAsyncHttpClientIsManagedBySdk()328     public void noAsyncClientProvided_DefaultAsyncHttpClientIsManagedBySdk() {
329         TestAsyncClient client = testAsyncClientBuilder().build();
330         assertThat(client.clientConfiguration.option(SdkClientOption.ASYNC_HTTP_CLIENT))
331                 .isNotInstanceOf(SdkDefaultClientBuilder.NonManagedSdkAsyncHttpClient.class);
332         verify(defaultAsyncHttpClientFactory, times(1)).buildWithDefaults(any());
333     }
334 
335     @Test
clientFactoryProvided_ClientIsManagedBySdk()336     public void clientFactoryProvided_ClientIsManagedBySdk() {
337         TestClient client = testClientBuilder().httpClientBuilder((SdkHttpClient.Builder) serviceDefaults -> {
338             Assertions.assertThat(serviceDefaults).isEqualTo(MOCK_DEFAULTS);
339             return mock(SdkHttpClient.class);
340         })
341                 .build();
342         assertThat(client.clientConfiguration.option(SdkClientOption.SYNC_HTTP_CLIENT))
343                 .isNotInstanceOf(SdkDefaultClientBuilder.NonManagedSdkHttpClient.class);
344         verify(defaultHttpClientFactory, never()).buildWithDefaults(any());
345     }
346 
347     @Test
asyncHttpClientFactoryProvided_ClientIsManagedBySdk()348     public void asyncHttpClientFactoryProvided_ClientIsManagedBySdk() {
349         TestAsyncClient client = testAsyncClientBuilder()
350                 .httpClientBuilder((SdkAsyncHttpClient.Builder) serviceDefaults -> {
351                     assertThat(serviceDefaults).isEqualTo(MOCK_DEFAULTS);
352                     return mock(SdkAsyncHttpClient.class);
353                 })
354                 .build();
355         assertThat(client.clientConfiguration.option(SdkClientOption.ASYNC_HTTP_CLIENT))
356                 .isNotInstanceOf(SdkDefaultClientBuilder.NonManagedSdkAsyncHttpClient.class);
357         verify(defaultAsyncHttpClientFactory, never()).buildWithDefaults(any());
358     }
359 
360     @Test
explicitClientProvided_ClientIsNotManagedBySdk()361     public void explicitClientProvided_ClientIsNotManagedBySdk() {
362         TestClient client = testClientBuilder()
363 
364                 .httpClient(mock(SdkHttpClient.class))
365                 .build();
366         assertThat(client.clientConfiguration.option(SdkClientOption.SYNC_HTTP_CLIENT))
367                 .isInstanceOf(SdkDefaultClientBuilder.NonManagedSdkHttpClient.class);
368         verify(defaultHttpClientFactory, never()).buildWithDefaults(any());
369     }
370 
371     @Test
explicitAsyncHttpClientProvided_ClientIsNotManagedBySdk()372     public void explicitAsyncHttpClientProvided_ClientIsNotManagedBySdk() {
373         TestAsyncClient client = testAsyncClientBuilder()
374                 .httpClient(mock(SdkAsyncHttpClient.class))
375                 .build();
376         assertThat(client.clientConfiguration.option(SdkClientOption.ASYNC_HTTP_CLIENT))
377                 .isInstanceOf(SdkDefaultClientBuilder.NonManagedSdkAsyncHttpClient.class);
378         verify(defaultAsyncHttpClientFactory, never()).buildWithDefaults(any());
379     }
380 
381     @Test
clientBuilderFieldsHaveBeanEquivalents()382     public void clientBuilderFieldsHaveBeanEquivalents() throws Exception {
383         // Mutating properties might not have bean equivalents. This is probably fine, since very few customers require
384         // bean-equivalent methods and it's not clear what they'd expect them to be named anyway. Ignore these methods for now.
385         Set<String> NON_BEAN_EQUIVALENT_METHODS = ImmutableSet.of("addPlugin", "plugins", "putAuthScheme");
386         SdkClientBuilder<TestClientBuilder, TestClient> builder = testClientBuilder();
387 
388         BeanInfo beanInfo = Introspector.getBeanInfo(builder.getClass());
389         Method[] clientBuilderMethods = SdkClientBuilder.class.getDeclaredMethods();
390 
391         Arrays.stream(clientBuilderMethods).filter(m -> !m.isSynthetic()).forEach(builderMethod -> {
392             String propertyName = builderMethod.getName();
393 
394             if (NON_BEAN_EQUIVALENT_METHODS.contains(propertyName)) {
395                 return;
396             }
397 
398             Optional<PropertyDescriptor> propertyForMethod =
399                 Arrays.stream(beanInfo.getPropertyDescriptors())
400                       .filter(property -> property.getName().equals(propertyName))
401                       .findFirst();
402 
403             assertThat(propertyForMethod).as(propertyName + " property").hasValueSatisfying(property -> {
404                 assertThat(property.getReadMethod()).as(propertyName + " getter").isNull();
405                 assertThat(property.getWriteMethod()).as(propertyName + " setter").isNotNull();
406             });
407         });
408     }
409 
410     @Test
defaultProfileFileSupplier_isStaticOrHasIdentityCaching()411     public void defaultProfileFileSupplier_isStaticOrHasIdentityCaching() {
412         SdkClientConfiguration config =
413             testClientBuilder().build().clientConfiguration;
414 
415         Supplier<ProfileFile> defaultProfileFileSupplier = config.option(PROFILE_FILE_SUPPLIER);
416         ProfileFile firstGet = defaultProfileFileSupplier.get();
417         ProfileFile secondGet = defaultProfileFileSupplier.get();
418 
419         assertThat(secondGet).isSameAs(firstGet);
420     }
421 
testClientBuilder()422     private SdkDefaultClientBuilder<TestClientBuilder, TestClient> testClientBuilder() {
423         ClientOverrideConfiguration overrideConfig =
424                 ClientOverrideConfiguration.builder()
425                                            .putAdvancedOption(SIGNER, TEST_SIGNER)
426                                            .build();
427 
428         return new TestClientBuilder().overrideConfiguration(overrideConfig);
429     }
430 
testAsyncClientBuilder()431     private SdkDefaultClientBuilder<TestAsyncClientBuilder, TestAsyncClient> testAsyncClientBuilder() {
432         ClientOverrideConfiguration overrideConfig =
433                 ClientOverrideConfiguration.builder()
434                                            .putAdvancedOption(SIGNER, TEST_SIGNER)
435                                            .build();
436 
437         return new TestAsyncClientBuilder().overrideConfiguration(overrideConfig);
438     }
439 
440     private static class TestClient {
441         private final SdkClientConfiguration clientConfiguration;
442 
TestClient(SdkClientConfiguration clientConfiguration)443         private TestClient(SdkClientConfiguration clientConfiguration) {
444             this.clientConfiguration = clientConfiguration;
445         }
446     }
447 
448     private class TestClientWithoutEndpointDefaultBuilder extends SdkDefaultClientBuilder<TestClientBuilder, TestClient>
449         implements SdkClientBuilder<TestClientBuilder, TestClient> {
450 
TestClientWithoutEndpointDefaultBuilder()451         public TestClientWithoutEndpointDefaultBuilder() {
452             super(defaultHttpClientFactory, null);
453         }
454 
455         @Override
buildClient()456         protected TestClient buildClient() {
457             return new TestClient(super.syncClientConfiguration());
458         }
459 
460         @Override
mergeChildDefaults(SdkClientConfiguration configuration)461         protected SdkClientConfiguration mergeChildDefaults(SdkClientConfiguration configuration) {
462             return configuration.merge(c -> c.option(SdkAdvancedClientOption.SIGNER, TEST_SIGNER));
463         }
464 
465         @Override
childHttpConfig()466         protected AttributeMap childHttpConfig() {
467             return MOCK_DEFAULTS;
468         }
469     }
470 
471     private class TestClientBuilder extends SdkDefaultClientBuilder<TestClientBuilder, TestClient>
472         implements SdkClientBuilder<TestClientBuilder, TestClient> {
473 
TestClientBuilder()474         public TestClientBuilder() {
475             super(defaultHttpClientFactory, null);
476         }
477 
478         @Override
buildClient()479         protected TestClient buildClient() {
480             return new TestClient(super.syncClientConfiguration());
481         }
482 
483         @Override
mergeChildDefaults(SdkClientConfiguration configuration)484         protected SdkClientConfiguration mergeChildDefaults(SdkClientConfiguration configuration) {
485             return configuration.merge(c -> c.option(SdkClientOption.ENDPOINT, DEFAULT_ENDPOINT));
486         }
487 
488         @Override
childHttpConfig()489         protected AttributeMap childHttpConfig() {
490             return MOCK_DEFAULTS;
491         }
492     }
493 
494     private static class TestAsyncClient {
495         private final SdkClientConfiguration clientConfiguration;
496 
TestAsyncClient(SdkClientConfiguration clientConfiguration)497         private TestAsyncClient(SdkClientConfiguration clientConfiguration) {
498             this.clientConfiguration = clientConfiguration;
499         }
500     }
501 
502     private class TestAsyncClientBuilder extends SdkDefaultClientBuilder<TestAsyncClientBuilder, TestAsyncClient>
503         implements SdkClientBuilder<TestAsyncClientBuilder, TestAsyncClient> {
504 
TestAsyncClientBuilder()505         public TestAsyncClientBuilder() {
506             super(defaultHttpClientFactory, defaultAsyncHttpClientFactory);
507         }
508 
509         @Override
buildClient()510         protected TestAsyncClient buildClient() {
511             return new TestAsyncClient(super.asyncClientConfiguration());
512         }
513 
514         @Override
mergeChildDefaults(SdkClientConfiguration configuration)515         protected SdkClientConfiguration mergeChildDefaults(SdkClientConfiguration configuration) {
516             return configuration.merge(c -> c.option(SdkClientOption.ENDPOINT, DEFAULT_ENDPOINT));
517         }
518 
519         @Override
childHttpConfig()520         protected AttributeMap childHttpConfig() {
521             return MOCK_DEFAULTS;
522         }
523     }
524 }
525