]> git.basschouten.com Git - openhab-addons.git/blob
c29a9f15c3c5adbe8782fe5742b42434e6ea3b7d
[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.binding.mielecloud.internal.handler;
14
15 import static org.junit.jupiter.api.Assertions.*;
16 import static org.mockito.ArgumentMatchers.*;
17 import static org.mockito.Mockito.*;
18 import static org.openhab.binding.mielecloud.internal.MieleCloudBindingConstants.Channels.*;
19 import static org.openhab.binding.mielecloud.internal.util.MieleCloudBindingIntegrationTestConstants.*;
20 import static org.openhab.binding.mielecloud.internal.util.ReflectionUtil.setPrivate;
21
22 import java.util.ArrayList;
23 import java.util.Collections;
24 import java.util.List;
25 import java.util.Objects;
26 import java.util.Optional;
27
28 import org.eclipse.jdt.annotation.NonNullByDefault;
29 import org.eclipse.jdt.annotation.Nullable;
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.binding.mielecloud.internal.MieleCloudBindingConstants;
34 import org.openhab.binding.mielecloud.internal.auth.OAuthTokenRefresher;
35 import org.openhab.binding.mielecloud.internal.auth.OpenHabOAuthTokenRefresher;
36 import org.openhab.binding.mielecloud.internal.util.MieleCloudBindingIntegrationTestConstants;
37 import org.openhab.binding.mielecloud.internal.util.ReflectionUtil;
38 import org.openhab.binding.mielecloud.internal.webservice.MieleWebservice;
39 import org.openhab.binding.mielecloud.internal.webservice.MieleWebserviceFactory;
40 import org.openhab.binding.mielecloud.internal.webservice.api.DeviceState;
41 import org.openhab.binding.mielecloud.internal.webservice.api.ProgramStatus;
42 import org.openhab.binding.mielecloud.internal.webservice.api.json.DeviceType;
43 import org.openhab.binding.mielecloud.internal.webservice.api.json.ProcessAction;
44 import org.openhab.binding.mielecloud.internal.webservice.api.json.StateType;
45 import org.openhab.binding.mielecloud.internal.webservice.exception.MieleWebserviceException;
46 import org.openhab.core.auth.client.oauth2.AccessTokenResponse;
47 import org.openhab.core.auth.client.oauth2.OAuthClientService;
48 import org.openhab.core.auth.client.oauth2.OAuthFactory;
49 import org.openhab.core.config.core.Configuration;
50 import org.openhab.core.items.Item;
51 import org.openhab.core.items.ItemBuilder;
52 import org.openhab.core.items.ItemBuilderFactory;
53 import org.openhab.core.items.ItemRegistry;
54 import org.openhab.core.library.types.DecimalType;
55 import org.openhab.core.library.types.OnOffType;
56 import org.openhab.core.library.types.StringType;
57 import org.openhab.core.test.java.JavaOSGiTest;
58 import org.openhab.core.thing.Bridge;
59 import org.openhab.core.thing.Channel;
60 import org.openhab.core.thing.ChannelUID;
61 import org.openhab.core.thing.Thing;
62 import org.openhab.core.thing.ThingRegistry;
63 import org.openhab.core.thing.ThingStatus;
64 import org.openhab.core.thing.ThingStatusDetail;
65 import org.openhab.core.thing.ThingTypeUID;
66 import org.openhab.core.thing.ThingUID;
67 import org.openhab.core.thing.binding.ThingHandler;
68 import org.openhab.core.thing.binding.ThingHandlerFactory;
69 import org.openhab.core.thing.binding.builder.BridgeBuilder;
70 import org.openhab.core.thing.binding.builder.ChannelBuilder;
71 import org.openhab.core.thing.binding.builder.ThingBuilder;
72 import org.openhab.core.thing.link.ItemChannelLink;
73 import org.openhab.core.thing.link.ItemChannelLinkRegistry;
74 import org.openhab.core.thing.type.ChannelDefinition;
75 import org.openhab.core.thing.type.ChannelType;
76 import org.openhab.core.thing.type.ChannelTypeRegistry;
77 import org.openhab.core.thing.type.ChannelTypeUID;
78 import org.openhab.core.thing.type.ThingType;
79 import org.openhab.core.thing.type.ThingTypeRegistry;
80 import org.openhab.core.types.State;
81 import org.openhab.core.types.UnDefType;
82
83 /**
84  * @author Björn Lange - Initial contribution
85  */
86 @NonNullByDefault
87 public abstract class AbstractMieleThingHandlerTest extends JavaOSGiTest {
88     protected static final State NULL_VALUE_STATE = UnDefType.UNDEF;
89
90     @Nullable
91     private Bridge bridge;
92     @Nullable
93     private MieleBridgeHandler bridgeHandler;
94     @Nullable
95     private ThingRegistry thingRegistry;
96     @Nullable
97     private MieleWebservice webserviceMock;
98     @Nullable
99     private AbstractMieleThingHandler thingHandler;
100
101     @Nullable
102     private ItemRegistry itemRegistry;
103
104     protected Bridge getBridge() {
105         assertNotNull(bridge);
106         return Objects.requireNonNull(bridge);
107     }
108
109     protected MieleBridgeHandler getBridgeHandler() {
110         assertNotNull(bridgeHandler);
111         return Objects.requireNonNull(bridgeHandler);
112     }
113
114     protected ThingRegistry getThingRegistry() {
115         assertNotNull(thingRegistry);
116         return Objects.requireNonNull(thingRegistry);
117     }
118
119     protected MieleWebservice getWebserviceMock() {
120         assertNotNull(webserviceMock);
121         return Objects.requireNonNull(webserviceMock);
122     }
123
124     protected AbstractMieleThingHandler getThingHandler() {
125         assertNotNull(thingHandler);
126         return Objects.requireNonNull(thingHandler);
127     }
128
129     protected ItemRegistry getItemRegistry() {
130         assertNotNull(itemRegistry);
131         return Objects.requireNonNull(itemRegistry);
132     }
133
134     @Override
135     protected void waitForAssert(Runnable assertion) {
136         // Use a larger timeout to avoid test failures in the CI build.
137         waitForAssert(assertion, 2 * DFL_TIMEOUT, 2 * DFL_SLEEP_TIME);
138     }
139
140     private void setUpThingRegistry() {
141         thingRegistry = getService(ThingRegistry.class, ThingRegistry.class);
142         assertNotNull(thingRegistry, "Thing registry is missing");
143     }
144
145     private void setUpItemRegistry() {
146         itemRegistry = getService(ItemRegistry.class, ItemRegistry.class);
147         assertNotNull(itemRegistry);
148     }
149
150     private void setUpWebservice()
151             throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
152         webserviceMock = mock(MieleWebservice.class);
153         when(getWebserviceMock().hasAccessToken()).thenReturn(true);
154
155         MieleWebserviceFactory webserviceFactory = mock(MieleWebserviceFactory.class);
156         when(webserviceFactory.create(any())).thenReturn(getWebserviceMock());
157
158         MieleHandlerFactory factory = getService(ThingHandlerFactory.class, MieleHandlerFactory.class);
159         assertNotNull(factory);
160         setPrivate(Objects.requireNonNull(factory), "webserviceFactory", webserviceFactory);
161     }
162
163     private void setUpBridge() throws Exception {
164         AccessTokenResponse accessTokenResponse = new AccessTokenResponse();
165         accessTokenResponse.setAccessToken(ACCESS_TOKEN);
166
167         OAuthClientService oAuthClientService = mock(OAuthClientService.class);
168         when(oAuthClientService.getAccessTokenResponse()).thenReturn(accessTokenResponse);
169
170         OAuthFactory oAuthFactory = mock(OAuthFactory.class);
171         when(oAuthFactory
172                 .getOAuthClientService(MieleCloudBindingIntegrationTestConstants.BRIDGE_THING_UID.getAsString()))
173                 .thenReturn(oAuthClientService);
174
175         OpenHabOAuthTokenRefresher tokenRefresher = getService(OAuthTokenRefresher.class,
176                 OpenHabOAuthTokenRefresher.class);
177         assertNotNull(tokenRefresher);
178         setPrivate(Objects.requireNonNull(tokenRefresher), "oauthFactory", oAuthFactory);
179
180         bridge = BridgeBuilder
181                 .create(MieleCloudBindingConstants.THING_TYPE_BRIDGE,
182                         MieleCloudBindingIntegrationTestConstants.BRIDGE_THING_UID)
183                 .withLabel("Miele@home Account")
184                 .withConfiguration(
185                         new Configuration(Collections.singletonMap(MieleCloudBindingConstants.CONFIG_PARAM_EMAIL,
186                                 MieleCloudBindingIntegrationTestConstants.EMAIL)))
187                 .build();
188         assertNotNull(bridge);
189
190         getThingRegistry().add(getBridge());
191
192         // then:
193         waitForAssert(() -> {
194             assertNotNull(getBridge().getHandler());
195             assertTrue(getBridge().getHandler() instanceof MieleBridgeHandler, "Handler type is wrong");
196         });
197
198         MieleBridgeHandler bridgeHandler = (MieleBridgeHandler) getBridge().getHandler();
199         assertNotNull(bridgeHandler);
200
201         waitForAssert(() -> {
202             assertNotNull(bridgeHandler.getThing());
203         });
204
205         bridgeHandler.initialize();
206         bridgeHandler.onConnectionAlive();
207         setPrivate(bridgeHandler, "discoveryService", null);
208         this.bridgeHandler = bridgeHandler;
209     }
210
211     protected AbstractMieleThingHandler createThingHandler(ThingTypeUID thingTypeUid, ThingUID thingUid,
212             Class<? extends AbstractMieleThingHandler> expectedHandlerClass, String deviceIdentifier,
213             String thingTypeVersion) {
214         ThingRegistry registry = getThingRegistry();
215
216         List<Channel> channels = createChannelsForThingHandler(thingTypeUid, thingUid);
217
218         Thing thing = ThingBuilder.create(thingTypeUid, thingUid)
219                 .withConfiguration(new Configuration(Collections
220                         .singletonMap(MieleCloudBindingConstants.CONFIG_PARAM_DEVICE_IDENTIFIER, deviceIdentifier)))
221                 .withBridge(getBridge().getUID()).withChannels(channels).withLabel("DA-6996")
222                 .withProperty("thingTypeVersion", thingTypeVersion).build();
223         assertNotNull(thing);
224
225         registry.add(thing);
226
227         waitForAssert(() -> {
228             ThingHandler handler = thing.getHandler();
229             assertNotNull(handler);
230             assertTrue(expectedHandlerClass.isAssignableFrom(handler.getClass()), "Handler type is wrong");
231         });
232
233         createItemsForChannels(thing);
234         linkChannelsToItems(thing);
235
236         ThingHandler handler = thing.getHandler();
237         assertNotNull(handler);
238         AbstractMieleThingHandler mieleThingHandler = (AbstractMieleThingHandler) Objects.requireNonNull(handler);
239
240         waitForAssert(() -> {
241             try {
242                 assertNotNull(ReflectionUtil.invokePrivate(mieleThingHandler, "getBridge"));
243             } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException e) {
244                 throw new RuntimeException(e);
245             }
246             assertNotNull(getBridge().getThing(thingUid));
247         });
248
249         return mieleThingHandler;
250     }
251
252     private List<Channel> createChannelsForThingHandler(ThingTypeUID thingTypeUid, ThingUID thingUid) {
253         ChannelTypeRegistry channelTypeRegistry = getService(ChannelTypeRegistry.class, ChannelTypeRegistry.class);
254         assertNotNull(channelTypeRegistry);
255
256         ThingTypeRegistry thingTypeRegistry = getService(ThingTypeRegistry.class, ThingTypeRegistry.class);
257         assertNotNull(thingTypeRegistry);
258
259         ThingType thingType = thingTypeRegistry.getThingType(thingTypeUid);
260         assertNotNull(thingType);
261
262         List<ChannelDefinition> channelDefinitions = thingType.getChannelDefinitions();
263         assertNotNull(channelDefinitions);
264
265         List<Channel> channels = new ArrayList<Channel>();
266         for (ChannelDefinition channelDefinition : channelDefinitions) {
267             ChannelTypeUID channelTypeUid = channelDefinition.getChannelTypeUID();
268             assertNotNull(channelTypeUid);
269
270             ChannelType channelType = channelTypeRegistry.getChannelType(channelTypeUid);
271             assertNotNull(channelType);
272
273             String acceptedItemType = channelType.getItemType();
274             assertNotNull(acceptedItemType);
275
276             String channelId = channelDefinition.getId();
277             assertNotNull(channelId);
278
279             ChannelUID channelUid = new ChannelUID(thingUid, channelId);
280             assertNotNull(channelUid);
281
282             Channel channel = ChannelBuilder.create(channelUid, acceptedItemType).build();
283             assertNotNull(channel);
284
285             channels.add(channel);
286         }
287
288         return channels;
289     }
290
291     private void createItemsForChannels(Thing thing) {
292         ItemBuilderFactory itemBuilderFactory = getService(ItemBuilderFactory.class);
293         assertNotNull(itemBuilderFactory);
294
295         for (Channel channel : thing.getChannels()) {
296             String acceptedItemType = channel.getAcceptedItemType();
297             assertNotNull(acceptedItemType);
298
299             ItemBuilder itemBuilder = itemBuilderFactory.newItemBuilder(Objects.requireNonNull(acceptedItemType),
300                     channel.getUID().getId());
301             assertNotNull(itemBuilder);
302
303             Item item = itemBuilder.build();
304             assertNotNull(item);
305
306             getItemRegistry().add(item);
307         }
308     }
309
310     private void linkChannelsToItems(Thing thing) {
311         ItemChannelLinkRegistry itemChannelLinkRegistry = getService(ItemChannelLinkRegistry.class,
312                 ItemChannelLinkRegistry.class);
313         assertNotNull(itemChannelLinkRegistry);
314
315         for (Channel channel : thing.getChannels()) {
316             String itemName = channel.getUID().getId();
317             assertNotNull(itemName);
318
319             ChannelUID channelUid = channel.getUID();
320             assertNotNull(channelUid);
321
322             ItemChannelLink link = itemChannelLinkRegistry.add(new ItemChannelLink(itemName, channelUid));
323             assertNotNull(link);
324         }
325     }
326
327     protected ChannelUID channel(String id) {
328         return new ChannelUID(getThingHandler().getThing().getUID(), id);
329     }
330
331     @BeforeEach
332     public void setUpAbstractMieleThingHandlerTest() throws Exception {
333         registerVolatileStorageService();
334         setUpThingRegistry();
335         setUpItemRegistry();
336         setUpWebservice();
337     }
338
339     protected void setUpBridgeAndThing() throws Exception {
340         setUpBridge();
341         thingHandler = setUpThingHandler();
342     }
343
344     private void assertThingStatusIs(Thing thing, ThingStatus expectedStatus, ThingStatusDetail expectedStatusDetail) {
345         assertThingStatusIs(thing, expectedStatus, expectedStatusDetail, null);
346     }
347
348     private void assertThingStatusIs(Thing thing, ThingStatus expectedStatus, ThingStatusDetail expectedStatusDetail,
349             @Nullable String expectedDescription) {
350         assertEquals(expectedStatus, thing.getStatus());
351         assertEquals(expectedStatusDetail, thing.getStatusInfo().getStatusDetail());
352         if (expectedDescription == null) {
353             assertNull(thing.getStatusInfo().getDescription());
354         } else {
355             assertEquals(expectedDescription, thing.getStatusInfo().getDescription());
356         }
357     }
358
359     protected State getChannelState(String channelUid) {
360         Item item = getItemRegistry().get(channelUid);
361         assertNotNull(item, "Item for channel UID " + channelUid + " is null.");
362         return item.getState();
363     }
364
365     /**
366      * Sets up the {@link ThingHandler} under test.
367      *
368      * @return The created {@link ThingHandler}.
369      */
370     protected abstract AbstractMieleThingHandler setUpThingHandler();
371
372     @AfterEach
373     public void tearDownAbstractMieleThingHandlerTest() {
374         getThingRegistry().forceRemove(getThingHandler().getThing().getUID());
375         getThingRegistry().forceRemove(getBridge().getUID());
376     }
377
378     @Test
379     public void testCachedStateIsQueriedOnInitialize() throws Exception {
380         // given:
381         setUpBridgeAndThing();
382
383         // then:
384         verify(getWebserviceMock()).dispatchDeviceState(SERIAL_NUMBER);
385     }
386
387     @Test
388     public void testThingStatusIsOfflineWithDetailGoneAndDetailMessageWhenDeviceIsRemoved() throws Exception {
389         // given:
390         setUpBridgeAndThing();
391
392         // when:
393         getBridgeHandler().onDeviceRemoved(SERIAL_NUMBER);
394
395         // then:
396         Thing thing = getThingHandler().getThing();
397         assertThingStatusIs(thing, ThingStatus.OFFLINE, ThingStatusDetail.GONE,
398                 "@text/mielecloud.thing.status.removed");
399     }
400
401     private DeviceState createDeviceStateMock(StateType stateType, String localizedState) {
402         DeviceState deviceState = mock(DeviceState.class);
403         when(deviceState.getDeviceIdentifier()).thenReturn(getThingHandler().getThing().getUID().getId());
404         when(deviceState.getRawType()).thenReturn(DeviceType.UNKNOWN);
405         when(deviceState.getStateType()).thenReturn(Optional.of(stateType));
406         when(deviceState.isInState(any())).thenCallRealMethod();
407         when(deviceState.getStatus()).thenReturn(Optional.of(localizedState));
408         return deviceState;
409     }
410
411     @Test
412     public void testStatusIsSetToOnlineWhenDeviceStateIsValid() throws Exception {
413         // given:
414         setUpBridgeAndThing();
415
416         DeviceState deviceState = createDeviceStateMock(StateType.ON, "On");
417
418         // when:
419         getBridgeHandler().onDeviceStateUpdated(deviceState);
420
421         // then:
422         assertThingStatusIs(getThingHandler().getThing(), ThingStatus.ONLINE, ThingStatusDetail.NONE);
423     }
424
425     @Test
426     public void testStatusIsSetToOfflineWhenDeviceIsNotConnected() throws Exception {
427         // given:
428         setUpBridgeAndThing();
429
430         DeviceState deviceState = createDeviceStateMock(StateType.NOT_CONNECTED, "Not connected");
431
432         // when:
433         getBridgeHandler().onDeviceStateUpdated(deviceState);
434
435         // then:
436         assertThingStatusIs(getThingHandler().getThing(), ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
437                 "@text/mielecloud.thing.status.disconnected");
438     }
439
440     @Test
441     public void testFailingPutProcessActionDoesNotSetTheDeviceToOffline() throws Exception {
442         // given:
443         doThrow(MieleWebserviceException.class).when(getWebserviceMock()).putProcessAction(any(),
444                 eq(ProcessAction.STOP));
445
446         setUpBridgeAndThing();
447
448         DeviceState deviceState = createDeviceStateMock(StateType.ON, "On");
449         getBridgeHandler().onDeviceStateUpdated(deviceState);
450         assertThingStatusIs(getThingHandler().getThing(), ThingStatus.ONLINE, ThingStatusDetail.NONE);
451
452         // when:
453         getThingHandler().triggerProcessAction(ProcessAction.STOP);
454
455         // then:
456         assertThingStatusIs(getThingHandler().getThing(), ThingStatus.ONLINE, ThingStatusDetail.NONE);
457     }
458
459     @Test
460     public void testHandleCommandProgramStartToStartStopChannel() throws Exception {
461         // given:
462         setUpBridgeAndThing();
463
464         // when:
465         getThingHandler().handleCommand(channel(PROGRAM_START_STOP),
466                 new StringType(ProgramStatus.PROGRAM_STARTED.getState()));
467
468         // then:
469         waitForAssert(() -> {
470             verify(getWebserviceMock()).putProcessAction(getThingHandler().getDeviceId(), ProcessAction.START);
471         });
472     }
473
474     @Test
475     public void testHandleCommandProgramStopToStartStopChannel() throws Exception {
476         // given:
477         setUpBridgeAndThing();
478
479         // when:
480         getThingHandler().handleCommand(channel(PROGRAM_START_STOP),
481                 new StringType(ProgramStatus.PROGRAM_STOPPED.getState()));
482
483         // then:
484         waitForAssert(() -> {
485             verify(getWebserviceMock()).putProcessAction(getThingHandler().getDeviceId(), ProcessAction.STOP);
486         });
487     }
488
489     @Test
490     public void testHandleCommandProgramStartToStartStopPauseChannel() throws Exception {
491         // given:
492         setUpBridgeAndThing();
493
494         // when:
495         getThingHandler().handleCommand(channel(PROGRAM_START_STOP_PAUSE),
496                 new StringType(ProgramStatus.PROGRAM_STARTED.getState()));
497
498         // then:
499         waitForAssert(() -> {
500             verify(getWebserviceMock()).putProcessAction(getThingHandler().getDeviceId(), ProcessAction.START);
501         });
502     }
503
504     @Test
505     public void testHandleCommandProgramStopToStartStopPauseChannel() throws Exception {
506         // given:
507         setUpBridgeAndThing();
508
509         // when:
510         getThingHandler().handleCommand(channel(PROGRAM_START_STOP_PAUSE),
511                 new StringType(ProgramStatus.PROGRAM_STOPPED.getState()));
512
513         // then:
514         waitForAssert(() -> {
515             verify(getWebserviceMock()).putProcessAction(getThingHandler().getDeviceId(), ProcessAction.STOP);
516         });
517     }
518
519     @Test
520     public void testHandleCommandProgramPauseToStartStopPauseChannel() throws Exception {
521         // given:
522         setUpBridgeAndThing();
523
524         // when:
525         getThingHandler().handleCommand(channel(PROGRAM_START_STOP_PAUSE),
526                 new StringType(ProgramStatus.PROGRAM_PAUSED.getState()));
527
528         // then:
529         waitForAssert(() -> {
530             verify(getWebserviceMock()).putProcessAction(getThingHandler().getDeviceId(), ProcessAction.PAUSE);
531         });
532     }
533
534     @Test
535     public void testFailingPutLightDoesNotSetTheDeviceToOffline() throws Exception {
536         // given:
537         doThrow(MieleWebserviceException.class).when(getWebserviceMock()).putLight(any(), eq(true));
538
539         setUpBridgeAndThing();
540
541         DeviceState deviceState = createDeviceStateMock(StateType.ON, "On");
542         getBridgeHandler().onDeviceStateUpdated(deviceState);
543         assertThingStatusIs(getThingHandler().getThing(), ThingStatus.ONLINE, ThingStatusDetail.NONE);
544
545         // when:
546         getThingHandler().triggerLight(true);
547
548         // then:
549         assertThingStatusIs(getThingHandler().getThing(), ThingStatus.ONLINE, ThingStatusDetail.NONE);
550     }
551
552     @Test
553     public void testHandleCommandLightOff() throws Exception {
554         // given:
555         setUpBridgeAndThing();
556
557         // when:
558         getThingHandler().handleCommand(channel(LIGHT_SWITCH), OnOffType.OFF);
559
560         // then:
561         waitForAssert(() -> {
562             verify(getWebserviceMock()).putLight(getThingHandler().getDeviceId(), false);
563         });
564     }
565
566     @Test
567     public void testHandleCommandLightOn() throws Exception {
568         // given:
569         setUpBridgeAndThing();
570
571         // when:
572         getThingHandler().handleCommand(channel(LIGHT_SWITCH), OnOffType.ON);
573
574         // then:
575         waitForAssert(() -> {
576             verify(getWebserviceMock()).putLight(getThingHandler().getDeviceId(), true);
577         });
578     }
579
580     @Test
581     public void testHandleCommandDoesNothingWhenCommandIsNotOfOnOffType() throws Exception {
582         // given:
583         setUpBridgeAndThing();
584
585         // when:
586         getThingHandler().handleCommand(channel(LIGHT_SWITCH), new DecimalType(0));
587
588         // then:
589         verify(getWebserviceMock(), never()).putLight(anyString(), anyBoolean());
590     }
591
592     @Test
593     public void testHandleCommandPowerOn() throws Exception {
594         // given:
595         setUpBridgeAndThing();
596
597         // when:
598         getThingHandler().handleCommand(channel(POWER_ON_OFF), OnOffType.ON);
599
600         // then:
601         waitForAssert(() -> {
602             verify(getWebserviceMock()).putPowerState(getThingHandler().getDeviceId(), true);
603         });
604     }
605
606     @Test
607     public void testHandleCommandPowerOff() throws Exception {
608         // given:
609         setUpBridgeAndThing();
610
611         // when:
612         getThingHandler().handleCommand(channel(POWER_ON_OFF), OnOffType.OFF);
613
614         // then:
615         waitForAssert(() -> {
616             verify(getWebserviceMock()).putPowerState(getThingHandler().getDeviceId(), false);
617         });
618     }
619
620     @Test
621     public void testHandleCommandDoesNothingWhenPowerCommandIsNotOfOnOffType() throws Exception {
622         // given:
623         setUpBridgeAndThing();
624
625         // when:
626         getThingHandler().handleCommand(channel(POWER_ON_OFF), new DecimalType(0));
627
628         // then:
629         verify(getWebserviceMock(), never()).putPowerState(anyString(), anyBoolean());
630     }
631
632     @Test
633     public void testMissingPropertiesAreSetWhenAStateUpdateIsReceivedFromTheCloud() throws Exception {
634         // given:
635         setUpBridgeAndThing();
636
637         assertFalse(getThingHandler().getThing().getProperties().containsKey(Thing.PROPERTY_SERIAL_NUMBER));
638         assertFalse(getThingHandler().getThing().getProperties().containsKey(Thing.PROPERTY_MODEL_ID));
639
640         var deviceState = mock(DeviceState.class);
641         when(deviceState.getRawType()).thenReturn(DeviceType.UNKNOWN);
642         when(deviceState.getDeviceIdentifier()).thenReturn(MieleCloudBindingIntegrationTestConstants.SERIAL_NUMBER);
643         when(deviceState.getFabNumber())
644                 .thenReturn(Optional.of(MieleCloudBindingIntegrationTestConstants.SERIAL_NUMBER));
645         when(deviceState.getType()).thenReturn(Optional.of("Unknown device type"));
646         when(deviceState.getTechType()).thenReturn(Optional.of("UK-4567"));
647
648         // when:
649         getThingHandler().onDeviceStateUpdated(deviceState);
650
651         // then:
652         assertEquals(MieleCloudBindingIntegrationTestConstants.SERIAL_NUMBER,
653                 getThingHandler().getThing().getProperties().get(Thing.PROPERTY_SERIAL_NUMBER));
654         assertEquals("Unknown device type UK-4567",
655                 getThingHandler().getThing().getProperties().get(Thing.PROPERTY_MODEL_ID));
656     }
657 }