]> git.basschouten.com Git - openhab-addons.git/blob
f22b2b4abd068e85d37cdfc1ff4874044df846b0
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2024 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.binding.mqtt.generic.internal.handler;
14
15 import static org.hamcrest.CoreMatchers.is;
16 import static org.hamcrest.MatcherAssert.assertThat;
17 import static org.junit.jupiter.api.Assertions.assertThrows;
18 import static org.mockito.ArgumentMatchers.*;
19 import static org.mockito.Mockito.*;
20 import static org.openhab.binding.mqtt.generic.internal.handler.ThingChannelConstants.*;
21
22 import java.util.concurrent.CompletableFuture;
23
24 import org.eclipse.jdt.annotation.NonNullByDefault;
25 import org.eclipse.jdt.annotation.Nullable;
26 import org.junit.jupiter.api.BeforeEach;
27 import org.junit.jupiter.api.Test;
28 import org.junit.jupiter.api.extension.ExtendWith;
29 import org.mockito.Mock;
30 import org.mockito.junit.jupiter.MockitoExtension;
31 import org.mockito.junit.jupiter.MockitoSettings;
32 import org.mockito.quality.Strictness;
33 import org.openhab.binding.mqtt.generic.ChannelConfig;
34 import org.openhab.binding.mqtt.generic.ChannelConfigBuilder;
35 import org.openhab.binding.mqtt.generic.ChannelState;
36 import org.openhab.binding.mqtt.generic.MqttChannelStateDescriptionProvider;
37 import org.openhab.binding.mqtt.generic.ThingHandlerHelper;
38 import org.openhab.binding.mqtt.generic.TransformationServiceProvider;
39 import org.openhab.binding.mqtt.generic.values.OnOffValue;
40 import org.openhab.binding.mqtt.generic.values.TextValue;
41 import org.openhab.binding.mqtt.generic.values.ValueFactory;
42 import org.openhab.binding.mqtt.handler.AbstractBrokerHandler;
43 import org.openhab.core.config.core.Configuration;
44 import org.openhab.core.io.transport.mqtt.MqttBrokerConnection;
45 import org.openhab.core.library.types.StringType;
46 import org.openhab.core.thing.ChannelUID;
47 import org.openhab.core.thing.Thing;
48 import org.openhab.core.thing.ThingStatus;
49 import org.openhab.core.thing.ThingStatusDetail;
50 import org.openhab.core.thing.ThingStatusInfo;
51 import org.openhab.core.thing.binding.ThingHandlerCallback;
52 import org.openhab.core.types.RefreshType;
53 import org.openhab.core.types.UnDefType;
54
55 /**
56  * Tests cases for {@link GenericMQTTThingHandler}.
57  *
58  * @author David Graeff - Initial contribution
59  */
60 @ExtendWith(MockitoExtension.class)
61 @MockitoSettings(strictness = Strictness.LENIENT)
62 @NonNullByDefault
63 public class GenericThingHandlerTests {
64
65     private @Mock @NonNullByDefault({}) ThingHandlerCallback callbackMock;
66     private @Mock @NonNullByDefault({}) Thing thingMock;
67     private @Mock @NonNullByDefault({}) AbstractBrokerHandler bridgeHandlerMock;
68     private @Mock @NonNullByDefault({}) MqttBrokerConnection connectionMock;
69
70     private @NonNullByDefault({}) GenericMQTTThingHandler thingHandler;
71
72     @BeforeEach
73     public void setUp() {
74         ThingStatusInfo thingStatus = new ThingStatusInfo(ThingStatus.ONLINE, ThingStatusDetail.NONE, null);
75
76         // Mock the thing: We need the thingUID and the bridgeUID
77         when(thingMock.getUID()).thenReturn(TEST_GENERIC_THING);
78         when(thingMock.getChannels()).thenReturn(THING_CHANNEL_LIST);
79         when(thingMock.getStatusInfo()).thenReturn(thingStatus);
80         when(thingMock.getConfiguration()).thenReturn(new Configuration());
81
82         // Return the mocked connection object if the bridge handler is asked for it
83         when(bridgeHandlerMock.getConnectionAsync()).thenReturn(CompletableFuture.completedFuture(connectionMock));
84
85         CompletableFuture<@Nullable Void> voidFutureComplete = new CompletableFuture<>();
86         voidFutureComplete.complete(null);
87         doReturn(voidFutureComplete).when(connectionMock).unsubscribeAll();
88         doReturn(CompletableFuture.completedFuture(true)).when(connectionMock).subscribe(any(), any());
89         doReturn(CompletableFuture.completedFuture(true)).when(connectionMock).unsubscribe(any(), any());
90         doReturn(CompletableFuture.completedFuture(true)).when(connectionMock).publish(any(), any(), anyInt(),
91                 anyBoolean());
92
93         thingHandler = spy(new GenericMQTTThingHandler(thingMock, mock(MqttChannelStateDescriptionProvider.class),
94                 mock(TransformationServiceProvider.class), 1500));
95         thingHandler.setCallback(callbackMock);
96
97         // Return the bridge handler if the thing handler asks for it
98         doReturn(bridgeHandlerMock).when(thingHandler).getBridgeHandler();
99
100         // The broker connection bridge is by default online
101         doReturn(thingStatus).when(thingHandler).getBridgeStatus();
102     }
103
104     @Test
105     public void initializeWithUnknownThingUID() {
106         ChannelConfig config = textConfiguration().as(ChannelConfig.class);
107         assertThrows(IllegalArgumentException.class,
108                 () -> thingHandler.createChannelState(config, new ChannelUID(TEST_GENERIC_THING, "test"),
109                         ValueFactory.createValueState(config, UNKNOWN_CHANNEL.getId())));
110     }
111
112     @Test
113     public void initialize() {
114         thingHandler.initialize();
115         verify(thingHandler).bridgeStatusChanged(any());
116         verify(thingHandler).start(any());
117         assertThat(thingHandler.getConnection(), is(connectionMock));
118
119         ChannelState channelConfig = thingHandler.channelStateByChannelUID.get(TEXT_CHANNEL_UID);
120         assertThat(channelConfig.getStateTopic(), is("test/state"));
121         assertThat(channelConfig.getCommandTopic(), is("test/command"));
122
123         verify(connectionMock).subscribe(eq(channelConfig.getStateTopic()), eq(channelConfig));
124
125         verify(callbackMock).statusUpdated(eq(thingMock), argThat(arg -> ThingStatus.ONLINE.equals(arg.getStatus())
126                 && ThingStatusDetail.NONE.equals(arg.getStatusDetail())));
127     }
128
129     @Test
130     public void handleCommandRefresh() {
131         TextValue value = spy(new TextValue());
132         value.update(new StringType("DEMOVALUE"));
133
134         ChannelState channelConfig = mock(ChannelState.class);
135         doReturn(CompletableFuture.completedFuture(true)).when(channelConfig).start(any(), any(), anyInt());
136         doReturn(CompletableFuture.completedFuture(true)).when(channelConfig).stop();
137         doReturn(value).when(channelConfig).getCache();
138         doReturn(channelConfig).when(thingHandler).createChannelState(any(), any(), any());
139         thingHandler.initialize();
140
141         ThingHandlerHelper.setConnection(thingHandler, connectionMock);
142
143         thingHandler.handleCommand(TEXT_CHANNEL_UID, RefreshType.REFRESH);
144         verify(callbackMock).stateUpdated(eq(TEXT_CHANNEL_UID), argThat(arg -> "DEMOVALUE".equals(arg.toString())));
145     }
146
147     @Test
148     public void handleCommandUpdateString() {
149         TextValue value = spy(new TextValue());
150         ChannelState channelConfig = spy(
151                 new ChannelState(ChannelConfigBuilder.create("stateTopic", "commandTopic").build(), TEXT_CHANNEL_UID,
152                         value, thingHandler));
153         doReturn(channelConfig).when(thingHandler).createChannelState(any(), any(), any());
154         thingHandler.initialize();
155         ThingHandlerHelper.setConnection(thingHandler, connectionMock);
156
157         StringType updateValue = new StringType("UPDATE");
158         thingHandler.handleCommand(TEXT_CHANNEL_UID, updateValue);
159         verify(value).parseCommand(eq(updateValue));
160         // It didn't update the cached state
161         assertThat(value.getChannelState(), is(UnDefType.UNDEF));
162     }
163
164     @Test
165     public void handleCommandUpdateBoolean() {
166         OnOffValue value = spy(new OnOffValue("ON", "OFF"));
167         ChannelState channelConfig = spy(
168                 new ChannelState(ChannelConfigBuilder.create("stateTopic", "commandTopic").build(), TEXT_CHANNEL_UID,
169                         value, thingHandler));
170         doReturn(channelConfig).when(thingHandler).createChannelState(any(), any(), any());
171         thingHandler.initialize();
172         ThingHandlerHelper.setConnection(thingHandler, connectionMock);
173
174         StringType updateValue = new StringType("ON");
175         thingHandler.handleCommand(TEXT_CHANNEL_UID, updateValue);
176
177         verify(value).parseCommand(eq(updateValue));
178     }
179
180     @Test
181     public void processMessage() {
182         TextValue textValue = new TextValue();
183         ChannelState channelConfig = spy(
184                 new ChannelState(ChannelConfigBuilder.create("test/state", "test/state/set").build(), TEXT_CHANNEL_UID,
185                         textValue, thingHandler));
186         doReturn(channelConfig).when(thingHandler).createChannelState(any(), any(), any());
187         thingHandler.initialize();
188         byte[] payload = "UPDATE".getBytes();
189         // Test process message
190         channelConfig.processMessage("test/state", payload);
191
192         verify(callbackMock, atLeastOnce()).statusUpdated(eq(thingMock),
193                 argThat(arg -> ThingStatus.ONLINE.equals(arg.getStatus())));
194
195         verify(callbackMock).stateUpdated(eq(TEXT_CHANNEL_UID), argThat(arg -> "UPDATE".equals(arg.toString())));
196         assertThat(textValue.getChannelState().toString(), is("UPDATE"));
197     }
198
199     @Test
200     public void handleBridgeStatusChange() {
201         Configuration config = new Configuration();
202         config.put("availabilityTopic", "test/LWT");
203         when(thingMock.getConfiguration()).thenReturn(config);
204         thingHandler.initialize();
205         thingHandler
206                 .bridgeStatusChanged(new ThingStatusInfo(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE, null));
207         thingHandler.bridgeStatusChanged(new ThingStatusInfo(ThingStatus.ONLINE, ThingStatusDetail.NONE, null));
208         verify(connectionMock, times(2)).subscribe(eq("test/LWT"), any());
209     }
210 }