]> git.basschouten.com Git - openhab-addons.git/blob
86cae0f05f6559915fb8c6e10ee210caaca2636f
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.io.hueemulation.internal.rest;
14
15 import static org.hamcrest.CoreMatchers.*;
16 import static org.hamcrest.MatcherAssert.assertThat;
17 import static org.junit.jupiter.api.Assertions.*;
18
19 import java.io.IOException;
20 import java.lang.reflect.Type;
21 import java.util.Map;
22 import java.util.Map.Entry;
23 import java.util.Random;
24
25 import org.eclipse.jdt.annotation.NonNullByDefault;
26 import org.eclipse.jetty.client.api.ContentResponse;
27 import org.glassfish.jersey.server.ResourceConfig;
28 import org.junit.jupiter.api.AfterEach;
29 import org.junit.jupiter.api.BeforeEach;
30 import org.junit.jupiter.api.Test;
31 import org.openhab.core.automation.Condition;
32 import org.openhab.core.automation.Rule;
33 import org.openhab.core.automation.RuleRegistry;
34 import org.openhab.core.automation.Trigger;
35 import org.openhab.core.automation.util.RuleBuilder;
36 import org.openhab.core.items.GenericItem;
37 import org.openhab.core.items.ItemRegistry;
38 import org.openhab.core.library.items.ColorItem;
39 import org.openhab.core.library.items.SwitchItem;
40 import org.openhab.core.library.types.HSBType;
41 import org.openhab.core.library.types.OnOffType;
42 import org.openhab.core.types.State;
43 import org.openhab.io.hueemulation.internal.ConfigStore;
44 import org.openhab.io.hueemulation.internal.RuleUtils;
45 import org.openhab.io.hueemulation.internal.dto.HueRuleEntry;
46 import org.openhab.io.hueemulation.internal.dto.HueRuleEntry.Operator;
47 import org.openhab.io.hueemulation.internal.dto.HueSceneEntry;
48 import org.openhab.io.hueemulation.internal.dto.changerequest.HueCommand;
49 import org.openhab.io.hueemulation.internal.rest.mocks.DummyItemRegistry;
50 import org.openhab.io.hueemulation.internal.rest.mocks.DummyRuleRegistry;
51
52 import com.google.gson.Gson;
53 import com.google.gson.reflect.TypeToken;
54
55 /**
56  * Tests for various rules API endpoints.
57  *
58  * @author David Graeff - Initial contribution
59  */
60 @NonNullByDefault
61 public class RulesTests {
62
63     protected @NonNullByDefault({}) CommonSetup commonSetup;
64     protected @NonNullByDefault({}) ConfigStore cs;
65     protected @NonNullByDefault({}) ItemRegistry itemRegistry;
66     protected @NonNullByDefault({}) RuleRegistry ruleRegistry;
67
68     Rules subject = new Rules();
69     LightsAndGroups lightsAndGroups = new LightsAndGroups();
70
71     private void addItemToReg(GenericItem item, State state, String tag, String label) {
72         item.setState(state);
73         item.setLabel(label);
74         item.addTag(tag);
75         itemRegistry.add(item);
76     }
77
78     @BeforeEach
79     public void setUp() throws IOException {
80         commonSetup = new CommonSetup(false);
81         this.cs = commonSetup.cs;
82
83         itemRegistry = new DummyItemRegistry();
84         ruleRegistry = new DummyRuleRegistry();
85
86         subject.cs = commonSetup.cs;
87         subject.userManagement = commonSetup.userManagement;
88         subject.ruleRegistry = ruleRegistry;
89         subject.itemRegistry = itemRegistry;
90         subject.activate();
91
92         // We need the LightsAndGroups class to convert registry entries into HueDatastore
93         // light entries
94         lightsAndGroups.cs = cs;
95         lightsAndGroups.eventPublisher = commonSetup.eventPublisher;
96         lightsAndGroups.userManagement = commonSetup.userManagement;
97         lightsAndGroups.itemRegistry = itemRegistry;
98         lightsAndGroups.activate();
99
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", "");
103
104         commonSetup.start(new ResourceConfig().registerInstances(subject));
105     }
106
107     @AfterEach
108     public void tearDown() throws Exception {
109         RuleUtils.random = new Random();
110         commonSetup.dispose();
111     }
112
113     @Test
114     public void addUpdateRemoveScheduleToRegistry() {
115         assertThat(cs.ds.lights.get("switch1"), is(notNullValue()));
116
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);
119
120         Entry<Trigger, Condition> triggerCond = Rules.hueConditionToAutomation(command.address.replace("/", "-"),
121                 condition, itemRegistry);
122
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();
126
127         ruleRegistry.add(rule);
128
129         // Check hue entry
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}"));
136
137         // Update
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);
143
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"));
149
150         // Remove
151
152         ruleRegistry.remove("demo1");
153         entry = cs.ds.rules.get("demo1");
154         assertThat(entry, nullValue());
155     }
156
157     @SuppressWarnings("null")
158     @Test
159     public void addGetRemoveRuleViaRest() throws Exception {
160         // 1. Create
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         ContentResponse response = commonSetup.sendPost("/testuser/rules", body);
163         assertEquals(200, response.getStatus());
164         assertThat(response.getContentAsString(), containsString("success"));
165
166         // 1.1 Check for entry
167         Entry<String, HueRuleEntry> idAndEntry = cs.ds.rules.entrySet().stream().findAny().get();
168         HueRuleEntry entry = idAndEntry.getValue();
169         assertThat(entry.name, is("test name"));
170         assertThat(entry.actions.get(0).address, is("/lights/switch1/state"));
171         assertThat(entry.conditions.get(0).address, is("/lights/switch1/state/on"));
172
173         // 1.2 Check for rule
174         Rule rule = ruleRegistry.get(idAndEntry.getKey());
175         assertThat(rule.getName(), is("test name"));
176         assertThat(rule.getActions().get(0).getId(), is("-api-testuser-lights-switch1-state"));
177         assertThat(rule.getActions().get(0).getTypeUID(), is("rules.HttpAction"));
178
179         // 2. Get
180         response = commonSetup.sendGet("/testuser/rules/" + idAndEntry.getKey());
181         assertEquals(200, response.getStatus());
182         HueSceneEntry fromJson = new Gson().fromJson(response.getContentAsString(), HueSceneEntry.class);
183         assertThat(fromJson.name, is(idAndEntry.getValue().name));
184
185         // 3. Remove
186         response = commonSetup.sendDelete("/testuser/rules/" + idAndEntry.getKey());
187         assertEquals(200, response.getStatus());
188         assertTrue(cs.ds.rules.isEmpty());
189     }
190
191     @Test
192     public void updateRuleViaRest() throws Exception {
193         HueCommand command = new HueCommand("/api/testuser/lights/switch1/state", "PUT", "{'on':true}");
194         HueRuleEntry.Condition condition = new HueRuleEntry.Condition("/lights/switch1/state/on", Operator.dx, null);
195
196         Entry<Trigger, Condition> triggerCond = Rules.hueConditionToAutomation(command.address.replace("/", "-"),
197                 condition, itemRegistry);
198
199         Rule rule = RuleBuilder.create("demo1").withName("test name").withTags(Rules.RULES_TAG) //
200                 .withActions(RuleUtils.createHttpAction(command, "command")) //
201                 .withTriggers(triggerCond.getKey()).withConditions(triggerCond.getValue()).build();
202
203         ruleRegistry.add(rule);
204
205         // Modify (just the name)
206         String body = "{ 'name':'A new name'}";
207         ContentResponse response = commonSetup.sendPut("/testuser/rules/demo1", body);
208         assertEquals(200, response.getStatus());
209         assertThat(response.getContentAsString(), containsString("name"));
210
211         Entry<String, HueRuleEntry> idAndEntry = cs.ds.rules.entrySet().stream().findAny().get();
212         HueRuleEntry entry = idAndEntry.getValue();
213         assertThat(entry.name, is("A new name"));
214         assertThat(entry.actions.get(0).address, is("/lights/switch1/state"));
215         assertThat(entry.conditions.get(0).address, is("/lights/switch1/state/on"));
216
217         // Reset
218         rule = RuleBuilder.create("demo1").withName("test name").withTags(Rules.RULES_TAG) //
219                 .withActions(RuleUtils.createHttpAction(command, "command")) //
220                 .withTriggers(triggerCond.getKey()).withConditions(triggerCond.getValue()).build();
221
222         ruleRegistry.update(rule); // Reset rule
223
224         idAndEntry = cs.ds.rules.entrySet().stream().findAny().get();
225
226         // Modify (Change condition)
227         body = "{\"conditions\":[{\"address\":\"/lights/switch1/state/on\",\"operator\":\"ddx\"}]}";
228         response = commonSetup.sendPut("/testuser/rules/demo1", body);
229         assertEquals(200, response.getStatus());
230         assertThat(response.getContentAsString(), containsString("conditions"));
231
232         idAndEntry = cs.ds.rules.entrySet().stream().findAny().get();
233         entry = idAndEntry.getValue();
234         assertThat(entry.name, is("test name")); // should not have changed
235         assertThat(entry.conditions.get(0).operator, is(Operator.ddx));
236
237         // Modify (Change action)
238         body = "{\"actions\":[{\"address\":\"/lights/switch2/state\",\"method\":\"PUT\",\"body\":\"{\\u0027on\\u0027:false}\"}]}";
239         response = commonSetup.sendPut("/testuser/rules/demo1", body);
240         assertEquals(200, response.getStatus());
241         assertThat(response.getContentAsString(), containsString("actions"));
242
243         idAndEntry = cs.ds.rules.entrySet().stream().findAny().get();
244         entry = idAndEntry.getValue();
245         assertThat(entry.name, is("test name")); // should not have changed
246         assertThat(entry.actions.get(0).address, is("/lights/switch2/state"));
247     }
248
249     @Test
250     public void getAll() throws Exception {
251         HueCommand command = new HueCommand("/api/testuser/lights/switch1/state", "PUT", "{'on':true}");
252         HueRuleEntry.Condition condition = new HueRuleEntry.Condition("/lights/switch1/state/on", Operator.dx, null);
253
254         Entry<Trigger, Condition> triggerCond = Rules.hueConditionToAutomation(command.address.replace("/", "-"),
255                 condition, itemRegistry);
256
257         Rule rule = RuleBuilder.create("demo1").withName("test name").withTags(Rules.RULES_TAG) //
258                 .withActions(RuleUtils.createHttpAction(command, "command")) //
259                 .withTriggers(triggerCond.getKey()).withConditions(triggerCond.getValue()).build();
260
261         ruleRegistry.add(rule);
262
263         ContentResponse response = commonSetup.sendGet("/testuser/rules");
264         Type type = new TypeToken<Map<String, HueRuleEntry>>() {
265         }.getType();
266         String body = response.getContentAsString();
267         Map<String, HueRuleEntry> fromJson = new Gson().fromJson(body, type);
268         HueRuleEntry entry = fromJson.get("demo1");
269         assertThat(entry.name, is("test name"));
270         assertThat(entry.actions.get(0).address, is("/lights/switch1/state"));
271         assertThat(entry.conditions.get(0).address, is("/lights/switch1/state/on"));
272     }
273 }