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.hamcrest.MatcherAssert.assertThat;
17 import static org.junit.jupiter.api.Assertions.*;
19 import java.io.IOException;
20 import java.lang.reflect.Type;
22 import java.util.Map.Entry;
23 import java.util.Random;
25 import javax.ws.rs.client.Entity;
26 import javax.ws.rs.core.Response;
28 import org.eclipse.jdt.annotation.NonNullByDefault;
29 import org.glassfish.jersey.server.ResourceConfig;
30 import org.junit.jupiter.api.AfterEach;
31 import org.junit.jupiter.api.BeforeEach;
32 import org.junit.jupiter.api.Test;
33 import org.openhab.core.automation.Condition;
34 import org.openhab.core.automation.Rule;
35 import org.openhab.core.automation.RuleRegistry;
36 import org.openhab.core.automation.Trigger;
37 import org.openhab.core.automation.util.RuleBuilder;
38 import org.openhab.core.items.GenericItem;
39 import org.openhab.core.items.ItemRegistry;
40 import org.openhab.core.library.items.ColorItem;
41 import org.openhab.core.library.items.SwitchItem;
42 import org.openhab.core.library.types.HSBType;
43 import org.openhab.core.library.types.OnOffType;
44 import org.openhab.core.types.State;
45 import org.openhab.io.hueemulation.internal.ConfigStore;
46 import org.openhab.io.hueemulation.internal.RuleUtils;
47 import org.openhab.io.hueemulation.internal.dto.HueRuleEntry;
48 import org.openhab.io.hueemulation.internal.dto.HueRuleEntry.Operator;
49 import org.openhab.io.hueemulation.internal.dto.HueSceneEntry;
50 import org.openhab.io.hueemulation.internal.dto.changerequest.HueCommand;
51 import org.openhab.io.hueemulation.internal.rest.mocks.DummyItemRegistry;
52 import org.openhab.io.hueemulation.internal.rest.mocks.DummyRuleRegistry;
54 import com.google.gson.Gson;
55 import com.google.gson.reflect.TypeToken;
58 * Tests for various rules API endpoints.
60 * @author David Graeff - Initial contribution
63 public class RulesTests {
64 protected @NonNullByDefault({}) CommonSetup commonSetup;
65 protected @NonNullByDefault({}) ConfigStore cs;
66 protected @NonNullByDefault({}) ItemRegistry itemRegistry;
67 protected @NonNullByDefault({}) RuleRegistry ruleRegistry;
69 Rules subject = new Rules();
70 LightsAndGroups lightsAndGroups = new LightsAndGroups();
72 private void addItemToReg(GenericItem item, State state, String tag, String label) {
76 itemRegistry.add(item);
80 public void setUp() throws IOException {
81 commonSetup = new CommonSetup(false);
82 this.cs = commonSetup.cs;
84 itemRegistry = new DummyItemRegistry();
85 ruleRegistry = new DummyRuleRegistry();
87 subject.cs = commonSetup.cs;
88 subject.userManagement = commonSetup.userManagement;
89 subject.ruleRegistry = ruleRegistry;
90 subject.itemRegistry = itemRegistry;
93 // We need the LightsAndGroups class to convert registry entries into HueDatastore
95 lightsAndGroups.cs = cs;
96 lightsAndGroups.eventPublisher = commonSetup.eventPublisher;
97 lightsAndGroups.userManagement = commonSetup.userManagement;
98 lightsAndGroups.itemRegistry = itemRegistry;
99 lightsAndGroups.activate();
101 addItemToReg(new SwitchItem("switch1"), OnOffType.ON, "Switchable", "name1");
102 addItemToReg(new SwitchItem("switch2"), OnOffType.ON, "Switchable", "name2");
103 addItemToReg(new ColorItem("color1"), HSBType.BLUE, "ColorLighting", "");
105 commonSetup.start(new ResourceConfig().registerInstances(subject));
109 public void tearDown() {
110 RuleUtils.random = new Random();
111 commonSetup.dispose();
115 public void addUpdateRemoveScheduleToRegistry() {
116 assertThat(cs.ds.lights.get("switch1"), is(notNullValue()));
118 HueCommand command = new HueCommand("/api/testuser/lights/switch1/state", "PUT", "{'on':true}");
119 HueRuleEntry.Condition condition = new HueRuleEntry.Condition("/lights/switch1/state/on", Operator.dx, null);
121 Entry<Trigger, Condition> triggerCond = Rules.hueConditionToAutomation(command.address.replace("/", "-"),
122 condition, itemRegistry);
124 Rule rule = RuleBuilder.create("demo1").withName("test name").withTags(Rules.RULES_TAG) //
125 .withActions(RuleUtils.createHttpAction(command, "command")) //
126 .withTriggers(triggerCond.getKey()).withConditions(triggerCond.getValue()).build();
128 ruleRegistry.add(rule);
131 HueRuleEntry entry = cs.ds.rules.get("demo1");
132 assertThat(entry.conditions.get(0).address, is("/lights/switch1/state/on"));
133 assertThat(entry.conditions.get(0).operator, is(Operator.dx));
134 assertThat(entry.actions.get(0).address, is("/lights/switch1/state"));
135 assertThat(entry.actions.get(0).method, is("PUT"));
136 assertThat(entry.actions.get(0).body, is("{'on':true}"));
139 command = new HueCommand("/api/testuser/lights/switch2/state", "PUT", "{'on':false}");
140 rule = RuleBuilder.create("demo1").withName("name2").withTags(Rules.RULES_TAG) //
141 .withActions(RuleUtils.createHttpAction(command, "command")) //
142 .withTriggers(triggerCond.getKey()).withConditions(triggerCond.getValue()).build();
143 ruleRegistry.update(rule);
145 entry = cs.ds.rules.get("demo1");
146 assertThat(entry.actions.get(0).address, is("/lights/switch2/state"));
147 assertThat(entry.actions.get(0).method, is("PUT"));
148 assertThat(entry.actions.get(0).body, is("{'on':false}"));
149 assertThat(entry.name, is("name2"));
153 ruleRegistry.remove("demo1");
154 entry = cs.ds.rules.get("demo1");
155 assertThat(entry, nullValue());
158 @SuppressWarnings("null")
160 public void addGetRemoveRuleViaRest() {
162 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}\"}]}";
163 Response response = commonSetup.client.target(commonSetup.basePath + "/testuser/rules").request()
164 .post(Entity.json(body));
165 assertEquals(200, response.getStatus());
166 assertThat(response.readEntity(String.class), containsString("success"));
168 // 1.1 Check for entry
169 Entry<String, HueRuleEntry> idAndEntry = cs.ds.rules.entrySet().stream().findAny().get();
170 HueRuleEntry entry = idAndEntry.getValue();
171 assertThat(entry.name, is("test name"));
172 assertThat(entry.actions.get(0).address, is("/lights/switch1/state"));
173 assertThat(entry.conditions.get(0).address, is("/lights/switch1/state/on"));
175 // 1.2 Check for rule
176 Rule rule = ruleRegistry.get(idAndEntry.getKey());
177 assertThat(rule.getName(), is("test name"));
178 assertThat(rule.getActions().get(0).getId(), is("-api-testuser-lights-switch1-state"));
179 assertThat(rule.getActions().get(0).getTypeUID(), is("rules.HttpAction"));
182 response = commonSetup.client.target(commonSetup.basePath + "/testuser/rules/" + idAndEntry.getKey()).request()
184 assertEquals(200, response.getStatus());
185 HueSceneEntry fromJson = new Gson().fromJson(response.readEntity(String.class), HueSceneEntry.class);
186 assertThat(fromJson.name, is(idAndEntry.getValue().name));
189 response = commonSetup.client.target(commonSetup.basePath + "/testuser/rules/" + idAndEntry.getKey()).request()
191 assertEquals(200, response.getStatus());
192 assertTrue(cs.ds.rules.isEmpty());
196 public void updateRuleViaRest() {
197 HueCommand command = new HueCommand("/api/testuser/lights/switch1/state", "PUT", "{'on':true}");
198 HueRuleEntry.Condition condition = new HueRuleEntry.Condition("/lights/switch1/state/on", Operator.dx, null);
200 Entry<Trigger, Condition> triggerCond = Rules.hueConditionToAutomation(command.address.replace("/", "-"),
201 condition, itemRegistry);
203 Rule rule = RuleBuilder.create("demo1").withName("test name").withTags(Rules.RULES_TAG) //
204 .withActions(RuleUtils.createHttpAction(command, "command")) //
205 .withTriggers(triggerCond.getKey()).withConditions(triggerCond.getValue()).build();
207 ruleRegistry.add(rule);
209 // Modify (just the name)
210 String body = "{ 'name':'A new name'}";
211 Response response = commonSetup.client.target(commonSetup.basePath + "/testuser/rules/demo1").request()
212 .put(Entity.json(body));
213 assertEquals(200, response.getStatus());
214 assertThat(response.readEntity(String.class), containsString("name"));
216 Entry<String, HueRuleEntry> idAndEntry = cs.ds.rules.entrySet().stream().findAny().get();
217 HueRuleEntry entry = idAndEntry.getValue();
218 assertThat(entry.name, is("A new name"));
219 assertThat(entry.actions.get(0).address, is("/lights/switch1/state"));
220 assertThat(entry.conditions.get(0).address, is("/lights/switch1/state/on"));
223 rule = RuleBuilder.create("demo1").withName("test name").withTags(Rules.RULES_TAG) //
224 .withActions(RuleUtils.createHttpAction(command, "command")) //
225 .withTriggers(triggerCond.getKey()).withConditions(triggerCond.getValue()).build();
227 ruleRegistry.update(rule); // Reset rule
229 idAndEntry = cs.ds.rules.entrySet().stream().findAny().get();
231 // Modify (Change condition)
232 body = "{\"conditions\":[{\"address\":\"/lights/switch1/state/on\",\"operator\":\"ddx\"}]}";
233 response = commonSetup.client.target(commonSetup.basePath + "/testuser/rules/demo1").request()
234 .put(Entity.json(body));
235 assertEquals(200, response.getStatus());
236 assertThat(response.readEntity(String.class), containsString("conditions"));
238 idAndEntry = cs.ds.rules.entrySet().stream().findAny().get();
239 entry = idAndEntry.getValue();
240 assertThat(entry.name, is("test name")); // should not have changed
241 assertThat(entry.conditions.get(0).operator, is(Operator.ddx));
243 // Modify (Change action)
244 body = "{\"actions\":[{\"address\":\"/lights/switch2/state\",\"method\":\"PUT\",\"body\":\"{\\u0027on\\u0027:false}\"}]}";
245 response = commonSetup.client.target(commonSetup.basePath + "/testuser/rules/demo1").request()
246 .put(Entity.json(body));
247 assertEquals(200, response.getStatus());
248 assertThat(response.readEntity(String.class), containsString("actions"));
250 idAndEntry = cs.ds.rules.entrySet().stream().findAny().get();
251 entry = idAndEntry.getValue();
252 assertThat(entry.name, is("test name")); // should not have changed
253 assertThat(entry.actions.get(0).address, is("/lights/switch2/state"));
257 public void getAll() {
258 HueCommand command = new HueCommand("/api/testuser/lights/switch1/state", "PUT", "{'on':true}");
259 HueRuleEntry.Condition condition = new HueRuleEntry.Condition("/lights/switch1/state/on", Operator.dx, null);
261 Entry<Trigger, Condition> triggerCond = Rules.hueConditionToAutomation(command.address.replace("/", "-"),
262 condition, itemRegistry);
264 Rule rule = RuleBuilder.create("demo1").withName("test name").withTags(Rules.RULES_TAG) //
265 .withActions(RuleUtils.createHttpAction(command, "command")) //
266 .withTriggers(triggerCond.getKey()).withConditions(triggerCond.getValue()).build();
268 ruleRegistry.add(rule);
270 Response response = commonSetup.client.target(commonSetup.basePath + "/testuser/rules").request().get();
271 Type type = new TypeToken<Map<String, HueRuleEntry>>() {
273 String body = response.readEntity(String.class);
274 Map<String, HueRuleEntry> fromJson = new Gson().fromJson(body, type);
275 HueRuleEntry entry = fromJson.get("demo1");
276 assertThat(entry.name, is("test name"));
277 assertThat(entry.actions.get(0).address, is("/lights/switch1/state"));
278 assertThat(entry.conditions.get(0).address, is("/lights/switch1/state/on"));