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