2 * Copyright (c) 2010-2020 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.io.hueemulation.internal.rest;
15 import static org.hamcrest.CoreMatchers.*;
16 import static org.junit.Assert.*;
18 import java.io.IOException;
19 import java.lang.reflect.Type;
21 import java.util.Map.Entry;
22 import java.util.Random;
24 import javax.ws.rs.client.Entity;
25 import javax.ws.rs.core.Response;
27 import org.eclipse.jdt.annotation.NonNullByDefault;
28 import org.openhab.core.items.GenericItem;
29 import org.openhab.core.items.ItemRegistry;
30 import org.openhab.core.library.items.ColorItem;
31 import org.openhab.core.library.items.SwitchItem;
32 import org.openhab.core.library.types.HSBType;
33 import org.openhab.core.library.types.OnOffType;
34 import org.openhab.core.types.State;
35 import org.glassfish.jersey.server.ResourceConfig;
36 import org.junit.After;
37 import org.junit.Before;
38 import org.junit.Test;
39 import org.openhab.core.automation.Condition;
40 import org.openhab.core.automation.Rule;
41 import org.openhab.core.automation.RuleRegistry;
42 import org.openhab.core.automation.Trigger;
43 import org.openhab.core.automation.util.RuleBuilder;
44 import org.openhab.io.hueemulation.internal.ConfigStore;
45 import org.openhab.io.hueemulation.internal.RuleUtils;
46 import org.openhab.io.hueemulation.internal.dto.HueRuleEntry;
47 import org.openhab.io.hueemulation.internal.dto.HueRuleEntry.Operator;
48 import org.openhab.io.hueemulation.internal.dto.HueSceneEntry;
49 import org.openhab.io.hueemulation.internal.dto.changerequest.HueCommand;
50 import org.openhab.io.hueemulation.internal.rest.mocks.DummyItemRegistry;
51 import org.openhab.io.hueemulation.internal.rest.mocks.DummyRuleRegistry;
53 import com.google.gson.Gson;
54 import com.google.gson.reflect.TypeToken;
57 * Tests for various rules API endpoints.
59 * @author David Graeff - Initial contribution
62 public class RulesTests {
63 protected @NonNullByDefault({}) CommonSetup commonSetup;
64 protected @NonNullByDefault({}) ConfigStore cs;
65 protected @NonNullByDefault({}) ItemRegistry itemRegistry;
66 protected @NonNullByDefault({}) RuleRegistry ruleRegistry;
68 Rules subject = new Rules();
69 LightsAndGroups lightsAndGroups = new LightsAndGroups();
71 private void addItemToReg(GenericItem item, State state, String tag, String label) {
75 itemRegistry.add(item);
79 public void setUp() throws IOException {
80 commonSetup = new CommonSetup(false);
81 this.cs = commonSetup.cs;
83 itemRegistry = new DummyItemRegistry();
84 ruleRegistry = new DummyRuleRegistry();
86 subject.cs = commonSetup.cs;
87 subject.userManagement = commonSetup.userManagement;
88 subject.ruleRegistry = ruleRegistry;
89 subject.itemRegistry = itemRegistry;
92 // We need the LightsAndGroups class to convert registry entries into HueDatastore
94 lightsAndGroups.cs = cs;
95 lightsAndGroups.eventPublisher = commonSetup.eventPublisher;
96 lightsAndGroups.userManagement = commonSetup.userManagement;
97 lightsAndGroups.itemRegistry = itemRegistry;
98 lightsAndGroups.activate();
100 addItemToReg(new SwitchItem("switch1"), OnOffType.ON, "Switchable", "name1");
101 addItemToReg(new SwitchItem("switch2"), OnOffType.ON, "Switchable", "name2");
102 addItemToReg(new ColorItem("color1"), HSBType.BLUE, "ColorLighting", "");
104 commonSetup.start(new ResourceConfig().registerInstances(subject));
108 public void tearDown() {
109 RuleUtils.random = new Random();
110 commonSetup.dispose();
114 public void addUpdateRemoveScheduleToRegistry() {
115 assertThat(cs.ds.lights.get("switch1"), is(notNullValue()));
117 HueCommand command = new HueCommand("/api/testuser/lights/switch1/state", "PUT", "{'on':true}");
118 HueRuleEntry.Condition condition = new HueRuleEntry.Condition("/lights/switch1/state/on", Operator.dx, null);
120 Entry<Trigger, Condition> triggerCond = Rules.hueConditionToAutomation(command.address.replace("/", "-"),
121 condition, itemRegistry);
123 Rule rule = RuleBuilder.create("demo1").withName("test name").withTags(Rules.RULES_TAG) //
124 .withActions(RuleUtils.createHttpAction(command, "command")) //
125 .withTriggers(triggerCond.getKey()).withConditions(triggerCond.getValue()).build();
127 ruleRegistry.add(rule);
130 HueRuleEntry entry = cs.ds.rules.get("demo1");
131 assertThat(entry.conditions.get(0).address, is("/lights/switch1/state/on"));
132 assertThat(entry.conditions.get(0).operator, is(Operator.dx));
133 assertThat(entry.actions.get(0).address, is("/lights/switch1/state"));
134 assertThat(entry.actions.get(0).method, is("PUT"));
135 assertThat(entry.actions.get(0).body, is("{'on':true}"));
138 command = new HueCommand("/api/testuser/lights/switch2/state", "PUT", "{'on':false}");
139 rule = RuleBuilder.create("demo1").withName("name2").withTags(Rules.RULES_TAG) //
140 .withActions(RuleUtils.createHttpAction(command, "command")) //
141 .withTriggers(triggerCond.getKey()).withConditions(triggerCond.getValue()).build();
142 ruleRegistry.update(rule);
144 entry = cs.ds.rules.get("demo1");
145 assertThat(entry.actions.get(0).address, is("/lights/switch2/state"));
146 assertThat(entry.actions.get(0).method, is("PUT"));
147 assertThat(entry.actions.get(0).body, is("{'on':false}"));
148 assertThat(entry.name, is("name2"));
152 ruleRegistry.remove("demo1");
153 entry = cs.ds.rules.get("demo1");
154 assertThat(entry, nullValue());
157 @SuppressWarnings("null")
159 public void addGetRemoveRuleViaRest() {
161 String body = "{\"name\":\"test name\",\"description\":\"\",\"owner\":\"\",\"conditions\":[{\"address\":\"/lights/switch1/state/on\",\"operator\":\"dx\"}],\"actions\":[{\"address\":\"/lights/switch1/state\",\"method\":\"PUT\",\"body\":\"{\\u0027on\\u0027:true}\"}]}";
162 Response response = commonSetup.client.target(commonSetup.basePath + "/testuser/rules").request()
163 .post(Entity.json(body));
164 assertEquals(200, response.getStatus());
165 assertThat(response.readEntity(String.class), containsString("success"));
167 // 1.1 Check for entry
168 Entry<String, HueRuleEntry> idAndEntry = cs.ds.rules.entrySet().stream().findAny().get();
169 HueRuleEntry entry = idAndEntry.getValue();
170 assertThat(entry.name, is("test name"));
171 assertThat(entry.actions.get(0).address, is("/lights/switch1/state"));
172 assertThat(entry.conditions.get(0).address, is("/lights/switch1/state/on"));
174 // 1.2 Check for rule
175 Rule rule = ruleRegistry.get(idAndEntry.getKey());
176 assertThat(rule.getName(), is("test name"));
177 assertThat(rule.getActions().get(0).getId(), is("-api-testuser-lights-switch1-state"));
178 assertThat(rule.getActions().get(0).getTypeUID(), is("rules.HttpAction"));
181 response = commonSetup.client.target(commonSetup.basePath + "/testuser/rules/" + idAndEntry.getKey()).request()
183 assertEquals(200, response.getStatus());
184 HueSceneEntry fromJson = new Gson().fromJson(response.readEntity(String.class), HueSceneEntry.class);
185 assertThat(fromJson.name, is(idAndEntry.getValue().name));
188 response = commonSetup.client.target(commonSetup.basePath + "/testuser/rules/" + idAndEntry.getKey()).request()
190 assertEquals(200, response.getStatus());
191 assertTrue(cs.ds.rules.isEmpty());
195 public void updateRuleViaRest() {
196 HueCommand command = new HueCommand("/api/testuser/lights/switch1/state", "PUT", "{'on':true}");
197 HueRuleEntry.Condition condition = new HueRuleEntry.Condition("/lights/switch1/state/on", Operator.dx, null);
199 Entry<Trigger, Condition> triggerCond = Rules.hueConditionToAutomation(command.address.replace("/", "-"),
200 condition, itemRegistry);
202 Rule rule = RuleBuilder.create("demo1").withName("test name").withTags(Rules.RULES_TAG) //
203 .withActions(RuleUtils.createHttpAction(command, "command")) //
204 .withTriggers(triggerCond.getKey()).withConditions(triggerCond.getValue()).build();
206 ruleRegistry.add(rule);
208 // Modify (just the name)
209 String body = "{ 'name':'A new name'}";
210 Response response = commonSetup.client.target(commonSetup.basePath + "/testuser/rules/demo1").request()
211 .put(Entity.json(body));
212 assertEquals(200, response.getStatus());
213 assertThat(response.readEntity(String.class), containsString("name"));
215 Entry<String, HueRuleEntry> idAndEntry = cs.ds.rules.entrySet().stream().findAny().get();
216 HueRuleEntry entry = idAndEntry.getValue();
217 assertThat(entry.name, is("A new name"));
218 assertThat(entry.actions.get(0).address, is("/lights/switch1/state"));
219 assertThat(entry.conditions.get(0).address, is("/lights/switch1/state/on"));
222 rule = RuleBuilder.create("demo1").withName("test name").withTags(Rules.RULES_TAG) //
223 .withActions(RuleUtils.createHttpAction(command, "command")) //
224 .withTriggers(triggerCond.getKey()).withConditions(triggerCond.getValue()).build();
226 ruleRegistry.update(rule); // Reset rule
228 idAndEntry = cs.ds.rules.entrySet().stream().findAny().get();
230 // Modify (Change condition)
231 body = "{\"conditions\":[{\"address\":\"/lights/switch1/state/on\",\"operator\":\"ddx\"}]}";
232 response = commonSetup.client.target(commonSetup.basePath + "/testuser/rules/demo1").request()
233 .put(Entity.json(body));
234 assertEquals(200, response.getStatus());
235 assertThat(response.readEntity(String.class), containsString("conditions"));
237 idAndEntry = cs.ds.rules.entrySet().stream().findAny().get();
238 entry = idAndEntry.getValue();
239 assertThat(entry.name, is("test name")); // should not have changed
240 assertThat(entry.conditions.get(0).operator, is(Operator.ddx));
242 // Modify (Change action)
243 body = "{\"actions\":[{\"address\":\"/lights/switch2/state\",\"method\":\"PUT\",\"body\":\"{\\u0027on\\u0027:false}\"}]}";
244 response = commonSetup.client.target(commonSetup.basePath + "/testuser/rules/demo1").request()
245 .put(Entity.json(body));
246 assertEquals(200, response.getStatus());
247 assertThat(response.readEntity(String.class), containsString("actions"));
249 idAndEntry = cs.ds.rules.entrySet().stream().findAny().get();
250 entry = idAndEntry.getValue();
251 assertThat(entry.name, is("test name")); // should not have changed
252 assertThat(entry.actions.get(0).address, is("/lights/switch2/state"));
256 public void getAll() {
257 HueCommand command = new HueCommand("/api/testuser/lights/switch1/state", "PUT", "{'on':true}");
258 HueRuleEntry.Condition condition = new HueRuleEntry.Condition("/lights/switch1/state/on", Operator.dx, null);
260 Entry<Trigger, Condition> triggerCond = Rules.hueConditionToAutomation(command.address.replace("/", "-"),
261 condition, itemRegistry);
263 Rule rule = RuleBuilder.create("demo1").withName("test name").withTags(Rules.RULES_TAG) //
264 .withActions(RuleUtils.createHttpAction(command, "command")) //
265 .withTriggers(triggerCond.getKey()).withConditions(triggerCond.getValue()).build();
267 ruleRegistry.add(rule);
269 Response response = commonSetup.client.target(commonSetup.basePath + "/testuser/rules").request().get();
270 Type type = new TypeToken<Map<String, HueRuleEntry>>() {
272 String body = response.readEntity(String.class);
273 Map<String, HueRuleEntry> fromJson = new Gson().fromJson(body, type);
274 HueRuleEntry entry = fromJson.get("demo1");
275 assertThat(entry.name, is("test name"));
276 assertThat(entry.actions.get(0).address, is("/lights/switch1/state"));
277 assertThat(entry.conditions.get(0).address, is("/lights/switch1/state/on"));