1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.springframework.web.servlet.mvc.method.annotation;
18
19 import java.lang.reflect.Method;
20 import java.net.URI;
21 import java.util.Arrays;
22 import java.util.Collections;
23
24 import org.junit.Before;
25 import org.junit.Test;
26 import org.mockito.ArgumentCaptor;
27
28 import org.springframework.core.MethodParameter;
29 import org.springframework.http.HttpEntity;
30 import org.springframework.http.HttpHeaders;
31 import org.springframework.http.HttpInputMessage;
32 import org.springframework.http.HttpMethod;
33 import org.springframework.http.HttpOutputMessage;
34 import org.springframework.http.HttpStatus;
35 import org.springframework.http.MediaType;
36 import org.springframework.http.RequestEntity;
37 import org.springframework.http.ResponseEntity;
38 import org.springframework.http.converter.HttpMessageConverter;
39 import org.springframework.mock.web.test.MockHttpServletRequest;
40 import org.springframework.mock.web.test.MockHttpServletResponse;
41 import org.springframework.web.HttpMediaTypeNotAcceptableException;
42 import org.springframework.web.HttpMediaTypeNotSupportedException;
43 import org.springframework.web.bind.annotation.RequestMapping;
44 import org.springframework.web.context.request.ServletWebRequest;
45 import org.springframework.web.method.support.ModelAndViewContainer;
46
47 import static org.junit.Assert.*;
48 import static org.mockito.BDDMockito.any;
49 import static org.mockito.BDDMockito.*;
50 import static org.mockito.BDDMockito.isA;
51 import static org.mockito.Matchers.eq;
52 import static org.springframework.web.servlet.HandlerMapping.*;
53
54
55
56
57
58
59
60
61
62
63 public class HttpEntityMethodProcessorMockTests {
64
65 private HttpEntityMethodProcessor processor;
66
67 private HttpMessageConverter<String> messageConverter;
68
69 private MethodParameter paramHttpEntity;
70 private MethodParameter paramRequestEntity;
71 private MethodParameter paramResponseEntity;
72 private MethodParameter paramInt;
73 private MethodParameter returnTypeResponseEntity;
74 private MethodParameter returnTypeResponseEntityProduces;
75 private MethodParameter returnTypeHttpEntity;
76 private MethodParameter returnTypeHttpEntitySubclass;
77 private MethodParameter returnTypeInt;
78
79 private ModelAndViewContainer mavContainer;
80
81 private MockHttpServletRequest servletRequest;
82
83 private MockHttpServletResponse servletResponse;
84
85 private ServletWebRequest webRequest;
86
87
88 @SuppressWarnings("unchecked")
89 @Before
90 public void setUp() throws Exception {
91 messageConverter = mock(HttpMessageConverter.class);
92 given(messageConverter.getSupportedMediaTypes()).willReturn(Collections.singletonList(MediaType.TEXT_PLAIN));
93
94 processor = new HttpEntityMethodProcessor(Collections.<HttpMessageConverter<?>>singletonList(messageConverter));
95 reset(messageConverter);
96
97 Method handle1 = getClass().getMethod("handle1", HttpEntity.class, ResponseEntity.class, Integer.TYPE, RequestEntity.class);
98 paramHttpEntity = new MethodParameter(handle1, 0);
99 paramRequestEntity = new MethodParameter(handle1, 3);
100 paramResponseEntity = new MethodParameter(handle1, 1);
101 paramInt = new MethodParameter(handle1, 2);
102 returnTypeResponseEntity = new MethodParameter(handle1, -1);
103 returnTypeResponseEntityProduces = new MethodParameter(getClass().getMethod("handle4"), -1);
104 returnTypeHttpEntity = new MethodParameter(getClass().getMethod("handle2", HttpEntity.class), -1);
105 returnTypeHttpEntitySubclass = new MethodParameter(getClass().getMethod("handle2x", HttpEntity.class), -1);
106 returnTypeInt = new MethodParameter(getClass().getMethod("handle3"), -1);
107
108 mavContainer = new ModelAndViewContainer();
109 servletRequest = new MockHttpServletRequest();
110 servletResponse = new MockHttpServletResponse();
111 webRequest = new ServletWebRequest(servletRequest, servletResponse);
112 }
113
114
115 @Test
116 public void supportsParameter() {
117 assertTrue("HttpEntity parameter not supported", processor.supportsParameter(paramHttpEntity));
118 assertTrue("RequestEntity parameter not supported", processor.supportsParameter(paramRequestEntity));
119 assertFalse("ResponseEntity parameter supported", processor.supportsParameter(paramResponseEntity));
120 assertFalse("non-entity parameter supported", processor.supportsParameter(paramInt));
121 }
122
123 @Test
124 public void supportsReturnType() {
125 assertTrue("ResponseEntity return type not supported", processor.supportsReturnType(returnTypeResponseEntity));
126 assertTrue("HttpEntity return type not supported", processor.supportsReturnType(returnTypeHttpEntity));
127 assertTrue("Custom HttpEntity subclass not supported", processor.supportsReturnType(returnTypeHttpEntitySubclass));
128 assertFalse("RequestEntity parameter supported",
129 processor.supportsReturnType(paramRequestEntity));
130 assertFalse("non-ResponseBody return type supported", processor.supportsReturnType(returnTypeInt));
131 }
132
133 @Test
134 public void resolveArgument() throws Exception {
135 MediaType contentType = MediaType.TEXT_PLAIN;
136 servletRequest.addHeader("Content-Type", contentType.toString());
137
138 String body = "Foo";
139 given(messageConverter.canRead(String.class, contentType)).willReturn(true);
140 given(messageConverter.read(eq(String.class), isA(HttpInputMessage.class))).willReturn(body);
141
142 Object result = processor.resolveArgument(paramHttpEntity, mavContainer, webRequest, null);
143
144 assertTrue(result instanceof HttpEntity);
145 assertFalse("The requestHandled flag shouldn't change", mavContainer.isRequestHandled());
146 assertEquals("Invalid argument", body, ((HttpEntity<?>) result).getBody());
147 }
148
149 @Test
150 public void resolveArgumentRequestEntity() throws Exception {
151 MediaType contentType = MediaType.TEXT_PLAIN;
152 servletRequest.addHeader("Content-Type", contentType.toString());
153 servletRequest.setMethod("GET");
154 servletRequest.setServerName("www.example.com");
155 servletRequest.setServerPort(80);
156 servletRequest.setRequestURI("/path");
157
158 String body = "Foo";
159 given(messageConverter.canRead(String.class, contentType)).willReturn(true);
160 given(messageConverter.read(eq(String.class), isA(HttpInputMessage.class))).willReturn(body);
161
162 Object result = processor.resolveArgument(paramRequestEntity, mavContainer, webRequest, null);
163
164 assertTrue(result instanceof RequestEntity);
165 assertFalse("The requestHandled flag shouldn't change", mavContainer.isRequestHandled());
166 RequestEntity<?> requestEntity = (RequestEntity<?>) result;
167 assertEquals("Invalid method", HttpMethod.GET, requestEntity.getMethod());
168 assertEquals("Invalid url", new URI("http", null, "www.example.com", 80, "/path", null, null), requestEntity.getUrl());
169 assertEquals("Invalid argument", body, requestEntity.getBody());
170 }
171
172 @Test(expected = HttpMediaTypeNotSupportedException.class)
173 public void resolveArgumentNotReadable() throws Exception {
174 MediaType contentType = MediaType.TEXT_PLAIN;
175 servletRequest.addHeader("Content-Type", contentType.toString());
176
177 given(messageConverter.getSupportedMediaTypes()).willReturn(Arrays.asList(contentType));
178 given(messageConverter.canRead(String.class, contentType)).willReturn(false);
179
180 processor.resolveArgument(paramHttpEntity, mavContainer, webRequest, null);
181
182 fail("Expected exception");
183 }
184
185 @Test(expected = HttpMediaTypeNotSupportedException.class)
186 public void resolveArgumentNoContentType() throws Exception {
187 processor.resolveArgument(paramHttpEntity, mavContainer, webRequest, null);
188 fail("Expected exception");
189 }
190
191 @Test
192 public void handleReturnValue() throws Exception {
193 String body = "Foo";
194 ResponseEntity<String> returnValue = new ResponseEntity<String>(body, HttpStatus.OK);
195
196 MediaType accepted = MediaType.TEXT_PLAIN;
197 servletRequest.addHeader("Accept", accepted.toString());
198
199 given(messageConverter.canWrite(String.class, null)).willReturn(true);
200 given(messageConverter.getSupportedMediaTypes()).willReturn(Collections.singletonList(MediaType.TEXT_PLAIN));
201 given(messageConverter.canWrite(String.class, accepted)).willReturn(true);
202
203 processor.handleReturnValue(returnValue, returnTypeResponseEntity, mavContainer, webRequest);
204
205 assertTrue(mavContainer.isRequestHandled());
206 verify(messageConverter).write(eq(body), eq(accepted), isA(HttpOutputMessage.class));
207 }
208
209 @Test
210 public void handleReturnValueProduces() throws Exception {
211 String body = "Foo";
212 ResponseEntity<String> returnValue = new ResponseEntity<String>(body, HttpStatus.OK);
213
214 servletRequest.addHeader("Accept", "text/*");
215 servletRequest.setAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE, Collections.singleton(MediaType.TEXT_HTML));
216
217 given(messageConverter.canWrite(String.class, MediaType.TEXT_HTML)).willReturn(true);
218
219 processor.handleReturnValue(returnValue, returnTypeResponseEntityProduces, mavContainer, webRequest);
220
221 assertTrue(mavContainer.isRequestHandled());
222 verify(messageConverter).write(eq(body), eq(MediaType.TEXT_HTML), isA(HttpOutputMessage.class));
223 }
224
225 @Test
226 public void handleReturnValueWithResponseBodyAdvice() throws Exception {
227 ResponseEntity<String> returnValue = new ResponseEntity<>(HttpStatus.OK);
228
229 servletRequest.addHeader("Accept", "text/*");
230 servletRequest.setAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE, Collections.singleton(MediaType.TEXT_HTML));
231
232 ResponseBodyAdvice<String> advice = mock(ResponseBodyAdvice.class);
233 given(advice.supports(any(), any())).willReturn(true);
234 given(advice.beforeBodyWrite(any(), any(), any(), any(), any(), any())).willReturn("Foo");
235
236 HttpEntityMethodProcessor processor = new HttpEntityMethodProcessor(
237 Collections.singletonList(messageConverter), null, Collections.singletonList(advice));
238
239 reset(messageConverter);
240 given(messageConverter.canWrite(String.class, MediaType.TEXT_HTML)).willReturn(true);
241
242 processor.handleReturnValue(returnValue, returnTypeResponseEntity, mavContainer, webRequest);
243
244 assertTrue(mavContainer.isRequestHandled());
245 verify(messageConverter).write(eq("Foo"), eq(MediaType.TEXT_HTML), isA(HttpOutputMessage.class));
246 }
247
248 @Test(expected = HttpMediaTypeNotAcceptableException.class)
249 public void handleReturnValueNotAcceptable() throws Exception {
250 String body = "Foo";
251 ResponseEntity<String> returnValue = new ResponseEntity<String>(body, HttpStatus.OK);
252
253 MediaType accepted = MediaType.APPLICATION_ATOM_XML;
254 servletRequest.addHeader("Accept", accepted.toString());
255
256 given(messageConverter.canWrite(String.class, null)).willReturn(true);
257 given(messageConverter.getSupportedMediaTypes()).willReturn(Arrays.asList(MediaType.TEXT_PLAIN));
258 given(messageConverter.canWrite(String.class, accepted)).willReturn(false);
259
260 processor.handleReturnValue(returnValue, returnTypeResponseEntity, mavContainer, webRequest);
261
262 fail("Expected exception");
263 }
264
265 @Test(expected = HttpMediaTypeNotAcceptableException.class)
266 public void handleReturnValueNotAcceptableProduces() throws Exception {
267 String body = "Foo";
268 ResponseEntity<String> returnValue = new ResponseEntity<String>(body, HttpStatus.OK);
269
270 MediaType accepted = MediaType.TEXT_PLAIN;
271 servletRequest.addHeader("Accept", accepted.toString());
272
273 given(messageConverter.canWrite(String.class, null)).willReturn(true);
274 given(messageConverter.getSupportedMediaTypes()).willReturn(Collections.singletonList(MediaType.TEXT_PLAIN));
275 given(messageConverter.canWrite(String.class, accepted)).willReturn(false);
276
277 processor.handleReturnValue(returnValue, returnTypeResponseEntityProduces, mavContainer, webRequest);
278
279 fail("Expected exception");
280 }
281
282
283
284 @Test(expected=HttpMediaTypeNotAcceptableException.class)
285 public void handleReturnValueNotAcceptableParseError() throws Exception {
286 ResponseEntity<String> returnValue = new ResponseEntity<String>("Body", HttpStatus.ACCEPTED);
287 servletRequest.addHeader("Accept", "01");
288
289 processor.handleReturnValue(returnValue, returnTypeResponseEntity, mavContainer, webRequest);
290 fail("Expected exception");
291 }
292
293 @Test
294 public void responseHeaderNoBody() throws Exception {
295 HttpHeaders headers = new HttpHeaders();
296 headers.set("headerName", "headerValue");
297 ResponseEntity<String> returnValue = new ResponseEntity<String>(headers, HttpStatus.ACCEPTED);
298
299 processor.handleReturnValue(returnValue, returnTypeResponseEntity, mavContainer, webRequest);
300
301 assertTrue(mavContainer.isRequestHandled());
302 assertEquals("headerValue", servletResponse.getHeader("headerName"));
303 }
304
305 @Test
306 public void responseHeaderAndBody() throws Exception {
307 HttpHeaders responseHeaders = new HttpHeaders();
308 responseHeaders.set("header", "headerValue");
309 ResponseEntity<String> returnValue = new ResponseEntity<String>("body", responseHeaders, HttpStatus.ACCEPTED);
310
311 given(messageConverter.canWrite(String.class, null)).willReturn(true);
312 given(messageConverter.getSupportedMediaTypes()).willReturn(Collections.singletonList(MediaType.TEXT_PLAIN));
313 given(messageConverter.canWrite(String.class, MediaType.TEXT_PLAIN)).willReturn(true);
314
315 processor.handleReturnValue(returnValue, returnTypeResponseEntity, mavContainer, webRequest);
316
317 ArgumentCaptor<HttpOutputMessage> outputMessage = ArgumentCaptor.forClass(HttpOutputMessage.class);
318 verify(messageConverter).write(eq("body"), eq(MediaType.TEXT_PLAIN), outputMessage.capture());
319 assertTrue(mavContainer.isRequestHandled());
320 assertEquals("headerValue", outputMessage.getValue().getHeaders().get("header").get(0));
321 }
322
323 public ResponseEntity<String> handle1(HttpEntity<String> httpEntity, ResponseEntity<String> responseEntity, int i, RequestEntity<String> requestEntity) {
324 return responseEntity;
325 }
326
327 public HttpEntity<?> handle2(HttpEntity<?> entity) {
328 return entity;
329 }
330
331 public CustomHttpEntity handle2x(HttpEntity<?> entity) {
332 return new CustomHttpEntity();
333 }
334
335 public int handle3() {
336 return 42;
337 }
338
339 @RequestMapping(produces = {"text/html", "application/xhtml+xml"})
340 public ResponseEntity<String> handle4() {
341 return null;
342 }
343
344
345 public static class CustomHttpEntity extends HttpEntity<Object> {
346 }
347
348 }