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