/* * Copyright 2002-2015 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 * * http://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.reactive.result.method; import java.net.URI; import java.util.Collections; import java.util.Optional; import org.junit.Before; import org.junit.Test; import reactor.core.publisher.Mono; import reactor.core.test.TestSubscriber; import org.springframework.http.HttpMethod; import org.springframework.http.server.reactive.MockServerHttpRequest; import org.springframework.http.server.reactive.MockServerHttpResponse; import org.springframework.ui.ExtendedModelMap; import org.springframework.ui.ModelMap; import org.springframework.web.reactive.HandlerResult; import org.springframework.web.reactive.result.ResolvableMethod; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.adapter.DefaultServerWebExchange; import org.springframework.web.server.session.MockWebSessionManager; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; /** * Unit tests for {@link InvocableHandlerMethod}. * @author Rossen Stoyanchev */ @SuppressWarnings("ThrowableResultOfMethodCallIgnored") public class InvocableHandlerMethodTests { private ServerWebExchange exchange; private ModelMap model = new ExtendedModelMap(); @Before public void setUp() throws Exception { this.exchange = new DefaultServerWebExchange( new MockServerHttpRequest(HttpMethod.GET, new URI("http://localhost:8080/path")), new MockServerHttpResponse(), new MockWebSessionManager()); } @Test public void invokeMethodWithNoArguments() throws Exception { InvocableHandlerMethod hm = handlerMethod("noArgs"); Mono<HandlerResult> mono = hm.invokeForRequest(this.exchange, this.model); assertHandlerResultValue(mono, "success"); } @Test public void invokeMethodWithNoValue() throws Exception { InvocableHandlerMethod hm = handlerMethod("singleArg"); addResolver(hm, Mono.empty()); Mono<HandlerResult> mono = hm.invokeForRequest(this.exchange, this.model); assertHandlerResultValue(mono, "success:null"); } @Test public void invokeMethodWithValue() throws Exception { InvocableHandlerMethod hm = handlerMethod("singleArg"); addResolver(hm, Mono.just("value1")); Mono<HandlerResult> mono = hm.invokeForRequest(this.exchange, this.model); assertHandlerResultValue(mono, "success:value1"); } @Test public void noMatchingResolver() throws Exception { InvocableHandlerMethod hm = handlerMethod("singleArg"); Mono<HandlerResult> mono = hm.invokeForRequest(this.exchange, this.model); TestSubscriber.subscribe(mono) .assertError(IllegalStateException.class) .assertErrorMessage("No resolver for argument [0] of type [java.lang.String] " + "on method [" + hm.getMethod().toGenericString() + "]"); } @Test public void resolverThrowsException() throws Exception { InvocableHandlerMethod hm = handlerMethod("singleArg"); addResolver(hm, Mono.error(new IllegalStateException("boo"))); Mono<HandlerResult> mono = hm.invokeForRequest(this.exchange, this.model); TestSubscriber.subscribe(mono) .assertError(IllegalStateException.class) .assertErrorMessage("Error resolving argument [0] of type [java.lang.String] " + "on method [" + hm.getMethod().toGenericString() + "]"); } @Test public void resolverWithErrorSignal() throws Exception { InvocableHandlerMethod hm = handlerMethod("singleArg"); addResolver(hm, Mono.error(new IllegalStateException("boo"))); Mono<HandlerResult> mono = hm.invokeForRequest(this.exchange, this.model); TestSubscriber.subscribe(mono) .assertError(IllegalStateException.class) .assertErrorMessage("Error resolving argument [0] of type [java.lang.String] " + "on method [" + hm.getMethod().toGenericString() + "]"); } @Test public void illegalArgumentExceptionIsWrappedWithInvocationDetails() throws Exception { InvocableHandlerMethod hm = handlerMethod("singleArg"); addResolver(hm, Mono.just(1)); Mono<HandlerResult> mono = hm.invokeForRequest(this.exchange, this.model); TestSubscriber.subscribe(mono) .assertError(IllegalStateException.class) .assertErrorMessage("Failed to invoke controller with resolved arguments: " + "[0][type=java.lang.Integer][value=1] " + "on method [" + hm.getMethod().toGenericString() + "]"); } @Test public void invocationTargetExceptionIsUnwrapped() throws Exception { InvocableHandlerMethod hm = handlerMethod("exceptionMethod"); Mono<HandlerResult> mono = hm.invokeForRequest(this.exchange, this.model); TestSubscriber.subscribe(mono) .assertError(IllegalStateException.class) .assertErrorMessage("boo"); } private InvocableHandlerMethod handlerMethod(String name) throws Exception { TestController controller = new TestController(); return ResolvableMethod.on(controller).name(name).resolveHandlerMethod(); } private void addResolver(InvocableHandlerMethod handlerMethod, Mono<Object> resolvedValue) { HandlerMethodArgumentResolver resolver = mock(HandlerMethodArgumentResolver.class); when(resolver.supportsParameter(any())).thenReturn(true); when(resolver.resolveArgument(any(), any(), any())).thenReturn(resolvedValue); handlerMethod.setHandlerMethodArgumentResolvers(Collections.singletonList(resolver)); } private void assertHandlerResultValue(Mono<HandlerResult> mono, String expected) { TestSubscriber.subscribe(mono).assertValuesWith(result -> { Optional<?> optional = result.getReturnValue(); assertTrue(optional.isPresent()); assertEquals(expected, optional.get()); }); } @SuppressWarnings("unused") private static class TestController { public String noArgs() { return "success"; } public String singleArg(String q) { return "success:" + q; } public void exceptionMethod() { throw new IllegalStateException("boo"); } } }