@Test
  public void testCopyObjectOperations() throws Exception {
    String sourceContainer = getContainerName();
    String sourceObject = "original.txt";
    String sourcePath = "/" + sourceContainer + "/" + sourceObject;
    String badSource = "badsource";
    String destinationContainer = getContainerName();
    String destinationObject = "copy.txt";
    String destinationPath = "/" + destinationContainer + "/" + destinationObject;
    String badDestination = "baddestination";
    String data = "Hello World";
    SwiftObject sourceSwiftObject = newSwiftObject(data, sourceObject);

    getApi().putObject(sourceContainer, sourceSwiftObject);

    // test that not giving a destination name *doesn't* copy source name to the destination
    // container with
    // the source name but copy still returns success :(
    assertTrue(getApi().copyObject(sourceContainer, sourceObject, destinationContainer, ""));
    assertFalse(getApi().objectExists(destinationContainer, sourceObject));

    // test copy works
    assertTrue(
        getApi()
            .copyObject(sourceContainer, sourceObject, destinationContainer, destinationObject));
    assertTrue(getApi().objectExists(destinationContainer, destinationObject));

    SwiftObject destinationSwiftObject =
        getApi().getObject(destinationContainer, destinationObject);
    assertEquals(Strings2.toString(destinationSwiftObject.getPayload()), data);

    // test exception thrown on bad destination container
    try {
      assertFalse(
          getApi().copyObject(sourceContainer, sourceObject, badDestination, destinationObject));
      fail("Expected CopyObjectException");
    } catch (CopyObjectException e) {
      assertEquals(e.getSourcePath(), sourcePath);
      assertEquals(e.getDestinationPath(), "/" + badDestination + "/" + destinationObject);
    }

    // test exception thrown on bad source container
    try {
      assertFalse(
          getApi().copyObject(badSource, sourceObject, destinationContainer, destinationObject));
      fail("Expected CopyObjectException");
    } catch (CopyObjectException e) {
      assertEquals(e.getSourcePath(), "/" + badSource + "/" + sourceObject);
      assertEquals(e.getDestinationPath(), destinationPath);
    }

    // test exception thrown on bad source name
    try {
      assertFalse(
          getApi().copyObject(sourceContainer, badSource, destinationContainer, destinationObject));
      fail("Expected CopyObjectException");
    } catch (CopyObjectException e) {
      assertEquals(e.getSourcePath(), "/" + sourceContainer + "/" + badSource);
      assertEquals(e.getDestinationPath(), destinationPath);
    }
  }
 protected void testGetObjectContentType(SwiftObject getBlob) {
   String contentType = getBlob.getPayload().getContentMetadata().getContentType();
   assert contentType.startsWith("text/plain")
           || "application/x-www-form-urlencoded".equals(contentType)
       : contentType;
 }
  @Test
  public void testObjectOperations() throws Exception {
    String containerName = getContainerName();
    try {
      // Test PUT with string data, ETag hash, and a piece of metadata
      String data = "Here is my data";
      String key = "object";
      SwiftObject object = newSwiftObject(data, key);
      byte[] md5 = object.getPayload().getContentMetadata().getContentMD5();
      String newEtag = getApi().putObject(containerName, object);
      assert newEtag != null;

      assertEquals(
          base16().lowerCase().encode(md5),
          base16().lowerCase().encode(object.getPayload().getContentMetadata().getContentMD5()));

      // Test HEAD of missing object
      assert getApi().getObjectInfo(containerName, "non-existent-object") == null;

      // Test HEAD of object
      MutableObjectInfoWithMetadata metadata =
          getApi().getObjectInfo(containerName, object.getInfo().getName());
      assertEquals(metadata.getName(), object.getInfo().getName());

      assertEquals(metadata.getBytes(), Long.valueOf(data.length()));
      assert metadata.getContentType().startsWith("text/plain") : metadata.getContentType();

      assertEquals(
          base16().lowerCase().encode(md5), base16().lowerCase().encode(metadata.getHash()));
      assertEquals(metadata.getHash(), base16().lowerCase().decode(newEtag));
      assertEquals(metadata.getMetadata().entrySet().size(), 1);
      assertEquals(metadata.getMetadata().get("metadata"), "metadata-value");

      // // Test POST to update object's metadata
      Map<String, String> userMetadata = Maps.newHashMap();
      userMetadata.put("New-Metadata-1", "value-1");
      userMetadata.put("New-Metadata-2", "value-2");
      assertTrue(getApi().setObjectInfo(containerName, object.getInfo().getName(), userMetadata));

      // Test GET of missing object
      assert getApi().getObject(containerName, "non-existent-object") == null;
      // Test GET of object (including updated metadata)
      SwiftObject getBlob = getApi().getObject(containerName, object.getInfo().getName());
      assertEquals(Strings2.toString(getBlob.getPayload()), data);
      // TODO assertEquals(getBlob.getName(),
      // object.getMetadata().getName());
      assertEquals(getBlob.getInfo().getBytes(), Long.valueOf(data.length()));
      testGetObjectContentType(getBlob);
      assertEquals(
          base16().lowerCase().encode(md5),
          base16().lowerCase().encode(getBlob.getInfo().getHash()));
      assertEquals(base16().lowerCase().decode(newEtag), getBlob.getInfo().getHash());
      assertEquals(getBlob.getInfo().getMetadata().entrySet().size(), 2);
      assertEquals(getBlob.getInfo().getMetadata().get("new-metadata-1"), "value-1");
      assertEquals(getBlob.getInfo().getMetadata().get("new-metadata-2"), "value-2");

      // Test PUT with invalid ETag (as if object's data was corrupted in
      // transit)
      String correctEtag = newEtag;
      String incorrectEtag = "0" + correctEtag.substring(1);
      object.getInfo().setHash(base16().lowerCase().decode(incorrectEtag));
      try {
        getApi().putObject(containerName, object);
      } catch (HttpResponseException e) {
        assertEquals(e.getResponse().getStatusCode(), 422);
      }

      // Test PUT chunked/streamed upload with data of "unknown" length
      ByteArrayInputStream bais = new ByteArrayInputStream(data.getBytes(Charsets.UTF_8));
      SwiftObject blob = getApi().newSwiftObject();
      blob.getInfo().setName("chunked-object");
      blob.setPayload(bais);
      newEtag = getApi().putObject(containerName, blob);
      assertEquals(
          base16().lowerCase().encode(md5),
          base16().lowerCase().encode(getBlob.getInfo().getHash()));

      // Test GET with options
      // Non-matching ETag
      try {
        getApi()
            .getObject(
                containerName,
                object.getInfo().getName(),
                GetOptions.Builder.ifETagDoesntMatch(newEtag));
      } catch (HttpResponseException e) {
        assertEquals(e.getResponse().getStatusCode(), 304);
      }

      // Matching ETag
      getBlob =
          getApi()
              .getObject(
                  containerName,
                  object.getInfo().getName(),
                  GetOptions.Builder.ifETagMatches(newEtag));
      assertEquals(getBlob.getInfo().getHash(), base16().lowerCase().decode(newEtag));
      getBlob =
          getApi()
              .getObject(containerName, object.getInfo().getName(), GetOptions.Builder.startAt(8));
      assertEquals(Strings2.toString(getBlob.getPayload()), data.substring(8));

    } finally {
      returnContainer(containerName);
    }
  }