package org.zalando.riptide; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.mockito.ArgumentCaptor; import org.springframework.core.io.ClassPathResource; import org.springframework.http.HttpHeaders; import org.springframework.http.client.ClientHttpResponse; import org.springframework.test.web.client.MockRestServiceServer; import org.zalando.fauxpas.ThrowingConsumer; import org.zalando.fauxpas.ThrowingRunnable; import java.net.URI; import java.util.concurrent.CompletionException; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.function.BiConsumer; import java.util.function.Consumer; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.springframework.http.HttpStatus.OK; import static org.springframework.http.HttpStatus.Series.CLIENT_ERROR; import static org.springframework.http.HttpStatus.Series.SUCCESSFUL; import static org.springframework.http.MediaType.APPLICATION_JSON; import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo; import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess; import static org.zalando.riptide.Bindings.on; import static org.zalando.riptide.Navigators.series; import static org.zalando.riptide.Navigators.status; import static org.zalando.riptide.Route.pass; public final class AsyncTest { @Rule public final ExpectedException exception = ExpectedException.none(); private final URI url = URI.create("http://localhost"); private final Rest unit; private final MockRestServiceServer server; public AsyncTest() { final MockSetup setup = new MockSetup(); this.unit = setup.getRest(); this.server = setup.getServer(); } @Test public void shouldCall() throws Exception { server.expect(requestTo(url)).andRespond(withSuccess()); @SuppressWarnings("unchecked") final ThrowingConsumer<ClientHttpResponse, Exception> verifier = mock(ThrowingConsumer.class); unit.get(url).dispatch(series(), on(SUCCESSFUL).call(verifier)).join(); verify(verifier).tryAccept(any()); } @Test public void shouldExpand() throws Exception { server.expect(requestTo(URI.create("http://localhost/123"))).andRespond(withSuccess()); @SuppressWarnings("unchecked") final ThrowingConsumer<ClientHttpResponse, Exception> verifier = mock(ThrowingConsumer.class); unit.get("http://localhost/{id}", 123) .dispatch(series(), on(SUCCESSFUL).call(verifier)).join(); verify(verifier).tryAccept(any()); } @Test public void shouldCallWithoutParameters() throws Exception { server.expect(requestTo(url)).andRespond(withSuccess()); @SuppressWarnings("unchecked") final ThrowingRunnable<Exception> verifier = mock(ThrowingRunnable.class); unit.get(url).dispatch(series(), on(SUCCESSFUL).call(verifier)).join(); verify(verifier).tryRun(); } @Test public void shouldCallWithHeaders() throws Exception { server.expect(requestTo(url)).andRespond(withSuccess()); @SuppressWarnings("unchecked") final ThrowingConsumer<ClientHttpResponse, Exception> verifier = mock(ThrowingConsumer.class); unit.get(url).headers(new HttpHeaders()).dispatch(series(), on(SUCCESSFUL).call(verifier)).join(); verify(verifier).tryAccept(any()); } @Test public void shouldCallWithBody() throws Exception { server.expect(requestTo(url)).andRespond(withSuccess()); @SuppressWarnings("unchecked") final ThrowingConsumer<ClientHttpResponse, Exception> verifier = mock(ThrowingConsumer.class); unit.get(url).body("test").dispatch(series(), on(SUCCESSFUL).call(verifier)).join(); verify(verifier).tryAccept(any()); } @Test public void shouldCallWithHeadersAndBody() throws Exception { server.expect(requestTo(url)).andRespond(withSuccess()); @SuppressWarnings("unchecked") final ThrowingConsumer<ClientHttpResponse, Exception> verifier = mock(ThrowingConsumer.class); unit.get(url).headers(new HttpHeaders()).body("test").dispatch(series(), on(SUCCESSFUL).call(verifier)).join(); verify(verifier).tryAccept(any()); } @Test public void shouldCapture() throws InterruptedException, ExecutionException, TimeoutException { server.expect(requestTo(url)).andRespond( withSuccess() .body(new ClassPathResource("account.json")) .contentType(APPLICATION_JSON)); unit.get(url) .dispatch(status(), on(OK).call(response -> { assertThat(response.getStatusCode(), is(OK)); assertThat(response.getHeaders().getContentType(), is(APPLICATION_JSON)); })) .get(100, TimeUnit.MILLISECONDS); } @Test public void shouldIgnoreException() throws ExecutionException, InterruptedException { server.expect(requestTo(url)).andRespond(withSuccess()); unit.get(url).dispatch(series(), on(CLIENT_ERROR).call(pass())); } @Test public void shouldHandleExceptionWithGet() { server.expect(requestTo(url)).andRespond(withSuccess()); exception.expect(CompletionException.class); exception.expectCause(instanceOf(NoRouteException.class)); unit.get(url).dispatch(series(), on(CLIENT_ERROR).call(pass())) .join(); } @Test public void shouldHandleNoRouteExceptionWithCallback() { server.expect(requestTo(url)).andRespond(withSuccess()); @SuppressWarnings("unchecked") final BiConsumer<Void, Throwable> callback = mock(BiConsumer.class); unit.get(url).dispatch(series(), on(CLIENT_ERROR).call(pass())) .whenComplete(callback); final ArgumentCaptor<Exception> captor = ArgumentCaptor.forClass(Exception.class); verify(callback).accept(eq(null), captor.capture()); final Exception exception = captor.getValue(); assertThat(exception, is(instanceOf(CompletionException.class))); assertThat(exception.getCause(), is(instanceOf(NoRouteException.class))); } @Test public void shouldIgnoreSuccessWhenHandlingExceptionWithCallback() { server.expect(requestTo(url)).andRespond(withSuccess()); @SuppressWarnings("unchecked") final Consumer<Throwable> callback = mock(Consumer.class); unit.get(url).dispatch(series(), on(SUCCESSFUL).call(pass())) .whenComplete((result, throwable) -> { if (throwable != null) { callback.accept(throwable); } }).join(); verify(callback, never()).accept(any()); } }