001/**
002 * Copyright 2015 DuraSpace, Inc.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.fcrepo.client;
018
019import static java.net.URI.create;
020import static org.fcrepo.client.FedoraHeaderConstants.CONTENT_TYPE;
021import static org.fcrepo.client.FedoraHeaderConstants.LOCATION;
022import static org.fcrepo.client.TestUtils.RDF_XML;
023import static org.fcrepo.client.TestUtils.SPARQL_UPDATE;
024import static org.fcrepo.client.TestUtils.TEXT_TURTLE;
025import static org.fcrepo.client.TestUtils.baseUrl;
026import static org.fcrepo.client.TestUtils.rdfXml;
027import static org.fcrepo.client.TestUtils.setField;
028import static org.fcrepo.client.TestUtils.sparqlUpdate;
029import static org.junit.Assert.assertEquals;
030import static org.mockito.Matchers.any;
031import static org.mockito.Mockito.when;
032
033import java.io.ByteArrayInputStream;
034import java.io.IOException;
035import java.io.InputStream;
036import java.net.URI;
037
038import org.apache.commons.io.IOUtils;
039import org.apache.http.Header;
040import org.apache.http.HttpEntity;
041import org.apache.http.StatusLine;
042import org.apache.http.client.methods.CloseableHttpResponse;
043import org.apache.http.client.methods.HttpUriRequest;
044import org.apache.http.entity.ByteArrayEntity;
045import org.apache.http.impl.client.CloseableHttpClient;
046import org.apache.http.message.BasicHeader;
047import org.junit.Before;
048import org.junit.Ignore;
049import org.junit.Test;
050import org.junit.runner.RunWith;
051import org.mockito.Mock;
052import org.mockito.runners.MockitoJUnitRunner;
053
054/**
055 * @author acoburn
056 */
057@RunWith(MockitoJUnitRunner.class)
058public class FcrepoClientTest {
059
060    private FcrepoClient testClient;
061
062    @Mock
063    private CloseableHttpClient mockHttpclient;
064
065    @Mock
066    private CloseableHttpResponse mockResponse;
067
068    @Mock
069    private StatusLine mockStatus;
070
071    @Mock
072    private HttpEntity mockEntity;
073
074    @Before
075    public void setUp() throws IOException {
076        testClient = FcrepoClient.client().throwExceptionOnFailure().build();
077        setField(testClient, "httpclient", mockHttpclient);
078    }
079
080    @Test
081    public void testGet() throws IOException, FcrepoOperationFailedException {
082        final int status = 200;
083        final URI uri = create(baseUrl);
084        final ByteArrayEntity entity = new ByteArrayEntity(rdfXml.getBytes());
085        entity.setContentType(RDF_XML);
086
087        doSetupMockRequest(RDF_XML, entity, status);
088
089        final FcrepoResponse response = testClient.get(uri)
090                .accept(RDF_XML)
091                .preferMinimal()
092                .perform();
093
094        assertEquals(response.getUrl(), uri);
095        assertEquals(response.getStatusCode(), status);
096        assertEquals(response.getContentType(), RDF_XML);
097        assertEquals(response.getLocation(), null);
098        assertEquals(IOUtils.toString(response.getBody()), rdfXml);
099    }
100
101    @Test(expected = FcrepoOperationFailedException.class)
102    public void testGetError() throws Exception {
103        final int status = 400;
104        final URI uri = create(baseUrl);
105        final ByteArrayEntity entity = new ByteArrayEntity(rdfXml.getBytes());
106        entity.setContentType(RDF_XML);
107
108        doSetupMockRequest(RDF_XML, entity, status);
109        testClient.get(uri)
110                .accept(RDF_XML)
111                .preferRepresentation()
112                .perform();
113    }
114
115    @Test(expected = FcrepoOperationFailedException.class)
116    public void testGet100() throws Exception {
117        final int status = 100;
118        final URI uri = create(baseUrl);
119        final ByteArrayEntity entity = new ByteArrayEntity(rdfXml.getBytes());
120        entity.setContentType(RDF_XML);
121
122        doSetupMockRequest(RDF_XML, entity, status);
123        testClient.get(uri)
124                .accept(RDF_XML)
125                .perform();
126    }
127
128    @Test
129    public void testGet300() throws Exception {
130        final int status = 300;
131        final URI uri = create(baseUrl);
132        final String redirect = baseUrl + "/bar";
133        final Header linkHeader = new BasicHeader("Link", "<" + redirect + ">; rel=\"describedby\"");
134        final Header contentType = new BasicHeader(CONTENT_TYPE, RDF_XML);
135        final Header[] headers = new Header[] { contentType, linkHeader };
136        final CloseableHttpResponse mockResponse = doSetupMockRequest(RDF_XML, null, status);
137
138        when(mockResponse.getAllHeaders()).thenReturn(headers);
139
140        final FcrepoResponse response = testClient.get(uri)
141                .accept(RDF_XML)
142                .perform();
143
144        assertEquals(response.getUrl(), uri);
145        assertEquals(response.getStatusCode(), status);
146        assertEquals(response.getContentType(), RDF_XML);
147        assertEquals(response.getLocation(), create(redirect));
148        assertEquals(response.getBody(), null);
149    }
150
151    @Test
152    public void testGetNoAccept() throws Exception {
153        final int status = 200;
154        final URI uri = create(baseUrl);
155
156        doSetupMockRequest(RDF_XML, null, status);
157
158        final FcrepoResponse response = testClient.get(uri).perform();
159
160        assertEquals(response.getUrl(), uri);
161        assertEquals(response.getStatusCode(), status);
162        assertEquals(response.getContentType(), RDF_XML);
163        assertEquals(response.getLocation(), null);
164        assertEquals(response.getBody(), null);
165    }
166
167    @Test
168    public void testHead() throws IOException, FcrepoOperationFailedException {
169        final int status = 200;
170        final URI uri = create(baseUrl);
171
172        doSetupMockRequest(TEXT_TURTLE, null, status);
173
174        final FcrepoResponse response = testClient.head(uri).perform();
175
176        assertEquals(response.getUrl(), uri);
177        assertEquals(response.getStatusCode(), status);
178        assertEquals(response.getContentType(), TEXT_TURTLE);
179        assertEquals(response.getLocation(), null);
180        assertEquals(response.getBody(), null);
181    }
182
183    @Test(expected = FcrepoOperationFailedException.class)
184    public void testHeadError() throws IOException, FcrepoOperationFailedException {
185        doSetupMockRequest(TEXT_TURTLE, null, 404);
186        testClient.head(create(baseUrl)).perform();
187    }
188
189    @Test
190    public void testPut() throws IOException, FcrepoOperationFailedException {
191        final int status = 204;
192        final URI uri = create(baseUrl);
193        final InputStream body = new ByteArrayInputStream(rdfXml.getBytes());
194
195        doSetupMockRequest(RDF_XML, null, status);
196
197        final FcrepoResponse response = testClient.put(uri)
198                .body(body, RDF_XML)
199                .perform();
200
201        assertEquals(response.getUrl(), uri);
202        assertEquals(response.getStatusCode(), status);
203        assertEquals(response.getContentType(), RDF_XML);
204        assertEquals(response.getLocation(), null);
205        assertEquals(response.getBody(), null);
206    }
207
208    @Test
209    public void testPutNoBody() throws IOException, FcrepoOperationFailedException {
210        final int status = 204;
211        final URI uri = create(baseUrl);
212
213        doSetupMockRequest(null, null, status);
214
215        final FcrepoResponse response = testClient.put(uri).perform();
216
217        assertEquals(response.getUrl(), uri);
218        assertEquals(response.getStatusCode(), status);
219        assertEquals(response.getContentType(), null);
220        assertEquals(response.getLocation(), null);
221        assertEquals(response.getBody(), null);
222    }
223
224    @Test
225    public void testPutWithResponseBody() throws IOException, FcrepoOperationFailedException {
226        final int status = 201;
227        final URI uri = create(baseUrl);
228
229        doSetupMockRequest(null, new ByteArrayEntity(uri.toString().getBytes()), status);
230
231        final FcrepoResponse response = testClient.put(uri).perform();
232
233        assertEquals(response.getUrl(), uri);
234        assertEquals(response.getStatusCode(), status);
235        assertEquals(response.getContentType(), null);
236        assertEquals(response.getLocation(), null);
237        assertEquals(IOUtils.toString(response.getBody()), uri.toString());
238    }
239
240    @Test(expected = FcrepoOperationFailedException.class)
241    public void testPutError() throws IOException, FcrepoOperationFailedException {
242        final int status = 500;
243        final URI uri = create(baseUrl);
244        final InputStream body = new ByteArrayInputStream(rdfXml.getBytes());
245
246        doSetupMockRequest(RDF_XML, null, status);
247        testClient.put(uri)
248                .body(body, RDF_XML)
249                .perform();
250    }
251
252    @Test
253    public void testDelete() throws IOException, FcrepoOperationFailedException {
254        final int status = 204;
255        final URI uri = create(baseUrl);
256
257        doSetupMockRequest(SPARQL_UPDATE, null, status);
258
259        final FcrepoResponse response = testClient.delete(uri).perform();
260
261        assertEquals(response.getUrl(), uri);
262        assertEquals(response.getStatusCode(), status);
263        assertEquals(response.getContentType(), SPARQL_UPDATE);
264        assertEquals(response.getLocation(), null);
265        assertEquals(response.getBody(), null);
266    }
267
268    @Test
269    public void testDeleteWithResponseBody() throws IOException, FcrepoOperationFailedException {
270        final int status = 204;
271        final URI uri = create(baseUrl);
272        final String responseText = "tombstone found";
273
274        doSetupMockRequest(null, new ByteArrayEntity(responseText.getBytes()), status);
275
276        final FcrepoResponse response = testClient.delete(uri).perform();
277
278        assertEquals(response.getUrl(), uri);
279        assertEquals(response.getStatusCode(), status);
280        assertEquals(response.getContentType(), null);
281        assertEquals(response.getLocation(), null);
282        assertEquals(IOUtils.toString(response.getBody()), responseText);
283    }
284
285    @Test(expected = FcrepoOperationFailedException.class)
286    public void testDeleteError() throws IOException, FcrepoOperationFailedException {
287        final int status = 401;
288        final URI uri = create(baseUrl);
289
290        doSetupMockRequest(SPARQL_UPDATE, null, status);
291        testClient.delete(uri).perform();
292    }
293
294    @Test
295    public void testPatch() throws IOException, FcrepoOperationFailedException {
296        final int status = 204;
297        final URI uri = create(baseUrl);
298        final InputStream body = new ByteArrayInputStream(sparqlUpdate.getBytes());
299
300        doSetupMockRequest(SPARQL_UPDATE, null, status);
301
302        final FcrepoResponse response = testClient.patch(uri)
303                .body(body)
304                .perform();
305
306        assertEquals(response.getUrl(), uri);
307        assertEquals(response.getStatusCode(), status);
308        assertEquals(response.getContentType(), SPARQL_UPDATE);
309        assertEquals(response.getLocation(), null);
310        assertEquals(response.getBody(), null);
311    }
312
313    @Ignore
314    @Test(expected = IllegalArgumentException.class)
315    public void testPatchNoContent() throws IOException, FcrepoOperationFailedException {
316        final int status = 204;
317        final URI uri = create(baseUrl);
318
319        doSetupMockRequest(SPARQL_UPDATE, null, status);
320        testClient.patch(uri).perform();
321    }
322
323    @Test
324    public void testPatchResponseBody() throws IOException, FcrepoOperationFailedException {
325        final int status = 204;
326        final URI uri = create(baseUrl);
327        final String responseText = "Sparql-update response";
328        final InputStream body = new ByteArrayInputStream(sparqlUpdate.getBytes());
329
330        doSetupMockRequest(SPARQL_UPDATE, new ByteArrayEntity(responseText.getBytes()), status);
331
332        final FcrepoResponse response = testClient.patch(uri)
333                .body(body)
334                .perform();
335
336        assertEquals(response.getUrl(), uri);
337        assertEquals(response.getStatusCode(), status);
338        assertEquals(response.getContentType(), SPARQL_UPDATE);
339        assertEquals(IOUtils.toString(response.getBody()), responseText);
340    }
341
342    @Test(expected = FcrepoOperationFailedException.class)
343    public void testPatchError() throws IOException, FcrepoOperationFailedException {
344        final int status = 415;
345        final URI uri = create(baseUrl);
346        final InputStream body = new ByteArrayInputStream(sparqlUpdate.getBytes());
347
348        doSetupMockRequest(SPARQL_UPDATE, null, status);
349        testClient.patch(uri)
350                .body(body)
351                .perform();
352    }
353
354    @Test
355    public void testPost() throws IOException, FcrepoOperationFailedException {
356        final int status = 204;
357        final URI uri = create(baseUrl);
358        final InputStream body = new ByteArrayInputStream(sparqlUpdate.getBytes());
359
360        doSetupMockRequest(SPARQL_UPDATE, null, status);
361
362        final FcrepoResponse response = testClient.post(uri)
363                .body(body, SPARQL_UPDATE)
364                .perform();
365
366        assertEquals(response.getUrl(), uri);
367        assertEquals(response.getStatusCode(), status);
368        assertEquals(response.getContentType(), SPARQL_UPDATE);
369        assertEquals(response.getLocation(), null);
370        assertEquals(response.getBody(), null);
371    }
372
373    @Test
374    public void testPostResponseBody() throws IOException, FcrepoOperationFailedException {
375        final int status = 204;
376        final URI uri = create(baseUrl);
377        final String responseText = baseUrl + "/bar";
378        final InputStream body = new ByteArrayInputStream(sparqlUpdate.getBytes());
379
380        doSetupMockRequest(SPARQL_UPDATE, new ByteArrayEntity(responseText.getBytes()), status);
381
382        final FcrepoResponse response = testClient.post(uri)
383                .body(body, SPARQL_UPDATE)
384                .perform();
385
386        assertEquals(response.getUrl(), uri);
387        assertEquals(response.getStatusCode(), status);
388        assertEquals(response.getContentType(), SPARQL_UPDATE);
389        assertEquals(response.getLocation(), null);
390        assertEquals(IOUtils.toString(response.getBody()), responseText);
391    }
392
393    @Test
394    public void testPostNoBody() throws IOException, FcrepoOperationFailedException {
395        final int status = 204;
396        final URI uri = create(baseUrl);
397        final String responseText = baseUrl + "/bar";
398
399        doSetupMockRequest(null, new ByteArrayEntity(responseText.getBytes()), status);
400
401        final FcrepoResponse response = testClient.post(uri).perform();
402
403        assertEquals(response.getUrl(), uri);
404        assertEquals(response.getStatusCode(), status);
405        assertEquals(response.getContentType(), null);
406        assertEquals(response.getLocation(), null);
407        assertEquals(IOUtils.toString(response.getBody()), responseText);
408    }
409
410    @Test(expected = FcrepoOperationFailedException.class)
411    public void testPostError() throws IOException, FcrepoOperationFailedException {
412        final int status = 415;
413        final URI uri = create(baseUrl);
414        final InputStream body = new ByteArrayInputStream(sparqlUpdate.getBytes());
415
416        doSetupMockRequest(SPARQL_UPDATE, null, status);
417        testClient.post(uri)
418                .body(body, SPARQL_UPDATE)
419                .perform();
420    }
421
422    @Test(expected = IllegalArgumentException.class)
423    public void testPostErrorNullUrl() throws Exception {
424        final int status = 401;
425        final String statusPhrase = "Unauthorized";
426        final String response = "Response error";
427        final InputStream body = new ByteArrayInputStream(sparqlUpdate.getBytes());
428        final ByteArrayEntity responseBody = new ByteArrayEntity(response.getBytes());
429
430        doSetupMockRequest(SPARQL_UPDATE, responseBody, status, statusPhrase);
431
432        when(mockResponse.getAllHeaders()).thenReturn(null);
433
434        testClient.post(null)
435                .body(body, SPARQL_UPDATE)
436                .perform();
437    }
438
439    @Test
440    public void testBadRequest() throws IOException, FcrepoOperationFailedException {
441        final URI uri = create(baseUrl);
442        final InputStream body = new ByteArrayInputStream(sparqlUpdate.getBytes());
443
444        when(mockHttpclient.execute(any(HttpUriRequest.class))).thenThrow(new IOException("Expected error"));
445
446        try {
447            testClient.post(uri)
448                    .body(body, SPARQL_UPDATE)
449                    .perform();
450        } catch (FcrepoOperationFailedException ex) {
451            assertEquals(ex.getUrl(), uri);
452            assertEquals(ex.getStatusText(), "Expected error");
453            assertEquals(ex.getStatusCode(), -1);
454        }
455    }
456
457    @Test
458    public void testBadResponseBody() throws IOException, FcrepoOperationFailedException {
459        final int status = 200;
460        final URI uri = create(baseUrl);
461        final ByteArrayEntity entity = new ByteArrayEntity(rdfXml.getBytes());
462        entity.setContentType(RDF_XML);
463
464        doSetupMockRequest(RDF_XML, entity, status);
465        when(mockResponse.getEntity()).thenReturn(mockEntity);
466        when(mockEntity.getContent()).thenThrow(new IOException("Expected IO error"));
467
468        final FcrepoResponse response = testClient.get(uri)
469                .accept(RDF_XML)
470                .preferMinimal()
471                .perform();
472
473        assertEquals(response.getUrl(), uri);
474        assertEquals(response.getStatusCode(), status);
475        assertEquals(response.getContentType(), RDF_XML);
476        assertEquals(response.getLocation(), null);
477        assertEquals(response.getBody(), null);
478    }
479
480    private CloseableHttpResponse doSetupMockRequest(final String contentType, final ByteArrayEntity entity,
481            final int status) throws IOException {
482        return doSetupMockRequest(contentType, entity, status, null);
483    }
484
485    private CloseableHttpResponse doSetupMockRequest(final String contentType, final ByteArrayEntity entity,
486            final int status, final String statusPhrase) throws IOException {
487        final Header contentTypeHeader = new BasicHeader("Content-Type", contentType);
488        final Header locationHeader = new BasicHeader(LOCATION, null);
489        final Header[] responseHeaders = new Header[] { locationHeader, contentTypeHeader };
490
491        when(mockHttpclient.execute(any(HttpUriRequest.class))).thenReturn(mockResponse);
492        when(mockResponse.getAllHeaders()).thenReturn(responseHeaders);
493        when(mockResponse.getEntity()).thenReturn(entity);
494        when(mockResponse.getStatusLine()).thenReturn(mockStatus);
495        when(mockStatus.getStatusCode()).thenReturn(status);
496        when(mockStatus.getReasonPhrase()).thenReturn(statusPhrase);
497
498        return mockResponse;
499    }
500}