001/*
002 * Licensed to DuraSpace under one or more contributor license agreements.
003 * See the NOTICE file distributed with this work for additional information
004 * regarding copyright ownership.
005 *
006 * DuraSpace licenses this file to you under the Apache License,
007 * Version 2.0 (the "License"); you may not use this file except in
008 * compliance with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.fcrepo.client.integration;
019
020import static javax.ws.rs.core.Response.Status.BAD_REQUEST;
021import static javax.ws.rs.core.Response.Status.CONFLICT;
022import static javax.ws.rs.core.Response.Status.CREATED;
023import static javax.ws.rs.core.Response.Status.GONE;
024import static javax.ws.rs.core.Response.Status.NOT_FOUND;
025import static javax.ws.rs.core.Response.Status.NOT_MODIFIED;
026import static javax.ws.rs.core.Response.Status.NO_CONTENT;
027import static javax.ws.rs.core.Response.Status.OK;
028import static javax.ws.rs.core.Response.Status.PARTIAL_CONTENT;
029import static javax.ws.rs.core.Response.Status.PRECONDITION_FAILED;
030import static javax.ws.rs.core.Response.Status.TEMPORARY_REDIRECT;
031import static org.fcrepo.client.FedoraHeaderConstants.CONTENT_DISPOSITION_FILENAME;
032import static org.fcrepo.client.FedoraHeaderConstants.CONTENT_TYPE;
033import static org.fcrepo.client.FedoraHeaderConstants.DIGEST;
034import static org.fcrepo.client.FedoraHeaderConstants.ETAG;
035import static org.fcrepo.client.FedoraHeaderConstants.LAST_MODIFIED;
036import static org.fcrepo.client.FedoraHeaderConstants.STATE_TOKEN;
037import static org.fcrepo.client.FedoraTypes.LDP_DIRECT_CONTAINER;
038import static org.fcrepo.client.TestUtils.TEXT_TURTLE;
039import static org.fcrepo.client.TestUtils.sparqlUpdate;
040import static org.junit.Assert.assertEquals;
041import static org.junit.Assert.assertNotEquals;
042import static org.junit.Assert.assertNotNull;
043import static org.junit.Assert.assertNull;
044import static org.junit.Assert.assertTrue;
045
046import java.io.ByteArrayInputStream;
047import java.io.InputStream;
048import java.net.URI;
049import java.nio.file.Files;
050import java.nio.file.Path;
051import java.util.Calendar;
052import java.util.Date;
053import java.util.HashMap;
054import java.util.Map;
055
056import javax.ws.rs.core.EntityTag;
057
058import org.apache.commons.io.FileUtils;
059import org.apache.commons.io.IOUtils;
060import org.apache.http.client.utils.DateUtils;
061import org.fcrepo.client.ExternalContentHandling;
062import org.fcrepo.client.FcrepoClient;
063import org.fcrepo.client.FcrepoOperationFailedException;
064import org.fcrepo.client.FcrepoResponse;
065import org.fcrepo.client.HeaderHelpers;
066import org.jgroups.util.UUID;
067import org.junit.Before;
068import org.junit.Ignore;
069import org.junit.Test;
070import org.junit.runner.RunWith;
071import org.springframework.test.context.ContextConfiguration;
072import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
073
074/**
075 * @author bbpennel
076 */
077@RunWith(SpringJUnit4ClassRunner.class)
078@ContextConfiguration("/spring-test/test-container.xml")
079public class FcrepoClientIT extends AbstractResourceIT {
080
081    protected URI url;
082
083    final private String UNMODIFIED_DATE = "Mon, 1 Jan 2001 00:00:00 GMT";
084
085    public FcrepoClientIT() throws Exception {
086        super();
087
088        client = FcrepoClient.client()
089                .credentials("fedoraAdmin", "password")
090                .authScope("localhost")
091                .build();
092    }
093
094    @Before
095    public void before() {
096        url = URI.create(serverAddress + UUID.randomUUID().toString());
097    }
098
099    @Test
100    public void testPost() throws Exception {
101        final FcrepoResponse response = client.post(new URI(serverAddress))
102                .perform();
103
104        assertEquals(CREATED.getStatusCode(), response.getStatusCode());
105    }
106
107    @Test
108    public void testPostBinary() throws Exception {
109        final String slug = UUID.randomUUID().toString();
110        final String filename = "hello.txt";
111        final String mimetype = "text/plain";
112        final String bodyContent = "Hello world";
113        final FcrepoResponse response = client.post(new URI(serverAddress))
114                .body(new ByteArrayInputStream(bodyContent.getBytes()), mimetype)
115                .filename(filename)
116                .slug(slug)
117                .perform();
118
119        final String content = IOUtils.toString(response.getBody(), "UTF-8");
120        final int status = response.getStatusCode();
121
122        assertEquals("Didn't get a CREATED response! Got content:\n" + content,
123                CREATED.getStatusCode(), status);
124        assertEquals("Location did not match slug", serverAddress + slug, response.getLocation().toString());
125
126        assertNotNull("Didn't find linked description!", response.getLinkHeaders("describedby").get(0));
127
128        final FcrepoResponse getResponse = client.get(response.getLocation()).perform();
129        final Map<String, String> contentDisp = getResponse.getContentDisposition();
130        assertEquals(filename, contentDisp.get(CONTENT_DISPOSITION_FILENAME));
131
132        assertEquals(mimetype, getResponse.getContentType());
133
134        final String getContent = IOUtils.toString(getResponse.getBody(), "UTF-8");
135        assertEquals(bodyContent, getContent);
136    }
137
138    @Test
139    public void testPostExternalContent() throws Exception {
140        final String filename = "hello.txt";
141        final String mimetype = "text/plain";
142        final String fileContent = "Hello post world";
143
144        final Path contentPath = Files.createTempFile(null, ".txt");
145        FileUtils.write(contentPath.toFile(), fileContent, "UTF-8");
146
147        final FcrepoResponse response = client.post(new URI(serverAddress))
148                .externalContent(contentPath.toUri(), mimetype, ExternalContentHandling.PROXY)
149                .filename(filename)
150                .perform();
151
152        final String content = IOUtils.toString(response.getBody(), "UTF-8");
153        final int status = response.getStatusCode();
154
155        assertEquals("Didn't get a CREATED response! Got content:\n" + content,
156                CREATED.getStatusCode(), status);
157
158        final FcrepoResponse getResponse = client.get(response.getLocation()).perform();
159        final Map<String, String> contentDisp = getResponse.getContentDisposition();
160        assertEquals(filename, contentDisp.get(CONTENT_DISPOSITION_FILENAME));
161
162        assertEquals(mimetype, getResponse.getContentType());
163
164        final String getContent = IOUtils.toString(getResponse.getBody(), "UTF-8");
165        assertEquals(fileContent, getContent);
166    }
167
168    @Test
169    public void testPostDigestMismatch() throws Exception {
170        final String bodyContent = "Hello world";
171        final String invalidDigest = "adc83b19e793491b1c6ea0fd8b46cd9f32e592fc";
172
173        final FcrepoResponse response = client.post(new URI(serverAddress))
174                .body(new ByteArrayInputStream(bodyContent.getBytes()), "text/plain")
175                .digestSha1(invalidDigest)
176                .perform();
177
178        assertEquals("Invalid checksum was not rejected", CONFLICT.getStatusCode(), response.getStatusCode());
179    }
180
181    @Test
182    public void testPostDigestMultipleChecksums() throws Exception {
183        final String bodyContent = "Hello world";
184
185        final FcrepoResponse response = client.post(new URI(serverAddress))
186                .body(new ByteArrayInputStream(bodyContent.getBytes()), "text/plain")
187                .digestMd5("3e25960a79dbc69b674cd4ec67a72c62")
188                .digestSha1("7b502c3a1f48c8609ae212cdfb639dee39673f5e")
189                .digestSha256("64ec88ca00b268e5ba1a35678a1b5316d212f4f366b2477232534a8aeca37f3c")
190                .perform();
191
192        assertEquals("Checksums rejected", CREATED.getStatusCode(), response.getStatusCode());
193    }
194
195    @Test
196    public void testPostDigestMultipleChecksumsOneMismatch() throws Exception {
197        final String bodyContent = "Hello world";
198
199        final FcrepoResponse response = client.post(new URI(serverAddress))
200                .body(new ByteArrayInputStream(bodyContent.getBytes()), "text/plain")
201                .digestMd5("3e25960a79dbc69b674cd4ec67a72c62")
202                .digestSha1("7b502c3a1f48c8609ae212cdfb639dee39673f5e")
203                // Incorrect sha256
204                .digestSha256("123488ca00b268e5ba1a35678a1b5316d212f4f366b2477232534a8aeca37f3c")
205                .perform();
206
207        assertEquals("Invalid checksum was not rejected", CONFLICT.getStatusCode(), response.getStatusCode());
208    }
209
210    @Test
211    public void testPostBinaryNullFilename() throws Exception {
212        final String bodyContent = "Hello world";
213
214        final FcrepoResponse response = client.post(new URI(serverAddress))
215                .body(new ByteArrayInputStream(bodyContent.getBytes()), "text/plain")
216                .filename(null)
217                .perform();
218
219        assertEquals("Empty filename rejected", CREATED.getStatusCode(), response.getStatusCode());
220    }
221
222    @Test
223    public void testPostDirectContainer() throws Exception {
224        final FcrepoResponse response = client.post(new URI(serverAddress))
225                .addInteractionModel(LDP_DIRECT_CONTAINER)
226                .perform();
227
228        final int status = response.getStatusCode();
229
230        assertEquals("Didn't get a CREATED response!", CREATED.getStatusCode(), status);
231
232        final FcrepoResponse getResponse = client.get(response.getLocation()).perform();
233        assertTrue("Did not have ldp:DirectContainer type", getResponse.hasType(LDP_DIRECT_CONTAINER));
234    }
235
236    // FCREPO-3698
237    @Test
238    public void testPostBinaryFilenameSpecialCharacters() throws Exception {
239        final String slug = UUID.randomUUID().toString();
240        final String filename = "hello world\nof_we\0ird:+\tchar/acters\u0001.tx\rt";
241        final String expectedFilename = "hello world of_we ird:+\tchar/acters .tx t";
242        final String mimetype = "text/plain";
243        final String bodyContent = "Hello world";
244        final FcrepoResponse response = client.post(new URI(serverAddress))
245                .body(new ByteArrayInputStream(bodyContent.getBytes()), mimetype)
246                .filename(filename)
247                .slug(slug)
248                .perform();
249
250        final String content = IOUtils.toString(response.getBody(), "UTF-8");
251        final int status = response.getStatusCode();
252
253        assertEquals("Didn't get a CREATED response! Got content:\n" + content,
254                CREATED.getStatusCode(), status);
255        assertEquals("Location did not match slug", serverAddress + slug, response.getLocation().toString());
256
257        assertNotNull("Didn't find linked description!", response.getLinkHeaders("describedby").get(0));
258
259        final FcrepoResponse getResponse = client.get(response.getLocation()).perform();
260        final Map<String, String> contentDisp = getResponse.getContentDisposition();
261        assertEquals(expectedFilename, contentDisp.get(CONTENT_DISPOSITION_FILENAME));
262
263        assertEquals(mimetype, getResponse.getContentType());
264
265        final String getContent = IOUtils.toString(getResponse.getBody(), "UTF-8");
266        assertEquals(bodyContent, getContent);
267    }
268
269    @Test
270    public void testPutBinaryFilenameSpecialCharacters() throws Exception {
271        final String id = UUID.randomUUID().toString();
272        final String filename = "hello world\nof_we\0ird:+\tchar/acters\u0001.tx\rt";
273        final String expectedFilename = "hello world of_we ird:+\tchar/acters .tx t";
274        final String mimetype = "text/plain";
275        final String bodyContent = "Hello world";
276        final FcrepoResponse response = client.put(new URI(serverAddress + id))
277                .body(new ByteArrayInputStream(bodyContent.getBytes()), mimetype)
278                .filename(filename)
279                .perform();
280
281        final String content = IOUtils.toString(response.getBody(), "UTF-8");
282        final int status = response.getStatusCode();
283
284        assertEquals("Didn't get a CREATED response! Got content:\n" + content,
285                CREATED.getStatusCode(), status);
286        assertEquals("Location did not match slug", serverAddress + id, response.getLocation().toString());
287
288        assertNotNull("Didn't find linked description!", response.getLinkHeaders("describedby").get(0));
289
290        final FcrepoResponse getResponse = client.get(response.getLocation()).perform();
291        final Map<String, String> contentDisp = getResponse.getContentDisposition();
292        assertEquals(expectedFilename, contentDisp.get(CONTENT_DISPOSITION_FILENAME));
293
294        assertEquals(mimetype, getResponse.getContentType());
295
296        final String getContent = IOUtils.toString(getResponse.getBody(), "UTF-8");
297        assertEquals(bodyContent, getContent);
298    }
299
300    @Test
301    public void testPut() throws Exception {
302        final FcrepoResponse response = create();
303
304        assertEquals(CREATED.getStatusCode(), response.getStatusCode());
305        assertEquals(url, response.getLocation());
306    }
307
308    @Test
309    public void testPutEtag() throws Exception {
310        // Create object
311        final FcrepoResponse response = create();
312
313        // Get the etag of the nearly created object
314        final EntityTag etag = EntityTag.valueOf(response.getHeaderValue(ETAG));
315
316        // Retrieve the body of the resource so we can modify it
317        String body = getTurtle(url);
318        body += "\n<> <http://purl.org/dc/elements/1.1/title> \"some-title\"";
319
320        // Check that etag is making it through and being rejected
321        final FcrepoResponse updateResp = client.put(url)
322                .body(new ByteArrayInputStream(body.getBytes()), TEXT_TURTLE)
323                .ifMatch("\"bad-etag\"")
324                .perform();
325
326        assertEquals(PRECONDITION_FAILED.getStatusCode(), updateResp.getStatusCode());
327
328        // Verify that etag is retrieved and resubmitted correctly
329        final FcrepoResponse validResp = client.put(url)
330                .body(new ByteArrayInputStream(body.getBytes()), TEXT_TURTLE)
331                .ifMatch("\"" + etag.getValue() + "\"")
332                .perform();
333        assertEquals(NO_CONTENT.getStatusCode(), validResp.getStatusCode());
334    }
335
336    @Test
337    public void testPutUnmodifiedSince() throws Exception {
338        // Create object
339        final FcrepoResponse response = create();
340
341        // Retrieve the body of the resource so we can modify it
342        String body = getTurtle(url);
343        body += "\n<> <http://purl.org/dc/elements/1.1/title> \"some-title\"";
344
345        // Update the body the first time, which should succeed
346        final String originalModified = response.getHeaderValue(LAST_MODIFIED);
347        final FcrepoResponse matchResponse = client.put(url)
348                .body(new ByteArrayInputStream(body.getBytes()), TEXT_TURTLE)
349                .ifUnmodifiedSince(originalModified)
350                .perform();
351
352        assertEquals(NO_CONTENT.getStatusCode(), matchResponse.getStatusCode());
353
354        // Update the triples a second time with old timestamp
355        final FcrepoResponse mismatchResponse = client.put(url)
356                .body(new ByteArrayInputStream(body.getBytes()), TEXT_TURTLE)
357                .ifUnmodifiedSince(UNMODIFIED_DATE)
358                .perform();
359
360        assertEquals(PRECONDITION_FAILED.getStatusCode(), mismatchResponse.getStatusCode());
361    }
362
363    @Test
364    public void testPutLenient() throws Exception {
365        // Create object
366        create();
367        final String body = "<> <http://purl.org/dc/elements/1.1/title> \"some-title\"";
368
369        // try to update without lenient header
370        final FcrepoResponse strictResponse = client.put(url)
371                .body(new ByteArrayInputStream(body.getBytes()), TEXT_TURTLE)
372                .perform();
373        assertEquals(CONFLICT.getStatusCode(), strictResponse.getStatusCode());
374
375        // try again with lenient header
376        final FcrepoResponse lenientResponse = client.put(url)
377                .body(new ByteArrayInputStream(body.getBytes()), TEXT_TURTLE)
378                .preferLenient()
379                .perform();
380        assertEquals(NO_CONTENT.getStatusCode(), lenientResponse.getStatusCode());
381    }
382
383    @Test
384    public void testPutExternalContent() throws Exception {
385        final String filename = "put.txt";
386        final String mimetype = "text/plain";
387        final String fileContent = "Hello put world";
388
389        final Path contentPath = Files.createTempFile(null, ".txt");
390        FileUtils.write(contentPath.toFile(), fileContent, "UTF-8");
391
392        final FcrepoResponse response = client.put(url)
393                .externalContent(contentPath.toUri(), mimetype, ExternalContentHandling.PROXY)
394                .filename(filename)
395                .perform();
396
397        final String content = IOUtils.toString(response.getBody(), "UTF-8");
398        final int status = response.getStatusCode();
399
400        assertEquals("Didn't get a CREATED response! Got content:\n" + content,
401                CREATED.getStatusCode(), status);
402
403        final FcrepoResponse getResponse = client.get(response.getLocation()).perform();
404        final Map<String, String> contentDisp = getResponse.getContentDisposition();
405        assertEquals(filename, contentDisp.get(CONTENT_DISPOSITION_FILENAME));
406
407        assertEquals(mimetype, getResponse.getContentType());
408
409        final String getContent = IOUtils.toString(getResponse.getBody(), "UTF-8");
410        assertEquals(fileContent, getContent);
411    }
412
413    @Test
414    public void testPutBinaryNullFilename() throws Exception {
415        final String bodyContent = "Hello world";
416
417        final FcrepoResponse response = client.put(url)
418                .body(new ByteArrayInputStream(bodyContent.getBytes()), "text/plain")
419                .filename(null)
420                .perform();
421
422        assertEquals("Empty filename rejected", CREATED.getStatusCode(), response.getStatusCode());
423    }
424
425    @Test
426    public void testPatch() throws Exception {
427        // Create object
428        create();
429
430        final InputStream body = new ByteArrayInputStream(sparqlUpdate.getBytes());
431
432        // Update triples with sparql update
433        final FcrepoResponse response = client.patch(url)
434                .body(body)
435                .perform();
436
437        assertEquals(NO_CONTENT.getStatusCode(), response.getStatusCode());
438    }
439
440    @Test
441    public void testPatchEtagUpdated() throws Exception {
442        // Create object
443        final FcrepoResponse createResp = create();
444        final EntityTag createdEtag = EntityTag.valueOf(createResp.getHeaderValue(ETAG));
445
446        final InputStream body = new ByteArrayInputStream(sparqlUpdate.getBytes());
447
448        // Update triples with sparql update
449        final FcrepoResponse response = client.patch(url)
450                .body(body)
451                .ifMatch("\"" + createdEtag.getValue() + "\"")
452                .perform();
453
454        final EntityTag updateEtag = EntityTag.valueOf(response.getHeaderValue(ETAG));
455
456        assertEquals(NO_CONTENT.getStatusCode(), response.getStatusCode());
457        assertNotEquals("Etag did not change after patch", createdEtag, updateEtag);
458    }
459
460    @Test
461    public void testPatchNoBody() throws Exception {
462        create();
463
464        final FcrepoResponse response = client.patch(url)
465                .perform();
466
467        assertEquals(BAD_REQUEST.getStatusCode(), response.getStatusCode());
468    }
469
470    @Test
471    public void testDelete() throws Exception {
472        create();
473
474        final FcrepoResponse response = client.delete(url).perform();
475
476        assertEquals(NO_CONTENT.getStatusCode(), response.getStatusCode());
477
478        assertEquals(GONE.getStatusCode(), client.get(url).perform().getStatusCode());
479    }
480
481    @Test
482    public void testGet() throws Exception {
483        create();
484        final FcrepoResponse response = client.get(url).perform();
485
486        assertEquals(OK.getStatusCode(), response.getStatusCode());
487    }
488
489    @Test
490    public void testGetNotFound() throws Exception {
491        final FcrepoResponse response = client.get(url).perform();
492
493        assertEquals(NOT_FOUND.getStatusCode(), response.getStatusCode());
494    }
495
496    @Test
497    public void testGetUnmodified() throws Exception {
498        // Check that get returns a 304 if the item hasn't changed according to last-modified
499        final FcrepoResponse response = create();
500
501        // Get tomorrows date to provide as the modified-since date
502        final String lastModified = response.getHeaderValue(LAST_MODIFIED);
503        final Date modDate = DateUtils.parseDate(lastModified);
504        final Calendar cal = Calendar.getInstance();
505        cal.setTime(modDate);
506        cal.add(Calendar.DATE, 1);
507
508        final FcrepoResponse modResp = client.get(url)
509                .ifModifiedSince(DateUtils.formatDate(cal.getTime()))
510                .perform();
511
512        assertEquals(NOT_MODIFIED.getStatusCode(), modResp.getStatusCode());
513        assertNull("Response body should not be returned when unmodified", modResp.getBody());
514    }
515
516    @Test
517    public void testGetModified() throws Exception {
518        // Check that get returns a 200 if the item has changed according to last-modified
519        final FcrepoResponse response = create();
520
521        // Get yesterdays date to provide as the modified-since date
522        final String lastModified = response.getHeaderValue(LAST_MODIFIED);
523        final Date modDate = DateUtils.parseDate(lastModified);
524        final Calendar cal = Calendar.getInstance();
525        cal.setTime(modDate);
526        cal.add(Calendar.DATE, -1);
527
528        final FcrepoResponse modResp = client.get(url)
529                .ifModifiedSince(DateUtils.formatDate(cal.getTime()))
530                .perform();
531
532        assertEquals(OK.getStatusCode(), modResp.getStatusCode());
533        assertNotNull("GET response body should be normal when modified", modResp.getBody());
534    }
535
536    @Test
537    public void testGetAccept() throws Exception {
538        // Check that get returns a 304 if the item hasn't changed according to last-modified/etag
539        create();
540
541        final FcrepoResponse response = client.get(url)
542                .accept("application/n-triples")
543                .perform();
544
545        assertEquals("application/n-triples", response.getHeaderValue(CONTENT_TYPE));
546        assertEquals(OK.getStatusCode(), response.getStatusCode());
547    }
548
549    @Test
550    public void testGetPrefer() throws Exception {
551        // Check that get returns a 304 if the item hasn't changed according to last-modified/etag
552        create();
553
554        final FcrepoResponse response = client.get(url)
555                .preferMinimal()
556                .perform();
557
558        assertEquals(OK.getStatusCode(), response.getStatusCode());
559        assertEquals("return=minimal", response.getHeaderValue("Preference-Applied"));
560    }
561
562    @Test
563    public void testGetRange() throws Exception {
564        // Creating a binary for retrieval
565        final String mimetype = "text/plain";
566        final String bodyContent = "Hello world";
567        final FcrepoResponse response = client.post(new URI(serverAddress))
568                .body(new ByteArrayInputStream(bodyContent.getBytes()), mimetype)
569                .perform();
570
571        final URI url = response.getLocation();
572
573        // Get the content of the object after the first 6 bytes
574        final FcrepoResponse rangeResp = client.get(url)
575                .range(6L, null)
576                .perform();
577
578        final String content = IOUtils.toString(rangeResp.getBody(), "UTF-8");
579        assertEquals("Body did not contain correct range of original content", "world", content);
580        assertEquals(PARTIAL_CONTENT.getStatusCode(), rangeResp.getStatusCode());
581    }
582
583    @Test
584    public void testGetDisableRedirects() throws Exception {
585        final String filename = "example.html";
586        final String mimetype = "text/plain";
587
588        final URI externalURI = URI.create("http://example.com/");
589
590        final FcrepoResponse response = client.post(new URI(serverAddress))
591                .externalContent(externalURI, mimetype, ExternalContentHandling.REDIRECT)
592                .filename(filename)
593                .perform();
594
595        final FcrepoResponse getResponse = client.get(response.getLocation())
596                .disableRedirects()
597                .perform();
598        assertEquals("Didn't get a REDIRECT response!",
599                TEMPORARY_REDIRECT.getStatusCode(), getResponse.getStatusCode());
600        assertEquals(mimetype, getResponse.getContentType());
601
602        final Map<String, String> contentDisp = getResponse.getContentDisposition();
603        assertEquals(filename, contentDisp.get(CONTENT_DISPOSITION_FILENAME));
604
605        assertEquals(externalURI, getResponse.getLocation());
606    }
607
608    @Test
609    public void testGetWantDigest() throws Exception {
610        // Creating a binary for retrieval
611        final String mimetype = "text/plain";
612        final String bodyContent = "Hello world";
613        final FcrepoResponse response = client.post(new URI(serverAddress))
614                .body(new ByteArrayInputStream(bodyContent.getBytes()), mimetype)
615                .perform();
616
617        final URI url = response.getLocation();
618
619        // Request md5 digest with caching disabled
620        final FcrepoResponse getResp = client.get(url)
621                .wantDigest("md5")
622                .noCache()
623                .perform();
624        assertEquals(OK.getStatusCode(), getResp.getStatusCode());
625
626        final String digest = getResp.getHeaderValue(DIGEST);
627        assertTrue("Did not contain md5", digest.contains("md5=3e25960a79dbc69b674cd4ec67a72c62"));
628    }
629
630    @Test
631    public void testHead() throws Exception {
632        final FcrepoResponse response = create();
633        final FcrepoResponse headResp = client.head(url).perform();
634
635        assertEquals(OK.getStatusCode(), headResp.getStatusCode());
636        assertEquals(response.getHeaderValue(ETAG), headResp.getHeaderValue(ETAG));
637        assertNotNull(headResp.getHeaderValue("Allow"));
638    }
639
640    @Test
641    public void testHeadWantDigest() throws Exception {
642        // Creating a binary for retrieval
643        final String mimetype = "text/plain";
644        final String bodyContent = "Hello world";
645        final FcrepoResponse response = client.post(new URI(serverAddress))
646                .body(new ByteArrayInputStream(bodyContent.getBytes()), mimetype)
647                .perform();
648
649        final URI url = response.getLocation();
650
651        final Map<String, Double> qualityMap = new HashMap<>();
652        qualityMap.put("md5", 1.0);
653        qualityMap.put("sha", 1.0);
654        qualityMap.put("sha-256", 0.4);
655
656        final String wantDigest = HeaderHelpers.formatQualityValues(qualityMap);
657        // Request multiple digests
658        final FcrepoResponse getResp = client.head(url)
659                .wantDigest(wantDigest)
660                .perform();
661        assertEquals(OK.getStatusCode(), getResp.getStatusCode());
662
663        final String digest = getResp.getHeaderValue(DIGEST);
664        assertTrue("Did not contain md5", digest.contains("md5=3e25960a79dbc69b674cd4ec67a72c62"));
665        assertTrue("Did not contain sha1", digest.contains("sha=7b502c3a1f48c8609ae212cdfb639dee39673f5e"));
666        assertTrue("Did not contain sha256", digest
667                .contains("sha-256=64ec88ca00b268e5ba1a35678a1b5316d212f4f366b2477232534a8aeca37f3c"));
668    }
669
670    @Test
671    public void testOptions() throws Exception {
672        create();
673        final FcrepoResponse headResp = client.options(url).perform();
674
675        assertEquals(OK.getStatusCode(), headResp.getStatusCode());
676        assertNotNull(headResp.getHeaderValue("Allow"));
677        assertNotNull(headResp.getHeaderValue("Accept-Post"));
678        assertNotNull(headResp.getHeaderValue("Accept-Patch"));
679    }
680
681    @Test
682    public void testMove() throws Exception {
683        create();
684
685        final URI destUrl = new URI(url.toString() + "_dest");
686        final FcrepoResponse moveResp = client.move(url, destUrl).perform();
687        assertEquals(CREATED.getStatusCode(), moveResp.getStatusCode());
688
689        assertEquals("Object still at original url",
690                GONE.getStatusCode(), client.get(url).perform().getStatusCode());
691
692        assertEquals("Object not at expected new url",
693                OK.getStatusCode(), client.get(destUrl).perform().getStatusCode());
694    }
695
696    @Test
697    public void testCopy() throws Exception {
698        create();
699
700        // Add something identifiable to the record
701        final InputStream body = new ByteArrayInputStream(sparqlUpdate.getBytes());
702        client.patch(url).body(body).perform();
703
704        final URI destUrl = new URI(url.toString() + "_dest");
705        final FcrepoResponse copyResp = client.copy(url, destUrl).perform();
706        assertEquals(CREATED.getStatusCode(), copyResp.getStatusCode());
707
708        final FcrepoResponse originalResp = client.get(url).perform();
709        final String originalContent = IOUtils.toString(originalResp.getBody(), "UTF-8");
710        assertTrue(originalContent.contains("Foo"));
711
712        final FcrepoResponse destResp = client.get(destUrl).perform();
713        final String destContent = IOUtils.toString(destResp.getBody(), "UTF-8");
714        assertTrue(destContent.contains("Foo"));
715    }
716
717    @Ignore("Pending state token implementation in fcrepo")
718    @Test
719    public void testStateTokens() throws Exception {
720        create();
721
722        final FcrepoResponse getResp = client.get(url).perform();
723        final String stateToken = getResp.getHeaderValue(STATE_TOKEN);
724
725        // Attempt to update triples with an incorrect state token
726        final InputStream body = new ByteArrayInputStream(sparqlUpdate.getBytes());
727        final FcrepoResponse badTokenResp = client.patch(url)
728                .body(body)
729                .ifStateToken("bad_state_token")
730                .perform();
731        assertEquals(PRECONDITION_FAILED.getStatusCode(), badTokenResp.getStatusCode());
732
733        // Update triples with the correct state token
734        body.reset();
735        final FcrepoResponse goodTokenResp = client.patch(url)
736                .body(body)
737                .ifStateToken(stateToken)
738                .perform();
739        assertEquals(NO_CONTENT.getStatusCode(), goodTokenResp.getStatusCode());
740    }
741
742    private FcrepoResponse create() throws FcrepoOperationFailedException {
743        return client.put(url).perform();
744    }
745
746    private String getTurtle(final URI url) throws Exception {
747        final FcrepoResponse getResponse = client.get(url)
748                .accept("text/turtle")
749                .perform();
750        return IOUtils.toString(getResponse.getBody(), "UTF-8");
751    }
752}