Remove use of TestHttpClientAdapter

Now that HttpClientAdapter is deprecated and replaced by HttpExchangeAdapter
and ReactorHttpExchangeAdapter, our tests should use the new contracts.

See gh-30117
This commit is contained in:
Rossen Stoyanchev
2023-07-10 10:36:16 +01:00
committed by rstoyanchev
parent 3be4c0a893
commit 068dc7db28
22 changed files with 222 additions and 362 deletions

View File

@@ -35,9 +35,10 @@ import static org.assertj.core.api.Assertions.assertThat;
*/ */
class CookieValueArgumentResolverTests { class CookieValueArgumentResolverTests {
private final TestHttpClientAdapter client = new TestHttpClientAdapter(); private final TestExchangeAdapter client = new TestExchangeAdapter();
private final Service service = HttpServiceProxyFactory.builder(this.client).build().createClient(Service.class); private final Service service =
HttpServiceProxyFactory.builderFor(this.client).build().createClient(Service.class);
@Test @Test

View File

@@ -1,39 +0,0 @@
/*
* Copyright 2002-2023 the original author or authors.
*
* Licensed 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
*
* https://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.springframework.web.service.invoker;
import org.junit.jupiter.api.BeforeEach;
/**
* Tests for {@link HttpServiceMethod} with a test {@link TestHttpClientAdapter} that
* stubs the client invocations.
* <p>
* The tests do not create or invoke {@code HttpServiceMethod} directly but rather use
* {@link HttpServiceProxyFactory} to create a service proxy in order to use a strongly
* typed interface without the need for class casts.
*
* @author Olga Maciaszek-Sharma
*/
public class HttpClientServiceMethodTests extends ReactiveHttpServiceMethodTests {
@BeforeEach
void setUp(){
this.client = new TestHttpClientAdapter();
this.proxyFactory = HttpServiceProxyFactory.builder((HttpClientAdapter) this.client).build();
}
}

View File

@@ -19,7 +19,7 @@ package org.springframework.web.service.invoker;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
/** /**
* Tests for {@link HttpServiceMethod} with a blocking test {@link TestHttpExchangeAdapter} that * Tests for {@link HttpServiceMethod} with a blocking test {@link TestExchangeAdapter} that
* stubs the client invocations. * stubs the client invocations.
* <p> * <p>
* The tests do not create or invoke {@code HttpServiceMethod} directly but rather use * The tests do not create or invoke {@code HttpServiceMethod} directly but rather use
@@ -32,10 +32,8 @@ class HttpExchangeAdapterServiceMethodTests extends HttpServiceMethodTests {
@BeforeEach @BeforeEach
void setUp() { void setUp() {
this.client = new TestHttpExchangeAdapter(); this.client = new TestExchangeAdapter();
this.proxyFactory = HttpServiceProxyFactory.builder() this.proxyFactory = HttpServiceProxyFactory.builderFor(this.client).build();
.exchangeAdapter((HttpExchangeAdapter) this.client)
.build();
} }
} }

View File

@@ -35,9 +35,10 @@ import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
*/ */
class HttpMethodArgumentResolverTests { class HttpMethodArgumentResolverTests {
private final TestHttpClientAdapter client = new TestHttpClientAdapter(); private final TestExchangeAdapter client = new TestExchangeAdapter();
private final Service service = HttpServiceProxyFactory.builder(this.client).build().createClient(Service.class); private final Service service =
HttpServiceProxyFactory.builderFor(this.client).build().createClient(Service.class);
@Test @Test

View File

@@ -36,8 +36,8 @@ import static org.springframework.http.MediaType.APPLICATION_CBOR_VALUE;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
/** /**
* Base class for testing {@link HttpServiceMethod} with a test {@link TestHttpClientAdapter} * Base class for testing {@link HttpServiceMethod} with a test {@link TestExchangeAdapter}
* and a test {@link TestHttpExchangeAdapter} that stub the client invocations. * and a test {@link TestExchangeAdapter} that stub the client invocations.
* *
* <p> * <p>
* The tests do not create or invoke {@code HttpServiceMethod} directly but rather use * The tests do not create or invoke {@code HttpServiceMethod} directly but rather use
@@ -52,7 +52,7 @@ abstract class HttpServiceMethodTests {
protected static final ParameterizedTypeReference<String> BODY_TYPE = new ParameterizedTypeReference<>() { protected static final ParameterizedTypeReference<String> BODY_TYPE = new ParameterizedTypeReference<>() {
}; };
protected TestAdapter client; protected TestExchangeAdapter client;
protected HttpServiceProxyFactory proxyFactory; protected HttpServiceProxyFactory proxyFactory;
@@ -66,19 +66,19 @@ abstract class HttpServiceMethodTests {
assertThat(headers).isNotNull(); assertThat(headers).isNotNull();
String body = service.getBody(); String body = service.getBody();
assertThat(body).isEqualTo(client.getInvokedMethodReference()); assertThat(body).isEqualTo(client.getInvokedMethodName());
Optional<String> optional = service.getBodyOptional(); Optional<String> optional = service.getBodyOptional();
assertThat(optional).contains("body"); assertThat(optional.get()).startsWith("exchangeForBody");
ResponseEntity<String> entity = service.getEntity(); ResponseEntity<String> entity = service.getEntity();
assertThat(entity.getBody()).isEqualTo("entity"); assertThat(entity.getBody()).startsWith("exchangeForEntity");
ResponseEntity<Void> voidEntity = service.getVoidEntity(); ResponseEntity<Void> voidEntity = service.getVoidEntity();
assertThat(voidEntity.getBody()).isNull(); assertThat(voidEntity.getBody()).isNull();
List<String> list = service.getList(); List<String> list = service.getList();
assertThat(list.get(0)).isEqualTo("body"); assertThat(list.get(0)).startsWith("exchangeForBody");
} }
@Test @Test
@@ -104,10 +104,8 @@ abstract class HttpServiceMethodTests {
@Test @Test
void typeAndMethodAnnotatedService() { void typeAndMethodAnnotatedService() {
HttpExchangeAdapter actualClient = this.client instanceof HttpClientAdapter httpClient
? httpClient.asHttpExchangeAdapter() : (HttpExchangeAdapter) client;
HttpServiceProxyFactory proxyFactory = HttpServiceProxyFactory.builder() HttpServiceProxyFactory proxyFactory = HttpServiceProxyFactory.builder()
.exchangeAdapter(actualClient) .exchangeAdapter(this.client)
.embeddedValueResolver(value -> (value.equals("${baseUrl}") ? "/base" : value)) .embeddedValueResolver(value -> (value.equals("${baseUrl}") ? "/base" : value))
.build(); .build();
@@ -131,7 +129,7 @@ abstract class HttpServiceMethodTests {
} }
protected void verifyClientInvocation(String methodName, @Nullable ParameterizedTypeReference<?> expectedBodyType) { protected void verifyClientInvocation(String methodName, @Nullable ParameterizedTypeReference<?> expectedBodyType) {
assertThat(this.client.getInvokedMethodReference()).isEqualTo(methodName); assertThat(this.client.getInvokedMethodName()).isEqualTo(methodName);
assertThat(this.client.getBodyType()).isEqualTo(expectedBodyType); assertThat(this.client.getBodyType()).isEqualTo(expectedBodyType);
} }

View File

@@ -18,7 +18,6 @@ package org.springframework.web.service.invoker;
import java.util.Optional; import java.util.Optional;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.http.HttpEntity; import org.springframework.http.HttpEntity;
@@ -42,16 +41,10 @@ import static org.assertj.core.api.Assertions.assertThat;
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
class MultipartFileArgumentResolverTests { class MultipartFileArgumentResolverTests {
private final TestHttpClientAdapter clientAdapter = new TestHttpClientAdapter(); private final TestExchangeAdapter client = new TestExchangeAdapter();
private TestClient client; private final MultipartService multipartService =
HttpServiceProxyFactory.builderFor(this.client).build().createClient(MultipartService.class);
@BeforeEach
void setUp() {
HttpServiceProxyFactory factory = HttpServiceProxyFactory.builder(this.clientAdapter).build();
this.client = factory.createClient(TestClient.class);
}
@Test @Test
@@ -60,8 +53,8 @@ class MultipartFileArgumentResolverTests {
String originalFileName = "originalTestFileName"; String originalFileName = "originalTestFileName";
MultipartFile testFile = new MockMultipartFile(fileName, originalFileName, "text/plain", "test".getBytes()); MultipartFile testFile = new MockMultipartFile(fileName, originalFileName, "text/plain", "test".getBytes());
this.client.postMultipartFile(testFile); this.multipartService.postMultipartFile(testFile);
Object value = this.clientAdapter.getRequestValues().getBodyValue(); Object value = this.client.getRequestValues().getBodyValue();
assertThat(value).isInstanceOf(MultiValueMap.class); assertThat(value).isInstanceOf(MultiValueMap.class);
MultiValueMap<String, HttpEntity<?>> map = (MultiValueMap<String, HttpEntity<?>>) value; MultiValueMap<String, HttpEntity<?>> map = (MultiValueMap<String, HttpEntity<?>>) value;
@@ -80,8 +73,8 @@ class MultipartFileArgumentResolverTests {
@Test @Test
void optionalMultipartFile() { void optionalMultipartFile() {
this.client.postOptionalMultipartFile(Optional.empty(), "anotherPart"); this.multipartService.postOptionalMultipartFile(Optional.empty(), "anotherPart");
Object value = clientAdapter.getRequestValues().getBodyValue(); Object value = client.getRequestValues().getBodyValue();
assertThat(value).isInstanceOf(MultiValueMap.class); assertThat(value).isInstanceOf(MultiValueMap.class);
MultiValueMap<String, HttpEntity<?>> map = (MultiValueMap<String, HttpEntity<?>>) value; MultiValueMap<String, HttpEntity<?>> map = (MultiValueMap<String, HttpEntity<?>>) value;
@@ -90,7 +83,7 @@ class MultipartFileArgumentResolverTests {
@SuppressWarnings("OptionalUsedAsFieldOrParameterType") @SuppressWarnings("OptionalUsedAsFieldOrParameterType")
private interface TestClient { private interface MultipartService {
@PostExchange @PostExchange
void postMultipartFile(MultipartFile file); void postMultipartFile(MultipartFile file);

View File

@@ -27,7 +27,6 @@ import java.util.Map;
import java.util.Optional; import java.util.Optional;
import org.apache.groovy.util.Maps; import org.apache.groovy.util.Maps;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.core.MethodParameter; import org.springframework.core.MethodParameter;
@@ -52,21 +51,14 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
*/ */
class NamedValueArgumentResolverTests { class NamedValueArgumentResolverTests {
private final TestHttpClientAdapter client = new TestHttpClientAdapter(); private final TestExchangeAdapter client = new TestExchangeAdapter();
private final TestNamedValueArgumentResolver argumentResolver = new TestNamedValueArgumentResolver(); private final TestNamedValueArgumentResolver argumentResolver = new TestNamedValueArgumentResolver();
private Service service; private final Service service = HttpServiceProxyFactory.builderFor(this.client)
.customArgumentResolver(this.argumentResolver)
.build()
@BeforeEach .createClient(Service.class);
void setUp() throws Exception {
HttpServiceProxyFactory proxyFactory = HttpServiceProxyFactory.builder(this.client)
.customArgumentResolver(this.argumentResolver)
.build();
this.service = proxyFactory.createClient(Service.class);
}
@Test @Test

View File

@@ -34,9 +34,10 @@ import static org.assertj.core.api.Assertions.assertThat;
*/ */
class PathVariableArgumentResolverTests { class PathVariableArgumentResolverTests {
private final TestHttpClientAdapter client = new TestHttpClientAdapter(); private final TestExchangeAdapter client = new TestExchangeAdapter();
private final Service service = HttpServiceProxyFactory.builder(this.client).build().createClient(Service.class); private final Service service =
HttpServiceProxyFactory.builderFor(this.client).build().createClient(Service.class);
@Test @Test

View File

@@ -33,8 +33,7 @@ import static org.assertj.core.api.Assertions.assertThat;
/** /**
* Base class for testing reactive scenarios in {@link HttpServiceMethod} with * Base class for testing reactive scenarios in {@link HttpServiceMethod} with
* a test {@link TestHttpClientAdapter} and a test {@link TestHttpExchangeAdapter} * a test {@link TestReactorExchangeAdapter} that stub the client invocations.
* that stub the client invocations.
* *
* <p> * <p>
* The tests do not create or invoke {@code HttpServiceMethod} directly but rather use * The tests do not create or invoke {@code HttpServiceMethod} directly but rather use
@@ -52,33 +51,33 @@ abstract class ReactiveHttpServiceMethodTests extends HttpServiceMethodTests {
Mono<Void> voidMono = service.execute(); Mono<Void> voidMono = service.execute();
StepVerifier.create(voidMono).verifyComplete(); StepVerifier.create(voidMono).verifyComplete();
verifyClientInvocation("void", null); verifyClientInvocation("exchangeForMono", null);
Mono<HttpHeaders> headersMono = service.getHeaders(); Mono<HttpHeaders> headersMono = service.getHeaders();
StepVerifier.create(headersMono).expectNextCount(1).verifyComplete(); StepVerifier.create(headersMono).expectNextCount(1).verifyComplete();
verifyClientInvocation("headers", null); verifyClientInvocation("exchangeForHeadersMono", null);
Mono<String> body = service.getBody(); Mono<String> body = service.getBody();
StepVerifier.create(body).expectNext("body").verifyComplete(); StepVerifier.create(body).expectNext("exchangeForBodyMono").verifyComplete();
verifyClientInvocation("body", BODY_TYPE); verifyClientInvocation("exchangeForBodyMono", BODY_TYPE);
Flux<String> fluxBody = service.getFluxBody(); Flux<String> fluxBody = service.getFluxBody();
StepVerifier.create(fluxBody).expectNext("request", "To", "Body", "Flux").verifyComplete(); StepVerifier.create(fluxBody).expectNext("exchange", "For", "Body", "Flux").verifyComplete();
verifyClientInvocation("bodyFlux", BODY_TYPE); verifyClientInvocation("exchangeForBodyFlux", BODY_TYPE);
Mono<ResponseEntity<Void>> voidEntity = service.getVoidEntity(); Mono<ResponseEntity<Void>> voidEntity = service.getVoidEntity();
StepVerifier.create(voidEntity).expectNext(ResponseEntity.ok().build()).verifyComplete(); StepVerifier.create(voidEntity).expectNext(ResponseEntity.ok().build()).verifyComplete();
verifyClientInvocation("bodilessEntity", null); verifyClientInvocation("exchangeForBodilessEntityMono", null);
Mono<ResponseEntity<String>> entity = service.getEntity(); Mono<ResponseEntity<String>> entity = service.getEntity();
StepVerifier.create(entity).expectNext(ResponseEntity.ok("requestToEntity")); StepVerifier.create(entity).expectNext(ResponseEntity.ok("exchangeForEntityMono"));
verifyClientInvocation("entity", BODY_TYPE); verifyClientInvocation("exchangeForEntityMono", BODY_TYPE);
Mono<ResponseEntity<Flux<String>>> fluxEntity = service.getFluxEntity(); Mono<ResponseEntity<Flux<String>>> fluxEntity = service.getFluxEntity();
StepVerifier.create(fluxEntity.flatMapMany(HttpEntity::getBody)) StepVerifier.create(fluxEntity.flatMapMany(HttpEntity::getBody))
.expectNext("request", "To", "Entity", "Flux") .expectNext("exchange", "For", "Entity", "Flux")
.verifyComplete(); .verifyComplete();
verifyClientInvocation("entityFlux", BODY_TYPE); verifyClientInvocation("exchangeForEntityFlux", BODY_TYPE);
assertThat(service.getDefaultMethodValue()).isEqualTo("default value"); assertThat(service.getDefaultMethodValue()).isEqualTo("default value");
} }
@@ -93,20 +92,20 @@ abstract class ReactiveHttpServiceMethodTests extends HttpServiceMethodTests {
assertThat(headersSingle.blockingGet()).isNotNull(); assertThat(headersSingle.blockingGet()).isNotNull();
Single<String> bodySingle = service.getBody(); Single<String> bodySingle = service.getBody();
assertThat(bodySingle.blockingGet()).isEqualTo("body"); assertThat(bodySingle.blockingGet()).isEqualTo("exchangeForBodyMono");
Flowable<String> bodyFlow = service.getFlowableBody(); Flowable<String> bodyFlow = service.getFlowableBody();
assertThat(bodyFlow.toList().blockingGet()).asList().containsExactly("request", "To", "Body", "Flux"); assertThat(bodyFlow.toList().blockingGet()).asList().containsExactly("exchange", "For", "Body", "Flux");
Single<ResponseEntity<Void>> voidEntity = service.getVoidEntity(); Single<ResponseEntity<Void>> voidEntity = service.getVoidEntity();
assertThat(voidEntity.blockingGet().getBody()).isNull(); assertThat(voidEntity.blockingGet().getBody()).isNull();
Single<ResponseEntity<String>> entitySingle = service.getEntity(); Single<ResponseEntity<String>> entitySingle = service.getEntity();
assertThat(entitySingle.blockingGet().getBody()).isEqualTo("entity"); assertThat(entitySingle.blockingGet().getBody()).isEqualTo("exchangeForEntityMono");
Single<ResponseEntity<Flowable<String>>> entityFlow = service.getFlowableEntity(); Single<ResponseEntity<Flowable<String>>> entityFlow = service.getFlowableEntity();
Flowable<String> body = (entityFlow.blockingGet()).getBody(); Flowable<String> body = (entityFlow.blockingGet()).getBody();
assertThat(body.toList().blockingGet()).containsExactly("request", "To", "Entity", "Flux"); assertThat(body.toList().blockingGet()).containsExactly("exchange", "For", "Entity", "Flux");
} }
private interface ReactorService { private interface ReactorService {

View File

@@ -20,7 +20,7 @@ import org.junit.jupiter.api.BeforeEach;
/** /**
* Tests for {@link HttpServiceMethod} with an {@link HttpExchangeAdapter} * Tests for {@link HttpServiceMethod} with an {@link HttpExchangeAdapter}
* build from a test {@link TestHttpClientAdapter} that stubs the client invocations. * build from a test {@link TestReactorExchangeAdapter} that stubs the client invocations.
* <p> * <p>
* The tests do not create or invoke {@code HttpServiceMethod} directly but rather use * The tests do not create or invoke {@code HttpServiceMethod} directly but rather use
* {@link HttpServiceProxyFactory} to create a service proxy in order to use a strongly * {@link HttpServiceProxyFactory} to create a service proxy in order to use a strongly
@@ -32,10 +32,8 @@ public class ReactorExchangeAdapterHttpServiceMethodTests extends ReactiveHttpSe
@BeforeEach @BeforeEach
void setUp() { void setUp() {
this.client = new TestHttpClientAdapter(); this.client = new TestReactorExchangeAdapter();
this.proxyFactory = HttpServiceProxyFactory.builder() this.proxyFactory = HttpServiceProxyFactory.builder().exchangeAdapter(this.client).build();
.exchangeAdapter(((HttpClientAdapter) this.client).asHttpExchangeAdapter())
.build();
} }
} }

View File

@@ -32,9 +32,10 @@ import static org.assertj.core.api.Assertions.assertThat;
*/ */
class RequestAttributeArgumentResolverTests { class RequestAttributeArgumentResolverTests {
private final TestHttpClientAdapter client = new TestHttpClientAdapter(); private final TestExchangeAdapter client = new TestExchangeAdapter();
private final Service service = HttpServiceProxyFactory.builder(this.client).build().createClient(Service.class); private final Service service =
HttpServiceProxyFactory.builderFor(this.client).build().createClient(Service.class);
@Test @Test

View File

@@ -37,9 +37,10 @@ import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
*/ */
class RequestBodyArgumentResolverTests { class RequestBodyArgumentResolverTests {
private final TestHttpClientAdapter client = new TestHttpClientAdapter(); private final TestExchangeAdapter client = new TestExchangeAdapter();
private final Service service = HttpServiceProxyFactory.builder(this.client).build().createClient(Service.class); private final Service service =
HttpServiceProxyFactory.builderFor(this.client).build().createClient(Service.class);
@Test @Test

View File

@@ -18,7 +18,6 @@ package org.springframework.web.service.invoker;
import java.util.List; import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
@@ -36,16 +35,10 @@ import static org.assertj.core.api.Assertions.assertThat;
*/ */
class RequestHeaderArgumentResolverTests { class RequestHeaderArgumentResolverTests {
private final TestHttpClientAdapter client = new TestHttpClientAdapter(); private final TestExchangeAdapter client = new TestExchangeAdapter();
private Service service; private final Service service =
HttpServiceProxyFactory.builderFor(this.client).build().createClient(Service.class);
@BeforeEach
void setUp() throws Exception {
HttpServiceProxyFactory proxyFactory = HttpServiceProxyFactory.builder(this.client).build();
this.service = proxyFactory.createClient(Service.class);
}
// Base class functionality should be tested in NamedValueArgumentResolverTests. // Base class functionality should be tested in NamedValueArgumentResolverTests.

View File

@@ -39,9 +39,10 @@ import static org.assertj.core.api.Assertions.assertThat;
*/ */
class RequestParamArgumentResolverTests { class RequestParamArgumentResolverTests {
private final TestHttpClientAdapter client = new TestHttpClientAdapter(); private final TestExchangeAdapter client = new TestExchangeAdapter();
private final Service service = HttpServiceProxyFactory.builder(this.client).build().createClient(Service.class); private final Service service =
HttpServiceProxyFactory.builderFor(this.client).build().createClient(Service.class);
@Test @Test

View File

@@ -16,7 +16,6 @@
package org.springframework.web.service.invoker; package org.springframework.web.service.invoker;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
@@ -41,16 +40,10 @@ import static org.assertj.core.api.Assertions.assertThat;
*/ */
class RequestPartArgumentResolverTests { class RequestPartArgumentResolverTests {
private final TestHttpClientAdapter client = new TestHttpClientAdapter(); private final TestExchangeAdapter client = new TestExchangeAdapter();
private Service service; private final Service service =
HttpServiceProxyFactory.builderFor(this.client).build().createClient(Service.class);
@BeforeEach
void setUp() throws Exception {
HttpServiceProxyFactory proxyFactory = HttpServiceProxyFactory.builder(this.client).build();
this.service = proxyFactory.createClient(Service.class);
}
// Base class functionality should be tested in NamedValueArgumentResolverTests. // Base class functionality should be tested in NamedValueArgumentResolverTests.
@@ -77,7 +70,9 @@ class RequestPartArgumentResolverTests {
private interface Service { private interface Service {
@PostExchange @PostExchange
void postMultipart(@RequestPart String part1, @RequestPart HttpEntity<String> part2, @RequestPart Mono<String> part3); void postMultipart(
@RequestPart String part1, @RequestPart HttpEntity<String> part2,
@RequestPart Mono<String> part3);
} }

View File

@@ -1,37 +0,0 @@
/*
* Copyright 2002-2023 the original author or authors.
*
* Licensed 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
*
* https://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.springframework.web.service.invoker;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.lang.Nullable;
/**
* A helper interface for verifying method invoked on {@link HttpExchangeAdapter}
* and {@link HttpClientAdapter}, as well as their values.
*
* @author Olga Maciaszek-Sharma
*/
interface TestAdapter {
String getInvokedMethodReference();
HttpRequestValues getRequestValues();
@Nullable
ParameterizedTypeReference<?> getBodyType();
}

View File

@@ -28,10 +28,10 @@ import static org.assertj.core.api.Assertions.assertThat;
/** /**
* {@link HttpExchangeAdapter} with stubbed responses. * {@link HttpExchangeAdapter} with stubbed responses.
* *
* @author Rossen Stoyanchev
* @author Olga Maciaszek-Sharma * @author Olga Maciaszek-Sharma
*/ */
@SuppressWarnings("unchecked") public class TestExchangeAdapter implements HttpExchangeAdapter {
public class TestHttpExchangeAdapter implements HttpExchangeAdapter, TestAdapter {
@Nullable @Nullable
private String invokedMethodName; private String invokedMethodName;
@@ -42,7 +42,8 @@ public class TestHttpExchangeAdapter implements HttpExchangeAdapter, TestAdapter
@Nullable @Nullable
private ParameterizedTypeReference<?> bodyType; private ParameterizedTypeReference<?> bodyType;
public String getInvokedMethodReference() {
public String getInvokedMethodName() {
assertThat(this.invokedMethodName).isNotNull(); assertThat(this.invokedMethodName).isNotNull();
return this.invokedMethodName; return this.invokedMethodName;
} }
@@ -52,7 +53,6 @@ public class TestHttpExchangeAdapter implements HttpExchangeAdapter, TestAdapter
return this.requestValues; return this.requestValues;
} }
@Override
@Nullable @Nullable
public ParameterizedTypeReference<?> getBodyType() { public ParameterizedTypeReference<?> getBodyType() {
return this.bodyType; return this.bodyType;
@@ -60,45 +60,48 @@ public class TestHttpExchangeAdapter implements HttpExchangeAdapter, TestAdapter
@Override @Override
public void exchange(HttpRequestValues requestValues) { public void exchange(HttpRequestValues requestValues) {
saveInput("void", requestValues, null); saveInput("exchange", requestValues, null);
} }
@Override @Override
public HttpHeaders exchangeForHeaders(HttpRequestValues requestValues) { public HttpHeaders exchangeForHeaders(HttpRequestValues requestValues) {
saveInput("headers", requestValues, null); saveInput("exchangeForHeaders", requestValues, null);
return new HttpHeaders(); return new HttpHeaders();
} }
@SuppressWarnings("unchecked")
@Override @Override
public <T> T exchangeForBody(HttpRequestValues requestValues, ParameterizedTypeReference<T> bodyType) { public <T> T exchangeForBody(HttpRequestValues requestValues, ParameterizedTypeReference<T> bodyType) {
saveInput("body", requestValues, bodyType); saveInput("exchangeForBody", requestValues, bodyType);
return bodyType.getType().getTypeName().contains("List") return bodyType.getType().getTypeName().contains("List") ?
? (T) Collections.singletonList(getInvokedMethodReference()) : (T) getInvokedMethodReference(); (T) Collections.singletonList(getInvokedMethodName()) : (T) getInvokedMethodName();
} }
@Override @Override
public ResponseEntity<Void> exchangeForBodilessEntity(HttpRequestValues requestValues) { public ResponseEntity<Void> exchangeForBodilessEntity(HttpRequestValues requestValues) {
saveInput("bodilessEntity", requestValues, null); saveInput("exchangeForBodilessEntity", requestValues, null);
return ResponseEntity.ok().build(); return ResponseEntity.ok().build();
} }
@SuppressWarnings("unchecked")
@Override @Override
public <T> ResponseEntity<T> exchangeForEntity(HttpRequestValues requestValues, public <T> ResponseEntity<T> exchangeForEntity(
ParameterizedTypeReference<T> bodyType) { HttpRequestValues requestValues, ParameterizedTypeReference<T> bodyType) {
saveInput("entity", requestValues, bodyType);
return (ResponseEntity<T>) ResponseEntity.ok(this.getInvokedMethodReference()); saveInput("exchangeForEntity", requestValues, bodyType);
return (ResponseEntity<T>) ResponseEntity.ok(getInvokedMethodName());
} }
@Override @Override
public boolean supportsRequestAttributes() { public boolean supportsRequestAttributes() {
return false; return true;
} }
private <T> void saveInput(String methodName, HttpRequestValues requestValues, protected <T> void saveInput(
@Nullable ParameterizedTypeReference<T> bodyType) { String methodName, HttpRequestValues values, @Nullable ParameterizedTypeReference<T> bodyType) {
this.invokedMethodName = methodName; this.invokedMethodName = methodName;
this.requestValues = requestValues; this.requestValues = values;
this.bodyType = bodyType; this.bodyType = bodyType;
} }

View File

@@ -1,126 +0,0 @@
/*
* Copyright 2002-2023 the original author or authors.
*
* Licensed 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
*
* https://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.springframework.web.service.invoker;
import java.util.Collections;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.lang.Nullable;
import static org.assertj.core.api.Assertions.assertThat;
/**
* {@link HttpClientAdapter} with stubbed responses.
*
* @author Rossen Stoyanchev
* @author Olga Maciaszek-Sharma
*/
@SuppressWarnings("unchecked")
class TestHttpClientAdapter implements HttpClientAdapter, TestAdapter {
@Nullable
private String invokedForReturnMethodReference;
@Nullable
private HttpRequestValues requestValues;
@Nullable
private ParameterizedTypeReference<?> bodyType;
@Override
public String getInvokedMethodReference() {
assertThat(this.invokedForReturnMethodReference).isNotNull();
return this.invokedForReturnMethodReference;
}
@Override
public HttpRequestValues getRequestValues() {
assertThat(this.requestValues).isNotNull();
return this.requestValues;
}
@Override
@Nullable
public ParameterizedTypeReference<?> getBodyType() {
return this.bodyType;
}
// HttpClientAdapter implementation
@Override
public Mono<Void> requestToVoid(HttpRequestValues requestValues) {
saveInput("void", requestValues, null);
return Mono.empty();
}
@Override
public Mono<HttpHeaders> requestToHeaders(HttpRequestValues requestValues) {
saveInput("headers", requestValues, null);
return Mono.just(new HttpHeaders());
}
@Override
public <T> Mono<T> requestToBody(HttpRequestValues requestValues, ParameterizedTypeReference<T> bodyType) {
saveInput("body", requestValues, bodyType);
return bodyType.getType().getTypeName().contains("List") ?
(Mono<T>) Mono.just(Collections.singletonList(getInvokedMethodReference()))
: (Mono<T>) Mono.just(getInvokedMethodReference());
}
@Override
public <T> Flux<T> requestToBodyFlux(HttpRequestValues requestValues, ParameterizedTypeReference<T> bodyType) {
saveInput("bodyFlux", requestValues, bodyType);
return (Flux<T>) Flux.just("request", "To", "Body", "Flux");
}
@Override
public Mono<ResponseEntity<Void>> requestToBodilessEntity(HttpRequestValues requestValues) {
saveInput("bodilessEntity", requestValues, null);
return Mono.just(ResponseEntity.ok().build());
}
@Override
public <T> Mono<ResponseEntity<T>> requestToEntity(
HttpRequestValues requestValues, ParameterizedTypeReference<T> type) {
saveInput("entity", requestValues, type);
return Mono.just((ResponseEntity<T>) ResponseEntity.ok("entity"));
}
@Override
public <T> Mono<ResponseEntity<Flux<T>>> requestToEntityFlux(
HttpRequestValues requestValues, ParameterizedTypeReference<T> bodyType) {
saveInput("entityFlux", requestValues, bodyType);
return Mono.just(ResponseEntity.ok((Flux<T>) Flux.just("request", "To", "Entity", "Flux")));
}
private <T> void saveInput(
String reference, HttpRequestValues requestValues, @Nullable ParameterizedTypeReference<T> bodyType) {
this.invokedForReturnMethodReference = reference;
this.requestValues = requestValues;
this.bodyType = bodyType;
}
}

View File

@@ -0,0 +1,97 @@
/*
* Copyright 2002-2023 the original author or authors.
*
* Licensed 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
*
* https://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.springframework.web.service.invoker;
import java.time.Duration;
import java.util.Collections;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.ReactiveAdapterRegistry;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
/**
* {@link ReactorHttpExchangeAdapter} with stubbed responses.
*
* @author Rossen Stoyanchev
* @author Olga Maciaszek-Sharma
*/
@SuppressWarnings("unchecked")
class TestReactorExchangeAdapter extends TestExchangeAdapter implements ReactorHttpExchangeAdapter {
@Override
public ReactiveAdapterRegistry getReactiveAdapterRegistry() {
return ReactiveAdapterRegistry.getSharedInstance();
}
@Override
public Duration getBlockTimeout() {
return Duration.ofSeconds(5);
}
@Override
public Mono<Void> exchangeForMono(HttpRequestValues requestValues) {
saveInput("exchangeForMono", requestValues, null);
return Mono.empty();
}
@Override
public Mono<HttpHeaders> exchangeForHeadersMono(HttpRequestValues requestValues) {
saveInput("exchangeForHeadersMono", requestValues, null);
return Mono.just(new HttpHeaders());
}
@Override
public <T> Mono<T> exchangeForBodyMono(HttpRequestValues requestValues, ParameterizedTypeReference<T> bodyType) {
saveInput("exchangeForBodyMono", requestValues, bodyType);
return bodyType.getType().getTypeName().contains("List") ?
(Mono<T>) Mono.just(Collections.singletonList(getInvokedMethodName())) :
(Mono<T>) Mono.just(getInvokedMethodName());
}
@Override
public <T> Flux<T> exchangeForBodyFlux(HttpRequestValues requestValues, ParameterizedTypeReference<T> bodyType) {
saveInput("exchangeForBodyFlux", requestValues, bodyType);
return (Flux<T>) Flux.just("exchange", "For", "Body", "Flux");
}
@Override
public Mono<ResponseEntity<Void>> exchangeForBodilessEntityMono(HttpRequestValues requestValues) {
saveInput("exchangeForBodilessEntityMono", requestValues, null);
return Mono.just(ResponseEntity.ok().build());
}
@Override
public <T> Mono<ResponseEntity<T>> exchangeForEntityMono(
HttpRequestValues requestValues, ParameterizedTypeReference<T> bodyType) {
saveInput("exchangeForEntityMono", requestValues, bodyType);
return Mono.just((ResponseEntity<T>) ResponseEntity.ok("exchangeForEntityMono"));
}
@Override
public <T> Mono<ResponseEntity<Flux<T>>> exchangeForEntityFlux(
HttpRequestValues requestValues, ParameterizedTypeReference<T> bodyType) {
saveInput("exchangeForEntityFlux", requestValues, bodyType);
return Mono.just(ResponseEntity.ok((Flux<T>) Flux.just("exchange", "For", "Entity", "Flux")));
}
}

View File

@@ -18,7 +18,6 @@ package org.springframework.web.service.invoker;
import java.net.URI; import java.net.URI;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
@@ -34,16 +33,10 @@ import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
*/ */
class UrlArgumentResolverTests { class UrlArgumentResolverTests {
private final TestHttpClientAdapter client = new TestHttpClientAdapter(); private final TestExchangeAdapter client = new TestExchangeAdapter();
private Service service; private final Service service =
HttpServiceProxyFactory.builderFor(this.client).build().createClient(Service.class);
@BeforeEach
void setUp() throws Exception {
HttpServiceProxyFactory proxyFactory = HttpServiceProxyFactory.builder(this.client).build();
this.service = proxyFactory.createClient(Service.class);
}
@Test @Test

View File

@@ -36,79 +36,78 @@ import org.springframework.web.service.annotation.GetExchange
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
class KotlinHttpServiceMethodTests { class KotlinHttpServiceMethodTests {
private val webClientAdapter = TestHttpClientAdapter() private val exchangeAdapter = TestExchangeAdapter()
private val httpExchangeAdapter = TestHttpExchangeAdapter() private val reactorExchangeAdapter = TestReactorExchangeAdapter()
private val proxyFactory = HttpServiceProxyFactory.builder(webClientAdapter).build() private val proxyFactory = HttpServiceProxyFactory.builderFor(this.exchangeAdapter).build()
private val blockingProxyFactory = HttpServiceProxyFactory.builder() private val reactorProxyFactory = HttpServiceProxyFactory.builderFor(this.reactorExchangeAdapter).build()
.exchangeAdapter(httpExchangeAdapter).build()
@Test @Test
fun coroutinesService(): Unit = runBlocking { fun coroutinesService(): Unit = runBlocking {
val service = proxyFactory.createClient(FunctionsService::class.java) val service = reactorProxyFactory.createClient(FunctionsService::class.java)
val stringBody = service.stringBody() val stringBody = service.stringBody()
assertThat(stringBody).isEqualTo("body") assertThat(stringBody).isEqualTo("exchangeForBodyMono")
verifyClientInvocation("body", object : ParameterizedTypeReference<String>() {}) verifyClientInvocation("exchangeForBodyMono", object : ParameterizedTypeReference<String>() {})
service.listBody() service.listBody()
verifyClientInvocation("body", object : ParameterizedTypeReference<MutableList<String>>() {}) verifyClientInvocation("exchangeForBodyMono", object : ParameterizedTypeReference<MutableList<String>>() {})
val flowBody = service.flowBody() val flowBody = service.flowBody()
assertThat(flowBody.toList()).containsExactly("request", "To", "Body", "Flux") assertThat(flowBody.toList()).containsExactly("exchange", "For", "Body", "Flux")
verifyClientInvocation("bodyFlux", object : ParameterizedTypeReference<String>() {}) verifyClientInvocation("exchangeForBodyFlux", object : ParameterizedTypeReference<String>() {})
val stringEntity = service.stringEntity() val stringEntity = service.stringEntity()
assertThat(stringEntity).isEqualTo(ResponseEntity.ok<String>("entity")) assertThat(stringEntity).isEqualTo(ResponseEntity.ok<String>("exchangeForEntityMono"))
verifyClientInvocation("entity", object : ParameterizedTypeReference<String>() {}) verifyClientInvocation("exchangeForEntityMono", object : ParameterizedTypeReference<String>() {})
service.listEntity() service.listEntity()
verifyClientInvocation("entity", object : ParameterizedTypeReference<MutableList<String>>() {}) verifyClientInvocation("exchangeForEntityMono", object : ParameterizedTypeReference<MutableList<String>>() {})
val flowEntity = service.flowEntity() val flowEntity = service.flowEntity()
assertThat(flowEntity.statusCode).isEqualTo(HttpStatus.OK) assertThat(flowEntity.statusCode).isEqualTo(HttpStatus.OK)
assertThat(flowEntity.body!!.toList()).containsExactly("request", "To", "Entity", "Flux") assertThat(flowEntity.body!!.toList()).containsExactly("exchange", "For", "Entity", "Flux")
verifyClientInvocation("entityFlux", object : ParameterizedTypeReference<String>() {}) verifyClientInvocation("exchangeForEntityFlux", object : ParameterizedTypeReference<String>() {})
} }
@Test @Test
fun blockingServiceWithExchangeResponseFunction() { fun blockingServiceWithExchangeResponseFunction() {
val service = blockingProxyFactory.createClient(BlockingFunctionsService::class.java) val service = proxyFactory.createClient(BlockingFunctionsService::class.java)
val stringBody = service.stringBodyBlocking() val stringBody = service.stringBodyBlocking()
assertThat(stringBody).isEqualTo("body") assertThat(stringBody).isEqualTo("exchangeForBody")
verifyTemplateInvocation("body", object : ParameterizedTypeReference<String>() {}) verifyTemplateInvocation("exchangeForBody", object : ParameterizedTypeReference<String>() {})
val listBody = service.listBodyBlocking() val listBody = service.listBodyBlocking()
assertThat(listBody.size).isEqualTo(1) assertThat(listBody.size).isEqualTo(1)
verifyTemplateInvocation("body", object : ParameterizedTypeReference<MutableList<String>>() {}) verifyTemplateInvocation("exchangeForBody", object : ParameterizedTypeReference<MutableList<String>>() {})
val stringEntity = service.stringEntityBlocking() val stringEntity = service.stringEntityBlocking()
assertThat(stringEntity).isEqualTo(ResponseEntity.ok<String>("entity")) assertThat(stringEntity).isEqualTo(ResponseEntity.ok<String>("exchangeForEntity"))
verifyTemplateInvocation("entity", object : ParameterizedTypeReference<String>() {}) verifyTemplateInvocation("exchangeForEntity", object : ParameterizedTypeReference<String>() {})
service.listEntityBlocking() service.listEntityBlocking()
verifyTemplateInvocation("entity", object : ParameterizedTypeReference<MutableList<String>>() {}) verifyTemplateInvocation("exchangeForEntity", object : ParameterizedTypeReference<MutableList<String>>() {})
} }
@Test @Test
fun coroutineServiceWithExchangeResponseFunction() { fun coroutineServiceWithExchangeResponseFunction() {
assertThatIllegalStateException().isThrownBy { assertThatIllegalStateException().isThrownBy {
blockingProxyFactory.createClient(FunctionsService::class.java) proxyFactory.createClient(FunctionsService::class.java)
} }
assertThatIllegalStateException().isThrownBy { assertThatIllegalStateException().isThrownBy {
blockingProxyFactory.createClient(SuspendingFunctionsService::class.java) proxyFactory.createClient(SuspendingFunctionsService::class.java)
} }
} }
private fun verifyTemplateInvocation(methodReference: String, expectedBodyType: ParameterizedTypeReference<*>) { private fun verifyTemplateInvocation(methodReference: String, expectedBodyType: ParameterizedTypeReference<*>) {
assertThat(httpExchangeAdapter.invokedMethodReference).isEqualTo(methodReference) assertThat(exchangeAdapter.invokedMethodName).isEqualTo(methodReference)
assertThat(httpExchangeAdapter.bodyType).isEqualTo(expectedBodyType) assertThat(exchangeAdapter.bodyType).isEqualTo(expectedBodyType)
} }
private fun verifyClientInvocation(methodReference: String, expectedBodyType: ParameterizedTypeReference<*>) { private fun verifyClientInvocation(methodReference: String, expectedBodyType: ParameterizedTypeReference<*>) {
assertThat(webClientAdapter.invokedMethodReference).isEqualTo(methodReference) assertThat(reactorExchangeAdapter.invokedMethodName).isEqualTo(methodReference)
assertThat(webClientAdapter.bodyType).isEqualTo(expectedBodyType) assertThat(reactorExchangeAdapter.bodyType).isEqualTo(expectedBodyType)
} }
private interface FunctionsService : SuspendingFunctionsService { private interface FunctionsService : SuspendingFunctionsService {

View File

@@ -163,10 +163,8 @@ public class WebClientHttpServiceProxyTests {
} }
private TestHttpService initHttpService(WebClient webClient) { private TestHttpService initHttpService(WebClient webClient) {
return HttpServiceProxyFactory.builder() WebClientAdapter adapter = WebClientAdapter.forClient(webClient);
.clientAdapter(WebClientAdapter.forClient(webClient)) return HttpServiceProxyFactory.builderFor(adapter).build().createClient(TestHttpService.class);
.build()
.createClient(TestHttpService.class);
} }
private void prepareResponse(Consumer<MockResponse> consumer) { private void prepareResponse(Consumer<MockResponse> consumer) {