brooklyn-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From hadr...@apache.org
Subject [03/28] incubator-brooklyn git commit: brooklyn-rest-server: add org.apache package prefix
Date Sun, 09 Aug 2015 02:55:10 GMT
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/ApplicationResourceTest.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/ApplicationResourceTest.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/ApplicationResourceTest.java
new file mode 100644
index 0000000..047975b
--- /dev/null
+++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/ApplicationResourceTest.java
@@ -0,0 +1,598 @@
+/*
+ * 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.apache.brooklyn.rest.resources;
+
+import static com.google.common.collect.Iterables.find;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeoutException;
+
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Maps;
+import com.sun.jersey.api.client.ClientResponse;
+import com.sun.jersey.api.client.GenericType;
+import com.sun.jersey.api.client.UniformInterfaceException;
+import com.sun.jersey.api.client.WebResource;
+import com.sun.jersey.core.util.MultivaluedMapImpl;
+
+import brooklyn.entity.Application;
+import brooklyn.entity.basic.Attributes;
+import brooklyn.entity.basic.BasicApplication;
+import brooklyn.entity.basic.BasicEntity;
+import brooklyn.entity.basic.Entities;
+import brooklyn.entity.basic.EntityFunctions;
+import brooklyn.entity.basic.EntityLocal;
+import brooklyn.entity.basic.EntityPredicates;
+import brooklyn.entity.basic.Lifecycle;
+import brooklyn.location.basic.AbstractLocation;
+import brooklyn.location.basic.LocationConfigKeys;
+import brooklyn.location.basic.LocationInternal;
+import brooklyn.location.geo.HostGeoInfo;
+import brooklyn.rest.domain.ApiError;
+import brooklyn.rest.domain.ApplicationSpec;
+import brooklyn.rest.domain.ApplicationSummary;
+import brooklyn.rest.domain.EffectorSummary;
+import brooklyn.rest.domain.EntityConfigSummary;
+import brooklyn.rest.domain.EntitySpec;
+import brooklyn.rest.domain.EntitySummary;
+import brooklyn.rest.domain.PolicySummary;
+import brooklyn.rest.domain.SensorSummary;
+import brooklyn.rest.domain.Status;
+import brooklyn.rest.domain.TaskSummary;
+import org.apache.brooklyn.rest.testing.BrooklynRestResourceTest;
+import org.apache.brooklyn.rest.testing.mocks.CapitalizePolicy;
+import org.apache.brooklyn.rest.testing.mocks.NameMatcherGroup;
+import org.apache.brooklyn.rest.testing.mocks.RestMockApp;
+import org.apache.brooklyn.rest.testing.mocks.RestMockAppBuilder;
+import org.apache.brooklyn.rest.testing.mocks.RestMockSimpleEntity;
+import brooklyn.test.Asserts;
+import brooklyn.test.HttpTestUtils;
+import brooklyn.util.collections.CollectionFunctionals;
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.time.Duration;
+
+@Test(singleThreaded = true)
+public class ApplicationResourceTest extends BrooklynRestResourceTest {
+
+    /*
+     * In simpleSpec, not using EverythingGroup because caused problems! The group is a child of the
+     * app, and the app is a member of the group. It failed in jenkins with:
+     *   BasicApplicationImpl{id=GSPjBCe4} GSPjBCe4
+     *     service.isUp: true
+     *     service.problems: {service-lifecycle-indicators-from-children-and-members=Required entity not healthy: EverythingGroupImpl{id=KQ4mSEOJ}}
+     *     service.state: on-fire
+     *     service.state.expected: running @ 1412003485617 / Mon Sep 29 15:11:25 UTC 2014
+     *   EverythingGroupImpl{id=KQ4mSEOJ} KQ4mSEOJ
+     *     service.isUp: true
+     *     service.problems: {service-lifecycle-indicators-from-children-and-members=Required entities not healthy: BasicApplicationImpl{id=GSPjBCe4}, EverythingGroupImpl{id=KQ4mSEOJ}}
+     *     service.state: on-fire
+     * I'm guessing there's a race: the app was not yet healthy because EverythingGroup hadn't set itself to running; 
+     * but then the EverythingGroup would never transition to healthy because one of its members was not healthy.
+     */
+
+    private static final Logger log = LoggerFactory.getLogger(ApplicationResourceTest.class);
+    
+    private final ApplicationSpec simpleSpec = ApplicationSpec.builder().name("simple-app")
+          .entities(ImmutableSet.of(
+                  new EntitySpec("simple-ent", RestMockSimpleEntity.class.getName()),
+                  new EntitySpec("simple-group", NameMatcherGroup.class.getName(), ImmutableMap.of("namematchergroup.regex", "simple-ent"))
+          ))
+          .locations(ImmutableSet.of("localhost"))
+          .build();
+
+    // Convenience for finding an EntitySummary within a collection, based on its name
+    private static Predicate<EntitySummary> withName(final String name) {
+        return new Predicate<EntitySummary>() {
+            public boolean apply(EntitySummary input) {
+                return name.equals(input.getName());
+            }
+        };
+    }
+
+    // Convenience for finding a Map within a collection, based on the value of one of its keys
+    private static Predicate<? super Map<?,?>> withValueForKey(final Object key, final Object value) {
+        return new Predicate<Object>() {
+            public boolean apply(Object input) {
+                if (!(input instanceof Map)) return false;
+                return value.equals(((Map<?, ?>) input).get(key));
+            }
+        };
+    }
+
+    @Test
+    public void testGetUndefinedApplication() {
+        try {
+            client().resource("/v1/applications/dummy-not-found").get(ApplicationSummary.class);
+        } catch (UniformInterfaceException e) {
+            assertEquals(e.getResponse().getStatus(), 404);
+        }
+    }
+
+    private static void assertRegexMatches(String actual, String patternExpected) {
+        if (actual==null) Assert.fail("Actual value is null; expected "+patternExpected);
+        if (!actual.matches(patternExpected)) {
+            Assert.fail("Text '"+actual+"' does not match expected pattern "+patternExpected);
+        }
+    }
+
+    @Test
+    public void testDeployApplication() throws Exception {
+        ClientResponse response = clientDeploy(simpleSpec);
+
+        HttpTestUtils.assertHealthyStatusCode(response.getStatus());
+        assertEquals(getManagementContext().getApplications().size(), 1);
+        assertRegexMatches(response.getLocation().getPath(), "/v1/applications/.*");
+        // Object taskO = response.getEntity(Object.class);
+        TaskSummary task = response.getEntity(TaskSummary.class);
+        log.info("deployed, got " + task);
+        assertEquals(task.getEntityId(), getManagementContext().getApplications().iterator().next().getApplicationId());
+
+        waitForApplicationToBeRunning(response.getLocation());
+    }
+
+    @Test(dependsOnMethods = { "testDeleteApplication" })
+    // this must happen after we've deleted the main application, as testLocatedLocations assumes a single location
+    public void testDeployApplicationImpl() throws Exception {
+    ApplicationSpec spec = ApplicationSpec.builder()
+            .type(RestMockApp.class.getCanonicalName())
+            .name("simple-app-impl")
+            .locations(ImmutableSet.of("localhost"))
+            .build();
+
+        ClientResponse response = clientDeploy(spec);
+        assertTrue(response.getStatus() / 100 == 2, "response is " + response);
+
+        // Expect app to be running
+        URI appUri = response.getLocation();
+        waitForApplicationToBeRunning(response.getLocation());
+        assertEquals(client().resource(appUri).get(ApplicationSummary.class).getSpec().getName(), "simple-app-impl");
+    }
+
+    @Test(dependsOnMethods = { "testDeployApplication", "testLocatedLocation" })
+    public void testDeployApplicationFromInterface() throws Exception {
+        ApplicationSpec spec = ApplicationSpec.builder()
+                .type(BasicApplication.class.getCanonicalName())
+                .name("simple-app-interface")
+                .locations(ImmutableSet.of("localhost"))
+                .build();
+
+        ClientResponse response = clientDeploy(spec);
+        assertTrue(response.getStatus() / 100 == 2, "response is " + response);
+
+        // Expect app to be running
+        URI appUri = response.getLocation();
+        waitForApplicationToBeRunning(response.getLocation());
+        assertEquals(client().resource(appUri).get(ApplicationSummary.class).getSpec().getName(), "simple-app-interface");
+    }
+
+    @Test(dependsOnMethods = { "testDeployApplication", "testLocatedLocation" })
+    public void testDeployApplicationFromBuilder() throws Exception {
+        ApplicationSpec spec = ApplicationSpec.builder()
+                .type(RestMockAppBuilder.class.getCanonicalName())
+                .name("simple-app-builder")
+                .locations(ImmutableSet.of("localhost"))
+                .build();
+
+        ClientResponse response = clientDeploy(spec);
+        assertTrue(response.getStatus() / 100 == 2, "response is " + response);
+
+        // Expect app to be running
+        URI appUri = response.getLocation();
+        waitForApplicationToBeRunning(response.getLocation(), Duration.TEN_SECONDS);
+        assertEquals(client().resource(appUri).get(ApplicationSummary.class).getSpec().getName(), "simple-app-builder");
+
+        // Expect app to have the child-entity
+        Set<EntitySummary> entities = client().resource(appUri.toString() + "/entities")
+                .get(new GenericType<Set<EntitySummary>>() {});
+        assertEquals(entities.size(), 1);
+        assertEquals(Iterables.getOnlyElement(entities).getName(), "child1");
+        assertEquals(Iterables.getOnlyElement(entities).getType(), RestMockSimpleEntity.class.getCanonicalName());
+    }
+
+    @Test(dependsOnMethods = { "testDeployApplication", "testLocatedLocation" })
+    public void testDeployApplicationYaml() throws Exception {
+        String yaml = "{ name: simple-app-yaml, location: localhost, services: [ { serviceType: "+BasicApplication.class.getCanonicalName()+" } ] }";
+
+        ClientResponse response = client().resource("/v1/applications")
+                .entity(yaml, "application/x-yaml")
+                .post(ClientResponse.class);
+        assertTrue(response.getStatus()/100 == 2, "response is "+response);
+
+        // Expect app to be running
+        URI appUri = response.getLocation();
+        waitForApplicationToBeRunning(response.getLocation());
+        assertEquals(client().resource(appUri).get(ApplicationSummary.class).getSpec().getName(), "simple-app-yaml");
+    }
+
+    @SuppressWarnings("deprecation")
+    @Test
+    public void testReferenceCatalogEntity() throws Exception {
+        getManagementContext().getCatalog().addItem(BasicEntity.class);
+
+        String yaml = "{ name: simple-app-yaml, location: localhost, services: [ { serviceType: " + BasicEntity.class.getName() + " } ] }";
+
+        ClientResponse response = client().resource("/v1/applications")
+                .entity(yaml, "application/x-yaml")
+                .post(ClientResponse.class);
+        assertTrue(response.getStatus()/100 == 2, "response is "+response);
+
+        // Expect app to be running
+        URI appUri = response.getLocation();
+        waitForApplicationToBeRunning(response.getLocation());
+        assertEquals(client().resource(appUri).get(ApplicationSummary.class).getSpec().getName(), "simple-app-yaml");
+
+        ClientResponse response2 = client().resource(appUri.getPath())
+                .delete(ClientResponse.class);
+        assertEquals(response2.getStatus(), Response.Status.ACCEPTED.getStatusCode());
+    }
+
+    @Test
+    public void testDeployWithInvalidEntityType() {
+        try {
+            clientDeploy(ApplicationSpec.builder()
+                    .name("invalid-app")
+                    .entities(ImmutableSet.of(new EntitySpec("invalid-ent", "not.existing.entity")))
+                    .locations(ImmutableSet.of("localhost"))
+                    .build());
+
+        } catch (UniformInterfaceException e) {
+            ApiError error = e.getResponse().getEntity(ApiError.class);
+            assertEquals(error.getMessage(), "Undefined type 'not.existing.entity'");
+        }
+    }
+
+    @Test
+    public void testDeployWithInvalidLocation() {
+        try {
+            clientDeploy(ApplicationSpec.builder()
+                    .name("invalid-app")
+                    .entities(ImmutableSet.of(new EntitySpec("simple-ent", RestMockSimpleEntity.class.getName())))
+                    .locations(ImmutableSet.of("3423"))
+                    .build());
+
+        } catch (UniformInterfaceException e) {
+            ApiError error = e.getResponse().getEntity(ApiError.class);
+            assertEquals(error.getMessage(), "Undefined location '3423'");
+        }
+    }
+
+    @Test(dependsOnMethods = "testDeployApplication")
+    public void testListEntities() {
+        Set<EntitySummary> entities = client().resource("/v1/applications/simple-app/entities")
+                .get(new GenericType<Set<EntitySummary>>() {});
+
+        assertEquals(entities.size(), 2);
+
+        EntitySummary entity = Iterables.find(entities, withName("simple-ent"), null);
+        EntitySummary group = Iterables.find(entities, withName("simple-group"), null);
+        Assert.assertNotNull(entity);
+        Assert.assertNotNull(group);
+
+        client().resource(entity.getLinks().get("self")).get(ClientResponse.class);
+
+        Set<EntitySummary> children = client().resource(entity.getLinks().get("children"))
+                .get(new GenericType<Set<EntitySummary>>() {});
+        assertEquals(children.size(), 0);
+    }
+
+    @Test(dependsOnMethods = "testDeployApplication")
+    public void testListApplications() {
+        Set<ApplicationSummary> applications = client().resource("/v1/applications")
+                .get(new GenericType<Set<ApplicationSummary>>() { });
+        log.info("Applications listed are: " + applications);
+        for (ApplicationSummary app : applications) {
+            if (simpleSpec.getName().equals(app.getSpec().getName())) return;
+        }
+        Assert.fail("simple-app not found in list of applications: "+applications);
+    }
+
+    @Test(dependsOnMethods = "testDeployApplication")
+    public void testGetApplicationOnFire() {
+        Application app = Iterables.find(manager.getApplications(), EntityPredicates.displayNameEqualTo(simpleSpec.getName()));
+        Lifecycle origState = app.getAttribute(Attributes.SERVICE_STATE_ACTUAL);
+        
+        ApplicationSummary summary = client().resource("/v1/applications/"+app.getId())
+                .get(ApplicationSummary.class);
+        assertEquals(summary.getStatus(), Status.RUNNING);
+
+        ((EntityLocal)app).setAttribute(Attributes.SERVICE_STATE_ACTUAL, Lifecycle.ON_FIRE);
+        try {
+            ApplicationSummary summary2 = client().resource("/v1/applications/"+app.getId())
+                    .get(ApplicationSummary.class);
+            log.info("Application: " + summary2);
+            assertEquals(summary2.getStatus(), Status.ERROR);
+            
+        } finally {
+            ((EntityLocal)app).setAttribute(Attributes.SERVICE_STATE_ACTUAL, origState);
+        }
+    }
+
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    @Test(dependsOnMethods = "testDeployApplication")
+    public void testFetchApplicationsAndEntity() {
+        Collection apps = client().resource("/v1/applications/fetch").get(Collection.class);
+        log.info("Applications fetched are: " + apps);
+
+        Map app = null;
+        for (Object appI : apps) {
+            Object name = ((Map) appI).get("name");
+            if ("simple-app".equals(name)) {
+                app = (Map) appI;
+            }
+            if (ImmutableSet.of("simple-ent", "simple-group").contains(name))
+                Assert.fail(name + " should not be listed at high level: " + apps);
+        }
+
+        Assert.assertNotNull(app);
+        Collection children = (Collection) app.get("children");
+        Assert.assertEquals(children.size(), 2);
+
+        Map entitySummary = (Map) Iterables.find(children, withValueForKey("name", "simple-ent"), null);
+        Map groupSummary = (Map) Iterables.find(children, withValueForKey("name", "simple-group"), null);
+        Assert.assertNotNull(entitySummary);
+        Assert.assertNotNull(groupSummary);
+
+        String itemIds = app.get("id") + "," + entitySummary.get("id") + "," + groupSummary.get("id");
+        Collection entities = client().resource("/v1/applications/fetch?items="+itemIds)
+                .get(Collection.class);
+        log.info("Applications+Entities fetched are: " + entities);
+
+        Assert.assertEquals(entities.size(), apps.size() + 2);
+        Map entityDetails = (Map) Iterables.find(entities, withValueForKey("name", "simple-ent"), null);
+        Map groupDetails = (Map) Iterables.find(entities, withValueForKey("name", "simple-group"), null);
+        Assert.assertNotNull(entityDetails);
+        Assert.assertNotNull(groupDetails);
+
+        Assert.assertEquals(entityDetails.get("parentId"), app.get("id"));
+        Assert.assertNull(entityDetails.get("children"));
+        Assert.assertEquals(groupDetails.get("parentId"), app.get("id"));
+        Assert.assertNull(groupDetails.get("children"));
+
+        Collection entityGroupIds = (Collection) entityDetails.get("groupIds");
+        Assert.assertNotNull(entityGroupIds);
+        Assert.assertEquals(entityGroupIds.size(), 1);
+        Assert.assertEquals(entityGroupIds.iterator().next(), groupDetails.get("id"));
+
+        Collection groupMembers = (Collection) groupDetails.get("members");
+        Assert.assertNotNull(groupMembers);
+
+        for (Application appi : getManagementContext().getApplications()) {
+            Entities.dumpInfo(appi);
+        }
+        log.info("MEMBERS: " + groupMembers);
+
+        Assert.assertEquals(groupMembers.size(), 1);
+        Map entityMemberDetails = (Map) Iterables.find(groupMembers, withValueForKey("name", "simple-ent"), null);
+        Assert.assertNotNull(entityMemberDetails);
+        Assert.assertEquals(entityMemberDetails.get("id"), entityDetails.get("id"));
+    }
+
+    @Test(dependsOnMethods = "testDeployApplication")
+    public void testListSensors() {
+        Set<SensorSummary> sensors = client().resource("/v1/applications/simple-app/entities/simple-ent/sensors")
+                .get(new GenericType<Set<SensorSummary>>() { });
+        assertTrue(sensors.size() > 0);
+        SensorSummary sample = Iterables.find(sensors, new Predicate<SensorSummary>() {
+            @Override
+            public boolean apply(SensorSummary sensorSummary) {
+                return sensorSummary.getName().equals(RestMockSimpleEntity.SAMPLE_SENSOR.getName());
+            }
+        });
+        assertEquals(sample.getType(), "java.lang.String");
+    }
+
+    @Test(dependsOnMethods = "testDeployApplication")
+    public void testListConfig() {
+        Set<EntityConfigSummary> config = client().resource("/v1/applications/simple-app/entities/simple-ent/config")
+                .get(new GenericType<Set<EntityConfigSummary>>() { });
+        assertTrue(config.size() > 0);
+        System.out.println(("CONFIG: " + config));
+    }
+
+    @Test(dependsOnMethods = "testListConfig")
+    public void testListConfig2() {
+        Set<EntityConfigSummary> config = client().resource("/v1/applications/simple-app/entities/simple-ent/config")
+                .get(new GenericType<Set<EntityConfigSummary>>() {});
+        assertTrue(config.size() > 0);
+        System.out.println(("CONFIG: " + config));
+    }
+
+    @Test(dependsOnMethods = "testDeployApplication")
+    public void testListEffectors() {
+        Set<EffectorSummary> effectors = client().resource("/v1/applications/simple-app/entities/simple-ent/effectors")
+                .get(new GenericType<Set<EffectorSummary>>() {});
+
+        assertTrue(effectors.size() > 0);
+
+        EffectorSummary sampleEffector = find(effectors, new Predicate<EffectorSummary>() {
+            @Override
+            public boolean apply(EffectorSummary input) {
+                return input.getName().equals("sampleEffector");
+            }
+        });
+        assertEquals(sampleEffector.getReturnType(), "java.lang.String");
+    }
+
+    @Test(dependsOnMethods = "testListSensors")
+    public void testTriggerSampleEffector() throws InterruptedException, IOException {
+        ClientResponse response = client()
+                .resource("/v1/applications/simple-app/entities/simple-ent/effectors/"+RestMockSimpleEntity.SAMPLE_EFFECTOR.getName())
+                .type(MediaType.APPLICATION_JSON_TYPE)
+                .post(ClientResponse.class, ImmutableMap.of("param1", "foo", "param2", 4));
+
+        assertEquals(response.getStatus(), Response.Status.ACCEPTED.getStatusCode());
+
+        String result = response.getEntity(String.class);
+        assertEquals(result, "foo4");
+    }
+
+    @Test(dependsOnMethods = "testListSensors")
+    public void testTriggerSampleEffectorWithFormData() throws InterruptedException, IOException {
+        MultivaluedMap<String, String> data = new MultivaluedMapImpl();
+        data.add("param1", "foo");
+        data.add("param2", "4");
+        ClientResponse response = client()
+                .resource("/v1/applications/simple-app/entities/simple-ent/effectors/"+RestMockSimpleEntity.SAMPLE_EFFECTOR.getName())
+                .type(MediaType.APPLICATION_FORM_URLENCODED_TYPE)
+                .post(ClientResponse.class, data);
+
+        assertEquals(response.getStatus(), Response.Status.ACCEPTED.getStatusCode());
+
+        String result = response.getEntity(String.class);
+        assertEquals(result, "foo4");
+    }
+
+    @Test(dependsOnMethods = "testTriggerSampleEffector")
+    public void testBatchSensorValues() {
+        WebResource resource = client().resource("/v1/applications/simple-app/entities/simple-ent/sensors/current-state");
+        Map<String, Object> sensors = resource.get(new GenericType<Map<String, Object>>() {});
+        assertTrue(sensors.size() > 0);
+        assertEquals(sensors.get(RestMockSimpleEntity.SAMPLE_SENSOR.getName()), "foo4");
+    }
+
+    @Test(dependsOnMethods = "testBatchSensorValues")
+    public void testReadEachSensor() {
+    Set<SensorSummary> sensors = client().resource("/v1/applications/simple-app/entities/simple-ent/sensors")
+            .get(new GenericType<Set<SensorSummary>>() {});
+
+        Map<String, String> readings = Maps.newHashMap();
+        for (SensorSummary sensor : sensors) {
+            try {
+                readings.put(sensor.getName(), client().resource(sensor.getLinks().get("self")).accept(MediaType.TEXT_PLAIN).get(String.class));
+            } catch (UniformInterfaceException uie) {
+                if (uie.getResponse().getStatus() == 204) { // no content
+                    readings.put(sensor.getName(), null);
+                } else {
+                    Exceptions.propagate(uie);
+                }
+            }
+        }
+
+        assertEquals(readings.get(RestMockSimpleEntity.SAMPLE_SENSOR.getName()), "foo4");
+    }
+
+    @Test(dependsOnMethods = "testTriggerSampleEffector")
+    public void testPolicyWhichCapitalizes() {
+        String policiesEndpoint = "/v1/applications/simple-app/entities/simple-ent/policies";
+        Set<PolicySummary> policies = client().resource(policiesEndpoint).get(new GenericType<Set<PolicySummary>>(){});
+        assertEquals(policies.size(), 0);
+
+        ClientResponse response = client().resource(policiesEndpoint)
+                .queryParam("type", CapitalizePolicy.class.getCanonicalName())
+                .type(MediaType.APPLICATION_JSON_TYPE)
+                .post(ClientResponse.class, Maps.newHashMap());
+        assertEquals(response.getStatus(), 200);
+        PolicySummary policy = response.getEntity(PolicySummary.class);
+        assertNotNull(policy.getId());
+        String newPolicyId = policy.getId();
+        log.info("POLICY CREATED: " + newPolicyId);
+        policies = client().resource(policiesEndpoint).get(new GenericType<Set<PolicySummary>>() {});
+        assertEquals(policies.size(), 1);
+
+        Lifecycle status = client().resource(policiesEndpoint + "/" + newPolicyId).get(Lifecycle.class);
+        log.info("POLICY STATUS: " + status);
+
+        response = client().resource(policiesEndpoint+"/"+newPolicyId+"/start")
+                .post(ClientResponse.class);
+        assertEquals(response.getStatus(), 204);
+        status = client().resource(policiesEndpoint + "/" + newPolicyId).get(Lifecycle.class);
+        assertEquals(status, Lifecycle.RUNNING);
+
+        response = client().resource(policiesEndpoint+"/"+newPolicyId+"/stop")
+                .post(ClientResponse.class);
+        assertEquals(response.getStatus(), 204);
+        status = client().resource(policiesEndpoint + "/" + newPolicyId).get(Lifecycle.class);
+        assertEquals(status, Lifecycle.STOPPED);
+
+        response = client().resource(policiesEndpoint+"/"+newPolicyId+"/destroy")
+                .post(ClientResponse.class);
+        assertEquals(response.getStatus(), 204);
+
+        response = client().resource(policiesEndpoint+"/"+newPolicyId).get(ClientResponse.class);
+        log.info("POLICY STATUS RESPONSE AFTER DESTROY: " + response.getStatus());
+        assertEquals(response.getStatus(), 404);
+
+        policies = client().resource(policiesEndpoint).get(new GenericType<Set<PolicySummary>>() {});
+        assertEquals(0, policies.size());
+    }
+
+    @SuppressWarnings({ "rawtypes" })
+    @Test(dependsOnMethods = "testDeployApplication")
+    public void testLocatedLocation() {
+        log.info("starting testLocatedLocations");
+        testListApplications();
+
+        LocationInternal l = (LocationInternal) getManagementContext().getApplications().iterator().next().getLocations().iterator().next();
+        if (l.config().getLocalRaw(LocationConfigKeys.LATITUDE).isAbsent()) {
+            log.info("Supplying fake locations for localhost because could not be autodetected");
+            ((AbstractLocation) l).setHostGeoInfo(new HostGeoInfo("localhost", "localhost", 50, 0));
+        }
+        Map result = client().resource("/v1/locations/usage/LocatedLocations")
+                .get(Map.class);
+        log.info("LOCATIONS: " + result);
+        Assert.assertEquals(result.size(), 1);
+        Map details = (Map) result.values().iterator().next();
+        assertEquals(details.get("leafEntityCount"), 2);
+    }
+
+    @Test(dependsOnMethods = {"testListEffectors", "testFetchApplicationsAndEntity", "testTriggerSampleEffector", "testListApplications","testReadEachSensor","testPolicyWhichCapitalizes","testLocatedLocation"})
+    public void testDeleteApplication() throws TimeoutException, InterruptedException {
+        waitForPageFoundResponse("/v1/applications/simple-app", ApplicationSummary.class);
+        Collection<Application> apps = getManagementContext().getApplications();
+        log.info("Deleting simple-app from " + apps);
+        int size = apps.size();
+
+        ClientResponse response = client().resource("/v1/applications/simple-app")
+                .delete(ClientResponse.class);
+
+        assertEquals(response.getStatus(), Response.Status.ACCEPTED.getStatusCode());
+        TaskSummary task = response.getEntity(TaskSummary.class);
+        assertTrue(task.getDescription().toLowerCase().contains("destroy"), task.getDescription());
+        assertTrue(task.getDescription().toLowerCase().contains("simple-app"), task.getDescription());
+
+        waitForPageNotFoundResponse("/v1/applications/simple-app", ApplicationSummary.class);
+
+        log.info("App appears gone, apps are: " + getManagementContext().getApplications());
+        // more logging above, for failure in the check below
+
+        Asserts.eventually(
+                EntityFunctions.applications(getManagementContext()),
+                Predicates.compose(Predicates.equalTo(size-1), CollectionFunctionals.sizeFunction()) );
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/CatalogResetTest.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/CatalogResetTest.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/CatalogResetTest.java
new file mode 100644
index 0000000..560a24e
--- /dev/null
+++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/CatalogResetTest.java
@@ -0,0 +1,113 @@
+/*
+ * 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.apache.brooklyn.rest.resources;
+
+import static org.testng.Assert.assertNotNull;
+
+import javax.ws.rs.core.MediaType;
+
+import org.apache.http.HttpStatus;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import org.apache.brooklyn.catalog.BrooklynCatalog;
+import org.apache.brooklyn.rest.testing.BrooklynRestResourceTest;
+import brooklyn.test.TestHttpRequestHandler;
+import brooklyn.test.TestHttpServer;
+import brooklyn.util.ResourceUtils;
+
+import com.sun.jersey.api.client.UniformInterfaceException;
+
+public class CatalogResetTest extends BrooklynRestResourceTest {
+
+    private TestHttpServer server;
+    private String serverUrl;
+
+    @BeforeClass(alwaysRun=true)
+    @Override
+    public void setUp() throws Exception {
+        useLocalScannedCatalog();
+        super.setUp();
+        server = new TestHttpServer()
+            .handler("/404", new TestHttpRequestHandler().code(HttpStatus.SC_NOT_FOUND).response("Not Found"))
+            .handler("/200", new TestHttpRequestHandler().response("OK"))
+            .start();
+        serverUrl = server.getUrl();
+    }
+
+    @Override
+    protected void addBrooklynResources() {
+        addResource(new CatalogResource());
+    }
+
+    @AfterClass(alwaysRun=true)
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+        server.stop();
+    }
+
+    @Test(expectedExceptions=UniformInterfaceException.class, expectedExceptionsMessageRegExp="Client response status: 500")
+    public void testConnectionError() throws Exception {
+        reset("http://0.0.0.0/can-not-connect", false);
+    }
+
+    @Test
+    public void testConnectionErrorIgnore() throws Exception {
+        reset("http://0.0.0.0/can-not-connect", true);
+    }
+
+    @Test(expectedExceptions=UniformInterfaceException.class, expectedExceptionsMessageRegExp="Client response status: 500")
+    public void testResourceMissingError() throws Exception {
+        reset(serverUrl + "/404", false);
+    }
+
+    @Test
+    public void testResourceMissingIgnore() throws Exception {
+        reset(serverUrl + "/404", true);
+    }
+
+    @Test(expectedExceptions=UniformInterfaceException.class, expectedExceptionsMessageRegExp="Client response status: 500")
+    public void testResourceInvalidError() throws Exception {
+        reset(serverUrl + "/200", false);
+    }
+
+    @Test
+    public void testResourceInvalidIgnore() throws Exception {
+        reset(serverUrl + "/200", true);
+    }
+
+    private void reset(String bundleLocation, boolean ignoreErrors) throws Exception {
+        String xml = ResourceUtils.create(this).getResourceAsString("classpath://reset-catalog.xml");
+        client().resource("/v1/catalog/reset")
+            .queryParam("ignoreErrors", Boolean.toString(ignoreErrors))
+            .header("Content-type", MediaType.APPLICATION_XML)
+            .post(xml.replace("${bundle-location}", bundleLocation));
+        //if above succeeds assert catalog contents
+        assertItems();
+    }
+    
+    private void assertItems() {
+        BrooklynCatalog catalog = getManagementContext().getCatalog();
+        assertNotNull(catalog.getCatalogItem("brooklyn.entity.basic.BasicApplication", BrooklynCatalog.DEFAULT_VERSION));
+        assertNotNull(catalog.getCatalogItem("brooklyn.osgi.tests.SimpleApplication", BrooklynCatalog.DEFAULT_VERSION));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/CatalogResourceTest.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/CatalogResourceTest.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/CatalogResourceTest.java
new file mode 100644
index 0000000..51024a7
--- /dev/null
+++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/CatalogResourceTest.java
@@ -0,0 +1,432 @@
+/*
+ * 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.apache.brooklyn.rest.resources;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+import java.awt.Image;
+import java.awt.Toolkit;
+import java.io.IOException;
+import java.net.URI;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.eclipse.jetty.http.HttpStatus;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+import org.testng.reporters.Files;
+
+import org.apache.brooklyn.catalog.CatalogItem;
+import org.apache.brooklyn.catalog.CatalogItem.CatalogBundle;
+import brooklyn.catalog.internal.CatalogUtils;
+import brooklyn.management.osgi.OsgiStandaloneTest;
+import brooklyn.policy.autoscaling.AutoScalerPolicy;
+import brooklyn.rest.domain.CatalogEntitySummary;
+import brooklyn.rest.domain.CatalogItemSummary;
+import brooklyn.rest.domain.CatalogLocationSummary;
+import brooklyn.rest.domain.CatalogPolicySummary;
+import org.apache.brooklyn.rest.testing.BrooklynRestResourceTest;
+import org.apache.brooklyn.test.TestResourceUnavailableException;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.Iterables;
+import com.sun.jersey.api.client.ClientResponse;
+import com.sun.jersey.api.client.GenericType;
+
+public class CatalogResourceTest extends BrooklynRestResourceTest {
+
+    private static final Logger log = LoggerFactory.getLogger(CatalogResourceTest.class);
+    
+    private static String TEST_VERSION = "0.1.2";
+
+    @BeforeClass(alwaysRun=true)
+    @Override
+    public void setUp() throws Exception {
+        useLocalScannedCatalog();
+        super.setUp();
+    }
+    
+    @Override
+    protected void addBrooklynResources() {
+        addResource(new CatalogResource());
+    }
+
+    @Test
+    /** based on CampYamlLiteTest */
+    public void testRegisterCustomEntityTopLevelSyntaxWithBundleWhereEntityIsFromCoreAndIconFromBundle() {
+        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_PATH);
+
+        String symbolicName = "my.catalog.entity.id";
+        String bundleUrl = OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL;
+        String yaml =
+                "brooklyn.catalog:\n"+
+                "  id: " + symbolicName + "\n"+
+                "  name: My Catalog App\n"+
+                "  description: My description\n"+
+                "  icon_url: classpath:/brooklyn/osgi/tests/icon.gif\n"+
+                "  version: " + TEST_VERSION + "\n"+
+                "  libraries:\n"+
+                "  - url: " + bundleUrl + "\n"+
+                "\n"+
+                "services:\n"+
+                "- type: brooklyn.test.entity.TestEntity\n";
+
+        ClientResponse response = client().resource("/v1/catalog")
+                .post(ClientResponse.class, yaml);
+
+        assertEquals(response.getStatus(), Response.Status.CREATED.getStatusCode());
+
+        CatalogEntitySummary entityItem = client().resource("/v1/catalog/entities/"+symbolicName + "/" + TEST_VERSION)
+                .get(CatalogEntitySummary.class);
+
+        Assert.assertNotNull(entityItem.getPlanYaml());
+        Assert.assertTrue(entityItem.getPlanYaml().contains("brooklyn.test.entity.TestEntity"));
+
+        assertEquals(entityItem.getId(), ver(symbolicName));
+        assertEquals(entityItem.getSymbolicName(), symbolicName);
+        assertEquals(entityItem.getVersion(), TEST_VERSION);
+
+        // and internally let's check we have libraries
+        CatalogItem<?, ?> item = getManagementContext().getCatalog().getCatalogItem(symbolicName, TEST_VERSION);
+        Assert.assertNotNull(item);
+        Collection<CatalogBundle> libs = item.getLibraries();
+        assertEquals(libs.size(), 1);
+        assertEquals(Iterables.getOnlyElement(libs).getUrl(), bundleUrl);
+
+        // now let's check other things on the item
+        assertEquals(entityItem.getName(), "My Catalog App");
+        assertEquals(entityItem.getDescription(), "My description");
+        assertEquals(entityItem.getIconUrl(), "/v1/catalog/icon/" + symbolicName + "/" + entityItem.getVersion());
+        assertEquals(item.getIconUrl(), "classpath:/brooklyn/osgi/tests/icon.gif");
+
+        byte[] iconData = client().resource("/v1/catalog/icon/" + symbolicName + "/" + TEST_VERSION).get(byte[].class);
+        assertEquals(iconData.length, 43);
+    }
+
+    @Test
+    public void testRegisterOsgiPolicyTopLevelSyntax() {
+        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_PATH);
+
+        String symbolicName = "my.catalog.policy.id";
+        String policyType = "brooklyn.osgi.tests.SimplePolicy";
+        String bundleUrl = OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL;
+
+        String yaml =
+                "brooklyn.catalog:\n"+
+                "  id: " + symbolicName + "\n"+
+                "  name: My Catalog App\n"+
+                "  description: My description\n"+
+                "  version: " + TEST_VERSION + "\n" +
+                "  libraries:\n"+
+                "  - url: " + bundleUrl + "\n"+
+                "\n"+
+                "brooklyn.policies:\n"+
+                "- type: " + policyType;
+
+        CatalogPolicySummary entityItem = Iterables.getOnlyElement( client().resource("/v1/catalog")
+                .post(new GenericType<Map<String,CatalogPolicySummary>>() {}, yaml).values() );
+
+        Assert.assertNotNull(entityItem.getPlanYaml());
+        Assert.assertTrue(entityItem.getPlanYaml().contains(policyType));
+        assertEquals(entityItem.getId(), ver(symbolicName));
+        assertEquals(entityItem.getSymbolicName(), symbolicName);
+        assertEquals(entityItem.getVersion(), TEST_VERSION);
+    }
+
+    @Test
+    public void testListAllEntities() {
+        List<CatalogEntitySummary> entities = client().resource("/v1/catalog/entities")
+                .get(new GenericType<List<CatalogEntitySummary>>() {});
+        assertTrue(entities.size() > 0);
+    }
+
+    @Test
+    public void testListAllEntitiesAsItem() {
+        // ensure things are happily downcasted and unknown properties ignored (e.g. sensors, effectors)
+        List<CatalogItemSummary> entities = client().resource("/v1/catalog/entities")
+                .get(new GenericType<List<CatalogItemSummary>>() {});
+        assertTrue(entities.size() > 0);
+    }
+
+    @Test
+    public void testFilterListOfEntitiesByName() {
+        List<CatalogEntitySummary> entities = client().resource("/v1/catalog/entities")
+                .queryParam("fragment", "reDISclusTER").get(new GenericType<List<CatalogEntitySummary>>() {});
+        assertEquals(entities.size(), 1);
+
+        log.info("RedisCluster-like entities are: " + entities);
+
+        List<CatalogEntitySummary> entities2 = client().resource("/v1/catalog/entities")
+                .queryParam("regex", "[Rr]ed.[sulC]+ter").get(new GenericType<List<CatalogEntitySummary>>() {});
+        assertEquals(entities2.size(), 1);
+
+        assertEquals(entities, entities2);
+    
+        List<CatalogEntitySummary> entities3 = client().resource("/v1/catalog/entities")
+                .queryParam("fragment", "bweqQzZ").get(new GenericType<List<CatalogEntitySummary>>() {});
+        assertEquals(entities3.size(), 0);
+
+        List<CatalogEntitySummary> entities4 = client().resource("/v1/catalog/entities")
+                .queryParam("regex", "bweq+z+").get(new GenericType<List<CatalogEntitySummary>>() {});
+        assertEquals(entities4.size(), 0);
+    }
+
+    @Test
+    @Deprecated
+    // If we move to using a yaml catalog item, the details will be of the wrapping app,
+    // not of the entity itself, so the test won't make sense any more.
+    public void testGetCatalogEntityDetails() {
+        CatalogEntitySummary details = client()
+                .resource(URI.create("/v1/catalog/entities/org.apache.brooklyn.entity.nosql.redis.RedisStore"))
+                .get(CatalogEntitySummary.class);
+        assertTrue(details.toString().contains("redis.port"), "expected more config, only got: "+details);
+        String iconUrl = "/v1/catalog/icon/" + details.getSymbolicName();
+        assertTrue(details.getIconUrl().contains(iconUrl), "expected brooklyn URL for icon image, but got: " + details.getIconUrl());
+    }
+
+    @Test
+    @Deprecated
+    // If we move to using a yaml catalog item, the details will be of the wrapping app,
+    // not of the entity itself, so the test won't make sense any more.
+    public void testGetCatalogEntityPlusVersionDetails() {
+        CatalogEntitySummary details = client()
+                .resource(URI.create("/v1/catalog/entities/org.apache.brooklyn.entity.nosql.redis.RedisStore:0.0.0.SNAPSHOT"))
+                .get(CatalogEntitySummary.class);
+        assertTrue(details.toString().contains("redis.port"), "expected more config, only got: "+details);
+        String expectedIconUrl = "/v1/catalog/icon/" + details.getSymbolicName() + "/" + details.getVersion();
+        assertEquals(details.getIconUrl(), expectedIconUrl, "expected brooklyn URL for icon image ("+expectedIconUrl+"), but got: "+details.getIconUrl());
+    }
+
+    @Test
+    public void testGetCatalogEntityIconDetails() throws IOException {
+        String catalogItemId = "testGetCatalogEntityIconDetails";
+        addTestCatalogItemRedisAsEntity(catalogItemId);
+        ClientResponse response = client().resource(URI.create("/v1/catalog/icon/" + catalogItemId + "/" + TEST_VERSION))
+                .get(ClientResponse.class);
+        response.bufferEntity();
+        Assert.assertEquals(response.getStatus(), 200);
+        Assert.assertEquals(response.getType(), MediaType.valueOf("image/png"));
+        Image image = Toolkit.getDefaultToolkit().createImage(Files.readFile(response.getEntityInputStream()));
+        Assert.assertNotNull(image);
+    }
+
+    private void addTestCatalogItemRedisAsEntity(String catalogItemId) {
+        addTestCatalogItem(catalogItemId, null, TEST_VERSION, "org.apache.brooklyn.entity.nosql.redis.RedisStore");
+    }
+
+    private void addTestCatalogItem(String catalogItemId, String itemType, String version, String service) {
+        String yaml =
+                "brooklyn.catalog:\n"+
+                "  id: " + catalogItemId + "\n"+
+                "  name: My Catalog App\n"+
+                (itemType!=null ? "  item_type: "+itemType+"\n" : "")+
+                "  description: My description\n"+
+                "  icon_url: classpath:///redis-logo.png\n"+
+                "  version: " + version + "\n"+
+                "\n"+
+                "services:\n"+
+                "- type: " + service + "\n";
+
+        client().resource("/v1/catalog").post(yaml);
+    }
+
+
+
+    @Test
+    public void testListPolicies() {
+        Set<CatalogPolicySummary> policies = client().resource("/v1/catalog/policies")
+                .get(new GenericType<Set<CatalogPolicySummary>>() {});
+
+        assertTrue(policies.size() > 0);
+        CatalogItemSummary asp = null;
+        for (CatalogItemSummary p : policies) {
+            if (AutoScalerPolicy.class.getName().equals(p.getType()))
+                asp = p;
+        }
+        Assert.assertNotNull(asp, "didn't find AutoScalerPolicy");
+    }
+
+    @Test
+    public void testLocationAddGetAndRemove() {
+        String symbolicName = "my.catalog.location.id";
+        String locationType = "localhost";
+        String yaml = Joiner.on("\n").join(
+                "brooklyn.catalog:",
+                "  id: " + symbolicName,
+                "  name: My Catalog Location",
+                "  description: My description",
+                "  version: " + TEST_VERSION,
+                "",
+                "brooklyn.locations:",
+                "- type: " + locationType);
+
+        // Create location item
+        Map<String, CatalogLocationSummary> items = client().resource("/v1/catalog")
+                .post(new GenericType<Map<String,CatalogLocationSummary>>() {}, yaml);
+        CatalogLocationSummary locationItem = Iterables.getOnlyElement(items.values());
+
+        Assert.assertNotNull(locationItem.getPlanYaml());
+        Assert.assertTrue(locationItem.getPlanYaml().contains(locationType));
+        assertEquals(locationItem.getId(), ver(symbolicName));
+        assertEquals(locationItem.getSymbolicName(), symbolicName);
+        assertEquals(locationItem.getVersion(), TEST_VERSION);
+
+        // Retrieve location item
+        CatalogLocationSummary location = client().resource("/v1/catalog/locations/"+symbolicName+"/"+TEST_VERSION)
+                .get(CatalogLocationSummary.class);
+        assertEquals(location.getSymbolicName(), symbolicName);
+
+        // Retrieve all locations
+        Set<CatalogLocationSummary> locations = client().resource("/v1/catalog/locations")
+                .get(new GenericType<Set<CatalogLocationSummary>>() {});
+        boolean found = false;
+        for (CatalogLocationSummary contender : locations) {
+            if (contender.getSymbolicName().equals(symbolicName)) {
+                found = true;
+                break;
+            }
+        }
+        Assert.assertTrue(found, "contenders="+locations);
+        
+        // Delete
+        ClientResponse deleteResponse = client().resource("/v1/catalog/locations/"+symbolicName+"/"+TEST_VERSION)
+                .delete(ClientResponse.class);
+        assertEquals(deleteResponse.getStatus(), Response.Status.NO_CONTENT.getStatusCode());
+
+        ClientResponse getPostDeleteResponse = client().resource("/v1/catalog/locations/"+symbolicName+"/"+TEST_VERSION)
+                .get(ClientResponse.class);
+        assertEquals(getPostDeleteResponse.getStatus(), Response.Status.NOT_FOUND.getStatusCode());
+    }
+
+    @Test
+    public void testDeleteCustomEntityFromCatalog() {
+        String symbolicName = "my.catalog.app.id.to.subsequently.delete";
+        String yaml =
+                "name: "+symbolicName+"\n"+
+                // FIXME name above should be unnecessary when brooklyn.catalog below is working
+                "brooklyn.catalog:\n"+
+                "  id: " + symbolicName + "\n"+
+                "  name: My Catalog App To Be Deleted\n"+
+                "  description: My description\n"+
+                "  version: " + TEST_VERSION + "\n"+
+                "\n"+
+                "services:\n"+
+                "- type: brooklyn.test.entity.TestEntity\n";
+
+        client().resource("/v1/catalog")
+                .post(ClientResponse.class, yaml);
+
+        ClientResponse deleteResponse = client().resource("/v1/catalog/entities/"+symbolicName+"/"+TEST_VERSION)
+                .delete(ClientResponse.class);
+
+        assertEquals(deleteResponse.getStatus(), Response.Status.NO_CONTENT.getStatusCode());
+
+        ClientResponse getPostDeleteResponse = client().resource("/v1/catalog/entities/"+symbolicName+"/"+TEST_VERSION)
+                .get(ClientResponse.class);
+        assertEquals(getPostDeleteResponse.getStatus(), Response.Status.NOT_FOUND.getStatusCode());
+    }
+
+    @Test
+    public void testSetDeprecated() {
+        String itemId = "my.catalog.item.id.for.deprecation";
+        String serviceType = "brooklyn.entity.basic.BasicApplication";
+        addTestCatalogItem(itemId, "template", TEST_VERSION, serviceType);
+        addTestCatalogItem(itemId, "template", "2.0", serviceType);
+        List<CatalogEntitySummary> applications = client().resource("/v1/catalog/applications")
+                .queryParam("fragment", itemId).queryParam("allVersions", "true").get(new GenericType<List<CatalogEntitySummary>>() {});
+        assertEquals(applications.size(), 2);
+        CatalogItemSummary summary0 = applications.get(0);
+        CatalogItemSummary summary1 = applications.get(1);
+
+        // Ensure that the ID required by the API is in the 'usual' format of name:id
+        String id = String.format("%s:%s", summary0.getSymbolicName(), summary0.getVersion());
+        assertEquals(summary0.getId(), id);
+        ClientResponse getDeprecationResponse = client().resource(String.format("/v1/catalog/entities/%s/deprecated/true", id))
+                .post(ClientResponse.class);
+
+        assertEquals(getDeprecationResponse.getStatus(), Response.Status.NO_CONTENT.getStatusCode());
+
+        List<CatalogEntitySummary> applicationsAfterDeprecation = client().resource("/v1/catalog/applications")
+                .queryParam("fragment", "basicapp").queryParam("allVersions", "true").get(new GenericType<List<CatalogEntitySummary>>() {});
+
+        assertEquals(applicationsAfterDeprecation.size(), 1);
+        assertTrue(applicationsAfterDeprecation.contains(summary1));
+
+        ClientResponse getUnDeprecationResponse = client().resource(String.format("/v1/catalog/entities/%s/deprecated/false", summary0.getId()))
+                .post(ClientResponse.class);
+
+        assertEquals(getUnDeprecationResponse.getStatus(), Response.Status.NO_CONTENT.getStatusCode());
+
+        List<CatalogEntitySummary> applicationsAfterUnDeprecation = client().resource("/v1/catalog/applications")
+                .queryParam("fragment", "basicapp").queryParam("allVersions", "true").get(new GenericType<List<CatalogEntitySummary>>() {});
+
+        assertEquals(applications, applicationsAfterUnDeprecation);
+    }
+
+    @Test
+    public void testAddUnreachableItem() {
+        addInvalidCatalogItem("http://0.0.0.0/can-not-connect");
+    }
+
+    @Test
+    public void testAddInvalidItem() {
+        //equivalent to HTTP response 200 text/html
+        addInvalidCatalogItem("classpath://not-a-jar-file.txt");
+    }
+
+    @Test
+    public void testAddMissingItem() {
+        //equivalent to HTTP response 404 text/html
+        addInvalidCatalogItem("classpath://missing-jar-file.txt");
+    }
+
+    private void addInvalidCatalogItem(String bundleUrl) {
+        String symbolicName = "my.catalog.entity.id";
+        String yaml =
+                "brooklyn.catalog:\n"+
+                "  id: " + symbolicName + "\n"+
+                "  name: My Catalog App\n"+
+                "  description: My description\n"+
+                "  icon_url: classpath:/brooklyn/osgi/tests/icon.gif\n"+
+                "  version: " + TEST_VERSION + "\n"+
+                "  libraries:\n"+
+                "  - url: " + bundleUrl + "\n"+
+                "\n"+
+                "services:\n"+
+                "- type: brooklyn.test.entity.TestEntity\n";
+
+        ClientResponse response = client().resource("/v1/catalog")
+                .post(ClientResponse.class, yaml);
+
+        assertEquals(response.getStatus(), HttpStatus.INTERNAL_SERVER_ERROR_500);
+    }
+
+    private static String ver(String id) {
+        return CatalogUtils.getVersionedId(id, TEST_VERSION);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/DelegatingPrintStream.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/DelegatingPrintStream.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/DelegatingPrintStream.java
new file mode 100644
index 0000000..713dccb
--- /dev/null
+++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/DelegatingPrintStream.java
@@ -0,0 +1,183 @@
+/*
+ * 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.apache.brooklyn.rest.resources;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.util.Locale;
+
+public abstract class DelegatingPrintStream extends PrintStream {
+    
+    public DelegatingPrintStream() {
+        super(new IllegalOutputStream());
+    }
+
+    public static class IllegalOutputStream extends OutputStream {
+        @Override public void write(int b) {
+            throw new IllegalStateException("should not write to this output stream");
+        }
+        @Override public void write(byte[] b, int off, int len) {
+            throw new IllegalStateException("should not write to this output stream");
+        }
+    }
+    
+    protected abstract PrintStream getDelegate();
+
+    public int hashCode() {
+        return getDelegate().hashCode();
+    }
+
+    public void write(byte[] b) throws IOException {
+        getDelegate().write(b);
+    }
+
+    public boolean equals(Object obj) {
+        return getDelegate().equals(obj);
+    }
+
+    public String toString() {
+        return getDelegate().toString();
+    }
+
+    public void flush() {
+        getDelegate().flush();
+    }
+
+    public void close() {
+        getDelegate().close();
+    }
+
+    public boolean checkError() {
+        return getDelegate().checkError();
+    }
+
+    public void write(int b) {
+        getDelegate().write(b);
+    }
+
+    public void write(byte[] buf, int off, int len) {
+        getDelegate().write(buf, off, len);
+    }
+
+    public void print(boolean b) {
+        getDelegate().print(b);
+    }
+
+    public void print(char c) {
+        getDelegate().print(c);
+    }
+
+    public void print(int i) {
+        getDelegate().print(i);
+    }
+
+    public void print(long l) {
+        getDelegate().print(l);
+    }
+
+    public void print(float f) {
+        getDelegate().print(f);
+    }
+
+    public void print(double d) {
+        getDelegate().print(d);
+    }
+
+    public void print(char[] s) {
+        getDelegate().print(s);
+    }
+
+    public void print(String s) {
+        getDelegate().print(s);
+    }
+
+    public void print(Object obj) {
+        getDelegate().print(obj);
+    }
+
+    public void println() {
+        getDelegate().println();
+    }
+
+    public void println(boolean x) {
+        getDelegate().println(x);
+    }
+
+    public void println(char x) {
+        getDelegate().println(x);
+    }
+
+    public void println(int x) {
+        getDelegate().println(x);
+    }
+
+    public void println(long x) {
+        getDelegate().println(x);
+    }
+
+    public void println(float x) {
+        getDelegate().println(x);
+    }
+
+    public void println(double x) {
+        getDelegate().println(x);
+    }
+
+    public void println(char[] x) {
+        getDelegate().println(x);
+    }
+
+    public void println(String x) {
+        getDelegate().println(x);
+    }
+
+    public void println(Object x) {
+        getDelegate().println(x);
+    }
+
+    public PrintStream printf(String format, Object... args) {
+        return getDelegate().printf(format, args);
+    }
+
+    public PrintStream printf(Locale l, String format, Object... args) {
+        return getDelegate().printf(l, format, args);
+    }
+
+    public PrintStream format(String format, Object... args) {
+        return getDelegate().format(format, args);
+    }
+
+    public PrintStream format(Locale l, String format, Object... args) {
+        return getDelegate().format(l, format, args);
+    }
+
+    public PrintStream append(CharSequence csq) {
+        return getDelegate().append(csq);
+    }
+
+    public PrintStream append(CharSequence csq, int start, int end) {
+        return getDelegate().append(csq, start, end);
+    }
+
+    public PrintStream append(char c) {
+        return getDelegate().append(c);
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/DescendantsTest.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/DescendantsTest.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/DescendantsTest.java
new file mode 100644
index 0000000..093addf
--- /dev/null
+++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/DescendantsTest.java
@@ -0,0 +1,134 @@
+/*
+ * 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.apache.brooklyn.rest.resources;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeoutException;
+
+import org.codehaus.jackson.JsonGenerationException;
+import org.codehaus.jackson.map.JsonMappingException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.Test;
+
+import brooklyn.entity.Application;
+import brooklyn.entity.Entity;
+import brooklyn.entity.basic.EntityLocal;
+import brooklyn.event.basic.Sensors;
+import brooklyn.rest.domain.ApplicationSpec;
+import brooklyn.rest.domain.EntitySpec;
+import brooklyn.rest.domain.EntitySummary;
+import org.apache.brooklyn.rest.testing.BrooklynRestResourceTest;
+import org.apache.brooklyn.rest.testing.mocks.RestMockSimpleEntity;
+import brooklyn.util.collections.MutableList;
+import brooklyn.util.text.StringEscapes;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.sun.jersey.api.client.ClientHandlerException;
+import com.sun.jersey.api.client.ClientResponse;
+import com.sun.jersey.api.client.GenericType;
+import com.sun.jersey.api.client.UniformInterfaceException;
+
+@Test(singleThreaded = true)
+public class DescendantsTest extends BrooklynRestResourceTest {
+
+    private static final Logger log = LoggerFactory.getLogger(DescendantsTest.class);
+
+    private final ApplicationSpec simpleSpec = ApplicationSpec.builder().name("simple-app").
+        entities(ImmutableSet.of(
+            new EntitySpec("simple-ent-1", RestMockSimpleEntity.class.getName()),
+            new EntitySpec("simple-ent-2", RestMockSimpleEntity.class.getName()))).
+        locations(ImmutableSet.of("localhost")).
+        build();
+
+    @Test
+    public void testDescendantsInSimpleDeployedApplication() throws InterruptedException, TimeoutException, JsonGenerationException, JsonMappingException, UniformInterfaceException, ClientHandlerException, IOException {
+        ClientResponse response = clientDeploy(simpleSpec);
+        assertTrue(response.getStatus()/100 == 2, "response is "+response);
+        Application application = Iterables.getOnlyElement( getManagementContext().getApplications() );
+        List<Entity> entities = MutableList.copyOf( application.getChildren() );
+        log.debug("Created app "+application+" with children entities "+entities);
+        assertEquals(entities.size(), 2);
+        
+        Set<EntitySummary> descs;
+        descs = client().resource("/v1/applications/"+application.getApplicationId()+"/descendants")
+            .get(new GenericType<Set<EntitySummary>>() {});
+        // includes itself
+        assertEquals(descs.size(), 3);
+        
+        descs = client().resource("/v1/applications/"+application.getApplicationId()+"/descendants"
+            + "?typeRegex="+StringEscapes.escapeUrlParam(".*\\.RestMockSimpleEntity"))
+            .get(new GenericType<Set<EntitySummary>>() {});
+        assertEquals(descs.size(), 2);
+        
+        descs = client().resource("/v1/applications/"+application.getApplicationId()+"/descendants"
+            + "?typeRegex="+StringEscapes.escapeUrlParam(".*\\.BestBockSimpleEntity"))
+            .get(new GenericType<Set<EntitySummary>>() {});
+        assertEquals(descs.size(), 0);
+
+        descs = client().resource("/v1/applications/"+application.getApplicationId()
+            + "/entities/"+entities.get(1).getId()
+            + "/descendants"
+            + "?typeRegex="+StringEscapes.escapeUrlParam(".*\\.RestMockSimpleEntity"))
+            .get(new GenericType<Set<EntitySummary>>() {});
+        assertEquals(descs.size(), 1);
+        
+        Map<String,Object> sensors = client().resource("/v1/applications/"+application.getApplicationId()+"/descendants/sensor/foo"
+            + "?typeRegex="+StringEscapes.escapeUrlParam(".*\\.RestMockSimpleEntity"))
+            .get(new GenericType<Map<String,Object>>() {});
+        assertEquals(sensors.size(), 0);
+
+        long v = 0;
+        ((EntityLocal)application).setAttribute(Sensors.newLongSensor("foo"), v);
+        for (Entity e: entities)
+            ((EntityLocal)e).setAttribute(Sensors.newLongSensor("foo"), v+=123);
+        
+        sensors = client().resource("/v1/applications/"+application.getApplicationId()+"/descendants/sensor/foo")
+            .get(new GenericType<Map<String,Object>>() {});
+        assertEquals(sensors.size(), 3);
+        assertEquals(sensors.get(entities.get(1).getId()), 246);
+        
+        sensors = client().resource("/v1/applications/"+application.getApplicationId()+"/descendants/sensor/foo"
+            + "?typeRegex="+StringEscapes.escapeUrlParam(".*\\.RestMockSimpleEntity"))
+            .get(new GenericType<Map<String,Object>>() {});
+        assertEquals(sensors.size(), 2);
+        
+        sensors = client().resource("/v1/applications/"+application.getApplicationId()+"/"
+            + "entities/"+entities.get(1).getId()+"/"
+            + "descendants/sensor/foo"
+            + "?typeRegex="+StringEscapes.escapeUrlParam(".*\\.RestMockSimpleEntity"))
+            .get(new GenericType<Map<String,Object>>() {});
+        assertEquals(sensors.size(), 1);
+
+        sensors = client().resource("/v1/applications/"+application.getApplicationId()+"/"
+            + "entities/"+entities.get(1).getId()+"/"
+            + "descendants/sensor/foo"
+            + "?typeRegex="+StringEscapes.escapeUrlParam(".*\\.FestPockSimpleEntity"))
+            .get(new GenericType<Map<String,Object>>() {});
+        assertEquals(sensors.size(), 0);
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/EntityConfigResourceTest.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/EntityConfigResourceTest.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/EntityConfigResourceTest.java
new file mode 100644
index 0000000..66d7315
--- /dev/null
+++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/EntityConfigResourceTest.java
@@ -0,0 +1,173 @@
+/*
+ * 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.apache.brooklyn.rest.resources;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+
+import java.net.URI;
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.Nullable;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import brooklyn.entity.basic.EntityInternal;
+import brooklyn.entity.basic.EntityPredicates;
+import brooklyn.rest.domain.ApplicationSpec;
+import brooklyn.rest.domain.EntityConfigSummary;
+import brooklyn.rest.domain.EntitySpec;
+import org.apache.brooklyn.rest.testing.BrooklynRestResourceTest;
+import org.apache.brooklyn.rest.testing.mocks.RestMockSimpleEntity;
+import brooklyn.util.collections.MutableMap;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.sun.jersey.api.client.ClientResponse;
+import com.sun.jersey.api.client.GenericType;
+
+@Test(singleThreaded = true)
+public class EntityConfigResourceTest extends BrooklynRestResourceTest {
+    
+    private final static Logger log = LoggerFactory.getLogger(EntityConfigResourceTest.class);
+    private URI applicationUri;
+    private EntityInternal entity;
+
+    @BeforeClass(alwaysRun = true)
+    @Override
+    public void setUp() throws Exception {
+        super.setUp(); // We require that the superclass setup is done first, as we will be calling out to Jersey
+
+        // Deploy an application that we'll use to read the configuration of
+        final ApplicationSpec simpleSpec = ApplicationSpec.builder().name("simple-app").
+                  entities(ImmutableSet.of(new EntitySpec("simple-ent", RestMockSimpleEntity.class.getName(), ImmutableMap.of("install.version", "1.0.0")))).
+                  locations(ImmutableSet.of("localhost")).
+                  build();
+        
+        ClientResponse response = clientDeploy(simpleSpec);
+        int status = response.getStatus();
+        assertTrue(status >= 200 && status <= 299, "expected HTTP Response of 2xx but got " + status);
+        applicationUri = response.getLocation();
+        log.debug("Built app: application");
+        waitForApplicationToBeRunning(applicationUri);
+        
+        entity = (EntityInternal) Iterables.find(getManagementContext().getEntityManager().getEntities(), EntityPredicates.displayNameEqualTo("simple-ent"));
+    }
+
+    @Test
+    public void testList() throws Exception {
+        List<EntityConfigSummary> entityConfigSummaries = client().resource(
+                URI.create("/v1/applications/simple-app/entities/simple-ent/config"))
+                .get(new GenericType<List<EntityConfigSummary>>() {
+                });
+        
+        // Default entities have over a dozen config entries, but it's unnecessary to test them all; just pick one
+        // representative config key
+        Optional<EntityConfigSummary> configKeyOptional = Iterables.tryFind(entityConfigSummaries, new Predicate<EntityConfigSummary>() {
+            @Override
+            public boolean apply(@Nullable EntityConfigSummary input) {
+                return input != null && "install.version".equals(input.getName());
+            }
+        });
+        assertTrue(configKeyOptional.isPresent());
+        
+        assertEquals(configKeyOptional.get().getType(), "java.lang.String");
+        assertEquals(configKeyOptional.get().getDescription(), "Suggested version");
+        assertFalse(configKeyOptional.get().isReconfigurable());
+        assertNull(configKeyOptional.get().getDefaultValue());
+        assertNull(configKeyOptional.get().getLabel());
+        assertNull(configKeyOptional.get().getPriority());
+    }
+
+    @Test
+    public void testBatchConfigRead() throws Exception {
+        Map<String, Object> currentState = client().resource(
+                URI.create("/v1/applications/simple-app/entities/simple-ent/config/current-state"))
+                .get(new GenericType<Map<String, Object>>() {
+                });
+        assertTrue(currentState.containsKey("install.version"));
+        assertEquals(currentState.get("install.version"), "1.0.0");
+    }
+
+    @Test
+    public void testGetJson() throws Exception {
+        String configValue = client().resource(
+                URI.create("/v1/applications/simple-app/entities/simple-ent/config/install.version"))
+                .accept(MediaType.APPLICATION_JSON_TYPE)
+                .get(String.class);
+        assertEquals(configValue, "\"1.0.0\"");
+    }
+
+    @Test
+    public void testGetPlain() throws Exception {
+        String configValue = client().resource(
+                URI.create("/v1/applications/simple-app/entities/simple-ent/config/install.version"))
+                .accept(MediaType.TEXT_PLAIN_TYPE)
+                .get(String.class);
+        assertEquals(configValue, "1.0.0");
+    }
+
+    @Test
+    public void testSet() throws Exception {
+        try {
+            String uri = "/v1/applications/simple-app/entities/simple-ent/config/"+
+                RestMockSimpleEntity.SAMPLE_CONFIG.getName();
+            ClientResponse response = client().resource(uri)
+                .type(MediaType.APPLICATION_JSON_TYPE)
+                .post(ClientResponse.class, "\"hello world\"");
+            assertEquals(response.getStatus(), Response.Status.NO_CONTENT.getStatusCode());
+
+            assertEquals(entity.getConfig(RestMockSimpleEntity.SAMPLE_CONFIG), "hello world");
+            
+            String value = client().resource(uri).accept(MediaType.APPLICATION_JSON_TYPE).get(String.class);
+            assertEquals(value, "\"hello world\"");
+
+        } finally { entity.config().set(RestMockSimpleEntity.SAMPLE_CONFIG, RestMockSimpleEntity.SAMPLE_CONFIG.getDefaultValue()); }
+    }
+
+    @Test
+    public void testSetFromMap() throws Exception {
+        try {
+            String uri = "/v1/applications/simple-app/entities/simple-ent/config";
+            ClientResponse response = client().resource(uri)
+                .type(MediaType.APPLICATION_JSON_TYPE)
+                .post(ClientResponse.class, MutableMap.of(
+                    RestMockSimpleEntity.SAMPLE_CONFIG.getName(), "hello world"));
+            assertEquals(response.getStatus(), Response.Status.NO_CONTENT.getStatusCode());
+
+            assertEquals(entity.getConfig(RestMockSimpleEntity.SAMPLE_CONFIG), "hello world");
+            
+            String value = client().resource(uri+"/"+RestMockSimpleEntity.SAMPLE_CONFIG.getName()).accept(MediaType.APPLICATION_JSON_TYPE).get(String.class);
+            assertEquals(value, "\"hello world\"");
+
+        } finally { entity.config().set(RestMockSimpleEntity.SAMPLE_CONFIG, RestMockSimpleEntity.SAMPLE_CONFIG.getDefaultValue()); }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/EntityResourceTest.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/EntityResourceTest.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/EntityResourceTest.java
new file mode 100644
index 0000000..bfb4bf3
--- /dev/null
+++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/EntityResourceTest.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.apache.brooklyn.rest.resources;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.Nullable;
+import javax.ws.rs.core.MediaType;
+
+import org.codehaus.jackson.map.ObjectMapper;
+import org.codehaus.jackson.type.TypeReference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import brooklyn.entity.Entity;
+import brooklyn.entity.basic.BasicApplication;
+import brooklyn.entity.basic.Entities;
+import brooklyn.entity.basic.EntityInternal;
+import brooklyn.rest.domain.ApplicationSpec;
+import brooklyn.rest.domain.EntitySpec;
+import brooklyn.rest.domain.TaskSummary;
+import org.apache.brooklyn.rest.testing.BrooklynRestResourceTest;
+import org.apache.brooklyn.rest.testing.mocks.RestMockSimpleEntity;
+import brooklyn.test.HttpTestUtils;
+import brooklyn.test.entity.TestEntity;
+import brooklyn.util.collections.MutableList;
+import brooklyn.util.exceptions.Exceptions;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.sun.jersey.api.client.ClientResponse;
+
+@Test(singleThreaded = true)
+public class EntityResourceTest extends BrooklynRestResourceTest {
+
+    private static final Logger log = LoggerFactory.getLogger(EntityResourceTest.class);
+    
+    private final ApplicationSpec simpleSpec = ApplicationSpec.builder()
+            .name("simple-app")
+            .entities(ImmutableSet.of(new EntitySpec("simple-ent", RestMockSimpleEntity.class.getName())))
+            .locations(ImmutableSet.of("localhost"))
+            .build();
+
+    private EntityInternal entity;
+
+    private static final String entityEndpoint = "/v1/applications/simple-app/entities/simple-ent";
+
+    @BeforeClass(alwaysRun = true)
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+
+        // Deploy application
+        ClientResponse deploy = clientDeploy(simpleSpec);
+        waitForApplicationToBeRunning(deploy.getLocation());
+
+        // Add tag
+        entity = (EntityInternal) Iterables.find(getManagementContext().getEntityManager().getEntities(), new Predicate<Entity>() {
+            @Override
+            public boolean apply(@Nullable Entity input) {
+                return "RestMockSimpleEntity".equals(input.getEntityType().getSimpleName());
+            }
+        });
+    }
+
+    @Test
+    public void testTagsSanity() throws Exception {
+        entity.tags().addTag("foo");
+        
+        ClientResponse response = client().resource(entityEndpoint + "/tags")
+                .accept(MediaType.APPLICATION_JSON_TYPE)
+                .get(ClientResponse.class);
+        String data = response.getEntity(String.class);
+        
+        try {
+            List<Object> tags = new ObjectMapper().readValue(data, new TypeReference<List<Object>>() {});
+            Assert.assertTrue(tags.contains("foo"));
+            Assert.assertFalse(tags.contains("bar"));
+        } catch (Exception e) {
+            Exceptions.propagateIfFatal(e);
+            throw new IllegalStateException("Error with deserialization of tags list: "+e+"\n"+data, e);
+        }
+    }
+    
+    @Test
+    public void testRename() throws Exception {
+        try {
+            ClientResponse response = client().resource(entityEndpoint + "/name")
+                .queryParam("name", "New Name")
+                .post(ClientResponse.class);
+
+            HttpTestUtils.assertHealthyStatusCode(response.getStatus());
+            Assert.assertTrue(entity.getDisplayName().equals("New Name"));
+        } finally {
+            // restore it for other tests!
+            entity.setDisplayName("simple-ent");
+        }
+    }
+    
+    @Test
+    public void testAddChild() throws Exception {
+        try {
+            // to test in GUI: 
+            // services: [ { type: brooklyn.entity.basic.BasicEntity }]
+            ClientResponse response = client().resource(entityEndpoint + "/children?timeout=10s")
+                .entity("services: [ { type: "+TestEntity.class.getName()+" }]", "application/yaml")
+                .post(ClientResponse.class);
+
+            HttpTestUtils.assertHealthyStatusCode(response.getStatus());
+            Assert.assertEquals(entity.getChildren().size(), 1);
+            Entity child = Iterables.getOnlyElement(entity.getChildren());
+            Assert.assertTrue(Entities.isManaged(child));
+            
+            TaskSummary task = response.getEntity(TaskSummary.class);
+            Assert.assertEquals(task.getResult(), MutableList.of(child.getId()));
+            
+        } finally {
+            // restore it for other tests
+            Collection<Entity> children = entity.getChildren();
+            if (!children.isEmpty()) Entities.unmanage(Iterables.getOnlyElement(children));
+        }
+    }
+    
+    @Test
+    public void testTagsDoNotSerializeTooMuch() throws Exception {
+        entity.tags().addTag("foo");
+        entity.tags().addTag(entity.getParent());
+
+        ClientResponse response = client().resource(entityEndpoint + "/tags")
+                .accept(MediaType.APPLICATION_JSON)
+                .get(ClientResponse.class);
+        String raw = response.getEntity(String.class);
+        log.info("TAGS raw: "+raw);
+        HttpTestUtils.assertHealthyStatusCode(response.getStatus());
+        
+        Assert.assertTrue(raw.contains(entity.getParent().getId()), "unexpected app tag, does not include ID: "+raw);
+        
+        Assert.assertTrue(raw.length() < 1000, "unexpected app tag, includes too much mgmt info (len "+raw.length()+"): "+raw);
+        
+        Assert.assertFalse(raw.contains(entity.getManagementContext().getManagementNodeId()), "unexpected app tag, includes too much mgmt info: "+raw);
+        Assert.assertFalse(raw.contains("managementContext"), "unexpected app tag, includes too much mgmt info: "+raw);
+        Assert.assertFalse(raw.contains("localhost"), "unexpected app tag, includes too much mgmt info: "+raw);
+        Assert.assertFalse(raw.contains("catalog"), "unexpected app tag, includes too much mgmt info: "+raw);
+
+        @SuppressWarnings("unchecked")
+        List<Object> tags = mapper().readValue(raw, List.class);
+        log.info("TAGS are: "+tags);
+        
+        Assert.assertEquals(tags.size(), 2, "tags are: "+tags);
+
+        Assert.assertTrue(tags.contains("foo"));
+        Assert.assertFalse(tags.contains("bar"));
+        
+        MutableList<Object> appTags = MutableList.copyOf(tags);
+        appTags.remove("foo");
+        Object appTag = Iterables.getOnlyElement( appTags );
+        
+        // it's a map at this point, because there was no way to make it something stronger than Object
+        Assert.assertTrue(appTag instanceof Map, "Should have deserialized an entity: "+appTag);
+        // let's re-serialize it as an entity
+        appTag = mapper().readValue(mapper().writeValueAsString(appTag), Entity.class);
+        
+        Assert.assertTrue(appTag instanceof Entity, "Should have deserialized an entity: "+appTag);
+        Assert.assertEquals( ((Entity)appTag).getId(), entity.getApplicationId(), "Wrong ID: "+appTag);
+        Assert.assertTrue(appTag instanceof BasicApplication, "Should have deserialized BasicApplication: "+appTag);
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/ErrorResponseTest.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/ErrorResponseTest.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/ErrorResponseTest.java
new file mode 100644
index 0000000..f5dd1e6
--- /dev/null
+++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/resources/ErrorResponseTest.java
@@ -0,0 +1,98 @@
+/*
+ * 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.apache.brooklyn.rest.resources;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response.Status;
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import brooklyn.rest.domain.ApiError;
+import brooklyn.rest.domain.ApplicationSpec;
+import brooklyn.rest.domain.EntitySpec;
+import brooklyn.rest.domain.PolicySummary;
+import org.apache.brooklyn.rest.testing.BrooklynRestResourceTest;
+import org.apache.brooklyn.rest.testing.mocks.RestMockSimpleEntity;
+import org.apache.brooklyn.rest.testing.mocks.RestMockSimplePolicy;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+import com.sun.jersey.api.client.ClientResponse;
+
+public class ErrorResponseTest extends BrooklynRestResourceTest {
+
+    private final ApplicationSpec simpleSpec = ApplicationSpec.builder().name("simple-app").entities(
+            ImmutableSet.of(new EntitySpec("simple-ent", RestMockSimpleEntity.class.getName()))).locations(
+            ImmutableSet.of("localhost")).build();
+    private String policyId;
+
+    @BeforeClass(alwaysRun = true)
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+
+        ClientResponse aResponse = clientDeploy(simpleSpec);
+        waitForApplicationToBeRunning(aResponse.getLocation());
+
+        String policiesEndpoint = "/v1/applications/simple-app/entities/simple-ent/policies";
+
+        ClientResponse pResponse = client().resource(policiesEndpoint)
+                .queryParam("type", RestMockSimplePolicy.class.getCanonicalName())
+                .type(MediaType.APPLICATION_JSON_TYPE)
+                .post(ClientResponse.class, Maps.newHashMap());
+        PolicySummary response = pResponse.getEntity(PolicySummary.class);
+        assertNotNull(response.getId());
+        policyId = response.getId();
+    }
+
+    @Test
+    public void testResponseToBadRequest() {
+        String resource = "/v1/applications/simple-app/entities/simple-ent/policies/"+policyId+"/config/"
+                + RestMockSimplePolicy.INTEGER_CONFIG.getName() + "/set";
+
+        ClientResponse response = client().resource(resource)
+                .queryParam("value", "notanumber")
+                .post(ClientResponse.class);
+
+        assertEquals(response.getStatus(), Status.BAD_REQUEST.getStatusCode());
+        assertEquals(response.getHeaders().getFirst("Content-Type"), MediaType.APPLICATION_JSON);
+
+        ApiError error = response.getEntity(ApiError.class);
+        assertTrue(error.getMessage().toLowerCase().contains("cannot coerce"));
+    }
+
+    @Test
+    public void testResponseToWrongMethod() {
+        String resource = "/v1/applications/simple-app/entities/simple-ent/policies/"+policyId+"/config/"
+                + RestMockSimplePolicy.INTEGER_CONFIG.getName() + "/set";
+
+        // Should be POST, not GET
+        ClientResponse response = client().resource(resource)
+                .queryParam("value", "4")
+                .get(ClientResponse.class);
+
+        assertEquals(response.getStatus(), 405);
+        // Can we assert anything about the content type?
+    }
+}



Mime
View raw message