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