http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/test/java/org/jclouds/chef/functions/ParseSearchDataBagItemFromJsonTest.java
----------------------------------------------------------------------
diff --git 
a/apis/chef/src/test/java/org/jclouds/chef/functions/ParseSearchDataBagItemFromJsonTest.java
 
b/apis/chef/src/test/java/org/jclouds/chef/functions/ParseSearchDataBagItemFromJsonTest.java
new file mode 100644
index 0000000..3daf3a8
--- /dev/null
+++ 
b/apis/chef/src/test/java/org/jclouds/chef/functions/ParseSearchDataBagItemFromJsonTest.java
@@ -0,0 +1,62 @@
+/*
+ * 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.chef.functions;
+
+import static org.testng.Assert.assertEquals;
+
+import java.io.IOException;
+
+import org.jclouds.chef.ChefApiMetadata;
+import org.jclouds.chef.config.ChefParserModule;
+import org.jclouds.chef.domain.DatabagItem;
+import org.jclouds.chef.domain.SearchResult;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.json.config.GsonModule;
+import org.jclouds.rest.annotations.ApiVersion;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.Test;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+
+@Test(groups = { "unit" })
+public class ParseSearchDataBagItemFromJsonTest {
+   private ParseSearchDatabagFromJson handler;
+
+   @BeforeTest
+   protected void setUpInjector() throws IOException {
+      Injector injector = Guice.createInjector(new AbstractModule() {
+         @Override
+         protected void configure() {
+            
bind(String.class).annotatedWith(ApiVersion.class).toInstance(ChefApiMetadata.DEFAULT_API_VERSION);
+         }
+      }, new ChefParserModule(), new GsonModule());
+
+      handler = injector.getInstance(ParseSearchDatabagFromJson.class);
+   }
+
+   public void test1() {
+      String itemJson = "{\"my_key\":\"my_data\"}";
+      String searchJson = "{\"rows\":[{\"raw_data\": 
{\"id\":\"item1\",\"my_key\":\"my_data\"}}]}";
+      DatabagItem item = new DatabagItem("item1", itemJson);
+      SearchResult<DatabagItem> result = 
handler.apply(HttpResponse.builder().statusCode(200).message("ok")
+            .payload(searchJson).build());
+      assertEquals(result.size(), 1);
+      assertEquals(result.iterator().next(), item);
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/test/java/org/jclouds/chef/functions/ParseUploadSandboxFromJsonTest.java
----------------------------------------------------------------------
diff --git 
a/apis/chef/src/test/java/org/jclouds/chef/functions/ParseUploadSandboxFromJsonTest.java
 
b/apis/chef/src/test/java/org/jclouds/chef/functions/ParseUploadSandboxFromJsonTest.java
new file mode 100644
index 0000000..b92fb3a
--- /dev/null
+++ 
b/apis/chef/src/test/java/org/jclouds/chef/functions/ParseUploadSandboxFromJsonTest.java
@@ -0,0 +1,87 @@
+/*
+ * 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.chef.functions;
+
+import static com.google.common.io.BaseEncoding.base16;
+import static com.google.common.primitives.Bytes.asList;
+import static org.testng.Assert.assertEquals;
+
+import java.io.IOException;
+import java.net.URI;
+
+import org.jclouds.chef.ChefApiMetadata;
+import org.jclouds.chef.config.ChefParserModule;
+import org.jclouds.chef.domain.ChecksumStatus;
+import org.jclouds.chef.domain.UploadSandbox;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.http.functions.ParseJson;
+import org.jclouds.json.config.GsonModule;
+import org.jclouds.rest.annotations.ApiVersion;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.Test;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Key;
+import com.google.inject.TypeLiteral;
+
+/**
+ * Tests behavior of {@code ParseUploadSiteFromJson}
+ */
+@Test(groups = { "unit" }, singleThreaded = true)
+public class ParseUploadSandboxFromJsonTest {
+
+   private ParseJson<UploadSandbox> handler;
+   private Injector injector;
+
+   @BeforeTest
+   protected void setUpInjector() throws IOException {
+      injector = Guice.createInjector(new AbstractModule() {
+         @Override
+         protected void configure() {
+            
bind(String.class).annotatedWith(ApiVersion.class).toInstance(ChefApiMetadata.DEFAULT_API_VERSION);
+         }
+      }, new ChefParserModule(), new GsonModule());
+
+      handler = injector.getInstance(Key.get(new 
TypeLiteral<ParseJson<UploadSandbox>>() {
+      }));
+   }
+
+   public void test() {
+      assertEquals(
+            handler.apply(HttpResponse.builder().statusCode(200).message("ok")
+                  
.payload(ParseUploadSandboxFromJsonTest.class.getResourceAsStream("/upload-site.json")).build()),
+            UploadSandbox
+                  .builder()
+                  .uri(URI
+                        
.create("https://api.opscode.com/organizations/jclouds/sandboxes/d454f71e2a5f400c808d0c5d04c2c88c";))
+                  .checksum(
+                        
asList(base16().lowerCase().decode("0c5ecd7788cf4f6c7de2a57193897a6c")),
+                        ChecksumStatus
+                              .builder()
+                              .url(URI
+                                    
.create("https://s3.amazonaws.com/opscode-platform-production-data/organization-486ca3ac66264fea926aa0b4ff74341c/sandbox-d454f71e2a5f400c808d0c5d04c2c88c/checksum-0c5ecd7788cf4f6c7de2a57193897a6c?AWSAccessKeyId=AKIAJOZTD2N26S7W6APA&Expires=1277344702&Signature=FtKyqvYEjhhEKmRY%2B0M8aGPMM7g%3D";))
+                              .needsUpload(true).build())
+                  
.checksum(asList(base16().lowerCase().decode("0189e76ccc476701d6b374e5a1a27347")),
+                        ChecksumStatus.builder().build())
+                  
.checksum(asList(base16().lowerCase().decode("1dda05ed139664f1f89b9dec482b77c0")),
+                        
ChecksumStatus.builder().build()).sandboxId("d454f71e2a5f400c808d0c5d04c2c88c").build()
+
+      );
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/test/java/org/jclouds/chef/functions/RunListForGroupTest.java
----------------------------------------------------------------------
diff --git 
a/apis/chef/src/test/java/org/jclouds/chef/functions/RunListForGroupTest.java 
b/apis/chef/src/test/java/org/jclouds/chef/functions/RunListForGroupTest.java
new file mode 100644
index 0000000..28b8833
--- /dev/null
+++ 
b/apis/chef/src/test/java/org/jclouds/chef/functions/RunListForGroupTest.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.chef.functions;
+
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+import static org.testng.Assert.assertEquals;
+
+import java.io.IOException;
+
+import org.jclouds.chef.ChefApi;
+import org.jclouds.chef.ChefApiMetadata;
+import org.jclouds.chef.config.ChefParserModule;
+import org.jclouds.chef.domain.Client;
+import org.jclouds.chef.domain.DatabagItem;
+import org.jclouds.json.Json;
+import org.jclouds.json.config.GsonModule;
+import org.jclouds.rest.annotations.Api;
+import org.jclouds.rest.annotations.ApiVersion;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+import com.google.inject.AbstractModule;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+
+@Test(groups = "unit", testName = "RunListForGroupTest")
+public class RunListForGroupTest {
+   private Injector injector = Guice.createInjector(new AbstractModule() {
+      @Override
+      protected void configure() {
+         
bind(String.class).annotatedWith(ApiVersion.class).toInstance(ChefApiMetadata.DEFAULT_API_VERSION);
+      }
+   }, new ChefParserModule(), new GsonModule());
+
+   private Json json = injector.getInstance(Json.class);
+
+   @Test(expectedExceptions = IllegalStateException.class)
+   public void testWhenNoDatabagItem() throws IOException {
+      ChefApi chefApi = createMock(ChefApi.class);
+      Client client = createMock(Client.class);
+
+      RunListForGroup fn = new RunListForGroup(new 
BootstrapConfigForGroup("jclouds", chefApi), json);
+
+      expect(chefApi.getDatabagItem("jclouds", "foo")).andReturn(null);
+
+      replay(client);
+      replay(chefApi);
+
+      fn.apply("foo");
+
+      verify(client);
+      verify(chefApi);
+   }
+
+   @Test
+   public void testReadRunList() throws IOException {
+      ChefApi chefApi = createMock(ChefApi.class);
+      Api api = createMock(Api.class);
+
+      RunListForGroup fn = new RunListForGroup(new 
BootstrapConfigForGroup("jclouds", chefApi), json);
+      DatabagItem config = new DatabagItem("foo",
+            
"{\"tomcat6\":{\"ssl_port\":8433},\"run_list\":[\"recipe[apache2]\",\"role[webserver]\"]}");
+
+      expect(chefApi.getDatabagItem("jclouds", "foo")).andReturn(config);
+
+      replay(api);
+      replay(chefApi);
+
+      assertEquals(fn.apply("foo"), ImmutableList.of("recipe[apache2]", 
"role[webserver]"));
+
+      verify(api);
+      verify(chefApi);
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/test/java/org/jclouds/chef/functions/UriForResourceTest.java
----------------------------------------------------------------------
diff --git 
a/apis/chef/src/test/java/org/jclouds/chef/functions/UriForResourceTest.java 
b/apis/chef/src/test/java/org/jclouds/chef/functions/UriForResourceTest.java
new file mode 100644
index 0000000..3285a1c
--- /dev/null
+++ b/apis/chef/src/test/java/org/jclouds/chef/functions/UriForResourceTest.java
@@ -0,0 +1,54 @@
+/*
+ * 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.chef.functions;
+
+import static org.testng.Assert.assertEquals;
+
+import java.net.URI;
+
+import org.jclouds.chef.domain.Resource;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Function;
+
+/**
+ * Tests behavior of {@code UriForResource}
+ */
+@Test(groups = { "unit" })
+public class UriForResourceTest {
+
+   @Test(expectedExceptions = NullPointerException.class)
+   public void testWithNullInput() {
+      Function<Object, URI> function = new UriForResource();
+      function.apply(null);
+   }
+
+   @Test(expectedExceptions = IllegalArgumentException.class)
+   public void testWithInvalidInput() {
+      Function<Object, URI> function = new UriForResource();
+      function.apply(new Object());
+   }
+
+   @Test
+   public void testWithValidResource() {
+      Function<Object, URI> function = new UriForResource();
+      Resource res = 
Resource.builder().name("test").url(URI.create("http://foo/bar";)).build();
+      URI result = function.apply(res);
+      assertEquals(res.getUrl().toString(), result.toString());
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/test/java/org/jclouds/chef/handlers/ChefApiErrorRetryHandlerTest.java
----------------------------------------------------------------------
diff --git 
a/apis/chef/src/test/java/org/jclouds/chef/handlers/ChefApiErrorRetryHandlerTest.java
 
b/apis/chef/src/test/java/org/jclouds/chef/handlers/ChefApiErrorRetryHandlerTest.java
new file mode 100644
index 0000000..5e99675
--- /dev/null
+++ 
b/apis/chef/src/test/java/org/jclouds/chef/handlers/ChefApiErrorRetryHandlerTest.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.chef.handlers;
+
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+
+import org.jclouds.http.HttpCommand;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.http.handlers.BackoffLimitedRetryHandler;
+import org.testng.annotations.Test;
+
+/**
+ * Tests behavior of {@code ChefClientErrorRetryHandler}
+ */
+@Test(groups = { "unit" })
+public class ChefApiErrorRetryHandlerTest {
+   @Test
+   public void test401DoesNotRetry() {
+
+      HttpCommand command = createMock(HttpCommand.class);
+      HttpResponse response = createMock(HttpResponse.class);
+      BackoffLimitedRetryHandler retry = 
createMock(BackoffLimitedRetryHandler.class);
+
+      expect(command.getFailureCount()).andReturn(0);
+      expect(response.getStatusCode()).andReturn(401).atLeastOnce();
+
+      replay(response);
+      replay(retry);
+      replay(command);
+
+      ChefApiErrorRetryHandler handler = new ChefApiErrorRetryHandler(retry);
+
+      assert !handler.shouldRetryRequest(command, response);
+
+      verify(retry);
+      verify(command);
+      verify(response);
+
+   }
+
+   @Test
+   public void test400DoesNotRetry() {
+
+      HttpCommand command = createMock(HttpCommand.class);
+      HttpResponse response = createMock(HttpResponse.class);
+      BackoffLimitedRetryHandler retry = 
createMock(BackoffLimitedRetryHandler.class);
+
+      expect(command.getFailureCount()).andReturn(0);
+      expect(response.getStatusCode()).andReturn(401).atLeastOnce();
+
+      replay(response);
+      replay(retry);
+      replay(command);
+
+      ChefApiErrorRetryHandler handler = new ChefApiErrorRetryHandler(retry);
+
+      assert !handler.shouldRetryRequest(command, response);
+
+      verify(retry);
+      verify(command);
+      verify(response);
+
+   }
+
+   @Test
+   public void testRetryOn400PutSandbox() {
+
+      HttpCommand command = createMock(HttpCommand.class);
+      BackoffLimitedRetryHandler retry = 
createMock(BackoffLimitedRetryHandler.class);
+
+      HttpRequest request = HttpRequest.builder().method("PUT")
+            
.endpoint("https://api.opscode.com/organizations/jclouds/sandboxes/bfd68d4052f44053b2e593a33b5e1cd5";)
+            .build();
+      HttpResponse response = HttpResponse
+            .builder()
+            .statusCode(400)
+            .message("400 Bad Request")
+            .payload(
+                  "{\"error\":[\"Cannot update sandbox 
bfd68d4052f44053b2e593a33b5e1cd5: checksum 9b7c23369f4b576451216c39f214af6c was 
not uploaded\"]}")
+            .build();
+
+      expect(command.getFailureCount()).andReturn(0);
+      expect(command.getCurrentRequest()).andReturn(request).atLeastOnce();
+      expect(retry.shouldRetryRequest(command, response)).andReturn(true);
+
+      replay(retry);
+      replay(command);
+
+      ChefApiErrorRetryHandler handler = new ChefApiErrorRetryHandler(retry);
+
+      assert handler.shouldRetryRequest(command, response);
+
+      verify(retry);
+      verify(command);
+
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/test/java/org/jclouds/chef/internal/BaseChefApiLiveTest.java
----------------------------------------------------------------------
diff --git 
a/apis/chef/src/test/java/org/jclouds/chef/internal/BaseChefApiLiveTest.java 
b/apis/chef/src/test/java/org/jclouds/chef/internal/BaseChefApiLiveTest.java
new file mode 100644
index 0000000..225e593
--- /dev/null
+++ b/apis/chef/src/test/java/org/jclouds/chef/internal/BaseChefApiLiveTest.java
@@ -0,0 +1,542 @@
+/*
+ * 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.chef.internal;
+
+import static com.google.common.base.Throwables.propagate;
+import static com.google.common.collect.Iterables.any;
+import static com.google.common.collect.Iterables.isEmpty;
+import static com.google.common.hash.Hashing.md5;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static org.jclouds.util.Predicates2.retry;
+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 static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collections;
+import java.util.List;
+import java.util.Properties;
+import java.util.Set;
+
+import org.jclouds.chef.ChefApi;
+import org.jclouds.chef.domain.ChecksumStatus;
+import org.jclouds.chef.domain.Client;
+import org.jclouds.chef.domain.CookbookDefinition;
+import org.jclouds.chef.domain.CookbookVersion;
+import org.jclouds.chef.domain.DatabagItem;
+import org.jclouds.chef.domain.Environment;
+import org.jclouds.chef.domain.Metadata;
+import org.jclouds.chef.domain.Node;
+import org.jclouds.chef.domain.Resource;
+import org.jclouds.chef.domain.Role;
+import org.jclouds.chef.domain.Sandbox;
+import org.jclouds.chef.domain.SearchResult;
+import org.jclouds.chef.domain.UploadSandbox;
+import org.jclouds.chef.options.CreateClientOptions;
+import org.jclouds.chef.options.SearchOptions;
+import org.jclouds.crypto.Pems;
+import org.jclouds.io.ByteStreams2;
+import org.jclouds.io.Payloads;
+import org.jclouds.io.payloads.FilePayload;
+import org.jclouds.rest.ResourceNotFoundException;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.hash.Hashing;
+import com.google.common.io.Closeables;
+import com.google.common.io.Files;
+import com.google.common.primitives.Bytes;
+
+/**
+ * Tests behavior of {@code ChefApi}
+ */
+@Test(groups = { "live", "integration" })
+public abstract class BaseChefApiLiveTest<A extends ChefApi> extends 
BaseChefLiveTest<A> {
+   public static final String PREFIX = "jcloudstest-" + 
System.getProperty("user.name");
+   public static final String ADMIN_PREFIX = "jcloudstest-adm-" + 
System.getProperty("user.name");
+   public static final String ENV_NODE = PREFIX + "-env-node";
+
+   // It may take a bit until the search index is populated
+   protected int maxWaitForIndexInMs = 60000;
+
+   // The id of the data bag item used in search tests
+   private String databagitemId;
+
+   public void testCreateNewCookbook() throws Exception {
+      // Define the file you want in the cookbook
+      File file = new File(System.getProperty("user.dir"), "pom.xml");
+      FilePayload content = Payloads.newFilePayload(file);
+      content.getContentMetadata().setContentType("application/x-binary");
+
+      // Get an md5 so that you can see if the server already has it or not
+      
content.getContentMetadata().setContentMD5(Files.asByteSource(file).hash(Hashing.md5()).asBytes());
+
+      // Note that java collections cannot effectively do equals or hashcodes 
on
+      // byte arrays, so let's convert to a list of bytes.
+      List<Byte> md5 = 
Bytes.asList(content.getContentMetadata().getContentMD5());
+
+      // Request an upload site for this file
+      UploadSandbox site = 
api.createUploadSandboxForChecksums(ImmutableSet.of(md5));
+      assertTrue(site.getChecksums().containsKey(md5), md5 + " not in " + 
site.getChecksums());
+
+      try {
+         // Upload the file contents, if still not uploaded
+         ChecksumStatus status = site.getChecksums().get(md5);
+         if (status.needsUpload()) {
+            api.uploadContent(status.getUrl(), content);
+         }
+         Sandbox sandbox = api.commitSandbox(site.getSandboxId(), true);
+         assertTrue(sandbox.isCompleted(), "Sandbox should be completed after 
uploading");
+      } catch (RuntimeException e) {
+         api.commitSandbox(site.getSandboxId(), false);
+         fail("Could not upload content");
+      }
+
+      // Create the metadata of the cookbook
+      Metadata metadata = Metadata.builder() //
+            .name(PREFIX) //
+            .version("0.0.0") //
+            .description("Jclouds test uploaded cookbook") //
+            .maintainer("jclouds") //
+            .maintainerEmail("[email protected]") //
+            .license("Apache 2.0") //
+            .build();
+
+      // Create a new cookbook
+      CookbookVersion cookbook = CookbookVersion.builder(PREFIX, "0.0.0") //
+            .metadata(metadata) //
+            .rootFile(Resource.builder().fromPayload(content).build()) //
+            .build();
+
+      // upload the cookbook to the remote server
+      api.updateCookbook(PREFIX, "0.0.0", cookbook);
+   }
+
+   public void testListCookbooks() throws Exception {
+      Set<String> cookbookNames = api.listCookbooks();
+      assertFalse(cookbookNames.isEmpty(), "No cookbooks were found");
+
+      for (String cookbookName : cookbookNames) {
+         Set<String> versions = api.listVersionsOfCookbook(cookbookName);
+         assertFalse(versions.isEmpty(), "There are no versions of the 
cookbook: " + cookbookName);
+
+         for (String version : api.listVersionsOfCookbook(cookbookName)) {
+            CookbookVersion cookbook = api.getCookbook(cookbookName, version);
+            assertNotNull(cookbook, "Could not get cookbook: " + cookbookName);
+         }
+      }
+   }
+
+   @Test(dependsOnMethods = "testListCookbooks")
+   public void testListCookbookVersionsWithChefService() throws Exception {
+      Iterable<? extends CookbookVersion> cookbooks = 
chefService.listCookbookVersions();
+      assertFalse(isEmpty(cookbooks), "No cookbooks were found");
+   }
+
+   @Test(dependsOnMethods = "testListCookbookVersionsWithChefService")
+   public void testDownloadCookbooks() throws Exception {
+      Iterable<? extends CookbookVersion> cookbooks = 
chefService.listCookbookVersions();
+      for (CookbookVersion cookbook : cookbooks) {
+         for (Resource resource : ImmutableList.<Resource> 
builder().addAll(cookbook.getDefinitions())
+               
.addAll(cookbook.getFiles()).addAll(cookbook.getLibraries()).addAll(cookbook.getSuppliers())
+               
.addAll(cookbook.getRecipes()).addAll(cookbook.getResources()).addAll(cookbook.getRootFiles())
+               .addAll(cookbook.getTemplates()).build()) {
+
+            InputStream stream = api.getResourceContents(resource);
+            assertNotNull(stream, "Resource contents are null for resource: " 
+ resource.getName());
+
+            byte[] md5 = ByteStreams2.hashAndClose(stream, md5()).asBytes();
+            assertEquals(md5, resource.getChecksum());
+         }
+      }
+   }
+
+   @Test(dependsOnMethods = "testCreateNewCookbook")
+   public void testUpdateCookbook() throws Exception {
+      CookbookVersion cookbook = api.getCookbook(PREFIX, "0.0.0");
+      assertNotNull(cookbook, "Cookbook not found: " + PREFIX);
+      assertNotNull(api.updateCookbook(PREFIX, "0.0.0", cookbook), "Updated 
cookbook was null");
+   }
+
+   @Test(dependsOnMethods = { "testCreateNewCookbook", "testUpdateCookbook" })
+   public void testDeleteCookbook() throws Exception {
+      assertNotNull(api.deleteCookbook(PREFIX, "0.0.0"), "Deleted cookbook was 
null");
+   }
+
+   @Test
+   public void testCreateClient() throws Exception {
+      api.deleteClient(PREFIX);
+      String credential = Pems.pem(api.createClient(PREFIX).getPrivateKey());
+      assertClientCreated(PREFIX, credential);
+   }
+
+   @Test
+   public void testCreateAdminClient() throws Exception {
+      api.deleteClient(ADMIN_PREFIX);
+      String credential = Pems.pem(api.createClient(ADMIN_PREFIX, 
CreateClientOptions.Builder.admin()).getPrivateKey());
+      assertClientCreated(ADMIN_PREFIX, credential);
+   }
+
+   @Test(dependsOnMethods = "testCreateClient")
+   public void testGenerateKeyForClient() throws Exception {
+      String credential = 
Pems.pem(api.generateKeyForClient(PREFIX).getPrivateKey());
+      assertClientCreated(PREFIX, credential);
+   }
+
+   @Test
+   public void testListNodes() throws Exception {
+      Set<String> nodes = api.listNodes();
+      assertNotNull(nodes, "No nodes were found");
+   }
+
+   @Test(dependsOnMethods = "testCreateRole")
+   public void testCreateNode() throws Exception {
+      api.deleteNode(PREFIX);
+      api.createNode(Node.builder().name(PREFIX).runListElement("role[" + 
PREFIX + "]").environment("_default").build());
+      Node node = api.getNode(PREFIX);
+      // TODO check recipes
+      assertNotNull(node, "Created node should not be null");
+      Set<String> nodes = api.listNodes();
+      assertTrue(nodes.contains(PREFIX), String.format("node %s not in %s", 
PREFIX, nodes));
+   }
+
+   @Test(dependsOnMethods = "testCreateNode")
+   public void testUpdateNode() throws Exception {
+      for (String nodename : api.listNodes()) {
+         Node node = api.getNode(nodename);
+         api.updateNode(node);
+      }
+   }
+
+   @Test
+   public void testListRoles() throws Exception {
+      Set<String> roles = api.listRoles();
+      assertNotNull(roles, "Role list was null");
+   }
+
+   @Test
+   public void testCreateRole() throws Exception {
+      api.deleteRole(PREFIX);
+      
api.createRole(Role.builder().name(PREFIX).runListElement("recipe[java]").build());
+      Role role = api.getRole(PREFIX);
+      assertNotNull(role, "Created role should not be null");
+      assertEquals(role.getName(), PREFIX);
+      assertEquals(role.getRunList(), Collections.singleton("recipe[java]"));
+   }
+
+   @Test(dependsOnMethods = "testCreateRole")
+   public void testUpdateRole() throws Exception {
+      for (String rolename : api.listRoles()) {
+         Role role = api.getRole(rolename);
+         api.updateRole(role);
+      }
+   }
+
+   @Test
+   public void testListDatabags() throws Exception {
+      Set<String> databags = api.listDatabags();
+      assertNotNull(databags, "Data bag list was null");
+   }
+
+   @Test
+   public void testCreateDatabag() throws Exception {
+      api.deleteDatabag(PREFIX);
+      api.createDatabag(PREFIX);
+   }
+
+   @Test(dependsOnMethods = "testCreateDatabagItem")
+   public void testListDatabagItems() throws Exception {
+      Set<String> databagItems = api.listDatabagItems(PREFIX);
+      assertNotNull(databagItems, "Data bag item list was null");
+   }
+
+   @Test(dependsOnMethods = "testCreateDatabag")
+   public void testCreateDatabagItem() throws Exception {
+      Properties config = new Properties();
+      config.setProperty("foo", "bar");
+      api.deleteDatabagItem(PREFIX, PREFIX);
+      DatabagItem databagItem = api.createDatabagItem(PREFIX, new 
DatabagItem("config", json.toJson(config)));
+      databagitemId = databagItem.getId();
+      assertNotNull(databagItem, "Created data bag item should not be null");
+      assertEquals(databagItem.getId(), "config");
+
+      // The databagItem json contains extra keys: (the name and the type if 
the
+      // item)
+      Properties props = json.fromJson(databagItem.toString(), 
Properties.class);
+      for (Object key : config.keySet()) {
+         assertTrue(props.containsKey(key));
+         assertEquals(config.get(key), props.get(key));
+      }
+   }
+
+   @Test(dependsOnMethods = "testCreateDatabagItem")
+   public void testUpdateDatabagItem() throws Exception {
+      for (String databagItemId : api.listDatabagItems(PREFIX)) {
+         DatabagItem databagItem = api.getDatabagItem(PREFIX, databagItemId);
+         api.updateDatabagItem(PREFIX, databagItem);
+      }
+   }
+
+   @Test(dependsOnMethods = "testSearchDatabagWithOptions")
+   public void testDeleteDatabagItem() throws Exception {
+      for (String databagItemId : api.listDatabagItems(PREFIX)) {
+         DatabagItem databagItem = api.deleteDatabagItem(PREFIX, 
databagItemId);
+         assertNotNull(databagItem, "Deleted data bag item should not be 
null");
+         assertEquals(databagItem.getId(), databagItemId, "Deleted data bag 
item id must match the original id");
+         assertNull(api.getDatabagItem(PREFIX, databagItemId), "Data bag item 
should not exist");
+      }
+   }
+
+   @Test
+   public void testListSearchIndexes() throws Exception {
+      Set<String> indexes = api.listSearchIndexes();
+      assertNotNull(indexes, "The index list should not be null");
+      assertTrue(indexes.contains("node"));
+      assertTrue(indexes.contains("client"));
+      assertTrue(indexes.contains("role"));
+   }
+
+   @Test
+   public void testSearchNodes() throws Exception {
+      SearchResult<? extends Node> results = api.searchNodes();
+      assertNotNull(results, "Node result list should not be null");
+   }
+
+   @Test(dependsOnMethods = { "testListSearchIndexes", "testCreateNode" })
+   public void testSearchNodesWithOptions() throws Exception {
+      Predicate<SearchOptions> waitForIndex = retry(new 
Predicate<SearchOptions>() {
+         @Override
+         public boolean apply(SearchOptions input) {
+            SearchResult<? extends Node> results = api.searchNodes(input);
+            assertNotNull(results);
+            if (results.size() > 0) {
+               assertEquals(results.size(), 1);
+               assertEquals(results.iterator().next().getName(), PREFIX);
+               return true;
+            } else {
+               // The index may still not be populated
+               return false;
+            }
+         }
+      }, maxWaitForIndexInMs, 5000L, MILLISECONDS);
+
+      SearchOptions options = SearchOptions.Builder.query("name:" + PREFIX);
+      assertTrue(waitForIndex.apply(options));
+   }
+
+   @Test
+   public void testSearchClients() throws Exception {
+      SearchResult<? extends Client> results = api.searchClients();
+      assertNotNull(results, "Client result list should not be null");
+   }
+
+   @Test(dependsOnMethods = { "testListSearchIndexes", "testCreateClient" })
+   public void testSearchClientsWithOptions() throws Exception {
+      Predicate<SearchOptions> waitForIndex = retry(new 
Predicate<SearchOptions>() {
+         @Override
+         public boolean apply(SearchOptions input) {
+            SearchResult<? extends Client> results = api.searchClients(input);
+            assertNotNull(results);
+            if (results.size() > 0) {
+               assertEquals(results.size(), 1);
+               assertEquals(results.iterator().next().getName(), PREFIX);
+               return true;
+            } else {
+               // The index may still not be populated
+               return false;
+            }
+         }
+      }, maxWaitForIndexInMs, 5000L, MILLISECONDS);
+
+      SearchOptions options = SearchOptions.Builder.query("name:" + PREFIX);
+      assertTrue(waitForIndex.apply(options));
+   }
+
+   @Test
+   public void testSearchRoles() throws Exception {
+      SearchResult<? extends Role> results = api.searchRoles();
+      assertNotNull(results, "Role result list should not be null");
+   }
+
+   @Test(dependsOnMethods = { "testListSearchIndexes", "testCreateRole" })
+   public void testSearchRolesWithOptions() throws Exception {
+      Predicate<SearchOptions> waitForIndex = retry(new 
Predicate<SearchOptions>() {
+         @Override
+         public boolean apply(SearchOptions input) {
+            SearchResult<? extends Role> results = api.searchRoles(input);
+            assertNotNull(results);
+            if (results.size() > 0) {
+               assertEquals(results.size(), 1);
+               assertEquals(results.iterator().next().getName(), PREFIX);
+               return true;
+            } else {
+               // The index may still not be populated
+               return false;
+            }
+         }
+      }, maxWaitForIndexInMs, 5000L, MILLISECONDS);
+
+      SearchOptions options = SearchOptions.Builder.query("name:" + PREFIX);
+      assertTrue(waitForIndex.apply(options));
+   }
+
+   @Test(dependsOnMethods = { "testListSearchIndexes", "testCreateDatabagItem" 
})
+   public void testSearchDatabag() throws Exception {
+      SearchResult<? extends DatabagItem> results = 
api.searchDatabagItems(PREFIX);
+      assertNotNull(results, "Data bag item result list should not be null");
+   }
+
+   @Test(dependsOnMethods = { "testListSearchIndexes", "testCreateDatabagItem" 
})
+   public void testSearchDatabagWithOptions() throws Exception {
+      Predicate<SearchOptions> waitForIndex = retry(new 
Predicate<SearchOptions>() {
+         @Override
+         public boolean apply(SearchOptions input) {
+            SearchResult<? extends DatabagItem> results = 
api.searchDatabagItems(PREFIX, input);
+            assertNotNull(results);
+            if (results.size() > 0) {
+               assertEquals(results.size(), 1);
+               assertEquals(results.iterator().next().getId(), databagitemId);
+               return true;
+            } else {
+               // The index may still not be populated
+               return false;
+            }
+         }
+      }, maxWaitForIndexInMs, 5000L, MILLISECONDS);
+
+      SearchOptions options = SearchOptions.Builder.query("id:" + 
databagitemId);
+      assertTrue(waitForIndex.apply(options));
+   }
+
+   @Test(expectedExceptions = ResourceNotFoundException.class, 
dependsOnMethods = "testListSearchIndexes")
+   public void testSearchDatabagNotFound() throws Exception {
+      SearchResult<? extends DatabagItem> results = 
api.searchDatabagItems("whoopie");
+      assertNotNull(results, "Data bag item result list should not be null");
+   }
+
+   @Test
+   public void testCreateEnvironment() {
+      api.deleteEnvironment(PREFIX);
+      
api.createEnvironment(Environment.builder().name(PREFIX).description(PREFIX).build());
+      Environment env = api.getEnvironment(PREFIX);
+      assertNotNull(env, "Created environment should not be null");
+      assertEquals(env.getName(), PREFIX);
+      assertEquals(env.getDescription(), PREFIX);
+   }
+
+   @Test(dependsOnMethods = "testCreateEnvironment")
+   public void testListEnvironment() {
+      Set<String> envList = api.listEnvironments();
+      assertNotNull(envList, "Environment list was null");
+      assertTrue(envList.contains(PREFIX));
+   }
+
+   @Test(dependsOnMethods = "testCreateEnvironment")
+   public void testSearchEnvironments() throws Exception {
+      SearchResult<? extends Environment> results = api.searchEnvironments();
+      assertNotNull(results, "Environment result list was null");
+   }
+
+   @Test(dependsOnMethods = { "testListSearchIndexes", "testCreateEnvironment" 
})
+   public void testSearchEnvironmentsWithOptions() throws Exception {
+      Predicate<SearchOptions> waitForIndex = retry(new 
Predicate<SearchOptions>() {
+         @Override
+         public boolean apply(SearchOptions input) {
+            SearchResult<? extends Environment> results = 
api.searchEnvironments(input);
+            assertNotNull(results);
+            if (results.size() > 0) {
+               assertEquals(results.size(), 1);
+               assertEquals(results.iterator().next().getName(), PREFIX);
+               return true;
+            } else {
+               // The index may still not be populated
+               return false;
+            }
+         }
+      }, maxWaitForIndexInMs, 5000L, MILLISECONDS);
+
+      SearchOptions options = SearchOptions.Builder.query("name:" + PREFIX);
+      assertTrue(waitForIndex.apply(options));
+   }
+
+   @Test(dependsOnMethods = "testCreateEnvironment")
+   public void testListRecipesInEnvironment() {
+      Set<String> recipeList = api.listRecipesInEnvironment(PREFIX);
+      assertTrue(!recipeList.isEmpty());
+   }
+
+   @Test(dependsOnMethods = "testCreateEnvironment")
+   public void testListNodesInEnvironment() {
+      api.deleteNode(ENV_NODE);
+      api.createNode(Node.builder().name(ENV_NODE).runListElement("role[" + 
PREFIX + "]").environment(PREFIX).build());
+      Node node = api.getNode(ENV_NODE);
+      assertNotNull(node, "Created node should not be null");
+      Set<String> nodeList = api.listNodesInEnvironment(PREFIX);
+      assertTrue(!nodeList.isEmpty());
+   }
+
+   @Test(dependsOnMethods = "testCreateNewCookbook")
+   public void testListCookbooksInEnvironment() throws Exception {
+      Set<CookbookDefinition> cookbooks = 
api.listCookbooksInEnvironment("_default");
+      assertTrue(any(cookbooks, new Predicate<CookbookDefinition>() {
+         @Override
+         public boolean apply(CookbookDefinition input) {
+            return PREFIX.equals(input.getName());
+         }}), String.format("Cookbook %s not in %s", PREFIX, cookbooks));
+   }
+
+   @AfterClass(groups = { "live", "integration" })
+   @Override
+   public void tearDown() {
+      api.deleteClient(PREFIX);
+      api.deleteClient(ADMIN_PREFIX);
+      api.deleteNode(PREFIX);
+      api.deleteNode(ENV_NODE);
+      api.deleteRole(PREFIX);
+      api.deleteDatabag(PREFIX);
+      api.deleteEnvironment(PREFIX);
+      super.tearDown();
+   }
+
+   private void assertClientCreated(String identity, String credential) {
+      Properties overrides = super.setupProperties();
+      overrides.setProperty(provider + ".identity", identity);
+      overrides.setProperty(provider + ".credential", credential);
+
+      A clientApi = create(overrides, setupModules());
+
+      try {
+         Client client = clientApi.getClient(identity);
+         assertNotNull(client, "Client not found: " + identity);
+      } finally {
+         try {
+            Closeables.close(clientApi, true);
+         } catch (IOException e) {
+            throw propagate(e);
+         }
+      }
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/test/java/org/jclouds/chef/internal/BaseChefLiveTest.java
----------------------------------------------------------------------
diff --git 
a/apis/chef/src/test/java/org/jclouds/chef/internal/BaseChefLiveTest.java 
b/apis/chef/src/test/java/org/jclouds/chef/internal/BaseChefLiveTest.java
new file mode 100644
index 0000000..e314418
--- /dev/null
+++ b/apis/chef/src/test/java/org/jclouds/chef/internal/BaseChefLiveTest.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.chef.internal;
+
+import static org.jclouds.reflect.Types2.checkBound;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Properties;
+
+import org.jclouds.apis.BaseApiLiveTest;
+import org.jclouds.chef.ChefApi;
+import org.jclouds.chef.ChefService;
+import org.jclouds.json.Json;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Charsets;
+import com.google.common.base.Throwables;
+import com.google.common.io.Files;
+import com.google.common.reflect.TypeToken;
+import com.google.inject.Injector;
+import com.google.inject.Module;
+
+@Test(groups = "live")
+public abstract class BaseChefLiveTest<A extends ChefApi> extends 
BaseApiLiveTest<A> {
+
+   protected Injector injector;
+   protected ChefService chefService;
+   protected Json json;
+
+   protected BaseChefLiveTest() {
+      provider = "chef";
+   }
+
+   /**
+    * the credential is a path to the pem file.
+    */
+   @Override
+   protected Properties setupProperties() {
+      Properties overrides = super.setupProperties();
+      credential = setCredentialFromPemFile(overrides, identity, provider + 
".credential");
+      return overrides;
+   }
+
+   @Override
+   protected void initialize() {
+      super.initialize();
+      chefService = injector.getInstance(ChefService.class);
+      json = injector.getInstance(Json.class);
+   }
+
+   @Override
+   protected A create(Properties props, Iterable<Module> modules) {
+      injector = 
newBuilder().modules(modules).overrides(props).buildInjector();
+      return injector.getInstance(resolveApiClass());
+   }
+
+   protected String setCredentialFromPemFile(Properties overrides, String 
identity, String key) {
+      String val = null;
+      String credentialFromFile = null;
+      if (System.getProperties().containsKey("test." + key)) {
+         val = System.getProperty("test." + key);
+      } else {
+         val = System.getProperty("user.home") + "/.chef/" + identity + ".pem";
+      }
+      try {
+         credentialFromFile = Files.toString(new File(val), Charsets.UTF_8);
+      } catch (IOException e) {
+         throw Throwables.propagate(e);
+      }
+      overrides.setProperty(key, credentialFromFile);
+      return credentialFromFile;
+   }
+
+   @SuppressWarnings("unchecked")
+   private Class<A> resolveApiClass() {
+      return Class.class.cast(checkBound(new TypeToken<A>(getClass()) {
+         private static final long serialVersionUID = 1L;
+      }).getRawType());
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/test/java/org/jclouds/chef/internal/BaseChefServiceTest.java
----------------------------------------------------------------------
diff --git 
a/apis/chef/src/test/java/org/jclouds/chef/internal/BaseChefServiceTest.java 
b/apis/chef/src/test/java/org/jclouds/chef/internal/BaseChefServiceTest.java
new file mode 100644
index 0000000..e763d8f
--- /dev/null
+++ b/apis/chef/src/test/java/org/jclouds/chef/internal/BaseChefServiceTest.java
@@ -0,0 +1,99 @@
+/*
+ * 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.chef.internal;
+
+import static org.testng.Assert.assertEquals;
+
+import java.util.List;
+
+import org.jclouds.ContextBuilder;
+import org.jclouds.chef.ChefApiMetadata;
+import org.jclouds.chef.domain.BootstrapConfig;
+import org.jclouds.chef.filters.SignedHeaderAuthTest;
+import org.jclouds.chef.util.RunListBuilder;
+import org.jclouds.domain.JsonBall;
+import org.jclouds.logging.config.NullLoggingModule;
+import org.jclouds.rest.internal.BaseRestApiTest.MockModule;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.inject.Injector;
+import com.google.inject.Module;
+
+/**
+ * Unit tests for the <code>BaseChefService</code> class.
+ */
+@Test(groups = "unit", testName = "BaseChefServiceTest")
+public class BaseChefServiceTest {
+
+   private BaseChefService chefService;
+
+   @BeforeClass
+   public void setup() {
+      Injector injector = ContextBuilder.newBuilder(new ChefApiMetadata())
+            .credentials(SignedHeaderAuthTest.USER_ID, 
SignedHeaderAuthTest.PRIVATE_KEY)
+            .modules(ImmutableSet.<Module> of(new MockModule(), new 
NullLoggingModule())).buildInjector();
+
+      chefService = injector.getInstance(BaseChefService.class);
+   }
+
+   @Test(expectedExceptions = NullPointerException.class, 
expectedExceptionsMessageRegExp = "bootstrapConfig must not be null")
+   public void testBuildBootstrapConfigurationWithNullConfig() {
+      chefService.buildBootstrapConfiguration(null);
+   }
+
+   public void testBuildBootstrapConfigurationWithEmptyRunlist() {
+      BootstrapConfig bootstrapConfig = 
BootstrapConfig.builder().runList(ImmutableList.<String> of()).build();
+      String config = chefService.buildBootstrapConfiguration(bootstrapConfig);
+      assertEquals(config, "{\"run_list\":[]}");
+   }
+
+   public void testBuildBootstrapConfigurationWithRunlist() {
+      List<String> runlist = new 
RunListBuilder().addRecipe("apache2").addRole("webserver").build();
+      BootstrapConfig bootstrapConfig = 
BootstrapConfig.builder().runList(runlist).build();
+      String config = chefService.buildBootstrapConfiguration(bootstrapConfig);
+      assertEquals(config, 
"{\"run_list\":[\"recipe[apache2]\",\"role[webserver]\"]}");
+   }
+
+   public void testBuildBootstrapConfigurationWithRunlistAndEmptyAttributes() {
+      List<String> runlist = new 
RunListBuilder().addRecipe("apache2").addRole("webserver").build();
+      BootstrapConfig bootstrapConfig = 
BootstrapConfig.builder().runList(runlist).attributes(new JsonBall("{}"))
+            .build();
+      String config = chefService.buildBootstrapConfiguration(bootstrapConfig);
+      assertEquals(config, 
"{\"run_list\":[\"recipe[apache2]\",\"role[webserver]\"]}");
+   }
+
+   public void testBuildBootstrapConfigurationWithRunlistAndAttributes() {
+      List<String> runlist = new 
RunListBuilder().addRecipe("apache2").addRole("webserver").build();
+      BootstrapConfig bootstrapConfig = 
BootstrapConfig.builder().runList(runlist)
+            .attributes(new 
JsonBall("{\"tomcat6\":{\"ssl_port\":8433}}")).build();
+      String config = chefService.buildBootstrapConfiguration(bootstrapConfig);
+      assertEquals(config, 
"{\"tomcat6\":{\"ssl_port\":8433},\"run_list\":[\"recipe[apache2]\",\"role[webserver]\"]}");
+   }
+
+   public void 
testBuildBootstrapConfigurationWithRunlistAndAttributesAndEnvironment() {
+      List<String> runlist = new 
RunListBuilder().addRecipe("apache2").addRole("webserver").build();
+      BootstrapConfig bootstrapConfig = 
BootstrapConfig.builder().runList(runlist)
+            .attributes(new 
JsonBall("{\"tomcat6\":{\"ssl_port\":8433}}")).environment("env").build();
+      String config = chefService.buildBootstrapConfiguration(bootstrapConfig);
+      assertEquals(config,
+            
"{\"tomcat6\":{\"ssl_port\":8433},\"environment\":\"env\",\"run_list\":[\"recipe[apache2]\",\"role[webserver]\"]}");
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/test/java/org/jclouds/chef/internal/BaseStubbedOhaiLiveTest.java
----------------------------------------------------------------------
diff --git 
a/apis/chef/src/test/java/org/jclouds/chef/internal/BaseStubbedOhaiLiveTest.java
 
b/apis/chef/src/test/java/org/jclouds/chef/internal/BaseStubbedOhaiLiveTest.java
new file mode 100644
index 0000000..8779017
--- /dev/null
+++ 
b/apis/chef/src/test/java/org/jclouds/chef/internal/BaseStubbedOhaiLiveTest.java
@@ -0,0 +1,60 @@
+/*
+ * 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.chef.internal;
+
+import java.util.Map;
+
+import org.jclouds.apis.ApiMetadata;
+import org.jclouds.chef.ChefApi;
+import org.jclouds.chef.ChefApiMetadata;
+import org.jclouds.chef.config.ChefBootstrapModule;
+import org.jclouds.chef.config.ChefHttpApiModule;
+import org.jclouds.chef.config.ChefParserModule;
+import org.jclouds.domain.JsonBall;
+import org.jclouds.ohai.AutomaticSupplier;
+import org.jclouds.ohai.config.ConfiguresOhai;
+import org.jclouds.ohai.config.OhaiModule;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.inject.Module;
+
+@Test(groups = "live")
+@Deprecated
+public class BaseStubbedOhaiLiveTest extends BaseChefLiveTest<ChefApi> {
+
+   @ConfiguresOhai
+   static class TestOhaiModule extends OhaiModule {
+
+      @Override
+      protected Supplier<Map<String, JsonBall>> 
provideAutomatic(AutomaticSupplier in) {
+         return Suppliers.<Map<String, JsonBall>> 
ofInstance(ImmutableMap.of("foo", new JsonBall("bar")));
+      }
+   }
+
+   @Override
+   protected ApiMetadata createApiMetadata() {
+      return new ChefApiMetadata()
+            .toBuilder()
+            .defaultModules(
+                  ImmutableSet.<Class<? extends Module>> 
of(ChefHttpApiModule.class, ChefParserModule.class,
+                        ChefBootstrapModule.class, 
TestOhaiModule.class)).build();
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/test/java/org/jclouds/chef/strategy/internal/CleanupStaleNodesAndClientsImplLiveTest.java
----------------------------------------------------------------------
diff --git 
a/apis/chef/src/test/java/org/jclouds/chef/strategy/internal/CleanupStaleNodesAndClientsImplLiveTest.java
 
b/apis/chef/src/test/java/org/jclouds/chef/strategy/internal/CleanupStaleNodesAndClientsImplLiveTest.java
new file mode 100644
index 0000000..e8b249b
--- /dev/null
+++ 
b/apis/chef/src/test/java/org/jclouds/chef/strategy/internal/CleanupStaleNodesAndClientsImplLiveTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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.chef.strategy.internal;
+
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+
+import org.jclouds.chef.ChefApi;
+import org.jclouds.chef.internal.BaseChefLiveTest;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * Tests behavior of {@code CleanupStaleNodesAndClientsImpl} strategies
+ */
+@Test(groups = "live", testName = "CleanupStaleNodesAndClientsImplLiveTest")
+public class CleanupStaleNodesAndClientsImplLiveTest extends 
BaseChefLiveTest<ChefApi> {
+
+   private CreateNodeAndPopulateAutomaticAttributesImpl creator;
+   private CleanupStaleNodesAndClientsImpl strategy;
+
+   @Override
+   protected void initialize() {
+      super.initialize();
+      this.creator = 
injector.getInstance(CreateNodeAndPopulateAutomaticAttributesImpl.class);
+      this.strategy = 
injector.getInstance(CleanupStaleNodesAndClientsImpl.class);
+   }
+
+   @Test
+   public void testExecute() throws InterruptedException {
+      try {
+         creator.execute(prefix, ImmutableSet.<String> of());
+         // http://tickets.corp.opscode.com/browse/PL-522
+         // assert chef.nodeExists(prefix);
+         assertNotNull(api.getNode(prefix));
+         strategy.execute(prefix, 10);
+         assertNotNull(api.getNode(prefix));
+         Thread.sleep(1000);
+         strategy.execute(prefix, 1);
+         assertNull(api.getNode(prefix));
+      } finally {
+         api.deleteNode(prefix);
+      }
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/test/java/org/jclouds/chef/strategy/internal/CreateNodeAndPopulateAutomaticAttributesImplLiveTest.java
----------------------------------------------------------------------
diff --git 
a/apis/chef/src/test/java/org/jclouds/chef/strategy/internal/CreateNodeAndPopulateAutomaticAttributesImplLiveTest.java
 
b/apis/chef/src/test/java/org/jclouds/chef/strategy/internal/CreateNodeAndPopulateAutomaticAttributesImplLiveTest.java
new file mode 100644
index 0000000..60f2661
--- /dev/null
+++ 
b/apis/chef/src/test/java/org/jclouds/chef/strategy/internal/CreateNodeAndPopulateAutomaticAttributesImplLiveTest.java
@@ -0,0 +1,62 @@
+/*
+ * 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.chef.strategy.internal;
+
+import static org.testng.Assert.assertEquals;
+
+import java.util.Set;
+
+import org.jclouds.chef.ChefApi;
+import org.jclouds.chef.domain.Node;
+import org.jclouds.chef.internal.BaseChefLiveTest;
+import org.jclouds.ohai.config.OhaiModule.CurrentUserProvider;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * Tests behavior of {@code CreateNodeAndPopulateAutomaticAttributesImpl}
+ * strategies
+ */
+@Test(groups = "live", testName = 
"CreateNodeAndPopulateAutomaticAttributesImplLiveTest")
+public class CreateNodeAndPopulateAutomaticAttributesImplLiveTest extends 
BaseChefLiveTest<ChefApi> {
+
+   private CurrentUserProvider currentUserProvider;
+   private CreateNodeAndPopulateAutomaticAttributesImpl strategy;
+
+   @Override
+   protected void initialize() {
+      super.initialize();
+      this.currentUserProvider = 
injector.getInstance(CurrentUserProvider.class);
+      this.strategy = 
injector.getInstance(CreateNodeAndPopulateAutomaticAttributesImpl.class);
+   }
+
+   @Test
+   public void testExecute() {
+      Set<String> runList = ImmutableSet.of("role[" + prefix + "]");
+      try {
+         strategy.execute(prefix, runList);
+         Node node = api.getNode(prefix);
+         assertEquals(node.getName(), prefix);
+         assertEquals(node.getRunList(), runList);
+         
assertEquals(node.getAutomaticAttributes().get("current_user").toString(), 
currentUserProvider.get().toString());
+      } finally {
+         api.deleteNode(prefix);
+      }
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/test/java/org/jclouds/chef/strategy/internal/CreateNodeAndPopulateAutomaticAttributesImplTest.java
----------------------------------------------------------------------
diff --git 
a/apis/chef/src/test/java/org/jclouds/chef/strategy/internal/CreateNodeAndPopulateAutomaticAttributesImplTest.java
 
b/apis/chef/src/test/java/org/jclouds/chef/strategy/internal/CreateNodeAndPopulateAutomaticAttributesImplTest.java
new file mode 100644
index 0000000..3ba7110
--- /dev/null
+++ 
b/apis/chef/src/test/java/org/jclouds/chef/strategy/internal/CreateNodeAndPopulateAutomaticAttributesImplTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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.chef.strategy.internal;
+
+import static org.easymock.classextension.EasyMock.createMock;
+import static org.easymock.classextension.EasyMock.replay;
+import static org.easymock.classextension.EasyMock.verify;
+
+import java.util.Map;
+
+import org.jclouds.chef.ChefApi;
+import org.jclouds.chef.domain.Node;
+import org.jclouds.domain.JsonBall;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * Tests behavior of {@code CreateNodeAndPopulateAutomaticAttributesImpl}
+ */
+@Test(groups = "unit", testName = 
"CreateNodeAndPopulateAutomaticAttributesImplTest")
+public class CreateNodeAndPopulateAutomaticAttributesImplTest {
+
+   @Test
+   public void testWithNoRunlist() {
+      ChefApi chef = createMock(ChefApi.class);
+
+      Supplier<Map<String, JsonBall>> automaticSupplier = 
Suppliers.<Map<String, JsonBall>> ofInstance(ImmutableMap.<String, JsonBall> 
of());
+
+      Node nodeWithAutomatic = 
Node.builder().name("name").environment("_default")
+            .automaticAttributes(automaticSupplier.get()).build();
+
+      chef.createNode(nodeWithAutomatic);
+
+      replay(chef);
+
+      CreateNodeAndPopulateAutomaticAttributesImpl updater = new 
CreateNodeAndPopulateAutomaticAttributesImpl(chef,
+            automaticSupplier);
+
+      updater.execute("name", ImmutableSet.<String> of());
+      verify(chef);
+
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/test/java/org/jclouds/chef/strategy/internal/DeleteAllApisAndNodesInListImplLiveTest.java
----------------------------------------------------------------------
diff --git 
a/apis/chef/src/test/java/org/jclouds/chef/strategy/internal/DeleteAllApisAndNodesInListImplLiveTest.java
 
b/apis/chef/src/test/java/org/jclouds/chef/strategy/internal/DeleteAllApisAndNodesInListImplLiveTest.java
new file mode 100644
index 0000000..6f0e595
--- /dev/null
+++ 
b/apis/chef/src/test/java/org/jclouds/chef/strategy/internal/DeleteAllApisAndNodesInListImplLiveTest.java
@@ -0,0 +1,64 @@
+/*
+ * 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.chef.strategy.internal;
+
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+
+import org.jclouds.chef.ChefApi;
+import org.jclouds.chef.internal.BaseChefLiveTest;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * Tests behavior of {@code DeleteAllApisAndNodesInListImpl} strategies
+ */
+@Test(groups = "live", testName = "DeleteAllApisAndNodesInListImplLiveTest")
+public class DeleteAllApisAndNodesInListImplLiveTest extends 
BaseChefLiveTest<ChefApi> {
+
+   private DeleteAllNodesInListImpl strategy;
+   private CreateNodeAndPopulateAutomaticAttributesImpl creator;
+
+   @Override
+   protected void initialize() {
+      super.initialize();
+      this.creator = 
injector.getInstance(CreateNodeAndPopulateAutomaticAttributesImpl.class);
+      this.strategy = injector.getInstance(DeleteAllNodesInListImpl.class);
+   }
+
+   @Test
+   public void testExecute() throws InterruptedException {
+      try {
+         creator.execute(prefix, ImmutableSet.<String> of());
+         creator.execute(prefix + 1, ImmutableSet.<String> of());
+
+         // http://tickets.corp.opscode.com/browse/PL-522
+         // assert api.nodeExists(prefix);
+         assertNotNull(api.getNode(prefix));
+         assertNotNull(api.getNode(prefix + 1));
+
+         strategy.execute(ImmutableSet.of(prefix, prefix + 1));
+         assertNull(api.getNode(prefix));
+         assertNull(api.getNode(prefix + 1));
+      } finally {
+         api.deleteNode(prefix);
+         api.deleteNode(prefix + 1);
+      }
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/test/java/org/jclouds/chef/strategy/internal/ListCookbookVersionsInEnvironmentImplLiveTest.java
----------------------------------------------------------------------
diff --git 
a/apis/chef/src/test/java/org/jclouds/chef/strategy/internal/ListCookbookVersionsInEnvironmentImplLiveTest.java
 
b/apis/chef/src/test/java/org/jclouds/chef/strategy/internal/ListCookbookVersionsInEnvironmentImplLiveTest.java
new file mode 100644
index 0000000..5f68fcf
--- /dev/null
+++ 
b/apis/chef/src/test/java/org/jclouds/chef/strategy/internal/ListCookbookVersionsInEnvironmentImplLiveTest.java
@@ -0,0 +1,217 @@
+/*
+ * 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.chef.strategy.internal;
+
+import static com.google.common.collect.Iterables.size;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+
+import java.io.File;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+import org.jclouds.chef.ChefApi;
+import org.jclouds.chef.domain.ChecksumStatus;
+import org.jclouds.chef.domain.CookbookVersion;
+import org.jclouds.chef.domain.Metadata;
+import org.jclouds.chef.domain.Resource;
+import org.jclouds.chef.domain.Sandbox;
+import org.jclouds.chef.domain.UploadSandbox;
+import org.jclouds.chef.internal.BaseChefLiveTest;
+import org.jclouds.io.Payloads;
+import org.jclouds.io.payloads.FilePayload;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.hash.Hashing;
+import com.google.common.io.Files;
+import com.google.common.primitives.Bytes;
+
+/**
+ * Tests behavior of {@code ListCookbookVersionsInEnvironmentImpl} strategies
+ */
+@Test(groups = "live", testName = 
"ListCookbookVersionsInEnvironmentImplLiveTest")
+public class ListCookbookVersionsInEnvironmentImplLiveTest extends 
BaseChefLiveTest<ChefApi> {
+   public static final String PREFIX = "jcloudstest-strategy-" + 
System.getProperty("user.name");
+
+   private ListCookbookVersionsInEnvironmentImpl strategy;
+   private CreateNodeAndPopulateAutomaticAttributesImpl creator;
+
+   private ExecutorService testExecutorService;
+   private ListeningExecutorService testListeningExecutorService;
+
+   @Override
+   protected void initialize() {
+      super.initialize();
+
+      try {
+         createCookbooksWithMultipleVersions(PREFIX);
+         createCookbooksWithMultipleVersions(PREFIX + 1);
+      } catch (Exception e) {
+         fail("Could not create cookbooks", e);
+      }
+
+      this.strategy = 
injector.getInstance(ListCookbookVersionsInEnvironmentImpl.class);
+      this.testExecutorService = Executors.newFixedThreadPool(5);
+      this.testListeningExecutorService = 
MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(5));
+   }
+
+   @AfterClass(groups = { "integration", "live" })
+   @Override
+   protected void tearDown() {
+      api.deleteCookbook(PREFIX, "0.0.0");
+      api.deleteCookbook(PREFIX, "1.0.0");
+      api.deleteCookbook(PREFIX + 1, "0.0.0");
+      api.deleteCookbook(PREFIX + 1, "1.0.0");
+
+      this.testExecutorService.shutdown();
+      this.testListeningExecutorService.shutdown();
+
+      super.tearDown();
+   }
+
+   @Test
+   public void testExecute() {
+      assertTrue(size(strategy.execute("_default")) > 0, "Expected one or more 
elements");
+   }
+
+   @Test
+   public void testExecuteConcurrentlyWithExecutorService() {
+      assertTrue(size(strategy.execute(testExecutorService, "_default")) > 0,
+            "Expected one or more elements");
+   }
+
+   @Test
+   public void testExecuteConcurrentlyWithListeningExecutorService() {
+      assertTrue(size(strategy.execute(testListeningExecutorService, 
"_default")) > 0,
+            "Expected one or more elements");
+   }
+
+   @Test
+   public void testExecuteWithNumVersions() {
+      assertTrue(size(strategy.execute("_default", "2")) > 0, "Expected one or 
more elements");
+   }
+
+   @Test
+   public void testExecuteConcurrentlyWithNumVersionsAndExecutorService() {
+      assertTrue(size(strategy.execute(testExecutorService, "_default", "2")) 
> 0,
+            "Expected one or more elements");
+   }
+
+   @Test
+   public void 
testExecuteConcurrentlyWithNumVersionsAndListeningExecutorService() {
+      assertTrue(size(strategy.execute(testListeningExecutorService, 
"_default", "2")) > 0,
+            "Expected one or more elements");
+   }
+
+   @Test
+   public void testExecuteWithNumVersionsAll() {
+      assertTrue(size(strategy.execute("_default", "all")) > 0, "Expected one 
or more elements");
+   }
+
+   @Test
+   public void testExecuteConcurrentlyWithNumVersionsAllAndExecutorService() {
+      assertTrue(size(strategy.execute(testExecutorService, "_default", 
"all")) > 0,
+            "Expected one or more elements");
+   }
+
+   @Test
+   public void 
testExecuteConcurrentlyWithNumVersionsAllAndListeningExecutorService() {
+      assertTrue(size(strategy.execute(testListeningExecutorService, 
"_default", "all")) > 0,
+            "Expected one or more elements");
+   }
+
+   private FilePayload uploadContent(String fileName) throws Exception {
+      // Define the file you want in the cookbook
+      File file = new File(System.getProperty("user.dir"), fileName);
+      FilePayload content = Payloads.newFilePayload(file);
+      content.getContentMetadata().setContentType("application/x-binary");
+
+      // Get an md5 so that you can see if the server already has it or not
+      
content.getContentMetadata().setContentMD5(Files.asByteSource(file).hash(Hashing.md5()).asBytes());
+
+      // Note that java collections cannot effectively do equals or hashcodes 
on
+      // byte arrays, so let's convert to a list of bytes.
+      List<Byte> md5 = 
Bytes.asList(content.getContentMetadata().getContentMD5());
+
+      // Request an upload site for this file
+      UploadSandbox site = 
api.createUploadSandboxForChecksums(ImmutableSet.of(md5));
+      assertTrue(site.getChecksums().containsKey(md5), md5 + " not in " + 
site.getChecksums());
+
+      try {
+         // Upload the file contents, if still not uploaded
+         ChecksumStatus status = site.getChecksums().get(md5);
+         if (status.needsUpload()) {
+            api.uploadContent(status.getUrl(), content);
+         }
+         Sandbox sandbox = api.commitSandbox(site.getSandboxId(), true);
+         assertTrue(sandbox.isCompleted(), "Sandbox should be completed after 
uploading");
+      } catch (RuntimeException e) {
+         api.commitSandbox(site.getSandboxId(), false);
+         fail("Could not upload content", e);
+      }
+
+      return content;
+   }
+
+   private void createCookbooksWithMultipleVersions(String cookbookName) 
throws Exception {
+      FilePayload v0content = uploadContent("pom.xml");
+      FilePayload v1content = uploadContent("../README.md");
+
+      // Create the metadata of the cookbook
+      Metadata metadata = Metadata.builder() //
+            .name(cookbookName) //
+            .version("0.0.0") //
+            .description("Jclouds test uploaded cookbook") //
+            .maintainer("jclouds") //
+            .maintainerEmail("[email protected]") //
+            .license("Apache 2.0") //
+            .build();
+
+      // Create new cookbook version
+      CookbookVersion cookbook = CookbookVersion.builder(cookbookName, 
"0.0.0") //
+            .metadata(metadata) //
+            .rootFile(Resource.builder().fromPayload(v0content).build()) //
+            .build();
+
+      // upload the cookbook to the remote server
+      api.updateCookbook(cookbookName, "0.0.0", cookbook);
+
+      // Create the metadata of the cookbook
+      metadata = Metadata.builder() //
+            .name(cookbookName) //
+            .version("1.0.0") //
+            .description("Jclouds test uploaded cookbook") //
+            .maintainer("jclouds") //
+            .maintainerEmail("[email protected]") //
+            .license("Apache 2.0") //
+            .build();
+
+      // Create a new cookbook version
+      cookbook = CookbookVersion.builder(cookbookName, "1.0.0") //
+            .metadata(metadata) //
+            .rootFile(Resource.builder().fromPayload(v1content).build()) //
+            .build();
+
+      // upload the cookbook to the remote server
+      api.updateCookbook(cookbookName, "1.0.0", cookbook);
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/test/java/org/jclouds/chef/strategy/internal/ListNodesImplLiveTest.java
----------------------------------------------------------------------
diff --git 
a/apis/chef/src/test/java/org/jclouds/chef/strategy/internal/ListNodesImplLiveTest.java
 
b/apis/chef/src/test/java/org/jclouds/chef/strategy/internal/ListNodesImplLiveTest.java
new file mode 100644
index 0000000..903b998
--- /dev/null
+++ 
b/apis/chef/src/test/java/org/jclouds/chef/strategy/internal/ListNodesImplLiveTest.java
@@ -0,0 +1,82 @@
+/*
+ * 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.chef.strategy.internal;
+
+import static com.google.common.collect.Iterables.size;
+import static org.testng.Assert.assertTrue;
+
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+import org.jclouds.chef.ChefApi;
+import org.jclouds.chef.internal.BaseChefLiveTest;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableSet;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+ * Tests behavior of {@code ListNodesImpl} strategies
+ */
+@Test(groups = "live", testName = "ListNodesImplLiveTest")
+public class ListNodesImplLiveTest extends BaseChefLiveTest<ChefApi> {
+
+   private ListNodesImpl strategy;
+   private CreateNodeAndPopulateAutomaticAttributesImpl creator;
+
+   private ExecutorService testExecutorService;
+   private ListeningExecutorService testListeningExecutorService;
+
+   @Override
+   protected void initialize() {
+      super.initialize();
+      this.creator = 
injector.getInstance(CreateNodeAndPopulateAutomaticAttributesImpl.class);
+      this.strategy = injector.getInstance(ListNodesImpl.class);
+      creator.execute(prefix, ImmutableSet.<String> of());
+      creator.execute(prefix + 1, ImmutableSet.<String> of());
+
+      this.testExecutorService = Executors.newFixedThreadPool(5);
+      this.testListeningExecutorService = 
MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(5));
+   }
+
+   @AfterClass(groups = { "integration", "live" })
+   @Override
+   protected void tearDown() {
+      api.deleteNode(prefix);
+      api.deleteNode(prefix + 1);
+
+      this.testExecutorService.shutdown();
+      this.testListeningExecutorService.shutdown();
+
+      super.tearDown();
+   }
+
+   @Test
+   public void testExecute() {
+      assertTrue(size(strategy.execute()) > 0, "Expected one or more 
elements");
+   }
+
+   public void testExecuteConcurrentlyWithExecutorService() {
+      assertTrue(size(strategy.execute(testExecutorService)) > 0, "Expected 
one or more elements");
+   }
+
+   public void testExecuteConcurrentlyWithListeningExecutorService() {
+      assertTrue(size(strategy.execute(testListeningExecutorService)) > 0, 
"Expected one or more elements");
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/test/java/org/jclouds/chef/strategy/internal/ListNodesInEnvironmentImplLiveTest.java
----------------------------------------------------------------------
diff --git 
a/apis/chef/src/test/java/org/jclouds/chef/strategy/internal/ListNodesInEnvironmentImplLiveTest.java
 
b/apis/chef/src/test/java/org/jclouds/chef/strategy/internal/ListNodesInEnvironmentImplLiveTest.java
new file mode 100644
index 0000000..f655d52
--- /dev/null
+++ 
b/apis/chef/src/test/java/org/jclouds/chef/strategy/internal/ListNodesInEnvironmentImplLiveTest.java
@@ -0,0 +1,86 @@
+/*
+ * 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.chef.strategy.internal;
+
+import static com.google.common.collect.Iterables.size;
+import static org.testng.Assert.assertTrue;
+
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+import org.jclouds.chef.ChefApi;
+import org.jclouds.chef.internal.BaseChefLiveTest;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableSet;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+ * Tests behavior of {@code ListNodesInEnvironmentImpl} strategies
+ */
+@Test(groups = "live", testName = "ListNodesInEnvironmentImplLiveTest")
+public class ListNodesInEnvironmentImplLiveTest extends 
BaseChefLiveTest<ChefApi> {
+
+   private ListNodesInEnvironmentImpl strategy;
+   private CreateNodeAndPopulateAutomaticAttributesImpl creator;
+
+   private ExecutorService testExecutorService;
+   private ListeningExecutorService testListeningExecutorService;
+
+   @Override
+   protected void initialize() {
+      super.initialize();
+      this.creator = 
injector.getInstance(CreateNodeAndPopulateAutomaticAttributesImpl.class);
+      this.strategy = injector.getInstance(ListNodesInEnvironmentImpl.class);
+      creator.execute(prefix, ImmutableSet.<String>of());
+      creator.execute(prefix + 1, ImmutableSet.<String>of());
+
+      this.testExecutorService = Executors.newFixedThreadPool(5);
+      this.testListeningExecutorService = 
MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(5));
+   }
+
+   @AfterClass(groups = { "integration", "live" })
+   @Override
+   protected void tearDown() {
+      api.deleteNode(prefix);
+      api.deleteNode(prefix + 1);
+
+      this.testExecutorService.shutdown();
+      this.testListeningExecutorService.shutdown();
+
+      super.tearDown();
+   }
+
+   @Test
+   public void testExecute() {
+      assertTrue(size(strategy.execute("_default")) > 0, "Expected one or more 
elements");
+   }
+
+   @Test
+   public void testExecuteConcurrentlyWithExecutorService() {
+      assertTrue(size(strategy.execute(testExecutorService, "_default")) > 0,
+            "Expected one or more elements");
+   }
+
+   @Test
+   public void testExecuteConcurrentlyWithListeningExecutorService() {
+      assertTrue(size(strategy.execute(testListeningExecutorService, 
"_default")) > 0,
+            "Expected one or more elements");
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/test/java/org/jclouds/chef/strategy/internal/UpdateAutomaticAttributesOnNodeImplLiveTest.java
----------------------------------------------------------------------
diff --git 
a/apis/chef/src/test/java/org/jclouds/chef/strategy/internal/UpdateAutomaticAttributesOnNodeImplLiveTest.java
 
b/apis/chef/src/test/java/org/jclouds/chef/strategy/internal/UpdateAutomaticAttributesOnNodeImplLiveTest.java
new file mode 100644
index 0000000..a8f7438
--- /dev/null
+++ 
b/apis/chef/src/test/java/org/jclouds/chef/strategy/internal/UpdateAutomaticAttributesOnNodeImplLiveTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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.chef.strategy.internal;
+
+import static org.testng.Assert.assertEquals;
+
+import java.util.Set;
+
+import org.jclouds.chef.ChefApi;
+import org.jclouds.chef.domain.Node;
+import org.jclouds.chef.internal.BaseChefLiveTest;
+import org.jclouds.ohai.config.OhaiModule.CurrentUserProvider;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * Tests behavior of {@code UpdateAutomaticAttributesOnNodeImpl} strategies
+ */
+@Test(groups = "live", testName = 
"UpdateAutomaticAttributesOnNodeImplLiveTest")
+public class UpdateAutomaticAttributesOnNodeImplLiveTest extends 
BaseChefLiveTest<ChefApi> {
+
+   private CurrentUserProvider currentUserProvider;
+   private UpdateAutomaticAttributesOnNodeImpl strategy;
+
+   @Override
+   protected void initialize() {
+      super.initialize();
+      this.currentUserProvider = 
injector.getInstance(CurrentUserProvider.class);
+      this.strategy = 
injector.getInstance(UpdateAutomaticAttributesOnNodeImpl.class);
+   }
+
+   @Test
+   public void testExecute() {
+      Set<String> runList = ImmutableSet.of("role[" + prefix + "]");
+      try {
+         
api.createNode(Node.builder().name(prefix).runList(runList).environment("_default").build());
+         strategy.execute(prefix);
+         Node node = api.getNode(prefix);
+         assertEquals(node.getName(), prefix);
+         assertEquals(node.getRunList(), runList);
+         
assertEquals(node.getAutomaticAttributes().get("current_user").toString(), 
currentUserProvider.get().toString());
+      } finally {
+         api.deleteNode(prefix);
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/test/java/org/jclouds/chef/strategy/internal/UpdateAutomaticAttributesOnNodeImplTest.java
----------------------------------------------------------------------
diff --git 
a/apis/chef/src/test/java/org/jclouds/chef/strategy/internal/UpdateAutomaticAttributesOnNodeImplTest.java
 
b/apis/chef/src/test/java/org/jclouds/chef/strategy/internal/UpdateAutomaticAttributesOnNodeImplTest.java
new file mode 100644
index 0000000..2e0d198
--- /dev/null
+++ 
b/apis/chef/src/test/java/org/jclouds/chef/strategy/internal/UpdateAutomaticAttributesOnNodeImplTest.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.chef.strategy.internal;
+
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+
+import java.util.Map;
+
+import org.jclouds.chef.ChefApi;
+import org.jclouds.chef.domain.Node;
+import org.jclouds.domain.JsonBall;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+import com.google.common.collect.ImmutableMap;
+
+/**
+ * Tests behavior of {@code UpdateAutomaticAttributesOnNodeImpl}
+ */
+@Test(groups = { "unit" })
+public class UpdateAutomaticAttributesOnNodeImplTest {
+
+   @Test
+   public void test() {
+      ChefApi chef = createMock(ChefApi.class);
+
+      Map<String, JsonBall> automatic = ImmutableMap.<String, JsonBall> of();
+      Supplier<Map<String, JsonBall>> automaticSupplier = 
Suppliers.<Map<String, JsonBall>> ofInstance(automatic);
+
+      Node node = Node.builder().name("name").environment("_default").build();
+      Node nodeWithAutomatic = 
Node.builder().name("name").environment("_default").automaticAttributes(automatic)
+            .build();
+
+      expect(chef.getNode("name")).andReturn(node);
+      expect(chef.updateNode(nodeWithAutomatic)).andReturn(null);
+
+      replay(chef);
+
+      UpdateAutomaticAttributesOnNodeImpl updater = new 
UpdateAutomaticAttributesOnNodeImpl(chef, automaticSupplier);
+
+      updater.execute("name");
+      verify(chef);
+
+   }
+}

Reply via email to