2 * Copyright (c) 2010-2023 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;
24 import org.eclipse.jdt.annotation.NonNullByDefault;
25 import org.eclipse.jetty.client.api.ContentResponse;
26 import org.glassfish.jersey.server.ResourceConfig;
27 import org.hamcrest.CoreMatchers;
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.Rule;
32 import org.openhab.core.automation.RuleRegistry;
33 import org.openhab.core.automation.util.RuleBuilder;
34 import org.openhab.core.items.GenericItem;
35 import org.openhab.core.items.GroupItem;
36 import org.openhab.core.items.ItemRegistry;
37 import org.openhab.core.library.items.ColorItem;
38 import org.openhab.core.library.items.DimmerItem;
39 import org.openhab.core.library.items.RollershutterItem;
40 import org.openhab.core.library.items.SwitchItem;
41 import org.openhab.core.library.types.HSBType;
42 import org.openhab.core.library.types.OnOffType;
43 import org.openhab.core.library.types.PercentType;
44 import org.openhab.core.types.Command;
45 import org.openhab.core.types.State;
46 import org.openhab.io.hueemulation.internal.ConfigStore;
47 import org.openhab.io.hueemulation.internal.dto.HueSceneEntry;
48 import org.openhab.io.hueemulation.internal.rest.mocks.DummyItemRegistry;
49 import org.openhab.io.hueemulation.internal.rest.mocks.DummyRuleRegistry;
51 import com.google.gson.Gson;
52 import com.google.gson.reflect.TypeToken;
55 * Tests for various scene API endpoints.
57 * @author David Graeff - Initial contribution
60 public class SceneTests {
61 protected @NonNullByDefault({}) CommonSetup commonSetup;
62 protected @NonNullByDefault({}) ConfigStore cs;
63 protected @NonNullByDefault({}) ItemRegistry itemRegistry;
64 protected @NonNullByDefault({}) RuleRegistry ruleRegistry;
66 Scenes subject = new Scenes();
68 private void addItemToReg(GenericItem item, State state, String tag) {
71 itemRegistry.add(item);
75 public void setUp() throws IOException {
76 commonSetup = new CommonSetup(false);
77 this.cs = commonSetup.cs;
79 itemRegistry = new DummyItemRegistry();
80 ruleRegistry = new DummyRuleRegistry();
82 subject.cs = commonSetup.cs;
83 subject.userManagement = commonSetup.userManagement;
84 subject.itemRegistry = itemRegistry;
85 subject.ruleRegistry = ruleRegistry;
88 // Add simulated lights
89 addItemToReg(new SwitchItem("switch1"), OnOffType.ON, "Switchable");
90 addItemToReg(new ColorItem("color1"), HSBType.BLUE, "ColorLighting");
91 addItemToReg(new DimmerItem("white1"), new PercentType(12), "Lighting");
92 addItemToReg(new RollershutterItem("roller1"), new PercentType(12), "Lighting");
93 addItemToReg(new DimmerItem("white1"), new PercentType(12), "Lighting");
94 addItemToReg(new GroupItem("group1"), OnOffType.ON, "Switchable");
96 commonSetup.start(new ResourceConfig().registerInstances(subject));
100 public void tearDown() throws Exception {
101 commonSetup.dispose();
104 @SuppressWarnings("null")
106 public void addUpdateRemoveSceneToRegistry() {
107 Rule rule = RuleBuilder.create("demo1").withTags("scene") //
108 .withActions(Scenes.actionFromState("switch1", (Command) OnOffType.ON)).build();
110 ruleRegistry.add(rule);
112 HueSceneEntry sceneEntry = cs.ds.scenes.get("demo1");
113 assertThat(sceneEntry.lights.get(0), CoreMatchers.is("switch1"));
116 rule = RuleBuilder.create("demo1").withTags("scene") //
117 .withActions(Scenes.actionFromState("white1", (Command) OnOffType.ON)).build();
118 ruleRegistry.update(rule);
120 sceneEntry = cs.ds.scenes.get("demo1");
121 assertThat(sceneEntry.lights.get(0), CoreMatchers.is("white1"));
125 ruleRegistry.remove("demo1");
126 sceneEntry = cs.ds.scenes.get("demo1");
127 assertThat(sceneEntry, CoreMatchers.nullValue());
130 @SuppressWarnings("null")
132 public void addGetRemoveSceneViaRest() throws Exception {
134 String body = "{ 'name':'Cozy dinner', 'recycle':false, 'lights':['switch1','white1'], 'type':'LightScene'}";
135 ContentResponse response = commonSetup.sendPost("/testuser/scenes", body);
136 assertEquals(200, response.getStatus());
137 assertThat(response.getContentAsString(), containsString("success"));
139 // 1.1 Check for scene entry
140 Entry<String, HueSceneEntry> entry = cs.ds.scenes.entrySet().stream().findAny().get();
141 assertThat(entry.getValue().name, is("Cozy dinner"));
142 assertThat(entry.getValue().lights.get(0), is("switch1"));
143 assertThat(entry.getValue().lights.get(1), is("white1"));
145 // 1.2 Check for rule
146 Rule rule = ruleRegistry.get(entry.getKey());
147 assertThat(rule.getName(), is("Cozy dinner"));
148 assertThat(rule.getActions().get(0).getId(), is("switch1"));
149 assertThat(rule.getActions().get(1).getId(), is("white1"));
152 response = commonSetup.sendGet("/testuser/scenes/" + entry.getKey());
153 assertEquals(200, response.getStatus());
154 HueSceneEntry fromJson = new Gson().fromJson(response.getContentAsString(), HueSceneEntry.class);
155 assertThat(fromJson.name, is(entry.getValue().name));
158 response = commonSetup.sendDelete("/testuser/scenes/" + entry.getKey());
159 assertEquals(200, response.getStatus());
160 assertTrue(cs.ds.scenes.isEmpty());
163 @SuppressWarnings("null")
165 public void updateSceneViaRest() throws Exception {
166 Rule rule = RuleBuilder.create("demo1").withTags("scene").withName("Some name") //
167 .withActions(Scenes.actionFromState("switch1", (Command) OnOffType.ON)).build();
169 ruleRegistry.add(rule);
171 // 3. Modify (just the name)
172 String body = "{ 'name':'A new name'}";
173 ContentResponse response = commonSetup.sendPut("/testuser/scenes/demo1", body);
174 assertEquals(200, response.getStatus());
175 assertThat(response.getContentAsString(), containsString("name"));
177 Entry<String, HueSceneEntry> sceneEntry = cs.ds.scenes.entrySet().stream().findAny().get();
178 assertThat(sceneEntry.getValue().name, is("A new name"));
179 assertThat(sceneEntry.getValue().lights.get(0), is("switch1")); // nothing else should have changed
181 // 3. Modify (just the lights)
182 rule = RuleBuilder.create("demo1").withTags("scene").withName("Some name") //
183 .withActions(Scenes.actionFromState("switch1", (Command) OnOffType.ON)).build();
185 ruleRegistry.update(rule); // Reset rule
187 sceneEntry = cs.ds.scenes.entrySet().stream().findAny().get();
188 String uid = sceneEntry.getKey();
190 // Without store lights
191 body = "{ 'lights':['white1']}";
192 response = commonSetup.sendPut("/testuser/scenes/demo1", body);
193 assertEquals(200, response.getStatus());
194 assertThat(response.getContentAsString(), containsString("lights"));
196 sceneEntry = cs.ds.scenes.entrySet().stream().findAny().get();
197 assertThat(sceneEntry.getValue().name, is("Some name")); // should not have changed
198 assertThat(sceneEntry.getKey(), is(uid));
199 assertThat(sceneEntry.getValue().lights.get(0), is("switch1")); // storelightstate not set, lights not changed
202 body = "{ 'lights':['white1'], 'storelightstate':true }";
203 response = commonSetup.sendPut("/testuser/scenes/demo1", body);
204 assertEquals(200, response.getStatus());
205 assertThat(response.getContentAsString(), containsString("lights"));
207 sceneEntry = cs.ds.scenes.entrySet().stream().findAny().get();
208 assertThat(sceneEntry.getValue().lights.get(0), is("white1"));
212 public void getAll() throws Exception {
213 Rule rule = RuleBuilder.create("demo1").withTags("scene") //
214 .withActions(Scenes.actionFromState("switch1", (Command) OnOffType.ON)).build();
216 ruleRegistry.add(rule);
218 ContentResponse response = commonSetup.sendGet("/testuser/scenes");
219 Type type = new TypeToken<Map<String, HueSceneEntry>>() {
221 Map<String, HueSceneEntry> fromJson = new Gson().fromJson(response.getContentAsString(), type);
222 assertTrue(fromJson.containsKey("demo1"));