Collapse hierarchy under HttpServiceMethodTests
See gh-30117
This commit is contained in:
@@ -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 blocking test {@link TestExchangeAdapter} 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
|
||||
*/
|
||||
class HttpExchangeAdapterServiceMethodTests extends HttpServiceMethodTests {
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
this.client = new TestExchangeAdapter();
|
||||
this.proxyFactory = HttpServiceProxyFactory.builderFor(this.client).build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -19,9 +19,16 @@ package org.springframework.web.service.invoker;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import io.reactivex.rxjava3.core.Completable;
|
||||
import io.reactivex.rxjava3.core.Flowable;
|
||||
import io.reactivex.rxjava3.core.Single;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
@@ -36,29 +43,35 @@ import static org.springframework.http.MediaType.APPLICATION_CBOR_VALUE;
|
||||
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
|
||||
|
||||
/**
|
||||
* Base class for testing {@link HttpServiceMethod} with a test {@link TestExchangeAdapter}
|
||||
* and a test {@link TestExchangeAdapter} that stub the client invocations.
|
||||
* Tests for {@link HttpServiceMethod} with
|
||||
* {@link TestExchangeAdapter} and {@link TestReactorExchangeAdapter}.
|
||||
*
|
||||
* <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.
|
||||
* <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 Rossen Stoyanchev
|
||||
* @author Olga Maciaszek-Sharma
|
||||
*/
|
||||
abstract class HttpServiceMethodTests {
|
||||
class HttpServiceMethodTests {
|
||||
|
||||
protected static final ParameterizedTypeReference<String> BODY_TYPE = new ParameterizedTypeReference<>() {
|
||||
};
|
||||
private static final ParameterizedTypeReference<String> BODY_TYPE = new ParameterizedTypeReference<>() {};
|
||||
|
||||
protected TestExchangeAdapter client;
|
||||
|
||||
protected HttpServiceProxyFactory proxyFactory;
|
||||
private final TestExchangeAdapter client = new TestExchangeAdapter();
|
||||
|
||||
private final TestReactorExchangeAdapter reactorClient = new TestReactorExchangeAdapter();
|
||||
|
||||
private final HttpServiceProxyFactory proxyFactory =
|
||||
HttpServiceProxyFactory.builder().exchangeAdapter(this.client).build();
|
||||
|
||||
private final HttpServiceProxyFactory reactorProxyFactory =
|
||||
HttpServiceProxyFactory.builder().exchangeAdapter(this.reactorClient).build();
|
||||
|
||||
|
||||
@Test
|
||||
void blockingService() {
|
||||
BlockingService service = this.proxyFactory.createClient(BlockingService.class);
|
||||
void service() {
|
||||
Service service = this.proxyFactory.createClient(Service.class);
|
||||
|
||||
service.execute();
|
||||
|
||||
@@ -66,19 +79,82 @@ abstract class HttpServiceMethodTests {
|
||||
assertThat(headers).isNotNull();
|
||||
|
||||
String body = service.getBody();
|
||||
assertThat(body).isEqualTo(client.getInvokedMethodName());
|
||||
assertThat(body).isEqualTo(this.client.getInvokedMethodName());
|
||||
|
||||
Optional<String> optional = service.getBodyOptional();
|
||||
assertThat(optional.get()).startsWith("exchangeForBody");
|
||||
assertThat(optional.get()).isEqualTo("exchangeForBody");
|
||||
|
||||
ResponseEntity<String> entity = service.getEntity();
|
||||
assertThat(entity.getBody()).startsWith("exchangeForEntity");
|
||||
assertThat(entity.getBody()).isEqualTo("exchangeForEntity");
|
||||
|
||||
ResponseEntity<Void> voidEntity = service.getVoidEntity();
|
||||
assertThat(voidEntity.getBody()).isNull();
|
||||
|
||||
List<String> list = service.getList();
|
||||
assertThat(list.get(0)).startsWith("exchangeForBody");
|
||||
assertThat(list.get(0)).isEqualTo("exchangeForBody");
|
||||
}
|
||||
|
||||
@Test
|
||||
void reactorService() {
|
||||
ReactorService service = this.reactorProxyFactory.createClient(ReactorService.class);
|
||||
|
||||
Mono<Void> voidMono = service.execute();
|
||||
StepVerifier.create(voidMono).verifyComplete();
|
||||
verifyReactorClientInvocation("exchangeForMono", null);
|
||||
|
||||
Mono<HttpHeaders> headersMono = service.getHeaders();
|
||||
StepVerifier.create(headersMono).expectNextCount(1).verifyComplete();
|
||||
verifyReactorClientInvocation("exchangeForHeadersMono", null);
|
||||
|
||||
Mono<String> body = service.getBody();
|
||||
StepVerifier.create(body).expectNext("exchangeForBodyMono").verifyComplete();
|
||||
verifyReactorClientInvocation("exchangeForBodyMono", BODY_TYPE);
|
||||
|
||||
Flux<String> fluxBody = service.getFluxBody();
|
||||
StepVerifier.create(fluxBody).expectNext("exchange", "For", "Body", "Flux").verifyComplete();
|
||||
verifyReactorClientInvocation("exchangeForBodyFlux", BODY_TYPE);
|
||||
|
||||
Mono<ResponseEntity<Void>> voidEntity = service.getVoidEntity();
|
||||
StepVerifier.create(voidEntity).expectNext(ResponseEntity.ok().build()).verifyComplete();
|
||||
verifyReactorClientInvocation("exchangeForBodilessEntityMono", null);
|
||||
|
||||
Mono<ResponseEntity<String>> entity = service.getEntity();
|
||||
StepVerifier.create(entity).expectNext(ResponseEntity.ok("exchangeForEntityMono"));
|
||||
verifyReactorClientInvocation("exchangeForEntityMono", BODY_TYPE);
|
||||
|
||||
Mono<ResponseEntity<Flux<String>>> fluxEntity = service.getFluxEntity();
|
||||
StepVerifier.create(fluxEntity.flatMapMany(HttpEntity::getBody))
|
||||
.expectNext("exchange", "For", "Entity", "Flux")
|
||||
.verifyComplete();
|
||||
verifyReactorClientInvocation("exchangeForEntityFlux", BODY_TYPE);
|
||||
|
||||
assertThat(service.getDefaultMethodValue()).isEqualTo("default value");
|
||||
}
|
||||
|
||||
@Test
|
||||
void rxJavaService() {
|
||||
RxJavaService service = this.reactorProxyFactory.createClient(RxJavaService.class);
|
||||
Completable completable = service.execute();
|
||||
assertThat(completable).isNotNull();
|
||||
|
||||
Single<HttpHeaders> headersSingle = service.getHeaders();
|
||||
assertThat(headersSingle.blockingGet()).isNotNull();
|
||||
|
||||
Single<String> bodySingle = service.getBody();
|
||||
assertThat(bodySingle.blockingGet()).isEqualTo("exchangeForBodyMono");
|
||||
|
||||
Flowable<String> bodyFlow = service.getFlowableBody();
|
||||
assertThat(bodyFlow.toList().blockingGet()).asList().containsExactly("exchange", "For", "Body", "Flux");
|
||||
|
||||
Single<ResponseEntity<Void>> voidEntity = service.getVoidEntity();
|
||||
assertThat(voidEntity.blockingGet().getBody()).isNull();
|
||||
|
||||
Single<ResponseEntity<String>> entitySingle = service.getEntity();
|
||||
assertThat(entitySingle.blockingGet().getBody()).isEqualTo("exchangeForEntityMono");
|
||||
|
||||
Single<ResponseEntity<Flowable<String>>> entityFlow = service.getFlowableEntity();
|
||||
Flowable<String> body = (entityFlow.blockingGet()).getBody();
|
||||
assertThat(body.toList().blockingGet()).containsExactly("exchange", "For", "Entity", "Flux");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -128,13 +204,14 @@ abstract class HttpServiceMethodTests {
|
||||
assertThat(requestValues.getHeaders().getAccept()).containsExactly(MediaType.APPLICATION_JSON);
|
||||
}
|
||||
|
||||
protected void verifyClientInvocation(String methodName, @Nullable ParameterizedTypeReference<?> expectedBodyType) {
|
||||
assertThat(this.client.getInvokedMethodName()).isEqualTo(methodName);
|
||||
assertThat(this.client.getBodyType()).isEqualTo(expectedBodyType);
|
||||
protected void verifyReactorClientInvocation(String methodName, @Nullable ParameterizedTypeReference<?> expectedBodyType) {
|
||||
assertThat(this.reactorClient.getInvokedMethodName()).isEqualTo(methodName);
|
||||
assertThat(this.reactorClient.getBodyType()).isEqualTo(expectedBodyType);
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private interface BlockingService {
|
||||
private interface Service {
|
||||
|
||||
@GetExchange
|
||||
void execute();
|
||||
@@ -159,6 +236,64 @@ abstract class HttpServiceMethodTests {
|
||||
|
||||
}
|
||||
|
||||
|
||||
private interface ReactorService {
|
||||
|
||||
@GetExchange
|
||||
Mono<Void> execute();
|
||||
|
||||
@GetExchange
|
||||
Mono<HttpHeaders> getHeaders();
|
||||
|
||||
@GetExchange
|
||||
Mono<String> getBody();
|
||||
|
||||
@GetExchange
|
||||
Flux<String> getFluxBody();
|
||||
|
||||
@GetExchange
|
||||
Mono<ResponseEntity<Void>> getVoidEntity();
|
||||
|
||||
@GetExchange
|
||||
Mono<ResponseEntity<String>> getEntity();
|
||||
|
||||
@GetExchange
|
||||
Mono<ResponseEntity<Flux<String>>> getFluxEntity();
|
||||
|
||||
default String getDefaultMethodValue() {
|
||||
return "default value";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private interface RxJavaService {
|
||||
|
||||
@GetExchange
|
||||
Completable execute();
|
||||
|
||||
@GetExchange
|
||||
Single<HttpHeaders> getHeaders();
|
||||
|
||||
@GetExchange
|
||||
Single<String> getBody();
|
||||
|
||||
@GetExchange
|
||||
Flowable<String> getFlowableBody();
|
||||
|
||||
@GetExchange
|
||||
Single<ResponseEntity<Void>> getVoidEntity();
|
||||
|
||||
@GetExchange
|
||||
Single<ResponseEntity<String>> getEntity();
|
||||
|
||||
@GetExchange
|
||||
Single<ResponseEntity<Flowable<String>>> getFlowableEntity();
|
||||
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private interface MethodLevelAnnotatedService {
|
||||
|
||||
@@ -173,7 +308,6 @@ abstract class HttpServiceMethodTests {
|
||||
@SuppressWarnings("unused")
|
||||
@HttpExchange(url = "${baseUrl}", contentType = APPLICATION_CBOR_VALUE, accept = APPLICATION_CBOR_VALUE)
|
||||
private interface TypeAndMethodLevelAnnotatedService extends MethodLevelAnnotatedService {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,166 +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 io.reactivex.rxjava3.core.Completable;
|
||||
import io.reactivex.rxjava3.core.Flowable;
|
||||
import io.reactivex.rxjava3.core.Single;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.service.annotation.GetExchange;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Base class for testing reactive scenarios in {@link HttpServiceMethod} with
|
||||
* a test {@link TestReactorExchangeAdapter} that stub 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 Rossen Stoyanchev
|
||||
* @author Olga Maciaszek-Sharma
|
||||
*/
|
||||
abstract class ReactiveHttpServiceMethodTests extends HttpServiceMethodTests {
|
||||
|
||||
@Test
|
||||
void reactorService() {
|
||||
ReactorService service = proxyFactory.createClient(ReactorService.class);
|
||||
|
||||
Mono<Void> voidMono = service.execute();
|
||||
StepVerifier.create(voidMono).verifyComplete();
|
||||
verifyClientInvocation("exchangeForMono", null);
|
||||
|
||||
Mono<HttpHeaders> headersMono = service.getHeaders();
|
||||
StepVerifier.create(headersMono).expectNextCount(1).verifyComplete();
|
||||
verifyClientInvocation("exchangeForHeadersMono", null);
|
||||
|
||||
Mono<String> body = service.getBody();
|
||||
StepVerifier.create(body).expectNext("exchangeForBodyMono").verifyComplete();
|
||||
verifyClientInvocation("exchangeForBodyMono", BODY_TYPE);
|
||||
|
||||
Flux<String> fluxBody = service.getFluxBody();
|
||||
StepVerifier.create(fluxBody).expectNext("exchange", "For", "Body", "Flux").verifyComplete();
|
||||
verifyClientInvocation("exchangeForBodyFlux", BODY_TYPE);
|
||||
|
||||
Mono<ResponseEntity<Void>> voidEntity = service.getVoidEntity();
|
||||
StepVerifier.create(voidEntity).expectNext(ResponseEntity.ok().build()).verifyComplete();
|
||||
verifyClientInvocation("exchangeForBodilessEntityMono", null);
|
||||
|
||||
Mono<ResponseEntity<String>> entity = service.getEntity();
|
||||
StepVerifier.create(entity).expectNext(ResponseEntity.ok("exchangeForEntityMono"));
|
||||
verifyClientInvocation("exchangeForEntityMono", BODY_TYPE);
|
||||
|
||||
Mono<ResponseEntity<Flux<String>>> fluxEntity = service.getFluxEntity();
|
||||
StepVerifier.create(fluxEntity.flatMapMany(HttpEntity::getBody))
|
||||
.expectNext("exchange", "For", "Entity", "Flux")
|
||||
.verifyComplete();
|
||||
verifyClientInvocation("exchangeForEntityFlux", BODY_TYPE);
|
||||
|
||||
assertThat(service.getDefaultMethodValue()).isEqualTo("default value");
|
||||
}
|
||||
|
||||
@Test
|
||||
void rxJavaService() {
|
||||
RxJavaService service = this.proxyFactory.createClient(RxJavaService.class);
|
||||
Completable completable = service.execute();
|
||||
assertThat(completable).isNotNull();
|
||||
|
||||
Single<HttpHeaders> headersSingle = service.getHeaders();
|
||||
assertThat(headersSingle.blockingGet()).isNotNull();
|
||||
|
||||
Single<String> bodySingle = service.getBody();
|
||||
assertThat(bodySingle.blockingGet()).isEqualTo("exchangeForBodyMono");
|
||||
|
||||
Flowable<String> bodyFlow = service.getFlowableBody();
|
||||
assertThat(bodyFlow.toList().blockingGet()).asList().containsExactly("exchange", "For", "Body", "Flux");
|
||||
|
||||
Single<ResponseEntity<Void>> voidEntity = service.getVoidEntity();
|
||||
assertThat(voidEntity.blockingGet().getBody()).isNull();
|
||||
|
||||
Single<ResponseEntity<String>> entitySingle = service.getEntity();
|
||||
assertThat(entitySingle.blockingGet().getBody()).isEqualTo("exchangeForEntityMono");
|
||||
|
||||
Single<ResponseEntity<Flowable<String>>> entityFlow = service.getFlowableEntity();
|
||||
Flowable<String> body = (entityFlow.blockingGet()).getBody();
|
||||
assertThat(body.toList().blockingGet()).containsExactly("exchange", "For", "Entity", "Flux");
|
||||
}
|
||||
|
||||
private interface ReactorService {
|
||||
|
||||
@GetExchange
|
||||
Mono<Void> execute();
|
||||
|
||||
@GetExchange
|
||||
Mono<HttpHeaders> getHeaders();
|
||||
|
||||
@GetExchange
|
||||
Mono<String> getBody();
|
||||
|
||||
@GetExchange
|
||||
Flux<String> getFluxBody();
|
||||
|
||||
@GetExchange
|
||||
Mono<ResponseEntity<Void>> getVoidEntity();
|
||||
|
||||
@GetExchange
|
||||
Mono<ResponseEntity<String>> getEntity();
|
||||
|
||||
@GetExchange
|
||||
Mono<ResponseEntity<Flux<String>>> getFluxEntity();
|
||||
|
||||
default String getDefaultMethodValue() {
|
||||
return "default value";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private interface RxJavaService {
|
||||
|
||||
@GetExchange
|
||||
Completable execute();
|
||||
|
||||
@GetExchange
|
||||
Single<HttpHeaders> getHeaders();
|
||||
|
||||
@GetExchange
|
||||
Single<String> getBody();
|
||||
|
||||
@GetExchange
|
||||
Flowable<String> getFlowableBody();
|
||||
|
||||
@GetExchange
|
||||
Single<ResponseEntity<Void>> getVoidEntity();
|
||||
|
||||
@GetExchange
|
||||
Single<ResponseEntity<String>> getEntity();
|
||||
|
||||
@GetExchange
|
||||
Single<ResponseEntity<Flowable<String>>> getFlowableEntity();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 an {@link HttpExchangeAdapter}
|
||||
* build from a test {@link TestReactorExchangeAdapter} 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 ReactorExchangeAdapterHttpServiceMethodTests extends ReactiveHttpServiceMethodTests {
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
this.client = new TestReactorExchangeAdapter();
|
||||
this.proxyFactory = HttpServiceProxyFactory.builder().exchangeAdapter(this.client).build();
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user