View Javadoc

1   package com.atlassian.httpclient.api;
2   
3   import com.atlassian.util.concurrent.Promise;
4   import com.google.common.base.Function;
5   import com.google.common.collect.ImmutableSet;
6   import com.google.common.util.concurrent.SettableFuture;
7   import org.junit.Assert;
8   import org.junit.Before;
9   import org.junit.Test;
10  import org.junit.runner.RunWith;
11  import org.mockito.Mock;
12  import org.mockito.Mockito;
13  import org.mockito.runners.MockitoJUnitRunner;
14  
15  import javax.annotation.Nullable;
16  import java.util.Set;
17  import java.util.concurrent.atomic.AtomicInteger;
18  
19  import static com.atlassian.httpclient.api.ResponsePromises.toResponsePromise;
20  import static com.atlassian.util.concurrent.Promises.forFuture;
21  import static com.atlassian.util.concurrent.Promises.forListenableFuture;
22  import static com.google.common.collect.Iterables.toArray;
23  import static org.mockito.Mockito.verify;
24  import static org.mockito.Mockito.verifyNoMoreInteractions;
25  import static org.mockito.Mockito.when;
26  
27  @RunWith(MockitoJUnitRunner.class)
28  public final class ResponseTransformationTest {
29      @Mock
30      private Response response;
31  
32      @Mock
33      private Function<Response, Object> informationalFunction;
34  
35      @Mock
36      private Function<Response, Object> successfulFunction;
37  
38      @Mock
39      private Function<Response, Object> okFunction;
40  
41      @Mock
42      private Function<Response, Object> createdFunction;
43  
44      @Mock
45      private Function<Response, Object> noContentFunction;
46  
47      @Mock
48      private Function<Response, Object> redirectionFunction;
49  
50      @Mock
51      private Function<Response, Object> clientErrorFunction;
52  
53      @Mock
54      private Function<Response, Object> notFoundFunction;
55  
56      @Mock
57      private Function<Response, Object> seeOtherFunction;
58  
59      @Mock
60      private Function<Response, Object> notModifiedFunction;
61  
62      @Mock
63      private Function<Response, Object> badRequestFunction;
64  
65      @Mock
66      private Function<Response, Object> unauthorizedFunction;
67  
68      @Mock
69      private Function<Response, Object> forbiddenFunction;
70  
71      @Mock
72      private Function<Response, Object> conflictFunction;
73  
74      @Mock
75      private Function<Response, Object> internalServerErrorFunction;
76  
77      @Mock
78      private Function<Response, Object> serviceUnavailableFunction;
79  
80      @Mock
81      private Function<Response, Object> serverErrorFunction;
82  
83      @Mock
84      private Function<Response, Object> othersFunction;
85  
86      private Set<Function<Response, Object>> allFunctions;
87  
88      private SettableFuture<Response> responseSettableFuture;
89  
90      @Before
91      public void setUp() {
92          responseSettableFuture = SettableFuture.create();
93          allFunctions = ImmutableSet.<Function<Response, Object>>builder()
94                  .add(informationalFunction)
95                  .add(successfulFunction)
96                  .add(okFunction)
97                  .add(noContentFunction)
98                  .add(createdFunction)
99                  .add(redirectionFunction)
100                 .add(clientErrorFunction)
101                 .add(notFoundFunction)
102                 .add(serverErrorFunction)
103                 .add(othersFunction)
104                 .build();
105     }
106 
107     @Test
108     public void testInformationalFunctionCalledOn1xx() {
109         for (int statusCode = HttpStatus.CONTINUE.code; statusCode < HttpStatus.OK.code; statusCode++) {
110             testFunctionCalledForStatus(rangesBuilder(), informationalFunction, statusCode);
111             resetAllMocks();
112         }
113     }
114 
115     @Test
116     public void testInformationalFunctionCalledOn2xx() {
117         for (int statusCode = HttpStatus.OK.code; statusCode < HttpStatus.MULTIPLE_CHOICES.code; statusCode++) {
118             testFunctionCalledForStatus(rangesBuilder(), successfulFunction, statusCode);
119             resetAllMocks();
120         }
121     }
122 
123     @Test
124     public void testOkFunctionCalledOn200() {
125         testFunctionCalledForStatus(okFunction, HttpStatus.OK.code);
126     }
127 
128     @Test
129     public void testCreatedFunctionCalledOn201() {
130         testFunctionCalledForStatus(createdFunction, HttpStatus.CREATED.code);
131     }
132 
133     @Test
134     public void testNoContentFunctionCalledOn204() {
135         testFunctionCalledForStatus(noContentFunction, HttpStatus.NO_CONTENT.code);
136     }
137 
138 
139     @Test
140     public void testInformationalFunctionCalledOn3xx() {
141         for (int statusCode = HttpStatus.MULTIPLE_CHOICES.code; statusCode < HttpStatus.BAD_REQUEST.code; statusCode++) {
142             testFunctionCalledForStatus(rangesBuilder(), redirectionFunction, statusCode);
143             resetAllMocks();
144         }
145     }
146 
147     @Test
148     public void testSeeOtherFunctionCalledOn303() {
149         testFunctionCalledForStatus(seeOtherFunction, HttpStatus.SEE_OTHER.code);
150     }
151 
152     @Test
153     public void testSeeOtherFunctionCalledOn304() {
154         testFunctionCalledForStatus(notModifiedFunction, HttpStatus.NOT_MODIFIED.code);
155     }
156 
157     @Test
158     public void testInformationalFunctionCalledOn4xx() {
159         for (int statusCode = HttpStatus.BAD_REQUEST.code; statusCode < HttpStatus.INTERNAL_SERVER_ERROR.code; statusCode++) {
160             testFunctionCalledForStatus(rangesBuilder(), clientErrorFunction, statusCode);
161             resetAllMocks();
162         }
163     }
164 
165     @Test
166     public void testBadRequestFunctionCalledOn400() {
167         testFunctionCalledForStatus(badRequestFunction, HttpStatus.BAD_REQUEST.code);
168     }
169 
170     @Test
171     public void testBadRequestFunctionCalledOn401() {
172         testFunctionCalledForStatus(unauthorizedFunction, HttpStatus.UNAUTHORIZED.code);
173     }
174 
175     @Test
176     public void testForbiddenFunctionCalledOn403() {
177         testFunctionCalledForStatus(forbiddenFunction, HttpStatus.FORBIDDEN.code);
178     }
179 
180     @Test
181     public void testNotFoundFunctionCalledOn404() {
182         testFunctionCalledForStatus(notFoundFunction, HttpStatus.NOT_FOUND.code);
183     }
184 
185     @Test
186     public void testConflictFunctionCalledOn409() {
187         testFunctionCalledForStatus(conflictFunction, HttpStatus.CONFLICT.code);
188     }
189 
190     @Test
191     public void testInformationalFunctionCalledOn5xx() {
192         for (int statusCode = HttpStatus.INTERNAL_SERVER_ERROR.code; statusCode < 600; statusCode++) {
193             testFunctionCalledForStatus(rangesBuilder(), serverErrorFunction, statusCode);
194             resetAllMocks();
195         }
196     }
197 
198     @Test
199     public void testInternalServerErrorFunctionCalledOn500() {
200         testFunctionCalledForStatus(internalServerErrorFunction, HttpStatus.INTERNAL_SERVER_ERROR.code);
201     }
202 
203     @Test
204     public void testServiceUnavailableFunctionCalledOn503() {
205         testFunctionCalledForStatus(serviceUnavailableFunction, HttpStatus.SERVICE_UNAVAILABLE.code);
206     }
207 
208     @Test(expected = IllegalStateException.class)
209     public void testFailThrowsException() {
210         ResponseTransformation<Object> responseTransformation = newBuilder().fail(new Function<Throwable, Object>() {
211             @Override
212             public Object apply(@Nullable Throwable input) {
213                 throw new IllegalStateException("foo");
214             }
215         }).build();
216         responseSettableFuture.setException(new RuntimeException());
217         Promise<Object> promise = toResponsePromise(forListenableFuture(responseSettableFuture)).transform(responseTransformation);
218         promise.claim();
219     }
220 
221     @Test
222     public void testDelayedExecutionExecutesOnce() {
223         final AtomicInteger counter = new AtomicInteger(0);
224         when(this.response.getStatusCode()).thenReturn(200);
225 
226         ResponsePromise responsePromise = toResponsePromise(forListenableFuture(responseSettableFuture));
227 
228         ResponseTransformation<String> responseTransformation = DefaultResponseTransformation.<String>builder()
229                 .ok(new Function<Response, String>() {
230                     @Override
231                     public String apply(@Nullable Response input) {
232                         return "foo" + counter.getAndIncrement();
233                     }
234                 })
235                 .fail(new Function<Throwable, String>() {
236                     @Override
237                     public String apply(@Nullable Throwable input) {
238                         throw new IllegalStateException();
239                     }
240                 }).build();
241         Promise<String> promise = responsePromise.transform(responseTransformation);
242         responseSettableFuture.set(this.response);
243         Assert.assertEquals("foo0", promise.claim());
244     }
245 
246     @Test
247     public void testDelayedExecutionExecutesDoneOnceWithException() {
248         final AtomicInteger counter = new AtomicInteger(0);
249 
250         when(this.response.getStatusCode()).thenReturn(200);
251 
252         ResponseTransformation<String> responseTransformation = DefaultResponseTransformation.<String>builder()
253                 .ok(new Function<Response, String>() {
254                     @Override
255                     public String apply(@Nullable Response input) {
256                         throw new IllegalArgumentException("foo" + counter.getAndIncrement());
257                     }
258                 })
259                 .fail(new Function<Throwable, String>() {
260                     @Override
261                     public String apply(@Nullable Throwable input) {
262                         return null;
263                     }
264                 }).build();
265         Promise<String> promise = toResponsePromise(forListenableFuture(responseSettableFuture)).transform(responseTransformation);
266 
267         responseSettableFuture.set(this.response);
268         try {
269             promise.claim();
270         } catch (IllegalArgumentException ex) {
271             Assert.assertEquals("foo0", ex.getMessage());
272         }
273     }
274 
275     @Test
276     public void testDelayedExecutionExecutesFailOnce() {
277         final AtomicInteger counter = new AtomicInteger(0);
278         when(this.response.getStatusCode()).thenReturn(200);
279 
280         ResponseTransformation<String> responseTransformation = DefaultResponseTransformation.<String>builder()
281                 .fail(new Function<Throwable, String>() {
282                     @Override
283                     public String apply(@Nullable Throwable input) {
284                         return "foo" + counter.getAndIncrement();
285                     }
286                 })
287                 .ok(new Function<Response, String>() {
288                     @Override
289                     public String apply(@Nullable Response input) {
290                         throw new IllegalStateException();
291                     }
292                 })
293                 .build();
294         responseSettableFuture.set(this.response);
295         Promise<String> promise = toResponsePromise(forListenableFuture(responseSettableFuture)).transform(responseTransformation);
296         Assert.assertEquals("foo0", promise.claim());
297     }
298 
299     @Test
300     public void testNotSuccessfulFunctionCalled() {
301 
302 
303         for (int statusCode = HttpStatus.CONTINUE.code; statusCode < 600; statusCode++) {
304             final ResponseTransformation<Object> transformation = newBuilder()
305                     .notSuccessful(clientErrorFunction)
306                     .others(successfulFunction).build();
307             if (HttpStatus.OK.code <= statusCode && statusCode < HttpStatus.MULTIPLE_CHOICES.code) {
308                 testFunctionCalledForStatus(transformation, successfulFunction, statusCode);
309             } else {
310                 testFunctionCalledForStatus(transformation, clientErrorFunction, statusCode);
311             }
312             resetAllMocks();
313         }
314     }
315 
316     @Test
317     public void testErrorFunctionCalled() {
318 
319         for (int statusCode = HttpStatus.CONTINUE.code; statusCode < 600; statusCode++) {
320             final ResponseTransformation<Object> responseTransformation = newBuilder()
321                     .error(clientErrorFunction)
322                     .others(successfulFunction).build();
323             if (HttpStatus.BAD_REQUEST.code <= statusCode && statusCode < 600) {
324                 testFunctionCalledForStatus(responseTransformation, clientErrorFunction, statusCode);
325             } else {
326                 testFunctionCalledForStatus(responseTransformation, successfulFunction, statusCode);
327             }
328             resetAllMocks();
329         }
330     }
331 
332     @Test
333     public void testOthersFunctionCalled() {
334         testFunctionCalledForStatus(
335                 newBuilder().others(othersFunction).build(),
336                 othersFunction,
337                 HttpStatus.OK.code);
338     }
339 
340     @Test
341     public void testFunctionAddedOnNonStandardHttpStatus() {
342         newBuilder().on(601, new Function<Response, Object>() {
343             @Override
344             public Object apply(@Nullable Response input) {
345                 return null;
346             }
347         });
348     }
349 
350     @Test(expected = IllegalMonitorStateException.class)
351     public void testFailCanThrowExceptions() {
352         responseSettableFuture.setException(new Throwable("Some message"));
353         ResponsePromise responsePromise = toResponsePromise(forListenableFuture(responseSettableFuture));
354         DefaultResponseTransformation.<String>builder()
355                 .ok(new Function<Response, String>() {
356                     @Override
357                     public String apply(Response input) {
358                         return "Ok";
359                     }
360                 })
361                 .fail(new Function<Throwable, String>() {
362                     @Override
363                     public String apply(Throwable input) {
364                         throw new IllegalMonitorStateException();
365                     }
366                 }).build().apply(responsePromise).claim();
367     }
368 
369     @Test(expected = IllegalMonitorStateException.class)
370     public void testFailExceptionsWithoutHandlersAreThrownAtClaim() {
371         responseSettableFuture.setException(new IllegalMonitorStateException("Some message"));
372         newBuilder().build().apply(toResponsePromise(forListenableFuture(responseSettableFuture))).claim();
373     }
374 
375     private void testFunctionCalledForStatus(Function<Response, Object> function, int statusCode) {
376         testFunctionCalledForStatus(
377                 newBuilder()
378                         .informational(informationalFunction)
379                         .ok(okFunction)
380                         .created(createdFunction)
381                         .noContent(noContentFunction)
382                         .notFound(notFoundFunction)
383                         .seeOther(seeOtherFunction)
384                         .notModified(notModifiedFunction)
385                         .badRequest(badRequestFunction)
386                         .unauthorized(unauthorizedFunction)
387                         .forbidden(forbiddenFunction)
388                         .conflict(conflictFunction)
389                         .internalServerError(internalServerErrorFunction)
390                         .serviceUnavailable(serviceUnavailableFunction)
391                         .others(othersFunction).build(),
392                 function,
393                 statusCode);
394     }
395 
396     private void testFunctionCalledForStatus(ResponseTransformation<Object> responseTransformation, Function<Response, Object> function, int statusCode) {
397         when(response.getStatusCode()).thenReturn(statusCode);
398         responseSettableFuture.set(response);
399 
400         responseTransformation.apply(toResponsePromise(forFuture(responseSettableFuture)));
401 
402         verify(function).apply(response);
403         verifyNoMoreInteractions(allFunctionsAsArray());
404     }
405 
406     private ResponseTransformation.Builder<Object> newBuilder() {
407         return DefaultResponseTransformation.builder();
408     }
409 
410     private ResponseTransformation<Object> rangesBuilder() {
411         return newBuilder()
412                 .successful(successfulFunction)
413                 .informational(informationalFunction)
414                 .redirection(redirectionFunction)
415                 .clientError(clientErrorFunction)
416                 .serverError(serverErrorFunction)
417                 .others(othersFunction)
418                 .build();
419     }
420 
421     private Function[] allFunctionsAsArray() {
422         return toArray(allFunctions, Function.class);
423     }
424 
425     private void resetAllMocks() {
426         Mockito.reset(allFunctionsAsArray());
427         responseSettableFuture = SettableFuture.create();
428     }
429 }