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? + } +}