You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jclouds.apache.org by na...@apache.org on 2014/02/05 10:40:20 UTC

[2/4] Added the DigitalOcean provider

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/224b2828/digitalocean/src/test/java/org/jclouds/digitalocean/DigitalOceanProviderMetadataTest.java
----------------------------------------------------------------------
diff --git a/digitalocean/src/test/java/org/jclouds/digitalocean/DigitalOceanProviderMetadataTest.java b/digitalocean/src/test/java/org/jclouds/digitalocean/DigitalOceanProviderMetadataTest.java
new file mode 100644
index 0000000..1164f53
--- /dev/null
+++ b/digitalocean/src/test/java/org/jclouds/digitalocean/DigitalOceanProviderMetadataTest.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.digitalocean;
+
+import org.jclouds.providers.internal.BaseProviderMetadataTest;
+import org.testng.annotations.Test;
+
+/**
+ * Unit tests for the {@link DigitalOceanApiMetadata}.
+ * 
+ * @author Sergi Castro
+ * @author Ignasi Barrera
+ */
+@Test(groups = "unit", testName = "DigitalOceanProviderMetadataTest")
+public class DigitalOceanProviderMetadataTest extends BaseProviderMetadataTest {
+
+   public DigitalOceanProviderMetadataTest() {
+      super(new DigitalOceanProviderMetadata(), new DigitalOceanApiMetadata());
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/224b2828/digitalocean/src/test/java/org/jclouds/digitalocean/compute/DigitalOceanComputeServiceLiveTest.java
----------------------------------------------------------------------
diff --git a/digitalocean/src/test/java/org/jclouds/digitalocean/compute/DigitalOceanComputeServiceLiveTest.java b/digitalocean/src/test/java/org/jclouds/digitalocean/compute/DigitalOceanComputeServiceLiveTest.java
new file mode 100644
index 0000000..96f2da9
--- /dev/null
+++ b/digitalocean/src/test/java/org/jclouds/digitalocean/compute/DigitalOceanComputeServiceLiveTest.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.digitalocean.compute;
+
+import org.jclouds.compute.ComputeService;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.internal.BaseComputeServiceLiveTest;
+import org.jclouds.sshj.config.SshjSshClientModule;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.inject.Module;
+
+/**
+ * Live tests for the {@link ComputeService} integration.
+ * 
+ * @author Sergi Castro
+ * @author Ignasi Barrera
+ */
+@Test(groups = "live", singleThreaded = true, testName = "DigitalOceanComputeServiceLiveTest")
+public class DigitalOceanComputeServiceLiveTest extends BaseComputeServiceLiveTest {
+
+   public DigitalOceanComputeServiceLiveTest() {
+      provider = "digitalocean";
+   }
+
+   @Override
+   protected Module getSshModule() {
+      return new SshjSshClientModule();
+   }
+
+   @Override
+   public void testOptionToNotBlock() throws Exception {
+      // DigitalOcean ComputeService implementation has to block until the node
+      // is provisioned, to be able to return it.
+   }
+
+   @Override
+   protected void checkTagsInNodeEquals(NodeMetadata node, ImmutableSet<String> tags) {
+      // DigitalOcean does not support tags
+   }
+
+   @Override
+   protected void checkUserMetadataContains(NodeMetadata node, ImmutableMap<String, String> userMetadata) {
+      // DigitalOcean does not support user metadata
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/224b2828/digitalocean/src/test/java/org/jclouds/digitalocean/compute/DigitalOceanImageExtensionLiveTest.java
----------------------------------------------------------------------
diff --git a/digitalocean/src/test/java/org/jclouds/digitalocean/compute/DigitalOceanImageExtensionLiveTest.java b/digitalocean/src/test/java/org/jclouds/digitalocean/compute/DigitalOceanImageExtensionLiveTest.java
new file mode 100644
index 0000000..e9c7efe
--- /dev/null
+++ b/digitalocean/src/test/java/org/jclouds/digitalocean/compute/DigitalOceanImageExtensionLiveTest.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.digitalocean.compute;
+
+import org.jclouds.compute.extensions.ImageExtension;
+import org.jclouds.compute.extensions.internal.BaseImageExtensionLiveTest;
+import org.jclouds.sshj.config.SshjSshClientModule;
+import org.testng.annotations.Test;
+
+import com.google.inject.Module;
+
+/**
+ * Live tests for the {@link ImageExtension} integration.
+ * 
+ * @author Sergi Castro
+ * @author Ignasi Barrera
+ */
+@Test(groups = "live", singleThreaded = true, testName = "DigitalOceanImageExtensionLiveTest")
+public class DigitalOceanImageExtensionLiveTest extends BaseImageExtensionLiveTest {
+
+   public DigitalOceanImageExtensionLiveTest() {
+      provider = "digitalocean";
+   }
+
+   @Override
+   protected Module getSshModule() {
+      return new SshjSshClientModule();
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/224b2828/digitalocean/src/test/java/org/jclouds/digitalocean/domain/OperatingSystemTest.java
----------------------------------------------------------------------
diff --git a/digitalocean/src/test/java/org/jclouds/digitalocean/domain/OperatingSystemTest.java b/digitalocean/src/test/java/org/jclouds/digitalocean/domain/OperatingSystemTest.java
new file mode 100644
index 0000000..f093f36
--- /dev/null
+++ b/digitalocean/src/test/java/org/jclouds/digitalocean/domain/OperatingSystemTest.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.digitalocean.domain;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+import org.testng.annotations.Test;
+
+/**
+ * Unit tests for the {@link OperatingSystem} class.
+ * 
+ * @author Sergi Casto
+ * @author Ignasi Barrera
+ */
+@Test(groups = "unit", testName = "OperatingSystemTest")
+public class OperatingSystemTest {
+
+   public void testParseStandard64bit() {
+      OperatingSystem os = OperatingSystem.builder().from("Ubuntu 12.10 x64", "Ubuntu").build();
+
+      assertEquals(os.getDistribution(), Distribution.UBUNTU);
+      assertEquals(os.getVersion(), "12.10");
+      assertEquals(os.getArch(), "x64");
+      assertTrue(os.is64bit());
+   }
+
+   public void testParseStandard() {
+      OperatingSystem os = OperatingSystem.builder().from("Ubuntu 12.10 x32", "Ubuntu").build();
+
+      assertEquals(os.getDistribution(), Distribution.UBUNTU);
+      assertEquals(os.getVersion(), "12.10");
+      assertEquals(os.getArch(), "x32");
+      assertFalse(os.is64bit());
+
+      os = OperatingSystem.builder().from("CentOS 6.5 x64", "CentOS").build();
+
+      assertEquals(os.getDistribution(), Distribution.CENTOS);
+      assertEquals(os.getVersion(), "6.5");
+      assertEquals(os.getArch(), "x64");
+      assertTrue(os.is64bit());
+
+      os = OperatingSystem.builder().from("CentOS 6.5 x64", "Centos").build();
+
+      assertEquals(os.getDistribution(), Distribution.CENTOS);
+      assertEquals(os.getVersion(), "6.5");
+      assertEquals(os.getArch(), "x64");
+      assertTrue(os.is64bit());
+   }
+
+   public void testParseNoArch() {
+      OperatingSystem os = OperatingSystem.builder().from("Ubuntu 12.10", "Ubuntu").build();
+
+      assertEquals(os.getDistribution(), Distribution.UBUNTU);
+      assertEquals(os.getVersion(), "12.10");
+      assertEquals(os.getArch(), "");
+      assertFalse(os.is64bit());
+   }
+
+   public void testParseNoVersion() {
+      OperatingSystem os = OperatingSystem.builder().from("Ubuntu x64", "Ubuntu").build();
+
+      assertEquals(os.getDistribution(), Distribution.UBUNTU);
+      assertEquals(os.getVersion(), "");
+      assertEquals(os.getArch(), "x64");
+      assertTrue(os.is64bit());
+   }
+
+   public void testParseUnknownDistribution() {
+      OperatingSystem os = OperatingSystem.builder().from("Ubuntu 12.04 x64", "Foo").build();
+
+      assertEquals(os.getDistribution(), Distribution.UNRECOGNIZED);
+      assertEquals(os.getVersion(), "12.04");
+      assertEquals(os.getArch(), "x64");
+      assertTrue(os.is64bit());
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/224b2828/digitalocean/src/test/java/org/jclouds/digitalocean/features/DropletApiLiveTest.java
----------------------------------------------------------------------
diff --git a/digitalocean/src/test/java/org/jclouds/digitalocean/features/DropletApiLiveTest.java b/digitalocean/src/test/java/org/jclouds/digitalocean/features/DropletApiLiveTest.java
new file mode 100644
index 0000000..9816729
--- /dev/null
+++ b/digitalocean/src/test/java/org/jclouds/digitalocean/features/DropletApiLiveTest.java
@@ -0,0 +1,190 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.digitalocean.features;
+
+import static com.google.common.collect.Iterables.tryFind;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+import java.util.List;
+
+import org.jclouds.digitalocean.domain.Droplet;
+import org.jclouds.digitalocean.domain.DropletCreation;
+import org.jclouds.digitalocean.domain.Image;
+import org.jclouds.digitalocean.domain.Size;
+import org.jclouds.digitalocean.internal.BaseDigitalOceanLiveTest;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Predicate;
+
+/**
+ * Live tests for the {@link DropletApi} class.
+ * 
+ * @author Sergi Castro
+ * @author Ignasi Barrera
+ */
+@Test(groups = "live", testName = "DropletApiLiveTest")
+public class DropletApiLiveTest extends BaseDigitalOceanLiveTest {
+
+   private DropletCreation dropletCreation;
+   private Droplet droplet;
+   private Image snapshot;
+
+   @Override
+   protected void initialize() {
+      super.initialize();
+      initializeImageSizeAndRegion();
+   }
+
+   @AfterClass
+   public void cleanup() {
+      if (droplet != null) {
+         int event = api.getDropletApi().destroy(droplet.getId(), true);
+         assertTrue(event > 0, "The event id should not be null");
+      }
+      if (snapshot != null) {
+         api.getImageApi().delete(snapshot.getId());
+      }
+   }
+
+   public void testCreateDroplet() {
+      dropletCreation = api.getDropletApi().create("droplettest", defaultImage.getId(), defaultSize.getId(),
+            defaultRegion.getId());
+
+      assertTrue(dropletCreation.getId() > 0, "Created droplet id should be > 0");
+      assertTrue(dropletCreation.getEventId() > 0, "Droplet creation event id should be > 0");
+   }
+
+   @Test(dependsOnMethods = "testCreateDroplet")
+   public void testGetDroplet() {
+      waitForEvent(dropletCreation.getEventId());
+      droplet = api.getDropletApi().get(dropletCreation.getId());
+
+      assertNotNull(droplet, "Created droplet should not be null");
+   }
+
+   @Test(dependsOnMethods = "testGetDroplet")
+   public void testListDroplets() {
+      List<Droplet> droplets = api.getDropletApi().list();
+
+      assertFalse(droplets.isEmpty(), "Droplet list should not be empty");
+   }
+
+   @Test(dependsOnMethods = "testGetDroplet")
+   public void testPowerOffDroplet() {
+      int event = api.getDropletApi().powerOff(droplet.getId());
+      assertTrue(event > 0, "The event id should be > 0");
+      waitForEvent(event);
+   }
+
+   @Test(dependsOnMethods = "testPowerOffDroplet")
+   public void testPowerOnDroplet() {
+      int event = api.getDropletApi().powerOn(droplet.getId());
+      assertTrue(event > 0, "The event id should be > 0");
+      waitForEvent(event);
+   }
+
+   @Test(dependsOnMethods = "testPowerOnDroplet")
+   public void testRebootDroplet() {
+      int event = api.getDropletApi().reboot(droplet.getId());
+      assertTrue(event > 0, "The event id should be > 0");
+      waitForEvent(event);
+   }
+
+   @Test(dependsOnMethods = "testRebootDroplet")
+   public void testPowerCycleDroplet() {
+      int event = api.getDropletApi().powerCycle(droplet.getId());
+      assertTrue(event > 0, "The event id should be > 0");
+      waitForEvent(event);
+   }
+
+   @Test(dependsOnMethods = "testPowerCycleDroplet")
+   public void testResetPasswordForDroplet() {
+      int event = api.getDropletApi().resetPassword(droplet.getId());
+      assertTrue(event > 0, "The event id should be > 0");
+      waitForEvent(event);
+   }
+
+   @Test(dependsOnMethods = "testResetPasswordForDroplet")
+   public void testRenameDroplet() {
+      int event = api.getDropletApi().rename(droplet.getId(), "droplettestupdated");
+      assertTrue(event > 0, "The event id should be > 0");
+      waitForEvent(event);
+      droplet = api.getDropletApi().get(droplet.getId());
+      assertEquals(droplet.getName(), "droplettestupdated", "The renamed droplet should have the new name");
+   }
+
+   @Test(dependsOnMethods = "testRenameDroplet")
+   public void testRebuildDroplet() {
+      int event = api.getDropletApi().rebuild(droplet.getId(), defaultImage.getId());
+      assertTrue(event > 0, "The event id should be > 0");
+      waitForEvent(event);
+   }
+
+   @Test(dependsOnMethods = "testRebuildDroplet")
+   public void testRestoreDroplet() {
+      int event = api.getDropletApi().restore(droplet.getId(), defaultImage.getId());
+      assertTrue(event > 0, "The event id should be > 0");
+      waitForEvent(event);
+   }
+
+   @Test(dependsOnMethods = "testRestoreDroplet")
+   public void testSnapshotDroplet() {
+      // Snapshot requires the droplet to be powered off
+      int powerOffEvent = api.getDropletApi().powerOff(droplet.getId());
+      assertTrue(powerOffEvent > 0, "The event id should be > 0");
+      waitForEvent(powerOffEvent);
+
+      int event = api.getDropletApi().snapshot(droplet.getId(), "testsnapshot");
+      assertTrue(event > 0, "The event id should be > 0");
+      waitForEvent(event);
+
+      Optional<Image> snapshot = tryFind(api.getImageApi().list(), new Predicate<Image>() {
+         @Override
+         public boolean apply(Image input) {
+            return input.getName().equals("testsnapshot");
+         }
+      });
+
+      assertTrue(snapshot.isPresent(), "The created snapshot should exist in the image list");
+      this.snapshot = snapshot.get();
+   }
+
+   @Test(dependsOnMethods = "testSnapshotDroplet")
+   public void testResizeDroplet() {
+      // Resize requires the droplet to be powered off
+      int powerOffEvent = api.getDropletApi().powerOff(droplet.getId());
+      assertTrue(powerOffEvent > 0, "The event id should be > 0");
+      waitForEvent(powerOffEvent);
+
+      Size newSize = sizes.get(1);
+      int resizeEvent = api.getDropletApi().resize(droplet.getId(), newSize.getId());
+      assertTrue(resizeEvent > 0, "The event id should be > 0");
+      waitForEvent(resizeEvent);
+   }
+
+   @Test(dependsOnMethods = "testResizeDroplet")
+   public void testShutdownDroplet() {
+      int event = api.getDropletApi().shutdown(droplet.getId());
+      assertTrue(event > 0, "The event id should be > 0");
+      waitForEvent(event);
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/224b2828/digitalocean/src/test/java/org/jclouds/digitalocean/features/DropletApiMockTest.java
----------------------------------------------------------------------
diff --git a/digitalocean/src/test/java/org/jclouds/digitalocean/features/DropletApiMockTest.java b/digitalocean/src/test/java/org/jclouds/digitalocean/features/DropletApiMockTest.java
new file mode 100644
index 0000000..32c247d
--- /dev/null
+++ b/digitalocean/src/test/java/org/jclouds/digitalocean/features/DropletApiMockTest.java
@@ -0,0 +1,721 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.digitalocean.features;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+
+import java.util.List;
+
+import org.jclouds.digitalocean.DigitalOceanApi;
+import org.jclouds.digitalocean.domain.Droplet;
+import org.jclouds.digitalocean.domain.Droplet.Status;
+import org.jclouds.digitalocean.domain.DropletCreation;
+import org.jclouds.digitalocean.domain.options.CreateDropletOptions;
+import org.jclouds.digitalocean.internal.BaseDigitalOceanMockTest;
+import org.jclouds.rest.ResourceNotFoundException;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableMultimap;
+import com.squareup.okhttp.mockwebserver.MockResponse;
+import com.squareup.okhttp.mockwebserver.MockWebServer;
+
+/**
+ * Mock tests for the {@link DropletApi} class.
+ * 
+ * @author Sergi Castro
+ * @author Ignasi Barrera
+ */
+@Test(groups = "unit", testName = "DropletApiMockTest")
+public class DropletApiMockTest extends BaseDigitalOceanMockTest {
+
+   public void testListDroplets() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setBody(payloadFromResource("/droplets.json")));
+
+      DigitalOceanApi api = api(server.getUrl("/"));
+      DropletApi dropletApi = api.getDropletApi();
+
+      try {
+         List<Droplet> sizes = dropletApi.list();
+
+         assertRequestHasCommonFields(server.takeRequest(), "/droplets");
+         assertEquals(sizes.size(), 1);
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   public void testGetDroplet() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setBody(payloadFromResource("/droplet.json")));
+
+      DigitalOceanApi api = api(server.getUrl("/"));
+      DropletApi dropletApi = api.getDropletApi();
+
+      try {
+         Droplet droplet = dropletApi.get(100823);
+
+         assertRequestHasCommonFields(server.takeRequest(), "/droplets/100823");
+         assertNotNull(droplet);
+         assertNotNull(droplet.getBackups());
+         assertNotNull(droplet.getSnapshots());
+         assertEquals(droplet.getName(), "test222");
+         assertEquals(droplet.getStatus(), Status.ACTIVE);
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   public void testGetNonexistentDroplet() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setResponseCode(404));
+
+      DigitalOceanApi api = api(server.getUrl("/"));
+      DropletApi dropletApi = api.getDropletApi();
+
+      try {
+         Droplet droplet = dropletApi.get(100823);
+
+         assertRequestHasCommonFields(server.takeRequest(), "/droplets/100823");
+         assertNull(droplet);
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   public void testCreateDroplet() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setBody(payloadFromResource("/droplet-creation.json")));
+
+      DigitalOceanApi api = api(server.getUrl("/"));
+      DropletApi dropletApi = api.getDropletApi();
+
+      try {
+         DropletCreation droplet = dropletApi.create("test", 419, 32, 1);
+
+         assertRequestHasParameters(server.takeRequest(), "/droplets/new",
+               ImmutableMultimap.of("name", "test", "image_id", "419", "size_id", "32", "region_id", "1"));
+
+         assertNotNull(droplet);
+         assertEquals(droplet.getName(), "test");
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   public void testCreateDropletWithOptions() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setBody(payloadFromResource("/droplet-creation.json")));
+
+      DigitalOceanApi api = api(server.getUrl("/"));
+      DropletApi dropletApi = api.getDropletApi();
+
+      try {
+         CreateDropletOptions options = CreateDropletOptions.builder().addSshKeyId(5).addSshKeyId(4)
+               .privateNetworking(true).backupsEnabled(false).build();
+         DropletCreation droplet = dropletApi.create("test", 419, 32, 1, options);
+
+         ImmutableMultimap.Builder<String, String> params = ImmutableMultimap.builder();
+         params.put("name", "test");
+         params.put("image_id", "419");
+         params.put("size_id", "32");
+         params.put("region_id", "1");
+         params.put("ssh_key_ids", "5,4");
+         params.put("private_networking", "true");
+         params.put("backups_enabled", "false");
+
+         assertRequestHasParameters(server.takeRequest(), "/droplets/new", params.build());
+
+         assertNotNull(droplet);
+         assertEquals(droplet.getName(), "test");
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   public void testRebootDroplet() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setBody(payloadFromResource("/eventid.json")));
+
+      DigitalOceanApi api = api(server.getUrl("/"));
+      DropletApi dropletApi = api.getDropletApi();
+
+      try {
+         int event = dropletApi.reboot(1);
+
+         assertRequestHasCommonFields(server.takeRequest(), "/droplets/1/reboot");
+         assertTrue(event > 0);
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   public void testRebootNonexistentDroplet() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setResponseCode(404));
+
+      DigitalOceanApi api = api(server.getUrl("/"));
+      DropletApi dropletApi = api.getDropletApi();
+
+      try {
+         try {
+            dropletApi.reboot(1);
+            fail("Reboot droplet should fail on 404");
+         } catch (ResourceNotFoundException ex) {
+            // Expected exception
+         }
+
+         assertRequestHasCommonFields(server.takeRequest(), "/droplets/1/reboot");
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   public void testPowerCycleDroplet() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setBody(payloadFromResource("/eventid.json")));
+
+      DigitalOceanApi api = api(server.getUrl("/"));
+      DropletApi dropletApi = api.getDropletApi();
+
+      try {
+         int event = dropletApi.powerCycle(1);
+
+         assertRequestHasCommonFields(server.takeRequest(), "/droplets/1/power_cycle");
+         assertTrue(event > 0);
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   public void testPowerCycleNonexistentDroplet() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setResponseCode(404));
+
+      DigitalOceanApi api = api(server.getUrl("/"));
+      DropletApi dropletApi = api.getDropletApi();
+
+      try {
+         try {
+            dropletApi.powerCycle(1);
+            fail("Power cycle droplet should fail on 404");
+         } catch (ResourceNotFoundException ex) {
+            // Expected exception
+         }
+
+         assertRequestHasCommonFields(server.takeRequest(), "/droplets/1/power_cycle");
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   public void testShutdownDroplet() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setBody(payloadFromResource("/eventid.json")));
+
+      DigitalOceanApi api = api(server.getUrl("/"));
+      DropletApi dropletApi = api.getDropletApi();
+
+      try {
+         int event = dropletApi.shutdown(1);
+
+         assertRequestHasCommonFields(server.takeRequest(), "/droplets/1/shutdown");
+         assertTrue(event > 0);
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   public void testShutdownNonexistentDroplet() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setResponseCode(404));
+
+      DigitalOceanApi api = api(server.getUrl("/"));
+      DropletApi dropletApi = api.getDropletApi();
+
+      try {
+         try {
+            dropletApi.shutdown(1);
+            fail("Shutdown droplet should fail on 404");
+         } catch (ResourceNotFoundException ex) {
+            // Expected exception
+         }
+
+         assertRequestHasCommonFields(server.takeRequest(), "/droplets/1/shutdown");
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   public void testPowerOffDroplet() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setBody(payloadFromResource("/eventid.json")));
+
+      DigitalOceanApi api = api(server.getUrl("/"));
+      DropletApi dropletApi = api.getDropletApi();
+
+      try {
+         int event = dropletApi.powerOff(1);
+
+         assertRequestHasCommonFields(server.takeRequest(), "/droplets/1/power_off");
+         assertTrue(event > 0);
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   public void testPowerOffNonexistentDroplet() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setResponseCode(404));
+
+      DigitalOceanApi api = api(server.getUrl("/"));
+      DropletApi dropletApi = api.getDropletApi();
+
+      try {
+         try {
+            dropletApi.powerOff(1);
+            fail("Power off droplet should fail on 404");
+         } catch (ResourceNotFoundException ex) {
+            // Expected exception
+         }
+
+         assertRequestHasCommonFields(server.takeRequest(), "/droplets/1/power_off");
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   public void testPowerOnDroplet() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setBody(payloadFromResource("/eventid.json")));
+
+      DigitalOceanApi api = api(server.getUrl("/"));
+      DropletApi dropletApi = api.getDropletApi();
+
+      try {
+         int event = dropletApi.powerOn(1);
+
+         assertRequestHasCommonFields(server.takeRequest(), "/droplets/1/power_on");
+         assertTrue(event > 0);
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   public void testPowerOnNonexistentDroplet() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setResponseCode(404));
+
+      DigitalOceanApi api = api(server.getUrl("/"));
+      DropletApi dropletApi = api.getDropletApi();
+
+      try {
+         try {
+            dropletApi.powerOn(1);
+            fail("Power on droplet should fail on 404");
+         } catch (ResourceNotFoundException ex) {
+            // Expected exception
+         }
+
+         assertRequestHasCommonFields(server.takeRequest(), "/droplets/1/power_on");
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   public void testResetPasswordForDroplet() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setBody(payloadFromResource("/eventid.json")));
+
+      DigitalOceanApi api = api(server.getUrl("/"));
+      DropletApi dropletApi = api.getDropletApi();
+
+      try {
+         int event = dropletApi.resetPassword(1);
+
+         assertRequestHasCommonFields(server.takeRequest(), "/droplets/1/password_reset");
+         assertTrue(event > 0);
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   public void testResetPasswordForNonexistentDroplet() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setResponseCode(404));
+
+      DigitalOceanApi api = api(server.getUrl("/"));
+      DropletApi dropletApi = api.getDropletApi();
+
+      try {
+         try {
+            dropletApi.resetPassword(1);
+            fail("Reset password for droplet should fail on 404");
+         } catch (ResourceNotFoundException ex) {
+            // Expected exception
+         }
+
+         assertRequestHasCommonFields(server.takeRequest(), "/droplets/1/password_reset");
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   public void testResizeDroplet() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setBody(payloadFromResource("/eventid.json")));
+
+      DigitalOceanApi api = api(server.getUrl("/"));
+      DropletApi dropletApi = api.getDropletApi();
+
+      try {
+         int event = dropletApi.resize(1, 3);
+
+         assertRequestHasParameters(server.takeRequest(), "/droplets/1/resize", ImmutableMultimap.of("size_id", "3"));
+         assertTrue(event > 0);
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   public void testResizeNonexistentDroplet() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setResponseCode(404));
+
+      DigitalOceanApi api = api(server.getUrl("/"));
+      DropletApi dropletApi = api.getDropletApi();
+
+      try {
+         try {
+            dropletApi.resize(1, 3);
+            fail("Resize droplet should fail on 404");
+         } catch (ResourceNotFoundException ex) {
+            // Expected exception
+         }
+
+         assertRequestHasParameters(server.takeRequest(), "/droplets/1/resize", ImmutableMultimap.of("size_id", "3"));
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   public void testSnapshotDroplet() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setBody(payloadFromResource("/eventid.json")));
+
+      DigitalOceanApi api = api(server.getUrl("/"));
+      DropletApi dropletApi = api.getDropletApi();
+
+      try {
+         int event = dropletApi.snapshot(1);
+
+         assertRequestHasCommonFields(server.takeRequest(), "/droplets/1/snapshot");
+         assertTrue(event > 0);
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   public void testSnapshotNonexistentDroplet() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setResponseCode(404));
+
+      DigitalOceanApi api = api(server.getUrl("/"));
+      DropletApi dropletApi = api.getDropletApi();
+
+      try {
+         try {
+            dropletApi.snapshot(1);
+            fail("Snapshot droplet should fail on 404");
+         } catch (ResourceNotFoundException ex) {
+            // Expected exception
+         }
+
+         assertRequestHasCommonFields(server.takeRequest(), "/droplets/1/snapshot");
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   public void testSnapshotWithNameDroplet() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setBody(payloadFromResource("/eventid.json")));
+
+      DigitalOceanApi api = api(server.getUrl("/"));
+      DropletApi dropletApi = api.getDropletApi();
+
+      try {
+         int event = dropletApi.snapshot(1, "foo");
+
+         assertRequestHasParameters(server.takeRequest(), "/droplets/1/snapshot", ImmutableMultimap.of("name", "foo"));
+         assertTrue(event > 0);
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   public void testSnapshotWithNameNonexistentDroplet() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setResponseCode(404));
+
+      DigitalOceanApi api = api(server.getUrl("/"));
+      DropletApi dropletApi = api.getDropletApi();
+
+      try {
+         try {
+            dropletApi.snapshot(1, "foo");
+            fail("Snapshot droplet should fail on 404");
+         } catch (ResourceNotFoundException ex) {
+            // Expected exception
+         }
+
+         assertRequestHasParameters(server.takeRequest(), "/droplets/1/snapshot", ImmutableMultimap.of("name", "foo"));
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   public void testRestoreDroplet() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setBody(payloadFromResource("/eventid.json")));
+
+      DigitalOceanApi api = api(server.getUrl("/"));
+      DropletApi dropletApi = api.getDropletApi();
+
+      try {
+         int event = dropletApi.restore(1, 3);
+
+         assertRequestHasParameters(server.takeRequest(), "/droplets/1/restore", ImmutableMultimap.of("image_id", "3"));
+         assertTrue(event > 0);
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   public void testRestoreNonexistentDroplet() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setResponseCode(404));
+
+      DigitalOceanApi api = api(server.getUrl("/"));
+      DropletApi dropletApi = api.getDropletApi();
+
+      try {
+         try {
+            dropletApi.restore(1, 3);
+            fail("Restore droplet should fail on 404");
+         } catch (ResourceNotFoundException ex) {
+            // Expected exception
+         }
+
+         assertRequestHasParameters(server.takeRequest(), "/droplets/1/restore", ImmutableMultimap.of("image_id", "3"));
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   public void testRebuildDroplet() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setBody(payloadFromResource("/eventid.json")));
+
+      DigitalOceanApi api = api(server.getUrl("/"));
+      DropletApi dropletApi = api.getDropletApi();
+
+      try {
+         int event = dropletApi.rebuild(1, 3);
+
+         assertRequestHasParameters(server.takeRequest(), "/droplets/1/rebuild", ImmutableMultimap.of("image_id", "3"));
+         assertTrue(event > 0);
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   public void testRebuildNonexistentDroplet() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setResponseCode(404));
+
+      DigitalOceanApi api = api(server.getUrl("/"));
+      DropletApi dropletApi = api.getDropletApi();
+
+      try {
+         try {
+            dropletApi.rebuild(1, 3);
+            fail("Rebuild droplet should fail on 404");
+         } catch (ResourceNotFoundException ex) {
+            // Expected exception
+         }
+
+         assertRequestHasParameters(server.takeRequest(), "/droplets/1/rebuild", ImmutableMultimap.of("image_id", "3"));
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   public void testRenameDroplet() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setBody(payloadFromResource("/eventid.json")));
+
+      DigitalOceanApi api = api(server.getUrl("/"));
+      DropletApi dropletApi = api.getDropletApi();
+
+      try {
+         int event = dropletApi.rename(1, "foo");
+
+         assertRequestHasParameters(server.takeRequest(), "/droplets/1/rename", ImmutableMultimap.of("name", "foo"));
+         assertTrue(event > 0);
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   public void testRenameNonexistentDroplet() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setResponseCode(404));
+
+      DigitalOceanApi api = api(server.getUrl("/"));
+      DropletApi dropletApi = api.getDropletApi();
+
+      try {
+         try {
+            dropletApi.rename(1, "foo");
+            fail("Rename droplet should fail on 404");
+         } catch (ResourceNotFoundException ex) {
+            // Expected exception
+         }
+
+         assertRequestHasParameters(server.takeRequest(), "/droplets/1/rename", ImmutableMultimap.of("name", "foo"));
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   public void testDestroyDroplet() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setBody(payloadFromResource("/eventid.json")));
+
+      DigitalOceanApi api = api(server.getUrl("/"));
+      DropletApi dropletApi = api.getDropletApi();
+
+      try {
+         int event = dropletApi.destroy(1);
+
+         assertRequestHasCommonFields(server.takeRequest(), "/droplets/1/destroy");
+         assertTrue(event > 0);
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   public void testDestroyNonexistentDroplet() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setResponseCode(404));
+
+      DigitalOceanApi api = api(server.getUrl("/"));
+      DropletApi dropletApi = api.getDropletApi();
+
+      try {
+         try {
+            dropletApi.destroy(1);
+            fail("Destroy droplet should fail on 404");
+         } catch (ResourceNotFoundException ex) {
+            // Expected exception
+         }
+
+         assertRequestHasCommonFields(server.takeRequest(), "/droplets/1/destroy");
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   public void testDestroyWithOptionsDroplet() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setBody(payloadFromResource("/eventid.json")));
+
+      DigitalOceanApi api = api(server.getUrl("/"));
+      DropletApi dropletApi = api.getDropletApi();
+
+      try {
+         int event = dropletApi.destroy(1, true);
+
+         assertRequestHasParameters(server.takeRequest(), "/droplets/1/destroy",
+               ImmutableMultimap.of("scrub_data", "true"));
+         assertTrue(event > 0);
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   public void testDestroyWithOptionsNonexistentDroplet() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setResponseCode(404));
+
+      DigitalOceanApi api = api(server.getUrl("/"));
+      DropletApi dropletApi = api.getDropletApi();
+
+      try {
+         try {
+            dropletApi.destroy(1, true);
+            fail("Destroy droplet should fail on 404");
+         } catch (ResourceNotFoundException ex) {
+            // Expected exception
+         }
+
+         assertRequestHasParameters(server.takeRequest(), "/droplets/1/destroy",
+               ImmutableMultimap.of("scrub_data", "true"));
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/224b2828/digitalocean/src/test/java/org/jclouds/digitalocean/features/EventApiLiveTest.java
----------------------------------------------------------------------
diff --git a/digitalocean/src/test/java/org/jclouds/digitalocean/features/EventApiLiveTest.java b/digitalocean/src/test/java/org/jclouds/digitalocean/features/EventApiLiveTest.java
new file mode 100644
index 0000000..e363081
--- /dev/null
+++ b/digitalocean/src/test/java/org/jclouds/digitalocean/features/EventApiLiveTest.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.digitalocean.features;
+
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+import org.jclouds.digitalocean.domain.DropletCreation;
+import org.jclouds.digitalocean.domain.Event;
+import org.jclouds.digitalocean.internal.BaseDigitalOceanLiveTest;
+import org.testng.annotations.Test;
+
+/**
+ * Live tests for the {@link EventApi} class.
+ * 
+ * @author Sergi Castro
+ * @author Ignasi Barrera
+ */
+@Test(groups = "live", testName = "EventApiLiveTest")
+public class EventApiLiveTest extends BaseDigitalOceanLiveTest {
+
+   @Override
+   protected void initialize() {
+      super.initialize();
+      initializeImageSizeAndRegion();
+   }
+
+   public void testGetEvent() {
+      DropletCreation droplet = null;
+
+      try {
+         droplet = api.getDropletApi().create("eventtest", defaultImage.getId(), defaultSize.getId(),
+               defaultRegion.getId());
+         Event event = api.getEventApi().get(droplet.getEventId());
+         assertNotNull(event, "Droplet creation event should not be null");
+         assertTrue(event.getId() > 0, "Event id should be > 0");
+      } finally {
+         if (droplet != null) {
+            waitForEvent(droplet.getEventId());
+            api.getDropletApi().destroy(droplet.getId(), true);
+         }
+      }
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/224b2828/digitalocean/src/test/java/org/jclouds/digitalocean/features/EventApiMockTest.java
----------------------------------------------------------------------
diff --git a/digitalocean/src/test/java/org/jclouds/digitalocean/features/EventApiMockTest.java b/digitalocean/src/test/java/org/jclouds/digitalocean/features/EventApiMockTest.java
new file mode 100644
index 0000000..6b1d994
--- /dev/null
+++ b/digitalocean/src/test/java/org/jclouds/digitalocean/features/EventApiMockTest.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.digitalocean.features;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+
+import org.jclouds.digitalocean.DigitalOceanApi;
+import org.jclouds.digitalocean.domain.Event;
+import org.jclouds.digitalocean.domain.Event.Status;
+import org.jclouds.digitalocean.internal.BaseDigitalOceanMockTest;
+import org.testng.annotations.Test;
+
+import com.squareup.okhttp.mockwebserver.MockResponse;
+import com.squareup.okhttp.mockwebserver.MockWebServer;
+
+/**
+ * Mock tests for the {@link EventApi} class.
+ * 
+ * @author Sergi Castro
+ * @author Ignasi Barrera
+ */
+@Test(groups = "unit", testName = "EventApiMockTest")
+public class EventApiMockTest extends BaseDigitalOceanMockTest {
+
+   public void testGetEvent() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setBody(payloadFromResource("/event.json")));
+
+      DigitalOceanApi api = api(server.getUrl("/"));
+      EventApi eventApi = api.getEventApi();
+
+      try {
+         Event event = eventApi.get(7499);
+
+         assertRequestHasCommonFields(server.takeRequest(), "/events/7499");
+         assertNotNull(event);
+         assertEquals(event.getStatus(), Status.DONE);
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   public void testGetPendingEvent() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setBody(payloadFromResource("/event-pending.json")));
+
+      DigitalOceanApi api = api(server.getUrl("/"));
+      EventApi eventApi = api.getEventApi();
+
+      try {
+         Event event = eventApi.get(7499);
+
+         assertRequestHasCommonFields(server.takeRequest(), "/events/7499");
+         assertNotNull(event);
+         assertEquals(event.getStatus(), Status.PENDING);
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   public void testGetUnexistingEvent() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setResponseCode(404));
+
+      DigitalOceanApi api = api(server.getUrl("/"));
+      EventApi eventApi = api.getEventApi();
+
+      try {
+         Event event = eventApi.get(7499);
+
+         assertRequestHasCommonFields(server.takeRequest(), "/events/7499");
+         assertNull(event);
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/224b2828/digitalocean/src/test/java/org/jclouds/digitalocean/features/ImageApiLiveTest.java
----------------------------------------------------------------------
diff --git a/digitalocean/src/test/java/org/jclouds/digitalocean/features/ImageApiLiveTest.java b/digitalocean/src/test/java/org/jclouds/digitalocean/features/ImageApiLiveTest.java
new file mode 100644
index 0000000..2625424
--- /dev/null
+++ b/digitalocean/src/test/java/org/jclouds/digitalocean/features/ImageApiLiveTest.java
@@ -0,0 +1,106 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.digitalocean.features;
+
+import static com.google.common.collect.Iterables.find;
+import static com.google.common.collect.Iterables.tryFind;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+
+import org.jclouds.digitalocean.domain.DropletCreation;
+import org.jclouds.digitalocean.domain.Image;
+import org.jclouds.digitalocean.domain.Region;
+import org.jclouds.digitalocean.internal.BaseDigitalOceanLiveTest;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Predicate;
+
+/**
+ * Live tests for the {@link ImageApi} class.
+ * 
+ * @author Sergi Castro
+ * @author Ignasi Barrera
+ */
+@Test(groups = "live", testName = "ImageApiLiveTest")
+public class ImageApiLiveTest extends BaseDigitalOceanLiveTest {
+
+   private Image snapshot;
+   private DropletCreation droplet;
+
+   @Override
+   protected void initialize() {
+      super.initialize();
+      initializeImageSizeAndRegion();
+   }
+
+   @AfterClass
+   public void cleanup() {
+      try {
+         if (droplet != null) {
+            api.getDropletApi().destroy(droplet.getId(), true);
+         }
+      } finally {
+         if (snapshot != null) {
+            api.getImageApi().delete(snapshot.getId());
+            assertFalse(tryFind(api.getImageApi().list(), byName(snapshot.getName())).isPresent(),
+                  "Snapshot should not exist after delete");
+         }
+      }
+   }
+
+   public void testGetImage() {
+      assertNotNull(api.getImageApi().get(defaultImage.getId()), "The image should not be null");
+   }
+
+   public void testGetImageNotFound() {
+      assertNull(api.getImageApi().get(-1));
+   }
+
+   public void testTransferImage() {
+      droplet = api.getDropletApi().create("imagetransferdroplet", defaultImage.getId(), defaultSize.getId(),
+            defaultRegion.getId());
+
+      assertTrue(droplet.getId() > 0, "Created droplet id should be > 0");
+      assertTrue(droplet.getEventId() > 0, "Droplet creation event id should be > 0");
+
+      waitForEvent(droplet.getEventId());
+      int powerOffEvent = api.getDropletApi().powerOff(droplet.getId());
+      waitForEvent(powerOffEvent);
+
+      int snapshotEvent = api.getDropletApi().snapshot(droplet.getId(), "imagetransfersnapshot");
+      waitForEvent(snapshotEvent);
+
+      snapshot = find(api.getImageApi().list(), byName("imagetransfersnapshot"));
+
+      Region newRegion = regions.get(1);
+      int transferEvent = api.getImageApi().transfer(snapshot.getId(), newRegion.getId());
+      assertTrue(transferEvent > 0, "Transfer event id should be > 0");
+      waitForEvent(transferEvent);
+   }
+
+   private static Predicate<Image> byName(final String name) {
+      return new Predicate<Image>() {
+         @Override
+         public boolean apply(Image input) {
+            return input.getName().equals(name);
+         }
+      };
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/224b2828/digitalocean/src/test/java/org/jclouds/digitalocean/features/ImageApiMockTest.java
----------------------------------------------------------------------
diff --git a/digitalocean/src/test/java/org/jclouds/digitalocean/features/ImageApiMockTest.java b/digitalocean/src/test/java/org/jclouds/digitalocean/features/ImageApiMockTest.java
new file mode 100644
index 0000000..52bbaa1
--- /dev/null
+++ b/digitalocean/src/test/java/org/jclouds/digitalocean/features/ImageApiMockTest.java
@@ -0,0 +1,213 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.digitalocean.features;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+
+import java.util.List;
+
+import org.jclouds.digitalocean.DigitalOceanApi;
+import org.jclouds.digitalocean.domain.Distribution;
+import org.jclouds.digitalocean.domain.Image;
+import org.jclouds.digitalocean.internal.BaseDigitalOceanMockTest;
+import org.jclouds.rest.ResourceNotFoundException;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableMultimap;
+import com.squareup.okhttp.mockwebserver.MockResponse;
+import com.squareup.okhttp.mockwebserver.MockWebServer;
+
+/**
+ * Mock tests for the {@link ImageApi} class.
+ * 
+ * @author Sergi Castro
+ * @author Ignasi Barrera
+ */
+@Test(groups = "unit", testName = "ImageApiMockTest")
+public class ImageApiMockTest extends BaseDigitalOceanMockTest {
+
+   public void testListImages() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setBody(payloadFromResource("/images.json")));
+
+      DigitalOceanApi api = api(server.getUrl("/"));
+      ImageApi imageApi = api.getImageApi();
+
+      try {
+         List<Image> images = imageApi.list();
+
+         assertRequestHasCommonFields(server.takeRequest(), "/images");
+         assertEquals(images.size(), 3);
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   public void testGetImage() throws Exception {
+      MockWebServer server = mockWebServer();
+      String[] imageJsons = new String[] { "/image1.json", "/image2.json", "/image3.json" };
+
+      for (String imageJson : imageJsons) {
+         server.enqueue(new MockResponse().setBody(payloadFromResource(imageJson)));
+      }
+
+      DigitalOceanApi api = api(server.getUrl("/"));
+      ImageApi imageApi = api.getImageApi();
+
+      try {
+         Image image = imageApi.get(1);
+
+         assertRequestHasCommonFields(server.takeRequest(), "/images/1");
+         assertNotNull(image);
+         assertEquals(image.getId(), 1);
+         assertEquals(image.getOs().getDistribution(), Distribution.ARCHLINUX);
+         assertEquals(image.getOs().getVersion(), "2013.05");
+         assertEquals(image.getOs().getArch(), "x32");
+         assertEquals(image.getName(), "Arch Linux 2013.05 x32");
+         assertTrue(image.isPublicImage());
+
+         image = imageApi.get(2);
+
+         assertRequestHasCommonFields(server.takeRequest(), "/images/2");
+         assertNotNull(image);
+         assertEquals(image.getId(), 2);
+         assertEquals(image.getOs().getDistribution(), Distribution.FEDORA);
+         assertEquals(image.getOs().getVersion(), "17");
+         assertEquals(image.getOs().getArch(), "x64");
+         assertEquals(image.getName(), "Fedora 17 x64 Desktop");
+         assertTrue(image.isPublicImage());
+
+         image = imageApi.get(3);
+
+         assertRequestHasCommonFields(server.takeRequest(), "/images/3");
+         assertNotNull(image);
+         assertEquals(image.getId(), 3);
+         assertEquals(image.getOs().getDistribution(), Distribution.UBUNTU);
+         assertEquals(image.getOs().getVersion(), "13.04");
+         assertEquals(image.getOs().getArch(), "");
+         assertEquals(image.getName(), "Dokku on Ubuntu 13.04 0.2.0rc3");
+         assertTrue(image.isPublicImage());
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   public void testGetUnexistingImage() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setResponseCode(404));
+
+      DigitalOceanApi api = api(server.getUrl("/"));
+      ImageApi imageApi = api.getImageApi();
+
+      try {
+         Image image = imageApi.get(15);
+
+         assertRequestHasCommonFields(server.takeRequest(), "/images/15");
+         assertNull(image);
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   public void testDeleteImage() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse());
+
+      DigitalOceanApi api = api(server.getUrl("/"));
+      ImageApi imageApi = api.getImageApi();
+
+      try {
+         imageApi.delete(15);
+
+         assertRequestHasCommonFields(server.takeRequest(), "/images/15/destroy");
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   public void testDeleteUnexistingImage() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setResponseCode(404));
+
+      DigitalOceanApi api = api(server.getUrl("/"));
+      ImageApi imageApi = api.getImageApi();
+
+      try {
+         try {
+            imageApi.delete(15);
+            fail("Delete image should fail on 404");
+         } catch (ResourceNotFoundException ex) {
+            // Expected exception
+         }
+
+         assertRequestHasCommonFields(server.takeRequest(), "/images/15/destroy");
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   public void testTransferUnexistingImage() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setResponseCode(404));
+
+      DigitalOceanApi api = api(server.getUrl("/"));
+      ImageApi imageApi = api.getImageApi();
+
+      try {
+         try {
+            imageApi.transfer(47, 23);
+            fail("Transfer image should fail on 404");
+         } catch (ResourceNotFoundException ex) {
+            // Expected exception
+         }
+
+         assertRequestHasParameters(server.takeRequest(), "/images/47/transfer",
+               ImmutableMultimap.of("region_id", "23"));
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   public void testTransferImage() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setBody(payloadFromResource("/eventid.json")));
+
+      DigitalOceanApi api = api(server.getUrl("/"));
+      ImageApi imageApi = api.getImageApi();
+
+      try {
+         int eventId = imageApi.transfer(47, 23);
+
+         assertRequestHasParameters(server.takeRequest(), "/images/47/transfer",
+               ImmutableMultimap.of("region_id", "23"));
+         assertEquals(eventId, 7499);
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/224b2828/digitalocean/src/test/java/org/jclouds/digitalocean/features/KeyPairApiLiveTest.java
----------------------------------------------------------------------
diff --git a/digitalocean/src/test/java/org/jclouds/digitalocean/features/KeyPairApiLiveTest.java b/digitalocean/src/test/java/org/jclouds/digitalocean/features/KeyPairApiLiveTest.java
new file mode 100644
index 0000000..ba7c829
--- /dev/null
+++ b/digitalocean/src/test/java/org/jclouds/digitalocean/features/KeyPairApiLiveTest.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.digitalocean.features;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+
+import java.io.IOException;
+import java.util.List;
+
+import org.jclouds.digitalocean.domain.SshKey;
+import org.jclouds.digitalocean.internal.BaseDigitalOceanLiveTest;
+import org.jclouds.util.Strings2;
+import org.testng.annotations.Test;
+
+/**
+ * Live tests for the {@link ImageApi} class.
+ * 
+ * @author Sergi Castro
+ * @author Ignasi Barrera
+ */
+@Test(groups = "live", testName = "ImageApiLiveTest")
+public class KeyPairApiLiveTest extends BaseDigitalOceanLiveTest {
+
+   private SshKey key;
+
+   public void testCreateKey() throws IOException {
+      String publicKey = Strings2.toStringAndClose(getClass().getResourceAsStream("/ssh-rsa.txt"));
+      key = api.getKeyPairApi().create("foo", publicKey);
+
+      assertNotNull(key);
+      assertNotNull(key.getId());
+      assertEquals(key.getName(), "foo");
+      assertNotNull(key.getPublicKey());
+      assertEquals(key.getPublicKey().getAlgorithm(), "RSA");
+   }
+
+   @Test(dependsOnMethods = "testCreateKey")
+   public void testListKeys() {
+      List<SshKey> keys = api.getKeyPairApi().list();
+      assertFalse(keys.isEmpty(), "SSH key list should not be empty");
+   }
+
+   @Test(dependsOnMethods = "testCreateKey")
+   public void testGetKey() {
+      assertNotNull(api.getKeyPairApi().get(key.getId()), "The SSH key should not be null");
+   }
+
+   @Test(dependsOnMethods = "testCreateKey")
+   public void testEditKey() throws IOException {
+      String newKey = Strings2.toStringAndClose(getClass().getResourceAsStream("/ssh-dsa.txt"));
+      SshKey updated = api.getKeyPairApi().edit(key.getId(), newKey);
+
+      assertNotNull(updated.getPublicKey(), "The SSH key should have a public key");
+      assertEquals(updated.getPublicKey().getAlgorithm(), "DSA");
+   }
+
+   @Test(dependsOnMethods = { "testListKeys", "testGetKey", "testEditKey" })
+   public void testDeleteKey() throws IOException {
+      api.getKeyPairApi().delete(key.getId());
+      assertNull(api.getKeyPairApi().get(key.getId()), "The SSH key should not exist after deleting it");
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/224b2828/digitalocean/src/test/java/org/jclouds/digitalocean/features/KeyPairApiMockTest.java
----------------------------------------------------------------------
diff --git a/digitalocean/src/test/java/org/jclouds/digitalocean/features/KeyPairApiMockTest.java b/digitalocean/src/test/java/org/jclouds/digitalocean/features/KeyPairApiMockTest.java
new file mode 100644
index 0000000..9df8e6d
--- /dev/null
+++ b/digitalocean/src/test/java/org/jclouds/digitalocean/features/KeyPairApiMockTest.java
@@ -0,0 +1,216 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.digitalocean.features;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.fail;
+
+import java.util.List;
+
+import org.jclouds.digitalocean.DigitalOceanApi;
+import org.jclouds.digitalocean.domain.SshKey;
+import org.jclouds.digitalocean.internal.BaseDigitalOceanMockTest;
+import org.jclouds.rest.ResourceNotFoundException;
+import org.jclouds.util.Strings2;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableMultimap;
+import com.squareup.okhttp.mockwebserver.MockResponse;
+import com.squareup.okhttp.mockwebserver.MockWebServer;
+
+/**
+ * Mock tests for the {@link KeyPairApi} class.
+ * 
+ * @author Sergi Castro
+ * @author Ignasi Barrera
+ */
+@Test(groups = "unit", testName = "KeyPairApiMockTest")
+public class KeyPairApiMockTest extends BaseDigitalOceanMockTest {
+
+   public void testListKeys() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setBody(payloadFromResource("/keys.json")));
+
+      DigitalOceanApi api = api(server.getUrl("/"));
+      KeyPairApi keyPairApi = api.getKeyPairApi();
+
+      try {
+         List<SshKey> keys = keyPairApi.list();
+
+         assertRequestHasCommonFields(server.takeRequest(), "/ssh_keys");
+         assertEquals(keys.size(), 1);
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   public void testGetKey() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setBody(payloadFromResource("/key.json")));
+
+      DigitalOceanApi api = api(server.getUrl("/"));
+      KeyPairApi keyPairApi = api.getKeyPairApi();
+
+      try {
+         SshKey key = keyPairApi.get(47);
+
+         assertRequestHasCommonFields(server.takeRequest(), "/ssh_keys/47");
+         assertEquals(key.getId(), 47);
+         assertEquals(key.getName(), "my_key");
+         assertNotNull(key.getPublicKey());
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   public void testGetUnexistingKey() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setResponseCode(404));
+
+      DigitalOceanApi api = api(server.getUrl("/"));
+      KeyPairApi keyPairApi = api.getKeyPairApi();
+
+      try {
+         SshKey key = keyPairApi.get(47);
+
+         assertRequestHasCommonFields(server.takeRequest(), "/ssh_keys/47");
+         assertNull(key);
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   @Test
+   public void testCreateKey() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setBody(payloadFromResource("/key.json")));
+
+      DigitalOceanApi api = api(server.getUrl("/"));
+      KeyPairApi keyPairApi = api.getKeyPairApi();
+
+      try {
+         String publicKey = Strings2.toStringAndClose(getClass().getResourceAsStream("/ssh-rsa.txt"));
+         SshKey key = keyPairApi.create("my_key", publicKey);
+
+         assertRequestHasParameters(server.takeRequest(), "/ssh_keys/new",
+               ImmutableMultimap.of("name", "my_key", "ssh_pub_key", publicKey));
+
+         assertEquals(key.getId(), 47);
+         assertEquals(key.getName(), "my_key");
+         assertNotNull(key.getPublicKey());
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   @Test
+   public void testEditKey() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setBody(payloadFromResource("/key.json")));
+
+      DigitalOceanApi api = api(server.getUrl("/"));
+      KeyPairApi keyPairApi = api.getKeyPairApi();
+
+      try {
+         String publicKey = Strings2.toStringAndClose(getClass().getResourceAsStream("/ssh-rsa.txt"));
+         SshKey key = keyPairApi.edit(47, publicKey);
+
+         assertRequestHasParameters(server.takeRequest(), "/ssh_keys/47/edit",
+               ImmutableMultimap.of("ssh_pub_key", publicKey));
+
+         assertEquals(key.getId(), 47);
+         assertEquals(key.getName(), "my_key");
+         assertNotNull(key.getPublicKey());
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   @Test
+   public void testEditUnexistingKey() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setResponseCode(404));
+
+      DigitalOceanApi api = api(server.getUrl("/"));
+      KeyPairApi keyPairApi = api.getKeyPairApi();
+
+      try {
+         String publicKey = Strings2.toStringAndClose(getClass().getResourceAsStream("/ssh-rsa.txt"));
+
+         try {
+            keyPairApi.edit(47, publicKey);
+            fail("Edit key should fail on 404");
+         } catch (ResourceNotFoundException ex) {
+            // Expected exception
+         }
+
+         assertRequestHasParameters(server.takeRequest(), "/ssh_keys/47/edit",
+               ImmutableMultimap.of("ssh_pub_key", publicKey));
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   @Test
+   public void testDeleteKey() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse());
+
+      DigitalOceanApi api = api(server.getUrl("/"));
+      KeyPairApi keyPairApi = api.getKeyPairApi();
+
+      try {
+         keyPairApi.delete(47);
+
+         assertRequestHasCommonFields(server.takeRequest(), "/ssh_keys/47/destroy");
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   @Test
+   public void testDeleteUnexistingKey() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setResponseCode(404));
+
+      DigitalOceanApi api = api(server.getUrl("/"));
+      KeyPairApi keyPairApi = api.getKeyPairApi();
+
+      try {
+         try {
+            keyPairApi.delete(47);
+            fail("Delete key should fail on 404");
+         } catch (ResourceNotFoundException ex) {
+            // Expected exception
+         }
+
+         assertRequestHasCommonFields(server.takeRequest(), "/ssh_keys/47/destroy");
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/224b2828/digitalocean/src/test/java/org/jclouds/digitalocean/features/RegionApiLiveTest.java
----------------------------------------------------------------------
diff --git a/digitalocean/src/test/java/org/jclouds/digitalocean/features/RegionApiLiveTest.java b/digitalocean/src/test/java/org/jclouds/digitalocean/features/RegionApiLiveTest.java
new file mode 100644
index 0000000..5e85502
--- /dev/null
+++ b/digitalocean/src/test/java/org/jclouds/digitalocean/features/RegionApiLiveTest.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.digitalocean.features;
+
+import static org.testng.Assert.assertFalse;
+
+import java.util.List;
+
+import org.jclouds.digitalocean.domain.Region;
+import org.jclouds.digitalocean.internal.BaseDigitalOceanLiveTest;
+import org.testng.annotations.Test;
+
+/**
+ * Live tests for the {@link RegionApi} class.
+ * 
+ * @author Sergi Castro
+ * @author Ignasi Barrera
+ */
+@Test(groups = "live", testName = "RegionApiLiveTest")
+public class RegionApiLiveTest extends BaseDigitalOceanLiveTest {
+
+   private RegionApi regionApi;
+
+   @Override
+   protected void initialize() {
+      super.initialize();
+      regionApi = api.getRegionApi();
+   }
+
+   public void testListRegions() {
+      List<Region> regions = regionApi.list();
+
+      assertFalse(regions.isEmpty(), "Region list should not be empty");
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/224b2828/digitalocean/src/test/java/org/jclouds/digitalocean/features/RegionApiMockTest.java
----------------------------------------------------------------------
diff --git a/digitalocean/src/test/java/org/jclouds/digitalocean/features/RegionApiMockTest.java b/digitalocean/src/test/java/org/jclouds/digitalocean/features/RegionApiMockTest.java
new file mode 100644
index 0000000..915c2b6
--- /dev/null
+++ b/digitalocean/src/test/java/org/jclouds/digitalocean/features/RegionApiMockTest.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.digitalocean.features;
+
+import static org.testng.Assert.assertEquals;
+
+import java.util.List;
+
+import org.jclouds.digitalocean.DigitalOceanApi;
+import org.jclouds.digitalocean.domain.Region;
+import org.jclouds.digitalocean.internal.BaseDigitalOceanMockTest;
+import org.testng.annotations.Test;
+
+import com.squareup.okhttp.mockwebserver.MockResponse;
+import com.squareup.okhttp.mockwebserver.MockWebServer;
+
+/**
+ * Mock tests for the {@link RegionApi} class.
+ * 
+ * @author Sergi Castro
+ * @author Ignasi Barrera
+ */
+@Test(groups = "unit", testName = "RegionApiMockTest")
+public class RegionApiMockTest extends BaseDigitalOceanMockTest {
+
+   public void testListRegions() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setBody(payloadFromResource("/regions.json")));
+
+      DigitalOceanApi api = api(server.getUrl("/"));
+      RegionApi regionApi = api.getRegionApi();
+
+      try {
+         List<Region> regions = regionApi.list();
+
+         assertRequestHasCommonFields(server.takeRequest(), "/regions");
+         assertEquals(regions.size(), 4);
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/224b2828/digitalocean/src/test/java/org/jclouds/digitalocean/features/SizeApiLiveTest.java
----------------------------------------------------------------------
diff --git a/digitalocean/src/test/java/org/jclouds/digitalocean/features/SizeApiLiveTest.java b/digitalocean/src/test/java/org/jclouds/digitalocean/features/SizeApiLiveTest.java
new file mode 100644
index 0000000..b8b63cd
--- /dev/null
+++ b/digitalocean/src/test/java/org/jclouds/digitalocean/features/SizeApiLiveTest.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.digitalocean.features;
+
+import static org.testng.Assert.assertFalse;
+
+import java.util.List;
+
+import org.jclouds.digitalocean.domain.Size;
+import org.jclouds.digitalocean.internal.BaseDigitalOceanLiveTest;
+import org.testng.annotations.Test;
+
+/**
+ * Live tests for the {@link SizesApi} class.
+ * 
+ * @author Sergi Castro
+ * @author Ignasi Barrera
+ */
+@Test(groups = "live", testName = "SizeApiLiveTest")
+public class SizeApiLiveTest extends BaseDigitalOceanLiveTest {
+
+   private SizesApi sizesApi;
+
+   @Override
+   protected void initialize() {
+      super.initialize();
+      sizesApi = api.getSizesApi();
+   }
+
+   public void testListSizes() {
+      List<Size> sizes = sizesApi.list();
+
+      assertFalse(sizes.isEmpty(), "Size list should not be empty");
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/224b2828/digitalocean/src/test/java/org/jclouds/digitalocean/features/SizesApiMockTest.java
----------------------------------------------------------------------
diff --git a/digitalocean/src/test/java/org/jclouds/digitalocean/features/SizesApiMockTest.java b/digitalocean/src/test/java/org/jclouds/digitalocean/features/SizesApiMockTest.java
new file mode 100644
index 0000000..270bdfc
--- /dev/null
+++ b/digitalocean/src/test/java/org/jclouds/digitalocean/features/SizesApiMockTest.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.digitalocean.features;
+
+import static org.testng.Assert.assertEquals;
+
+import java.util.List;
+
+import org.jclouds.digitalocean.DigitalOceanApi;
+import org.jclouds.digitalocean.domain.Size;
+import org.jclouds.digitalocean.internal.BaseDigitalOceanMockTest;
+import org.testng.annotations.Test;
+
+import com.squareup.okhttp.mockwebserver.MockResponse;
+import com.squareup.okhttp.mockwebserver.MockWebServer;
+
+/**
+ * Mock tests for the {@link SizesApi} class.
+ * 
+ * @author Sergi Castro
+ * @author Ignasi Barrera
+ */
+@Test(groups = "unit", testName = "SizesApiMockTest")
+public class SizesApiMockTest extends BaseDigitalOceanMockTest {
+
+   public void testListSizes() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setBody(payloadFromResource("/sizes.json")));
+
+      DigitalOceanApi api = api(server.getUrl("/"));
+      SizesApi sizesApi = api.getSizesApi();
+
+      try {
+         List<Size> sizes = sizesApi.list();
+
+         assertRequestHasCommonFields(server.takeRequest(), "/sizes");
+         assertEquals(sizes.size(), 4);
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/224b2828/digitalocean/src/test/java/org/jclouds/digitalocean/http/ResponseStatusFromPayloadHttpCommandExecutorServiceTest.java
----------------------------------------------------------------------
diff --git a/digitalocean/src/test/java/org/jclouds/digitalocean/http/ResponseStatusFromPayloadHttpCommandExecutorServiceTest.java b/digitalocean/src/test/java/org/jclouds/digitalocean/http/ResponseStatusFromPayloadHttpCommandExecutorServiceTest.java
new file mode 100644
index 0000000..6fed32a
--- /dev/null
+++ b/digitalocean/src/test/java/org/jclouds/digitalocean/http/ResponseStatusFromPayloadHttpCommandExecutorServiceTest.java
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.digitalocean.http;
+
+import static org.jclouds.Constants.PROPERTY_MAX_RETRIES;
+import static org.jclouds.digitalocean.http.ResponseStatusFromPayloadHttpCommandExecutorService.ACCESS_DENIED;
+import static org.jclouds.digitalocean.http.ResponseStatusFromPayloadHttpCommandExecutorService.NOT_FOUND;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+
+import java.util.Properties;
+
+import org.jclouds.digitalocean.DigitalOceanApi;
+import org.jclouds.digitalocean.features.ImageApi;
+import org.jclouds.digitalocean.internal.BaseDigitalOceanMockTest;
+import org.jclouds.http.HttpResponseException;
+import org.jclouds.rest.AuthorizationException;
+import org.jclouds.rest.ResourceNotFoundException;
+import org.testng.annotations.Test;
+
+import com.squareup.okhttp.mockwebserver.MockResponse;
+import com.squareup.okhttp.mockwebserver.MockWebServer;
+
+/**
+ * Mock tests for the {@link ResponseStatusFromPayloadHttpCommandExecutorService} class.
+ * 
+ * @author Sergi Castro
+ * @author Ignasi Barrera
+ */
+@Test(groups = "unit", testName = "ResponseStatusFromPayloadHttpCommandExecutorServiceTest")
+public class ResponseStatusFromPayloadHttpCommandExecutorServiceTest extends BaseDigitalOceanMockTest {
+
+   @Override
+   protected Properties setupProperties() {
+      Properties properties = super.setupProperties();
+      properties.setProperty(PROPERTY_MAX_RETRIES, "1");
+      return properties;
+   }
+
+   public void testAccessDenied() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setBody(payloadFromResource("/access-denied.json")));
+
+      DigitalOceanApi api = api(server.getUrl("/"));
+      ImageApi imageApi = api.getImageApi();
+
+      try {
+         imageApi.list();
+         fail("Request should have failed");
+      } catch (Exception ex) {
+         assertTrue(ex instanceof AuthorizationException, "Exception should be an AuthorizationException");
+         assertEquals(ex.getMessage(), ACCESS_DENIED);
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   public void testNotFound() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setBody(payloadFromResource("/not-found.json")));
+
+      DigitalOceanApi api = api(server.getUrl("/"));
+      ImageApi imageApi = api.getImageApi();
+
+      try {
+         imageApi.list();
+         fail("Request should have failed");
+      } catch (Exception ex) {
+         assertTrue(ex instanceof ResourceNotFoundException, "Exception should be a ResourceNotFoundException");
+         assertEquals(ex.getMessage(), NOT_FOUND);
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+
+   public void testInternalServerError() throws Exception {
+      MockWebServer server = mockWebServer();
+      server.enqueue(new MockResponse().setBody(payloadFromResource("/error.json")));
+      // Response to be sent for the retried request
+      server.enqueue(new MockResponse().setBody(payloadFromResource("/error.json")));
+
+      DigitalOceanApi api = api(server.getUrl("/"));
+      ImageApi imageApi = api.getImageApi();
+
+      try {
+         imageApi.list();
+         fail("Request should have failed after retrying");
+      } catch (Exception ex) {
+         assertTrue(ex instanceof HttpResponseException, "Exception should be an HttpResponseException");
+         HttpResponseException exception = HttpResponseException.class.cast(ex);
+         assertEquals(exception.getResponse().getStatusCode(), 500);
+         assertEquals(exception.getMessage(), "No Image Found");
+      } finally {
+         api.close();
+         server.shutdown();
+      }
+   }
+}