[mielecloud] Initial contribution of the Miele Cloud binding (#9146)

Also-by: Bert Plonus <bert.plonus@miele.com>
Also-by: Martin Lepsy <martin.lepsy@miele.com>
Also-by: Benjamin Bolte <benjamin.bolte@itemis.de>
Signed-off-by: Björn Lange <bjoern.lange@itemis.de>
This commit is contained in:
Björn Lange
2021-05-25 22:06:49 +02:00
committed by GitHub
parent 5ec535f37b
commit 705f5c577c
230 changed files with 61858 additions and 0 deletions

View File

@@ -0,0 +1,13 @@
This content is produced and maintained by the openHAB project.
* Project home: https://www.openhab.org
== Declared Project Licenses
This program and the accompanying materials are made available under the terms
of the Eclipse Public License 2.0 which is available at
https://www.eclipse.org/legal/epl-2.0/.
== Source Code
https://github.com/openhab/openhab-addons

View File

@@ -0,0 +1,88 @@
-include: ../itest-common.bndrun
Bundle-SymbolicName: ${project.artifactId}
Fragment-Host: org.openhab.binding.mielecloud
-runrequires: \
bnd.identity;id='org.openhab.binding.mielecloud.tests',\
bnd.identity;id='org.openhab.core.binding.xml',\
bnd.identity;id='org.openhab.core.thing.xml'
-runblacklist: \
bnd.identity;id='org.openhab.core.storage.json',\
bnd.identity;id='org.openhab.core.storage.mapdb'
#
# done
#
-runbundles: \
org.eclipse.equinox.event;version='[1.4.300,1.4.301)',\
org.osgi.service.event;version='[1.4.0,1.4.1)',\
org.apache.servicemix.specs.activation-api-1.2.1;version='[1.2.1,1.2.2)',\
org.apache.felix.http.servlet-api;version='[1.1.2,1.1.3)',\
com.sun.xml.bind.jaxb-osgi;version='[2.3.3,2.3.4)',\
jakarta.xml.bind-api;version='[2.3.3,2.3.4)',\
org.opentest4j;version='[1.2.0,1.2.1)',\
org.hamcrest;version='[2.2.0,2.2.1)',\
junit-jupiter-api;version='[5.7.0,5.7.1)',\
junit-jupiter-engine;version='[5.7.0,5.7.1)',\
junit-platform-commons;version='[1.7.0,1.7.1)',\
junit-platform-engine;version='[1.7.0,1.7.1)',\
junit-platform-launcher;version='[1.7.0,1.7.1)',\
net.bytebuddy.byte-buddy;version='[1.10.19,1.10.20)',\
net.bytebuddy.byte-buddy-agent;version='[1.10.19,1.10.20)',\
org.glassfish.hk2.osgi-resource-locator;version='[1.0.3,1.0.4)',\
org.mockito.mockito-core;version='[3.7.0,3.7.1)',\
org.objenesis;version='[3.1.0,3.1.1)',\
org.openhab.binding.mielecloud;version='[3.1.0,3.1.1)',\
org.openhab.binding.mielecloud.tests;version='[3.1.0,3.1.1)',\
org.openhab.core;version='[3.1.0,3.1.1)',\
org.openhab.core.auth.oauth2client;version='[3.1.0,3.1.1)',\
org.openhab.core.binding.xml;version='[3.1.0,3.1.1)',\
org.openhab.core.config.core;version='[3.1.0,3.1.1)',\
org.openhab.core.config.discovery;version='[3.1.0,3.1.1)',\
org.openhab.core.config.xml;version='[3.1.0,3.1.1)',\
org.openhab.core.io.console;version='[3.1.0,3.1.1)',\
org.openhab.core.io.net;version='[3.1.0,3.1.1)',\
org.openhab.core.test;version='[3.1.0,3.1.1)',\
org.openhab.core.thing;version='[3.1.0,3.1.1)',\
org.openhab.core.thing.xml;version='[3.1.0,3.1.1)',\
biz.aQute.tester.junit-platform;version='[5.3.0,5.3.1)',\
com.google.gson;version='[2.8.6,2.8.7)',\
org.apache.felix.scr;version='[2.1.26,2.1.27)',\
org.objectweb.asm;version='[9.1.0,9.1.1)',\
org.objectweb.asm.commons;version='[9.0.0,9.0.1)',\
org.objectweb.asm.tree;version='[9.0.0,9.0.1)',\
org.osgi.util.function;version='[1.1.0,1.1.1)',\
org.osgi.util.promise;version='[1.1.1,1.1.2)',\
jakarta.annotation-api;version='[2.0.0,2.0.1)',\
jakarta.inject.jakarta.inject-api;version='[2.0.0,2.0.1)',\
javax.measure.unit-api;version='[2.1.2,2.1.3)',\
org.apache.felix.configadmin;version='[1.9.22,1.9.23)',\
org.apache.xbean.bundleutils;version='[4.19.0,4.19.1)',\
org.apache.xbean.finder;version='[4.19.0,4.19.1)',\
org.eclipse.jetty.client;version='[9.4.40,9.4.41)',\
org.eclipse.jetty.http;version='[9.4.40,9.4.41)',\
org.eclipse.jetty.io;version='[9.4.40,9.4.41)',\
org.eclipse.jetty.security;version='[9.4.40,9.4.41)',\
org.eclipse.jetty.server;version='[9.4.40,9.4.41)',\
org.eclipse.jetty.servlet;version='[9.4.40,9.4.41)',\
org.eclipse.jetty.util;version='[9.4.40,9.4.41)',\
org.eclipse.jetty.util.ajax;version='[9.4.40,9.4.41)',\
org.eclipse.jetty.websocket.api;version='[9.4.40,9.4.41)',\
org.eclipse.jetty.websocket.client;version='[9.4.40,9.4.41)',\
org.eclipse.jetty.websocket.common;version='[9.4.40,9.4.41)',\
org.eclipse.jetty.xml;version='[9.4.40,9.4.41)',\
org.glassfish.hk2.external.javax.inject;version='[2.4.0,2.4.1)',\
org.jsr-305;version='[3.0.2,3.0.3)',\
org.ops4j.pax.web.pax-web-api;version='[7.3.16,7.3.17)',\
org.ops4j.pax.web.pax-web-jetty;version='[7.3.16,7.3.17)',\
org.ops4j.pax.web.pax-web-runtime;version='[7.3.16,7.3.17)',\
org.ops4j.pax.web.pax-web-spi;version='[7.3.16,7.3.17)',\
org.osgi.service.cm;version='[1.6.0,1.6.1)',\
si-units;version='[2.0.1,2.0.2)',\
si.uom.si-quantity;version='[2.0.1,2.0.2)',\
tech.units.indriya;version='[2.1.2,2.1.3)',\
uom-lib-common;version='[2.1.0,2.1.1)',\
xstream;version='[1.4.17,1.4.18)',\
org.ops4j.pax.logging.pax-logging-api;version='[2.0.9,2.0.10)'

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.openhab.addons.itests</groupId>
<artifactId>org.openhab.addons.reactor.itests</artifactId>
<version>3.1.0-SNAPSHOT</version>
</parent>
<artifactId>org.openhab.binding.mielecloud.tests</artifactId>
<name>openHAB Add-ons :: Integration Tests :: mielecloud Binding Tests</name>
<dependencies>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.mielecloud</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,134 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.mielecloud.internal.config;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
import static org.openhab.binding.mielecloud.internal.util.ReflectionUtil.setPrivate;
import java.util.Objects;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.Test;
import org.openhab.binding.mielecloud.internal.config.servlet.CreateBridgeServlet;
import org.openhab.binding.mielecloud.internal.config.servlet.ForwardToLoginServlet;
import org.openhab.binding.mielecloud.internal.handler.MieleBridgeHandler;
import org.openhab.binding.mielecloud.internal.handler.MieleHandlerFactory;
import org.openhab.binding.mielecloud.internal.util.AbstractConfigFlowTest;
import org.openhab.binding.mielecloud.internal.util.MieleCloudBindingIntegrationTestConstants;
import org.openhab.binding.mielecloud.internal.util.ReflectionUtil;
import org.openhab.binding.mielecloud.internal.util.Website;
import org.openhab.binding.mielecloud.internal.webservice.MieleWebservice;
import org.openhab.binding.mielecloud.internal.webservice.MieleWebserviceFactory;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerFactory;
/**
* @author Björn Lange - Initial Contribution
*/
@NonNullByDefault
public class ConfigFlowTest extends AbstractConfigFlowTest {
private void setUpAuthorizationHandler() throws NoSuchFieldException, IllegalAccessException {
OAuthAuthorizationHandler authorizationHandler = mock(OAuthAuthorizationHandler.class);
when(authorizationHandler.getAccessToken(MieleCloudBindingIntegrationTestConstants.EMAIL))
.thenReturn(MieleCloudBindingIntegrationTestConstants.ACCESS_TOKEN);
when(authorizationHandler.getBridgeUid())
.thenReturn(MieleCloudBindingIntegrationTestConstants.BRIDGE_THING_UID);
when(authorizationHandler.getEmail()).thenReturn(MieleCloudBindingIntegrationTestConstants.EMAIL);
setPrivate(getResultServlet(), "authorizationHandler", authorizationHandler);
}
private void setUpWebservice() throws Exception {
MieleWebservice webservice = mock(MieleWebservice.class);
doAnswer(invocation -> {
Thing bridge = getThingRegistry().get(MieleCloudBindingIntegrationTestConstants.BRIDGE_THING_UID);
assertNotNull(bridge);
ThingHandler handler = bridge.getHandler();
if (handler instanceof MieleBridgeHandler) {
((MieleBridgeHandler) handler).onConnectionAlive();
}
return null;
}).when(webservice).addConnectionStatusListener(any());
MieleWebserviceFactory webserviceFactory = mock(MieleWebserviceFactory.class);
when(webserviceFactory.create(any())).thenReturn(webservice);
MieleHandlerFactory handlerFactory = getService(ThingHandlerFactory.class, MieleHandlerFactory.class);
assertNotNull(handlerFactory);
setPrivate(Objects.requireNonNull(handlerFactory), "webserviceFactory", webserviceFactory);
}
private Website configureBridgeWithConfigFlow() throws Exception {
Website accountOverviewSite = getCrawler().doGetRelative("/mielecloud");
String pairAccountUrl = accountOverviewSite.getTargetOfLink("Pair Account");
Website pairAccountSite = getCrawler().doGetRelative(pairAccountUrl);
String forwardToLoginUrl = pairAccountSite.getFormAction();
Website mieleLoginSite = getCrawler().doGetRelative(forwardToLoginUrl + "?"
+ ForwardToLoginServlet.CLIENT_ID_PARAMETER_NAME + "="
+ MieleCloudBindingIntegrationTestConstants.CLIENT_ID + "&"
+ ForwardToLoginServlet.CLIENT_SECRET_PARAMETER_NAME + "="
+ MieleCloudBindingIntegrationTestConstants.CLIENT_SECRET + "&"
+ ForwardToLoginServlet.BRIDGE_ID_PARAMETER_NAME + "="
+ MieleCloudBindingIntegrationTestConstants.BRIDGE_ID + "&" + ForwardToLoginServlet.EMAIL_PARAMETER_NAME
+ "=" + MieleCloudBindingIntegrationTestConstants.EMAIL);
String redirectionUrl = mieleLoginSite.getValueOfInput("redirect_uri").replace("http://127.0.0.1:8080", "");
String state = mieleLoginSite.getValueOfInput("state");
Website resultSite = getCrawler().doGetRelative(redirectionUrl + "?code="
+ MieleCloudBindingIntegrationTestConstants.AUTHORIZATION_CODE + "&state=" + state);
String createBridgeUrl = resultSite.getFormAction();
Website finalOverview = getCrawler().doGetRelative(createBridgeUrl + "?"
+ CreateBridgeServlet.BRIDGE_UID_PARAMETER_NAME + "="
+ MieleCloudBindingIntegrationTestConstants.BRIDGE_THING_UID.toString() + "&"
+ CreateBridgeServlet.EMAIL_PARAMETER_NAME + "=" + MieleCloudBindingIntegrationTestConstants.EMAIL);
return finalOverview;
}
@Test
public void configFlowHappyPathCreatesABridge() throws Exception {
// given:
setUpAuthorizationHandler();
setUpWebservice();
// when:
Website finalOverview = configureBridgeWithConfigFlow();
// then:
assertTrue(finalOverview.contains("<span class=\"status online\">ONLINE</span>"));
Thing bridge = getThingRegistry().get(MieleCloudBindingIntegrationTestConstants.BRIDGE_THING_UID);
assertNotNull(bridge);
assertEquals(ThingStatus.ONLINE, bridge.getStatus());
}
@Test
public void configFlowWaitTimeoutExpiresWhenBridgeDoesNotComeOnline() throws Exception {
// given:
setUpAuthorizationHandler();
ReflectionUtil.setPrivateStaticFinal(CreateBridgeServlet.class, "ONLINE_WAIT_TIMEOUT_IN_MILLISECONDS", 0);
// when:
configureBridgeWithConfigFlow();
// then:
Thing bridge = getThingRegistry().get(MieleCloudBindingIntegrationTestConstants.BRIDGE_THING_UID);
assertNotNull(bridge);
}
}

View File

@@ -0,0 +1,105 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.mielecloud.internal.config.servlet;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.*;
import java.util.stream.Stream;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.Test;
import org.openhab.binding.mielecloud.internal.MieleCloudBindingConstants;
import org.openhab.binding.mielecloud.internal.util.AbstractConfigFlowTest;
import org.openhab.binding.mielecloud.internal.util.MieleCloudBindingIntegrationTestConstants;
import org.openhab.binding.mielecloud.internal.util.ReflectionUtil;
import org.openhab.binding.mielecloud.internal.util.Website;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.ThingRegistry;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.ThingStatusInfo;
import org.openhab.core.thing.ThingUID;
/**
* @author Björn Lange - Initial Contribution
*/
@NonNullByDefault
public class AccountOverviewServletTest extends AbstractConfigFlowTest {
@Test
public void whenAccountOverviewServletIsCalledOverNonSslConnectionThenAWarningIsShown() throws Exception {
// when:
Website accountOverviewSite = getCrawler().doGetRelative("/mielecloud");
// then:
assertTrue(accountOverviewSite
.contains("Warning: We strongly advice to proceed only with SSL enabled for a secure data exchange."));
assertTrue(accountOverviewSite.contains(
"See <a href=\"https://www.openhab.org/docs/installation/security.html\">Securing access to openHAB</a> for details."));
}
@Test
public void whenAccountOverviewServletIsCalledAndNoBridgeIsPresentThenThePageSaysThatThereIsNoBridgePaired()
throws Exception {
// when:
Website accountOverviewSite = getCrawler().doGetRelative("/mielecloud");
// then:
assertTrue(accountOverviewSite.contains("There is no account paired at the moment."));
}
@Test
public void whenAccountOverviewServletIsCalledAndBridgesArePresentThenThePageDisplaysInformationAboutThem()
throws Exception {
// given:
Configuration configuration1 = mock(Configuration.class);
when(configuration1.get(MieleCloudBindingConstants.CONFIG_PARAM_LOCALE)).thenReturn("de");
when(configuration1.get(MieleCloudBindingConstants.CONFIG_PARAM_EMAIL)).thenReturn("openhab@openhab.org");
Bridge bridge1 = mock(Bridge.class);
when(bridge1.getThingTypeUID()).thenReturn(MieleCloudBindingConstants.THING_TYPE_BRIDGE);
when(bridge1.getUID()).thenReturn(MieleCloudBindingIntegrationTestConstants.BRIDGE_THING_UID);
when(bridge1.getStatus()).thenReturn(ThingStatus.ONLINE);
when(bridge1.getStatusInfo()).thenReturn(new ThingStatusInfo(ThingStatus.ONLINE, ThingStatusDetail.NONE, null));
when(bridge1.getConfiguration()).thenReturn(configuration1);
Configuration configuration2 = mock(Configuration.class);
when(configuration2.get(MieleCloudBindingConstants.CONFIG_PARAM_LOCALE)).thenReturn("en");
when(configuration2.get(MieleCloudBindingConstants.CONFIG_PARAM_EMAIL)).thenReturn("everyone@openhab.org");
Bridge bridge2 = mock(Bridge.class);
when(bridge2.getThingTypeUID()).thenReturn(MieleCloudBindingConstants.THING_TYPE_BRIDGE);
when(bridge2.getUID()).thenReturn(new ThingUID(MieleCloudBindingConstants.THING_TYPE_BRIDGE, "test"));
when(bridge2.getStatus()).thenReturn(ThingStatus.OFFLINE);
when(bridge2.getStatusInfo()).thenReturn(
new ThingStatusInfo(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Error message"));
when(bridge2.getConfiguration()).thenReturn(configuration2);
ThingRegistry thingRegistry = mock(ThingRegistry.class);
when(thingRegistry.stream()).thenAnswer(invocation -> Stream.of(bridge1, bridge2));
ReflectionUtil.setPrivate(getAccountOverviewServlet(), "thingRegistry", thingRegistry);
// when:
Website accountOverviewSite = getCrawler().doGetRelative("/mielecloud");
// then:
assertTrue(accountOverviewSite.contains("The following bridges are paired"));
assertTrue(accountOverviewSite.contains("openhab@openhab.org"));
assertTrue(accountOverviewSite.contains("mielecloud:account:genesis"));
assertTrue(accountOverviewSite.contains("<span class=\"status online\">"));
assertTrue(accountOverviewSite.contains("everyone@openhab.org"));
assertTrue(accountOverviewSite.contains("mielecloud:account:test"));
assertTrue(accountOverviewSite.contains("<span class=\"status offline\">"));
}
}

View File

@@ -0,0 +1,242 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.mielecloud.internal.config.servlet;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import static org.openhab.binding.mielecloud.internal.util.ReflectionUtil.setPrivate;
import java.util.Objects;
import java.util.Optional;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.Test;
import org.openhab.binding.mielecloud.internal.MieleCloudBindingConstants;
import org.openhab.binding.mielecloud.internal.auth.OAuthTokenRefresher;
import org.openhab.binding.mielecloud.internal.config.MieleCloudConfigService;
import org.openhab.binding.mielecloud.internal.util.AbstractConfigFlowTest;
import org.openhab.binding.mielecloud.internal.util.MieleCloudBindingIntegrationTestConstants;
import org.openhab.binding.mielecloud.internal.util.Website;
import org.openhab.core.config.discovery.inbox.Inbox;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingRegistry;
import org.openhab.core.thing.binding.ThingHandler;
/**
* @author Björn Lange - Initial Contribution
*/
@NonNullByDefault
public class CreateBridgeServletTest extends AbstractConfigFlowTest {
@Test
public void whenBridgeCreationFailsThenAWarningIsShownOnTheSuccessPage() throws Exception {
// given:
MieleCloudConfigService configService = getService(MieleCloudConfigService.class);
assertNotNull(configService);
CreateBridgeServlet createBridgeServlet = configService.getCreateBridgeServlet();
assertNotNull(createBridgeServlet);
Inbox inbox = mock(Inbox.class);
when(inbox.add(any())).thenReturn(true);
when(inbox.approve(any(), anyString(), anyString())).thenReturn(null);
setPrivate(Objects.requireNonNull(createBridgeServlet), "inbox", inbox);
// when:
Website website = getCrawler().doGetRelative("/mielecloud/createBridgeThing?"
+ CreateBridgeServlet.BRIDGE_UID_PARAMETER_NAME + "="
+ MieleCloudBindingIntegrationTestConstants.BRIDGE_THING_UID.getAsString() + "&"
+ CreateBridgeServlet.EMAIL_PARAMETER_NAME + "=" + MieleCloudBindingIntegrationTestConstants.EMAIL);
// then:
assertTrue(website.contains("Pairing successful!"));
assertTrue(website.contains(
"Could not auto configure the bridge. Failed to approve the bridge from the inbox. Please try the configuration flow again."));
}
@Test
public void whenBridgeReconfigurationFailsDueToMissingBridgeThenAWarningIsShownOnTheSuccessPage() throws Exception {
// given:
MieleCloudConfigService configService = getService(MieleCloudConfigService.class);
assertNotNull(configService);
CreateBridgeServlet createBridgeServlet = configService.getCreateBridgeServlet();
assertNotNull(createBridgeServlet);
Inbox inbox = mock(Inbox.class);
when(inbox.add(any())).thenReturn(false);
setPrivate(Objects.requireNonNull(createBridgeServlet), "inbox", inbox);
ThingRegistry thingRegistry = mock(ThingRegistry.class);
when(thingRegistry.get(any())).thenReturn(null);
setPrivate(Objects.requireNonNull(createBridgeServlet), "thingRegistry", thingRegistry);
// when:
Website website = getCrawler().doGetRelative("/mielecloud/createBridgeThing?"
+ CreateBridgeServlet.BRIDGE_UID_PARAMETER_NAME + "="
+ MieleCloudBindingIntegrationTestConstants.BRIDGE_THING_UID.getAsString() + "&"
+ CreateBridgeServlet.EMAIL_PARAMETER_NAME + "=" + MieleCloudBindingIntegrationTestConstants.EMAIL);
// then:
assertTrue(website.contains("Pairing successful!"));
assertTrue(website.contains(
"Could not auto reconfigure the bridge. Bridge thing or thing handler is not available. Please try the configuration flow again."));
}
@Test
public void whenBridgeReconfigurationFailsDueToMissingBridgeHandlerThenAWarningIsShownOnTheSuccessPage()
throws Exception {
// given:
MieleCloudConfigService configService = getService(MieleCloudConfigService.class);
assertNotNull(configService);
CreateBridgeServlet createBridgeServlet = configService.getCreateBridgeServlet();
assertNotNull(createBridgeServlet);
Inbox inbox = mock(Inbox.class);
when(inbox.add(any())).thenReturn(false);
setPrivate(Objects.requireNonNull(createBridgeServlet), "inbox", inbox);
Thing bridge = mock(Thing.class);
when(bridge.getHandler()).thenReturn(null);
ThingRegistry thingRegistry = mock(ThingRegistry.class);
when(thingRegistry.get(any())).thenReturn(bridge);
setPrivate(Objects.requireNonNull(createBridgeServlet), "thingRegistry", thingRegistry);
// when:
Website website = getCrawler().doGetRelative("/mielecloud/createBridgeThing?"
+ CreateBridgeServlet.BRIDGE_UID_PARAMETER_NAME + "="
+ MieleCloudBindingIntegrationTestConstants.BRIDGE_THING_UID.getAsString() + "&"
+ CreateBridgeServlet.EMAIL_PARAMETER_NAME + "=" + MieleCloudBindingIntegrationTestConstants.EMAIL);
// then:
assertTrue(website.contains("Pairing successful!"));
assertTrue(website.contains(
"Could not auto reconfigure the bridge. Bridge thing or thing handler is not available. Please try the configuration flow again."));
}
@Test
public void whenBridgeIsReconfiguredThenTheConfigurationParametersAreUpdatedAndTheOverviewPageIsDisplayed()
throws Exception {
// given:
setUpBridge();
MieleCloudConfigService configService = getService(MieleCloudConfigService.class);
assertNotNull(configService);
CreateBridgeServlet createBridgeServlet = configService.getCreateBridgeServlet();
assertNotNull(createBridgeServlet);
OAuthTokenRefresher tokenRefresher = mock(OAuthTokenRefresher.class);
when(tokenRefresher.getAccessTokenFromStorage(anyString()))
.thenReturn(Optional.of(MieleCloudBindingIntegrationTestConstants.ALTERNATIVE_ACCESS_TOKEN));
Thing bridge = getThingRegistry().get(MieleCloudBindingIntegrationTestConstants.BRIDGE_THING_UID);
assertNotNull(bridge);
ThingHandler bridgeHandler = bridge.getHandler();
assertNotNull(bridgeHandler);
setPrivate(Objects.requireNonNull(bridgeHandler), "tokenRefresher", tokenRefresher);
// when:
Website website = getCrawler().doGetRelative("/mielecloud/createBridgeThing?"
+ CreateBridgeServlet.BRIDGE_UID_PARAMETER_NAME + "="
+ MieleCloudBindingIntegrationTestConstants.BRIDGE_THING_UID.getAsString() + "&"
+ CreateBridgeServlet.EMAIL_PARAMETER_NAME + "=" + MieleCloudBindingIntegrationTestConstants.EMAIL);
// then:
assertTrue(website.contains("<li class=\"active\">Overview</li>"));
assertEquals(MieleCloudBindingIntegrationTestConstants.ALTERNATIVE_ACCESS_TOKEN,
bridge.getProperties().get(MieleCloudBindingConstants.PROPERTY_ACCESS_TOKEN));
}
@Test
public void whenNoBridgeUidIsPassedToBridgeCreationThenTheBrowserIsRedirectedToTheFailurePageAndAnErrorIsShown()
throws Exception {
// when:
Website website = getCrawler().doGetRelative("/mielecloud/createBridgeThing?"
+ CreateBridgeServlet.EMAIL_PARAMETER_NAME + "=" + MieleCloudBindingIntegrationTestConstants.EMAIL);
// then:
assertTrue(website.contains("Pairing failed!"));
assertTrue(website.contains("Missing bridge UID."));
}
@Test
public void whenAnEmptyBridgeUidIsPassedToBridgeCreationThenTheBrowserIsRedirectedToTheFailurePageAndAnErrorIsShown()
throws Exception {
// when:
Website website = getCrawler().doGetRelative("/mielecloud/createBridgeThing?"
+ CreateBridgeServlet.BRIDGE_UID_PARAMETER_NAME + "=&" + CreateBridgeServlet.EMAIL_PARAMETER_NAME + "="
+ MieleCloudBindingIntegrationTestConstants.EMAIL);
// then:
assertTrue(website.contains("Pairing failed!"));
assertTrue(website.contains("Missing bridge UID."));
}
@Test
public void whenAMalformedBridgeUidIsPassedToBridgeCreationThenTheBrowserIsRedirectedToTheFailurePageAndAnErrorIsShown()
throws Exception {
// when:
Website website = getCrawler().doGetRelative("/mielecloud/createBridgeThing?"
+ CreateBridgeServlet.BRIDGE_UID_PARAMETER_NAME + "=gen!e!sis&"
+ CreateBridgeServlet.EMAIL_PARAMETER_NAME + "=" + MieleCloudBindingIntegrationTestConstants.EMAIL);
// then:
assertTrue(website.contains("Pairing failed!"));
assertTrue(website.contains("Malformed bridge UID."));
}
@Test
public void whenNoEmailIsPassedToBridgeCreationThenTheBrowserIsRedirectedToTheFailurePageAndAnErrorIsShown()
throws Exception {
// when:
Website website = getCrawler()
.doGetRelative("/mielecloud/createBridgeThing?" + CreateBridgeServlet.BRIDGE_UID_PARAMETER_NAME + "="
+ MieleCloudBindingIntegrationTestConstants.BRIDGE_THING_UID.getAsString());
// then:
assertTrue(website.contains("Pairing failed!"));
assertTrue(website.contains("Missing e-mail address."));
}
@Test
public void whenAnEmptyEmailIsPassedToBridgeCreationThenTheBrowserIsRedirectedToTheFailurePageAndAnErrorIsShown()
throws Exception {
// when:
Website website = getCrawler()
.doGetRelative("/mielecloud/createBridgeThing?" + CreateBridgeServlet.BRIDGE_UID_PARAMETER_NAME + "="
+ MieleCloudBindingIntegrationTestConstants.BRIDGE_THING_UID.getAsString() + "&"
+ CreateBridgeServlet.EMAIL_PARAMETER_NAME + "=");
// then:
assertTrue(website.contains("Pairing failed!"));
assertTrue(website.contains("Missing e-mail address."));
}
@Test
public void whenAMalformedEmailIsPassedToBridgeCreationThenTheBrowserIsRedirectedToTheFailurePageAndAnErrorIsShown()
throws Exception {
// when:
Website website = getCrawler()
.doGetRelative("/mielecloud/createBridgeThing?" + CreateBridgeServlet.BRIDGE_UID_PARAMETER_NAME + "="
+ MieleCloudBindingIntegrationTestConstants.BRIDGE_THING_UID.getAsString() + "&"
+ CreateBridgeServlet.EMAIL_PARAMETER_NAME + "=openhab.openhab.org");
// then:
assertTrue(website.contains("Pairing failed!"));
assertTrue(website.contains("Malformed e-mail address."));
}
}

View File

@@ -0,0 +1,289 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.mielecloud.internal.config.servlet;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import static org.openhab.binding.mielecloud.internal.util.ReflectionUtil.setPrivate;
import java.time.LocalDateTime;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.Test;
import org.openhab.binding.mielecloud.internal.auth.OAuthException;
import org.openhab.binding.mielecloud.internal.config.OAuthAuthorizationHandler;
import org.openhab.binding.mielecloud.internal.config.exception.NoOngoingAuthorizationException;
import org.openhab.binding.mielecloud.internal.config.exception.OngoingAuthorizationException;
import org.openhab.binding.mielecloud.internal.util.AbstractConfigFlowTest;
import org.openhab.binding.mielecloud.internal.util.MieleCloudBindingIntegrationTestConstants;
import org.openhab.binding.mielecloud.internal.util.Website;
/**
* @author Björn Lange - Initial Contribution
*/
@NonNullByDefault
public class ForwardToLoginServletTest extends AbstractConfigFlowTest {
@Test
public void whenAuthorizationCannotBeBegunThenTheBrowserIsRedirectedToThePairSiteAndAWarningIsDisplayed()
throws Exception {
// given:
OAuthAuthorizationHandler authorizationHandler = mock(OAuthAuthorizationHandler.class);
doThrow(new OngoingAuthorizationException("", LocalDateTime.now().plusMinutes(3))).when(authorizationHandler)
.beginAuthorization(anyString(), anyString(), any(), anyString());
setPrivate(getForwardToLoginServlet(), "authorizationHandler", authorizationHandler);
// when:
Website maybePairAccountSite = getCrawler().doGetRelative("/mielecloud/forwardToLogin?"
+ ForwardToLoginServlet.CLIENT_ID_PARAMETER_NAME + "="
+ MieleCloudBindingIntegrationTestConstants.CLIENT_ID + "&"
+ ForwardToLoginServlet.CLIENT_SECRET_PARAMETER_NAME + "="
+ MieleCloudBindingIntegrationTestConstants.CLIENT_SECRET + "&"
+ ForwardToLoginServlet.BRIDGE_ID_PARAMETER_NAME + "="
+ MieleCloudBindingIntegrationTestConstants.BRIDGE_ID + "&" + ForwardToLoginServlet.EMAIL_PARAMETER_NAME
+ "=" + MieleCloudBindingIntegrationTestConstants.EMAIL);
// then:
assertTrue(maybePairAccountSite.contains(
"Go to <a href=\"https://www.miele.com/f/com/en/register_api.aspx\">the Miele developer portal</a> to obtain your"));
assertTrue(maybePairAccountSite.contains(
"There is an authorization ongoing at the moment. Please complete that authorization prior to starting a new one or try again in 3 minutes."));
}
@Test
public void whenNoAuthorizationIsOngoingWhenTheAuthorizationUrlIsRequestedThenTheBrowserIsRedirectedToThePairSiteAndAWarningIsDisplayed()
throws Exception {
// given:
OAuthAuthorizationHandler authorizationHandler = mock(OAuthAuthorizationHandler.class);
doThrow(new NoOngoingAuthorizationException("")).when(authorizationHandler).getAuthorizationUrl(anyString());
setPrivate(getForwardToLoginServlet(), "authorizationHandler", authorizationHandler);
// when:
Website maybePairAccountSite = getCrawler().doGetRelative("/mielecloud/forwardToLogin?"
+ ForwardToLoginServlet.CLIENT_ID_PARAMETER_NAME + "="
+ MieleCloudBindingIntegrationTestConstants.CLIENT_ID + "&"
+ ForwardToLoginServlet.CLIENT_SECRET_PARAMETER_NAME + "="
+ MieleCloudBindingIntegrationTestConstants.CLIENT_SECRET + "&"
+ ForwardToLoginServlet.BRIDGE_ID_PARAMETER_NAME + "="
+ MieleCloudBindingIntegrationTestConstants.BRIDGE_ID + "&" + ForwardToLoginServlet.EMAIL_PARAMETER_NAME
+ "=" + MieleCloudBindingIntegrationTestConstants.EMAIL);
// then:
assertTrue(maybePairAccountSite.contains(
"Go to <a href=\"https://www.miele.com/f/com/en/register_api.aspx\">the Miele developer portal</a> to obtain your"));
assertTrue(maybePairAccountSite.contains(
"Failed to start auhtorization process. Are you trying to perform multiple authorizations at the same time?"));
}
@Test
public void whenNoClientIdIsPassedThenTheBrowserIsRedirectedToThePairSiteAndAWarningIsDisplayed() throws Exception {
// when:
Website maybePairAccountSite = getCrawler().doGetRelative("/mielecloud/forwardToLogin?"
+ ForwardToLoginServlet.CLIENT_SECRET_PARAMETER_NAME + "="
+ MieleCloudBindingIntegrationTestConstants.CLIENT_SECRET + "&"
+ ForwardToLoginServlet.BRIDGE_ID_PARAMETER_NAME + "="
+ MieleCloudBindingIntegrationTestConstants.BRIDGE_ID + "&" + ForwardToLoginServlet.EMAIL_PARAMETER_NAME
+ "=" + MieleCloudBindingIntegrationTestConstants.EMAIL);
// then:
assertTrue(maybePairAccountSite.contains(
"Go to <a href=\"https://www.miele.com/f/com/en/register_api.aspx\">the Miele developer portal</a> to obtain your"));
assertTrue(maybePairAccountSite.contains("Missing client ID."));
}
@Test
public void whenAnEmptyClientIdIsPassedThenTheBrowserIsRedirectedToThePairSiteAndAWarningIsDisplayed()
throws Exception {
// when:
Website maybePairAccountSite = getCrawler().doGetRelative("/mielecloud/forwardToLogin?"
+ ForwardToLoginServlet.CLIENT_ID_PARAMETER_NAME + "=&"
+ ForwardToLoginServlet.CLIENT_SECRET_PARAMETER_NAME + "="
+ MieleCloudBindingIntegrationTestConstants.CLIENT_SECRET + "&"
+ ForwardToLoginServlet.BRIDGE_ID_PARAMETER_NAME + "="
+ MieleCloudBindingIntegrationTestConstants.BRIDGE_ID + "&" + ForwardToLoginServlet.EMAIL_PARAMETER_NAME
+ "=" + MieleCloudBindingIntegrationTestConstants.EMAIL);
// then:
assertTrue(maybePairAccountSite.contains(
"Go to <a href=\"https://www.miele.com/f/com/en/register_api.aspx\">the Miele developer portal</a> to obtain your"));
assertTrue(maybePairAccountSite.contains("Missing client ID."));
}
@Test
public void whenNoClientSecretIsPassedThenTheBrowserIsRedirectedToThePairSiteAndAWarningIsDisplayed()
throws Exception {
// when:
Website maybePairAccountSite = getCrawler().doGetRelative("/mielecloud/forwardToLogin?"
+ ForwardToLoginServlet.CLIENT_ID_PARAMETER_NAME + "="
+ MieleCloudBindingIntegrationTestConstants.CLIENT_ID + "&"
+ ForwardToLoginServlet.BRIDGE_ID_PARAMETER_NAME + "="
+ MieleCloudBindingIntegrationTestConstants.BRIDGE_ID + "&" + ForwardToLoginServlet.EMAIL_PARAMETER_NAME
+ "=" + MieleCloudBindingIntegrationTestConstants.EMAIL);
// then:
assertTrue(maybePairAccountSite.contains(
"Go to <a href=\"https://www.miele.com/f/com/en/register_api.aspx\">the Miele developer portal</a> to obtain your"));
assertTrue(maybePairAccountSite.contains("Missing client secret."));
}
@Test
public void whenAnEmptyClientSecretIsPassedThenTheBrowserIsRedirectedToThePairSiteAndAWarningIsDisplayed()
throws Exception {
// when:
Website maybePairAccountSite = getCrawler().doGetRelative("/mielecloud/forwardToLogin?"
+ ForwardToLoginServlet.CLIENT_ID_PARAMETER_NAME + "="
+ MieleCloudBindingIntegrationTestConstants.CLIENT_ID + "&"
+ ForwardToLoginServlet.CLIENT_SECRET_PARAMETER_NAME + "=" + "&"
+ ForwardToLoginServlet.BRIDGE_ID_PARAMETER_NAME + "="
+ MieleCloudBindingIntegrationTestConstants.BRIDGE_ID + "&" + ForwardToLoginServlet.EMAIL_PARAMETER_NAME
+ "=" + MieleCloudBindingIntegrationTestConstants.EMAIL);
// then:
assertTrue(maybePairAccountSite.contains(
"Go to <a href=\"https://www.miele.com/f/com/en/register_api.aspx\">the Miele developer portal</a> to obtain your"));
assertTrue(maybePairAccountSite.contains("Missing client secret."));
}
@Test
public void whenOAuthClientDoesNotProvideAnAuthorizationUrlThenTheBrowserIsRedirectedToThePairSiteAndAWarningIsDisplayed()
throws Exception {
// given:
OAuthAuthorizationHandler authorizationHandler = mock(OAuthAuthorizationHandler.class);
doThrow(new OAuthException("")).when(authorizationHandler).getAuthorizationUrl(anyString());
setPrivate(getForwardToLoginServlet(), "authorizationHandler", authorizationHandler);
// when:
Website maybePairAccountSite = getCrawler().doGetRelative("/mielecloud/forwardToLogin?"
+ ForwardToLoginServlet.CLIENT_ID_PARAMETER_NAME + "="
+ MieleCloudBindingIntegrationTestConstants.CLIENT_ID + "&"
+ ForwardToLoginServlet.CLIENT_SECRET_PARAMETER_NAME + "="
+ MieleCloudBindingIntegrationTestConstants.CLIENT_SECRET + "&"
+ ForwardToLoginServlet.BRIDGE_ID_PARAMETER_NAME + "="
+ MieleCloudBindingIntegrationTestConstants.BRIDGE_ID + "&" + ForwardToLoginServlet.EMAIL_PARAMETER_NAME
+ "=" + MieleCloudBindingIntegrationTestConstants.EMAIL);
// then:
assertTrue(maybePairAccountSite.contains(
"Go to <a href=\"https://www.miele.com/f/com/en/register_api.aspx\">the Miele developer portal</a> to obtain your"));
assertTrue(maybePairAccountSite.contains("Failed to derive redirect URL."));
}
@Test
public void whenNoBridgeUidIsPassedThenTheBrowserIsRedirectedToThePairSiteAndAWarningIsDisplayed()
throws Exception {
// when:
Website maybePairAccountSite = getCrawler().doGetRelative("/mielecloud/forwardToLogin?"
+ ForwardToLoginServlet.CLIENT_ID_PARAMETER_NAME + "="
+ MieleCloudBindingIntegrationTestConstants.CLIENT_ID + "&"
+ ForwardToLoginServlet.CLIENT_SECRET_PARAMETER_NAME + "="
+ MieleCloudBindingIntegrationTestConstants.CLIENT_SECRET + "&"
+ ForwardToLoginServlet.EMAIL_PARAMETER_NAME + "=" + MieleCloudBindingIntegrationTestConstants.EMAIL);
// then:
assertTrue(maybePairAccountSite.contains(
"Go to <a href=\"https://www.miele.com/f/com/en/register_api.aspx\">the Miele developer portal</a> to obtain your"));
assertTrue(maybePairAccountSite.contains("Missing bridge ID."));
}
@Test
public void whenAnEmptyBridgeUidIsPassedThenTheBrowserIsRedirectedToThePairSiteAndAWarningIsDisplayed()
throws Exception {
// when:
Website maybePairAccountSite = getCrawler().doGetRelative("/mielecloud/forwardToLogin?"
+ ForwardToLoginServlet.CLIENT_ID_PARAMETER_NAME + "="
+ MieleCloudBindingIntegrationTestConstants.CLIENT_ID + "&"
+ ForwardToLoginServlet.CLIENT_SECRET_PARAMETER_NAME + "="
+ MieleCloudBindingIntegrationTestConstants.CLIENT_SECRET + "&"
+ ForwardToLoginServlet.BRIDGE_ID_PARAMETER_NAME + "=" + "&"
+ ForwardToLoginServlet.EMAIL_PARAMETER_NAME + "=" + MieleCloudBindingIntegrationTestConstants.EMAIL);
// then:
assertTrue(maybePairAccountSite.contains(
"Go to <a href=\"https://www.miele.com/f/com/en/register_api.aspx\">the Miele developer portal</a> to obtain your"));
assertTrue(maybePairAccountSite.contains("Missing bridge ID."));
}
@Test
public void whenAMalformedBridgeUidIsPassedThenTheBrowserIsRedirectedToThePairSiteAndAWarningIsDisplayed()
throws Exception {
// when:
Website maybePairAccountSite = getCrawler().doGetRelative("/mielecloud/forwardToLogin?"
+ ForwardToLoginServlet.CLIENT_ID_PARAMETER_NAME + "="
+ MieleCloudBindingIntegrationTestConstants.CLIENT_ID + "&"
+ ForwardToLoginServlet.CLIENT_SECRET_PARAMETER_NAME + "="
+ MieleCloudBindingIntegrationTestConstants.CLIENT_SECRET + "&"
+ ForwardToLoginServlet.BRIDGE_ID_PARAMETER_NAME + "=genesis!" + "&"
+ ForwardToLoginServlet.EMAIL_PARAMETER_NAME + "=" + MieleCloudBindingIntegrationTestConstants.EMAIL);
// then:
assertTrue(maybePairAccountSite.contains(
"Go to <a href=\"https://www.miele.com/f/com/en/register_api.aspx\">the Miele developer portal</a> to obtain your"));
assertTrue(maybePairAccountSite
.contains("Malformed bridge ID. A bridge ID may only contain letters, numbers, '-' and '_'!"));
}
@Test
public void whenNoEmailIsPassedThenTheBrowserIsRedirectedToThePairSiteAndAWarningIsDisplayed() throws Exception {
// when:
Website maybePairAccountSite = getCrawler()
.doGetRelative("/mielecloud/forwardToLogin?" + ForwardToLoginServlet.CLIENT_ID_PARAMETER_NAME + "="
+ MieleCloudBindingIntegrationTestConstants.CLIENT_ID + "&"
+ ForwardToLoginServlet.CLIENT_SECRET_PARAMETER_NAME + "="
+ MieleCloudBindingIntegrationTestConstants.CLIENT_SECRET + "&"
+ ForwardToLoginServlet.BRIDGE_ID_PARAMETER_NAME + "="
+ MieleCloudBindingIntegrationTestConstants.BRIDGE_ID);
// then:
assertTrue(maybePairAccountSite.contains(
"Go to <a href=\"https://www.miele.com/f/com/en/register_api.aspx\">the Miele developer portal</a> to obtain your"));
assertTrue(maybePairAccountSite.contains("Missing e-mail address."));
}
@Test
public void whenAnEmptyEmailIsPassedThenTheBrowserIsRedirectedToThePairSiteAndAWarningIsDisplayed()
throws Exception {
// when:
Website maybePairAccountSite = getCrawler()
.doGetRelative("/mielecloud/forwardToLogin?" + ForwardToLoginServlet.CLIENT_ID_PARAMETER_NAME + "="
+ MieleCloudBindingIntegrationTestConstants.CLIENT_ID + "&"
+ ForwardToLoginServlet.CLIENT_SECRET_PARAMETER_NAME + "="
+ MieleCloudBindingIntegrationTestConstants.CLIENT_SECRET + "&"
+ ForwardToLoginServlet.BRIDGE_ID_PARAMETER_NAME + "="
+ MieleCloudBindingIntegrationTestConstants.BRIDGE_ID + "&"
+ ForwardToLoginServlet.EMAIL_PARAMETER_NAME + "=");
// then:
assertTrue(maybePairAccountSite.contains(
"Go to <a href=\"https://www.miele.com/f/com/en/register_api.aspx\">the Miele developer portal</a> to obtain your"));
assertTrue(maybePairAccountSite.contains("Missing e-mail address."));
}
@Test
public void whenAMalformedEmailIsPassedThenTheBrowserIsRedirectedToThePairSiteAndAWarningIsDisplayed()
throws Exception {
// when:
Website maybePairAccountSite = getCrawler()
.doGetRelative("/mielecloud/forwardToLogin?" + ForwardToLoginServlet.CLIENT_ID_PARAMETER_NAME + "="
+ MieleCloudBindingIntegrationTestConstants.CLIENT_ID + "&"
+ ForwardToLoginServlet.CLIENT_SECRET_PARAMETER_NAME + "="
+ MieleCloudBindingIntegrationTestConstants.CLIENT_SECRET + "&"
+ ForwardToLoginServlet.BRIDGE_ID_PARAMETER_NAME + "="
+ MieleCloudBindingIntegrationTestConstants.BRIDGE_ID + "&"
+ ForwardToLoginServlet.EMAIL_PARAMETER_NAME + "=not_an_Email");
// then:
assertTrue(maybePairAccountSite.contains(
"Go to <a href=\"https://www.miele.com/f/com/en/register_api.aspx\">the Miele developer portal</a> to obtain your"));
assertTrue(maybePairAccountSite.contains("Malformed e-mail address"));
}
}

View File

@@ -0,0 +1,57 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.mielecloud.internal.config.servlet;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.Test;
import org.openhab.binding.mielecloud.internal.util.AbstractConfigFlowTest;
import org.openhab.binding.mielecloud.internal.util.MieleCloudBindingIntegrationTestConstants;
import org.openhab.binding.mielecloud.internal.util.Website;
/**
* @author Björn Lange - Initial Contribution
*/
@NonNullByDefault
public class PairAccountServletTest extends AbstractConfigFlowTest {
private static final String CLIENT_ID_INPUT_NAME = "clientId";
private static final String CLIENT_SECRET_INPUT_NAME = "clientSecret";
@Test
public void whenPairAccountIsInvokedWithClientIdParameterThenTheParameterIsPlacedInTheInputBox() throws Exception {
// when:
Website pairAccountSite = getCrawler()
.doGetRelative("/mielecloud/pair?" + PairAccountServlet.CLIENT_ID_PARAMETER_NAME + "="
+ MieleCloudBindingIntegrationTestConstants.CLIENT_ID);
// then:
assertEquals(MieleCloudBindingIntegrationTestConstants.CLIENT_ID,
pairAccountSite.getValueOfInput(CLIENT_ID_INPUT_NAME));
assertEquals("", pairAccountSite.getValueOfInput(CLIENT_SECRET_INPUT_NAME));
}
@Test
public void whenPairAccountIsInvokedWithClientSecretParameterThenTheParameterIsPlacedInTheInputBox()
throws Exception {
// when:
Website pairAccountSite = getCrawler()
.doGetRelative("/mielecloud/pair?" + PairAccountServlet.CLIENT_SECRET_PARAMETER_NAME + "="
+ MieleCloudBindingIntegrationTestConstants.CLIENT_SECRET);
// then:
assertEquals("", pairAccountSite.getValueOfInput(CLIENT_ID_INPUT_NAME));
assertEquals(MieleCloudBindingIntegrationTestConstants.CLIENT_SECRET,
pairAccountSite.getValueOfInput(CLIENT_SECRET_INPUT_NAME));
}
}

View File

@@ -0,0 +1,190 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.mielecloud.internal.config.servlet;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.*;
import static org.openhab.binding.mielecloud.internal.util.ReflectionUtil.setPrivate;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.Test;
import org.openhab.binding.mielecloud.internal.auth.OAuthException;
import org.openhab.binding.mielecloud.internal.config.OAuthAuthorizationHandler;
import org.openhab.binding.mielecloud.internal.config.exception.NoOngoingAuthorizationException;
import org.openhab.binding.mielecloud.internal.util.AbstractConfigFlowTest;
import org.openhab.binding.mielecloud.internal.util.Website;
/**
* @author Björn Lange - Initial Contribution
*/
@NonNullByDefault
public class ResultServletTest extends AbstractConfigFlowTest {
@Test
public void whenOAuthErrorAccessDeniedIsReturnedByMieleServiceThenTheFailurePageWithAccordingErrorMessageIsDisplayed()
throws Exception {
// when:
Website website = getCrawler().doGetRelative("/mielecloud/result?" + ResultServlet.ERROR_PARAMETER_NAME + "="
+ FailureServlet.OAUTH2_ERROR_ACCESS_DENIED);
// then:
assertTrue(website.contains("Pairing failed!"));
assertTrue(website.contains("OAuth2 authentication with Miele cloud service failed: Access denied."));
}
@Test
public void whenOAuthErrorInvalidRequestIsReturnedByMieleServiceThenTheFailurePageWithAccordingErrorMessageIsDisplayed()
throws Exception {
// when:
Website website = getCrawler().doGetRelative("/mielecloud/result?" + ResultServlet.ERROR_PARAMETER_NAME + "="
+ FailureServlet.OAUTH2_ERROR_INVALID_REQUEST);
// then:
assertTrue(website.contains("Pairing failed!"));
assertTrue(website.contains("OAuth2 authentication with Miele cloud service failed: Malformed request."));
}
@Test
public void whenOAuthErrorUnauthorizedClientIsReturnedByMieleServiceThenTheFailurePageWithAccordingErrorMessageIsDisplayed()
throws Exception {
// when:
Website website = getCrawler().doGetRelative("/mielecloud/result?" + ResultServlet.ERROR_PARAMETER_NAME + "="
+ FailureServlet.OAUTH2_ERROR_UNAUTHORIZED_CLIENT);
// then:
assertTrue(website.contains("Pairing failed!"));
assertTrue(website.contains(
"OAuth2 authentication with Miele cloud service failed: Account not authorized to request authorization code."));
}
@Test
public void whenOAuthErrorUnsupportedResponseTypeIsReturnedByMieleServiceThenTheFailurePageWithAccordingErrorMessageIsDisplayed()
throws Exception {
// when:
Website website = getCrawler().doGetRelative("/mielecloud/result?" + ResultServlet.ERROR_PARAMETER_NAME + "="
+ FailureServlet.OAUTH2_ERROR_UNSUPPORTED_RESPONSE_TYPE);
// then:
assertTrue(website.contains("Pairing failed!"));
assertTrue(website.contains(
"OAuth2 authentication with Miele cloud service failed: Obtaining an authorization code is not supported."));
}
@Test
public void whenOAuthErrorInvalidScopeIsReturnedByMieleServiceThenTheFailurePageWithAccordingErrorMessageIsDisplayed()
throws Exception {
// when:
Website website = getCrawler().doGetRelative("/mielecloud/result?" + ResultServlet.ERROR_PARAMETER_NAME + "="
+ FailureServlet.OAUTH2_ERROR_INVALID_SCOPE);
// then:
assertTrue(website.contains("Pairing failed!"));
assertTrue(website.contains("OAuth2 authentication with Miele cloud service failed: Invalid scope."));
}
@Test
public void whenOAuthErrorServerErrorIsReturnedByMieleServiceThenTheFailurePageWithAccordingErrorMessageIsDisplayed()
throws Exception {
// when:
Website website = getCrawler().doGetRelative("/mielecloud/result?" + ResultServlet.ERROR_PARAMETER_NAME + "="
+ FailureServlet.OAUTH2_ERROR_SERVER_ERROR);
// then:
assertTrue(website.contains("Pairing failed!"));
assertTrue(website.contains("OAuth2 authentication with Miele cloud service failed: Unexpected server error."));
}
@Test
public void whenOAuthErrorTemporarilyUnavailableIsReturnedByMieleServiceThenTheFailurePageWithAccordingErrorMessageIsDisplayed()
throws Exception {
// when:
Website website = getCrawler().doGetRelative("/mielecloud/result?" + ResultServlet.ERROR_PARAMETER_NAME + "="
+ FailureServlet.OAUTH2_ERROR_TEMPORARY_UNAVAILABLE);
// then:
assertTrue(website.contains("Pairing failed!"));
assertTrue(website.contains(
"OAuth2 authentication with Miele cloud service failed: Authorization server temporarily unavailable."));
}
@Test
public void whenUnknwonOAuthErrorIsReturnedByMieleServiceThenTheFailurePageWithAccordingErrorMessageIsDisplayed()
throws Exception {
// when:
Website website = getCrawler()
.doGetRelative("/mielecloud/result?" + ResultServlet.ERROR_PARAMETER_NAME + "=unknown_oauth_2_error");
// then:
assertTrue(website.contains("Pairing failed!"));
assertTrue(website.contains(
"OAuth2 authentication with Miele cloud service failed: Unknown error code \"unknown_oauth_2_error\"."));
}
@Test
public void whenCodeParameterIsNotPassedByMieleServiceThenTheFailurePageWithAccordingErrorMessageIsDisplayed()
throws Exception {
// when:
Website website = getCrawler()
.doGetRelative("/mielecloud/result?" + ResultServlet.STATE_PARAMETER_NAME + "=state");
// then:
assertTrue(website.contains("Pairing failed!"));
assertTrue(website.contains("Miele cloud service returned an illegal response."));
}
@Test
public void whenStateParameterIsNotPassedByMieleServiceThenTheFailurePageWithAccordingErrorMessageIsDisplayed()
throws Exception {
// when:
Website website = getCrawler()
.doGetRelative("/mielecloud/result?" + ResultServlet.CODE_PARAMETER_NAME + "=code");
// then:
assertTrue(website.contains("Pairing failed!"));
assertTrue(website.contains("Miele cloud service returned an illegal response."));
}
@Test
public void whenNoAuthorizationIsOngoingThenTheFailurePageWithAccordingErrorMessageIsDisplayed() throws Exception {
// given:
OAuthAuthorizationHandler authorizationHandler = mock(OAuthAuthorizationHandler.class);
doThrow(new NoOngoingAuthorizationException("")).when(authorizationHandler).completeAuthorization(anyString());
setPrivate(getResultServlet(), "authorizationHandler", authorizationHandler);
// when:
Website website = getCrawler().doGetRelative("/mielecloud/result?" + ResultServlet.CODE_PARAMETER_NAME
+ "=code&" + ResultServlet.STATE_PARAMETER_NAME + "=state");
// then:
assertTrue(website.contains("Pairing failed!"));
assertTrue(website.contains("There is no ongoing authorization. Please start an authorization first."));
}
@Test
public void whenLastStepOfAuthorizationFailsThenTheFailurePageWithAccordingErrorMessageIsDisplayed()
throws Exception {
// given:
OAuthAuthorizationHandler authorizationHandler = mock(OAuthAuthorizationHandler.class);
doThrow(new OAuthException("")).when(authorizationHandler).completeAuthorization(anyString());
setPrivate(getResultServlet(), "authorizationHandler", authorizationHandler);
// when:
Website website = getCrawler().doGetRelative("/mielecloud/result?" + ResultServlet.CODE_PARAMETER_NAME
+ "=code&" + ResultServlet.STATE_PARAMETER_NAME + "=state");
// then:
assertTrue(website.contains("Pairing failed!"));
assertTrue(website
.contains("Completing the final authorization request failed. Please try the config flow again."));
}
}

View File

@@ -0,0 +1,146 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.mielecloud.internal.config.servlet;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.*;
import static org.openhab.binding.mielecloud.internal.util.ReflectionUtil.setPrivate;
import java.util.Optional;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.Test;
import org.openhab.binding.mielecloud.internal.util.AbstractConfigFlowTest;
import org.openhab.binding.mielecloud.internal.util.MieleCloudBindingIntegrationTestConstants;
import org.openhab.binding.mielecloud.internal.util.Website;
import org.openhab.binding.mielecloud.internal.webservice.language.LanguageProvider;
/**
* @author Björn Lange - Initial Contribution
*/
@NonNullByDefault
public class SuccessServletTest extends AbstractConfigFlowTest {
@Test
public void whenTheSuccessPageIsShownThenAThingsFileTemplateIsPresent() throws Exception {
// when:
Website website = getCrawler().doGetRelative("/mielecloud/success?" + SuccessServlet.BRIDGE_UID_PARAMETER_NAME
+ "=" + MieleCloudBindingIntegrationTestConstants.BRIDGE_THING_UID.getAsString() + "&"
+ SuccessServlet.EMAIL_PARAMETER_NAME + "=" + MieleCloudBindingIntegrationTestConstants.EMAIL);
// then:
assertTrue(website.contains("Bridge mielecloud:account:genesis [ email=\"openhab@openhab.org\""));
}
@Test
public void whenTheSuccessPageIsShownThenTheLocaleIsSelectedAutomatically() throws Exception {
// given:
LanguageProvider languageProvider = mock(LanguageProvider.class);
when(languageProvider.getLanguage()).thenReturn(Optional.of("de"));
setPrivate(getSuccessServlet(), "languageProvider", languageProvider);
// when:
Website website = getCrawler().doGetRelative("/mielecloud/success?" + SuccessServlet.BRIDGE_UID_PARAMETER_NAME
+ "=" + MieleCloudBindingIntegrationTestConstants.BRIDGE_THING_UID.getAsString() + "&"
+ SuccessServlet.EMAIL_PARAMETER_NAME + "=" + MieleCloudBindingIntegrationTestConstants.EMAIL);
// then:
assertTrue(website.contains("<option value=\"de\" selected=\"selected\">Deutsch - de</option>"));
assertTrue(website.contains("locale=\"de\""));
}
@Test
public void whenTheSuccessPageIsShownAndNoLocaleIsProvidedThenEnglishIsSelectedAutomatically() throws Exception {
// given:
LanguageProvider languageProvider = mock(LanguageProvider.class);
when(languageProvider.getLanguage()).thenReturn(Optional.of("en"));
setPrivate(getSuccessServlet(), "languageProvider", languageProvider);
// when:
Website website = getCrawler().doGetRelative("/mielecloud/success?" + SuccessServlet.BRIDGE_UID_PARAMETER_NAME
+ "=" + MieleCloudBindingIntegrationTestConstants.BRIDGE_THING_UID.getAsString() + "&"
+ SuccessServlet.EMAIL_PARAMETER_NAME + "=" + MieleCloudBindingIntegrationTestConstants.EMAIL);
// then:
assertTrue(website.contains("<option value=\"en\" selected=\"selected\">English - en</option>"));
assertTrue(website.contains("locale=\"en\""));
}
@Test
public void whenTheSuccessPageIsRequestedAndNoBridgeUidIsPassedThenTheFailurePageIsShown() throws Exception {
// when:
Website website = getCrawler().doGetRelative("/mielecloud/success");
// then:
assertTrue(website.contains("Pairing failed!"));
assertTrue(website.contains("Missing bridge UID."));
}
@Test
public void whenTheSuccessPageIsRequestedAndAnEmptyBridgeUidIsPassedThenTheFailurePageIsShown() throws Exception {
// when:
Website website = getCrawler().doGetRelative("/mielecloud/success?" + SuccessServlet.BRIDGE_UID_PARAMETER_NAME
+ "=&" + SuccessServlet.EMAIL_PARAMETER_NAME + "=" + MieleCloudBindingIntegrationTestConstants.EMAIL);
// then:
assertTrue(website.contains("Pairing failed!"));
assertTrue(website.contains("Missing bridge UID."));
}
@Test
public void whenTheSuccessPageIsRequestedAndAMalformedBridgeUidIsPassedThenTheFailurePageIsShown()
throws Exception {
// when:
Website website = getCrawler()
.doGetRelative("/mielecloud/success?" + SuccessServlet.BRIDGE_UID_PARAMETER_NAME + "=!genesis&"
+ SuccessServlet.EMAIL_PARAMETER_NAME + "=" + MieleCloudBindingIntegrationTestConstants.EMAIL);
// then:
assertTrue(website.contains("Pairing failed!"));
assertTrue(website.contains("Malformed bridge UID."));
}
@Test
public void whenTheSuccessPageIsRequestedAndNoEmailIsPassedThenTheFailurePageIsShown() throws Exception {
// when:
Website website = getCrawler().doGetRelative("/mielecloud/success?" + SuccessServlet.BRIDGE_UID_PARAMETER_NAME
+ "=" + MieleCloudBindingIntegrationTestConstants.BRIDGE_THING_UID.getAsString());
// then:
assertTrue(website.contains("Pairing failed!"));
assertTrue(website.contains("Missing e-mail address."));
}
@Test
public void whenTheSuccessPageIsRequestedAndAnEmptyEmailIsPassedThenTheFailurePageIsShown() throws Exception {
// when:
Website website = getCrawler().doGetRelative("/mielecloud/success?" + SuccessServlet.BRIDGE_UID_PARAMETER_NAME
+ "=" + MieleCloudBindingIntegrationTestConstants.BRIDGE_THING_UID.getAsString() + "&"
+ SuccessServlet.EMAIL_PARAMETER_NAME + "=");
// then:
assertTrue(website.contains("Pairing failed!"));
assertTrue(website.contains("Missing e-mail address."));
}
@Test
public void whenTheSuccessPageIsRequestedAndAMalformedEmailIsPassedThenTheFailurePageIsShown() throws Exception {
// when:
Website website = getCrawler().doGetRelative("/mielecloud/success?" + SuccessServlet.BRIDGE_UID_PARAMETER_NAME
+ "=" + MieleCloudBindingIntegrationTestConstants.BRIDGE_THING_UID.getAsString() + "&"
+ SuccessServlet.EMAIL_PARAMETER_NAME + "=not:an!email");
// then:
assertTrue(website.contains("Pairing failed!"));
assertTrue(website.contains("Malformed e-mail address."));
}
}

View File

@@ -0,0 +1,406 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.mielecloud.internal.discovery;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
import static org.openhab.binding.mielecloud.internal.util.MieleCloudBindingIntegrationTestConstants.*;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.openhab.binding.mielecloud.internal.MieleCloudBindingConstants;
import org.openhab.binding.mielecloud.internal.util.MieleCloudBindingIntegrationTestConstants;
import org.openhab.binding.mielecloud.internal.util.OpenHabOsgiTest;
import org.openhab.binding.mielecloud.internal.webservice.api.DeviceState;
import org.openhab.binding.mielecloud.internal.webservice.api.json.Device;
import org.openhab.binding.mielecloud.internal.webservice.api.json.DeviceIdentLabel;
import org.openhab.binding.mielecloud.internal.webservice.api.json.DeviceType;
import org.openhab.binding.mielecloud.internal.webservice.api.json.Ident;
import org.openhab.binding.mielecloud.internal.webservice.api.json.Type;
import org.openhab.core.config.discovery.DiscoveryResult;
import org.openhab.core.config.discovery.DiscoveryService;
import org.openhab.core.config.discovery.inbox.InboxPredicates;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.ThingUID;
/**
* @author Björn Lange - Initial contribution
*/
@NonNullByDefault
public class ThingDiscoveryTest extends OpenHabOsgiTest {
private static final String DEVICE_TYPE_NAME_COFFEE_SYSTEM = "Coffee System";
private static final String DEVICE_TYPE_NAME_DISHWASHER = "Dishwasher";
private static final String DEVICE_TYPE_NAME_DISH_WARMER = "Dish Warmer";
private static final String DEVICE_TYPE_NAME_DRYER = "Dryer";
private static final String DEVICE_TYPE_NAME_FRIDGE_FREEZER = "Fridge Freezer";
private static final String DEVICE_TYPE_NAME_HOB = "Hob";
private static final String DEVICE_TYPE_NAME_HOOD = "Hood";
private static final String DEVICE_TYPE_NAME_OVEN = "Oven";
private static final String DEVICE_TYPE_NAME_ROBOTIC_VACUUM_CLEANER = "Robotic Vacuum Cleaner";
private static final String DEVICE_TYPE_NAME_WASHING_MACHINE = "Washing Machine";
private static final String DEVICE_TYPE_NAME_WINE_STORAGE = "Wine Storage";
private static final String TECH_TYPE = "WM1234";
private static final String TECH_TYPE_2 = "CM1234";
private static final String DEVICE_NAME = "My Device";
private static final String DEVICE_NAME_2 = "My Other Device";
private static final String SERIAL_NUMBER_2 = "900124430017";
private static final ThingUID DISHWASHER_DEVICE_THING_UID_WITH_SERIAL_NUMBER_2 = new ThingUID(
new ThingTypeUID(MieleCloudBindingConstants.BINDING_ID, "dishwasher"), BRIDGE_THING_UID, SERIAL_NUMBER_2);
@Nullable
private ThingDiscoveryService discoveryService;
private ThingDiscoveryService getDiscoveryService() {
assertNotNull(discoveryService);
return Objects.requireNonNull(discoveryService);
}
@BeforeEach
public void setUp()
throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
setUpBridge();
setUpDiscoveryService();
}
private void setUpDiscoveryService()
throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
waitForAssert(() -> {
discoveryService = getService(DiscoveryService.class, ThingDiscoveryService.class);
assertNotNull(discoveryService);
});
getDiscoveryService().activate();
}
private DeviceState createDeviceState(String fabNumber, String techType, String deviceName, DeviceType deviceType,
String deviceTypeText) {
// given:
DeviceIdentLabel deviceIdentLabel = mock(DeviceIdentLabel.class);
when(deviceIdentLabel.getFabNumber()).thenReturn(Optional.of(fabNumber));
when(deviceIdentLabel.getTechType()).thenReturn(Optional.of(techType));
Type type = mock(Type.class);
when(type.getValueRaw()).thenReturn(deviceType);
when(type.getValueLocalized()).thenReturn(Optional.of(deviceTypeText));
Ident ident = mock(Ident.class);
when(ident.getDeviceIdentLabel()).thenReturn(Optional.of(deviceIdentLabel));
when(ident.getType()).thenReturn(Optional.of(type));
when(ident.getDeviceName()).thenReturn(Optional.of(deviceName));
Device device = mock(Device.class);
when(device.getIdent()).thenReturn(Optional.of(ident));
return new DeviceState(fabNumber, device);
}
private void assertValidDiscoveryResult(ThingUID expectedThingUID, String expectedSerialNumber,
String expectedDeviceIdentifier, String expectedLabel, String expectedModelId) {
List<DiscoveryResult> results = getInbox().stream().filter(InboxPredicates.forThingUID(expectedThingUID))
.collect(Collectors.toList());
assertEquals(1, results.size(), "Amount of things in inbox does not match expected number");
DiscoveryResult result = results.get(0);
assertEquals(MieleCloudBindingConstants.BINDING_ID, result.getBindingId(), "Invalid binding ID");
assertEquals(MieleCloudBindingIntegrationTestConstants.BRIDGE_THING_UID, result.getBridgeUID(),
"Invalid bridge UID");
assertEquals(Thing.PROPERTY_SERIAL_NUMBER, result.getRepresentationProperty(),
"Invalid representation property");
assertEquals(expectedModelId, result.getProperties().get(Thing.PROPERTY_MODEL_ID), "Invalid model ID");
assertEquals(expectedLabel, result.getLabel(), "Invalid label");
assertEquals(expectedSerialNumber, result.getProperties().get(Thing.PROPERTY_SERIAL_NUMBER),
"Invalid serial number");
assertEquals(expectedDeviceIdentifier,
result.getProperties().get(MieleCloudBindingConstants.CONFIG_PARAM_DEVICE_IDENTIFIER),
"Invalid serial number");
}
private void testMieleDeviceInboxDiscoveryResult(DeviceType deviceType, ThingUID expectedThingUid,
String deviceTypeName) {
// given:
DeviceState deviceState = createDeviceState(SERIAL_NUMBER, TECH_TYPE, DEVICE_NAME, deviceType, deviceTypeName);
// when:
getDiscoveryService().onDeviceStateUpdated(deviceState);
// then:
assertValidDiscoveryResult(expectedThingUid, SERIAL_NUMBER, SERIAL_NUMBER, DEVICE_NAME,
deviceTypeName + " " + TECH_TYPE);
}
@Test
public void testWashingDeviceInboxDiscoveryResult() {
testMieleDeviceInboxDiscoveryResult(DeviceType.WASHING_MACHINE, WASHING_MACHINE_THING_UID,
DEVICE_TYPE_NAME_WASHING_MACHINE);
}
@Test
public void testOvenInboxDiscoveryResult() {
testMieleDeviceInboxDiscoveryResult(DeviceType.OVEN, OVEN_DEVICE_THING_UID, DEVICE_TYPE_NAME_OVEN);
}
@Test
public void testHobInboxDiscoveryResult() {
testMieleDeviceInboxDiscoveryResult(DeviceType.HOB_HIGHLIGHT, HOB_DEVICE_THING_UID, DEVICE_TYPE_NAME_HOB);
}
@Test
public void testCoolingDeviceInboxDiscoveryResult() {
testMieleDeviceInboxDiscoveryResult(DeviceType.FRIDGE_FREEZER_COMBINATION, FRIDGE_FREEZER_DEVICE_THING_UID,
DEVICE_TYPE_NAME_FRIDGE_FREEZER);
}
@Test
public void testHoodInboxDiscoveryResult() {
testMieleDeviceInboxDiscoveryResult(DeviceType.HOOD, HOOD_DEVICE_THING_UID, DEVICE_TYPE_NAME_HOOD);
}
@Test
public void testCoffeeDeviceInboxDiscoveryResult() {
testMieleDeviceInboxDiscoveryResult(DeviceType.COFFEE_SYSTEM, COFFEE_SYSTEM_THING_UID,
DEVICE_TYPE_NAME_COFFEE_SYSTEM);
}
@Test
public void testWineStorageDeviceInboxDiscoveryResult() {
testMieleDeviceInboxDiscoveryResult(DeviceType.WINE_CABINET, WINE_STORAGE_DEVICE_THING_UID,
DEVICE_TYPE_NAME_WINE_STORAGE);
}
@Test
public void testDryerInboxDiscoveryResult() {
testMieleDeviceInboxDiscoveryResult(DeviceType.TUMBLE_DRYER, DRYER_DEVICE_THING_UID, DEVICE_TYPE_NAME_DRYER);
}
@Test
public void testDishwasherInboxDiscoveryResult() {
testMieleDeviceInboxDiscoveryResult(DeviceType.DISHWASHER, DISHWASHER_DEVICE_THING_UID,
DEVICE_TYPE_NAME_DISHWASHER);
}
@Test
public void testDishWarmerInboxDiscoveryResult() {
testMieleDeviceInboxDiscoveryResult(DeviceType.DISH_WARMER, DISH_WARMER_DEVICE_THING_UID,
DEVICE_TYPE_NAME_DISH_WARMER);
}
@Test
public void testRoboticVacuumCleanerInboxDiscoveryResult() {
testMieleDeviceInboxDiscoveryResult(DeviceType.VACUUM_CLEANER, ROBOTIC_VACUUM_CLEANER_THING_UID,
DEVICE_TYPE_NAME_ROBOTIC_VACUUM_CLEANER);
}
@Test
public void testUnknownDeviceCreatesNoInboxDiscoveryResult() {
// given:
DeviceState deviceState = createDeviceState(SERIAL_NUMBER, TECH_TYPE, DEVICE_NAME, DeviceType.VACUUM_DRAWER,
"Vacuum Drawer");
// when:
getDiscoveryService().onDeviceStateUpdated(deviceState);
// then:
waitForAssert(() -> {
List<DiscoveryResult> results = getInbox().stream().collect(Collectors.toList());
assertEquals(0, results.size(), "Amount of things in inbox does not match expected number");
});
}
@Test
public void testDeviceDiscoveryResultOfDeviceRemovedInTheCloudIsRemovedFromTheInbox() throws InterruptedException {
// given:
testMieleDeviceInboxDiscoveryResult(DeviceType.HOOD, HOOD_DEVICE_THING_UID, DEVICE_TYPE_NAME_HOOD);
Thread.sleep(10);
// when:
getDiscoveryService().onDeviceRemoved(SERIAL_NUMBER);
// then:
waitForAssert(() -> {
List<DiscoveryResult> results = getInbox().stream().collect(Collectors.toList());
assertEquals(0, results.size(), "Amount of things in inbox does not match expected number");
});
}
@Test
public void testDiscoveryResultsForTwoDevices() {
// given:
DeviceState hoodDevice = createDeviceState(SERIAL_NUMBER, TECH_TYPE, DEVICE_NAME, DeviceType.HOOD,
DEVICE_TYPE_NAME_HOOD);
DeviceState dishwasherDevice = createDeviceState(SERIAL_NUMBER_2, TECH_TYPE_2, DEVICE_NAME_2,
DeviceType.DISHWASHER, DEVICE_TYPE_NAME_DISHWASHER);
// when:
getDiscoveryService().onDeviceStateUpdated(hoodDevice);
getDiscoveryService().onDeviceStateUpdated(dishwasherDevice);
// then:
waitForAssert(() -> {
List<DiscoveryResult> results = getInbox().stream().collect(Collectors.toList());
assertEquals(2, results.size(), "Amount of things in inbox does not match expected number");
assertValidDiscoveryResult(HOOD_DEVICE_THING_UID, SERIAL_NUMBER, SERIAL_NUMBER, DEVICE_NAME,
"Hood " + TECH_TYPE);
assertValidDiscoveryResult(DISHWASHER_DEVICE_THING_UID_WITH_SERIAL_NUMBER_2, SERIAL_NUMBER_2,
SERIAL_NUMBER_2, DEVICE_NAME_2, DEVICE_TYPE_NAME_DISHWASHER + " " + TECH_TYPE_2);
});
}
@Test
public void testOnlyDeviceDiscoveryResultsOfDevicesRemovedInTheCloudAreRemovedFromTheInbox()
throws InterruptedException {
// given:
DeviceState hoodDevice = createDeviceState(SERIAL_NUMBER, TECH_TYPE, DEVICE_NAME, DeviceType.HOOD,
DEVICE_TYPE_NAME_HOOD);
DeviceState dishwasherDevice = createDeviceState(SERIAL_NUMBER_2, TECH_TYPE_2, DEVICE_NAME_2,
DeviceType.DISHWASHER, DEVICE_TYPE_NAME_DISHWASHER);
getDiscoveryService().onDeviceStateUpdated(hoodDevice);
getDiscoveryService().onDeviceStateUpdated(dishwasherDevice);
Thread.sleep(10);
// when:
// This order of invocation is enforced by the webservice implementation.
getDiscoveryService().onDeviceRemoved(SERIAL_NUMBER_2);
getDiscoveryService().onDeviceStateUpdated(hoodDevice);
// then:
waitForAssert(() -> {
List<DiscoveryResult> results = getInbox().stream().collect(Collectors.toList());
assertEquals(1, results.size(), "Amount of things in inbox does not match expected number");
assertValidDiscoveryResult(HOOD_DEVICE_THING_UID, SERIAL_NUMBER, SERIAL_NUMBER, DEVICE_NAME,
DEVICE_TYPE_NAME_HOOD + " " + TECH_TYPE);
});
}
@Test
public void testIfNoDeviceNameIsSetThenTheDiscoveryLabelIsTheDeviceTypePlusTheTechType() {
// given:
DeviceState deviceState = createDeviceState(SERIAL_NUMBER, TECH_TYPE, "", DeviceType.FRIDGE_FREEZER_COMBINATION,
DEVICE_TYPE_NAME_FRIDGE_FREEZER);
// when:
getDiscoveryService().onDeviceStateUpdated(deviceState);
// then:
waitForAssert(() -> {
List<DiscoveryResult> results = getInbox().stream().collect(Collectors.toList());
assertEquals(1, results.size(), "Amount of things in inbox does not match expected number");
assertValidDiscoveryResult(FRIDGE_FREEZER_DEVICE_THING_UID, SERIAL_NUMBER, SERIAL_NUMBER,
"Fridge Freezer " + TECH_TYPE, DEVICE_TYPE_NAME_FRIDGE_FREEZER + " " + TECH_TYPE);
});
}
@Test
public void testIfNeitherDeviceTypeNorDeviceNameAreSetThenTheDiscoveryModelIdAndTheLabelAreTheTechType() {
// given:
DeviceState deviceState = createDeviceState(SERIAL_NUMBER, TECH_TYPE, "", DeviceType.FRIDGE_FREEZER_COMBINATION,
"");
// when:
getDiscoveryService().onDeviceStateUpdated(deviceState);
// then:
waitForAssert(() -> {
List<DiscoveryResult> results = getInbox().stream().collect(Collectors.toList());
assertEquals(1, results.size(), "Amount of things in inbox does not match expected number");
assertValidDiscoveryResult(FRIDGE_FREEZER_DEVICE_THING_UID, SERIAL_NUMBER, SERIAL_NUMBER, TECH_TYPE,
TECH_TYPE);
});
}
@Test
public void testIfNeitherTechTypeNorDeviceNameAreSetThenTheDiscoveryModelIdAndTheLabelAreTheDeviceType() {
// given:
DeviceState deviceState = createDeviceState(SERIAL_NUMBER, "", "", DeviceType.FRIDGE_FREEZER_COMBINATION,
DEVICE_TYPE_NAME_FRIDGE_FREEZER);
// when:
getDiscoveryService().onDeviceStateUpdated(deviceState);
// then:
waitForAssert(() -> {
List<DiscoveryResult> results = getInbox().stream().collect(Collectors.toList());
assertEquals(1, results.size(), "Amount of things in inbox does not match expected number");
assertValidDiscoveryResult(FRIDGE_FREEZER_DEVICE_THING_UID, SERIAL_NUMBER, SERIAL_NUMBER,
DEVICE_TYPE_NAME_FRIDGE_FREEZER, DEVICE_TYPE_NAME_FRIDGE_FREEZER);
});
}
@Test
public void testIfNeitherTechTypeNorDeviceTypeNorDeviceNameAreSetThenTheDiscoveryModelIdIsUnknownAndTheLabelIsMieleDevice() {
// given:
DeviceState deviceState = createDeviceState(SERIAL_NUMBER, "", "", DeviceType.FRIDGE_FREEZER_COMBINATION, "");
// when:
getDiscoveryService().onDeviceStateUpdated(deviceState);
// then:
waitForAssert(() -> {
List<DiscoveryResult> results = getInbox().stream().collect(Collectors.toList());
assertEquals(1, results.size(), "Amount of things in inbox does not match expected number");
assertValidDiscoveryResult(FRIDGE_FREEZER_DEVICE_THING_UID, SERIAL_NUMBER, SERIAL_NUMBER, "Miele Device",
"Unknown");
});
}
@Test
public void testIfNoSerialNumberIsSetThenTheDeviceIdentifierIsUsedAsReplacement() {
// given:
DeviceIdentLabel deviceIdentLabel = mock(DeviceIdentLabel.class);
when(deviceIdentLabel.getFabNumber()).thenReturn(Optional.of(""));
when(deviceIdentLabel.getTechType()).thenReturn(Optional.of(TECH_TYPE));
Type type = mock(Type.class);
when(type.getValueRaw()).thenReturn(DeviceType.FRIDGE_FREEZER_COMBINATION);
when(type.getValueLocalized()).thenReturn(Optional.of(DEVICE_TYPE_NAME_FRIDGE_FREEZER));
Ident ident = mock(Ident.class);
when(ident.getDeviceIdentLabel()).thenReturn(Optional.of(deviceIdentLabel));
when(ident.getType()).thenReturn(Optional.of(type));
when(ident.getDeviceName()).thenReturn(Optional.of(""));
Device device = mock(Device.class);
when(device.getIdent()).thenReturn(Optional.of(ident));
DeviceState deviceState = new DeviceState(SERIAL_NUMBER, device);
// when:
getDiscoveryService().onDeviceStateUpdated(deviceState);
// then:
waitForAssert(() -> {
List<DiscoveryResult> results = getInbox().stream().collect(Collectors.toList());
assertEquals(1, results.size(), "Amount of things in inbox does not match expected number");
assertValidDiscoveryResult(FRIDGE_FREEZER_DEVICE_THING_UID, SERIAL_NUMBER, SERIAL_NUMBER,
DEVICE_TYPE_NAME_FRIDGE_FREEZER + " " + TECH_TYPE,
DEVICE_TYPE_NAME_FRIDGE_FREEZER + " " + TECH_TYPE);
});
}
}

View File

@@ -0,0 +1,578 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.mielecloud.internal.handler;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import static org.openhab.binding.mielecloud.internal.MieleCloudBindingConstants.Channels.*;
import static org.openhab.binding.mielecloud.internal.util.MieleCloudBindingIntegrationTestConstants.*;
import static org.openhab.binding.mielecloud.internal.util.ReflectionUtil.setPrivate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.openhab.binding.mielecloud.internal.MieleCloudBindingConstants;
import org.openhab.binding.mielecloud.internal.auth.OAuthTokenRefresher;
import org.openhab.binding.mielecloud.internal.auth.OpenHabOAuthTokenRefresher;
import org.openhab.binding.mielecloud.internal.util.MieleCloudBindingIntegrationTestConstants;
import org.openhab.binding.mielecloud.internal.webservice.MieleWebservice;
import org.openhab.binding.mielecloud.internal.webservice.MieleWebserviceFactory;
import org.openhab.binding.mielecloud.internal.webservice.api.DeviceState;
import org.openhab.binding.mielecloud.internal.webservice.api.ProgramStatus;
import org.openhab.binding.mielecloud.internal.webservice.api.json.DeviceType;
import org.openhab.binding.mielecloud.internal.webservice.api.json.ProcessAction;
import org.openhab.binding.mielecloud.internal.webservice.api.json.StateType;
import org.openhab.binding.mielecloud.internal.webservice.exception.MieleWebserviceException;
import org.openhab.core.auth.client.oauth2.AccessTokenResponse;
import org.openhab.core.auth.client.oauth2.OAuthClientService;
import org.openhab.core.auth.client.oauth2.OAuthFactory;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.items.Item;
import org.openhab.core.items.ItemBuilder;
import org.openhab.core.items.ItemBuilderFactory;
import org.openhab.core.items.ItemRegistry;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.test.java.JavaOSGiTest;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Channel;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingRegistry;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.ThingUID;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerFactory;
import org.openhab.core.thing.binding.builder.BridgeBuilder;
import org.openhab.core.thing.binding.builder.ChannelBuilder;
import org.openhab.core.thing.binding.builder.ThingBuilder;
import org.openhab.core.thing.link.ItemChannelLink;
import org.openhab.core.thing.link.ItemChannelLinkRegistry;
import org.openhab.core.thing.type.ChannelDefinition;
import org.openhab.core.thing.type.ChannelType;
import org.openhab.core.thing.type.ChannelTypeRegistry;
import org.openhab.core.thing.type.ChannelTypeUID;
import org.openhab.core.thing.type.ThingType;
import org.openhab.core.thing.type.ThingTypeRegistry;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
/**
* @author Björn Lange - Initial contribution
*/
@NonNullByDefault
public abstract class AbstractMieleThingHandlerTest extends JavaOSGiTest {
protected static final State NULL_VALUE_STATE = UnDefType.UNDEF;
@Nullable
private Bridge bridge;
@Nullable
private MieleBridgeHandler bridgeHandler;
@Nullable
private ThingRegistry thingRegistry;
@Nullable
private MieleWebservice webserviceMock;
@Nullable
private AbstractMieleThingHandler thingHandler;
@Nullable
private ItemRegistry itemRegistry;
protected Bridge getBridge() {
assertNotNull(bridge);
return Objects.requireNonNull(bridge);
}
protected MieleBridgeHandler getBridgeHandler() {
assertNotNull(bridgeHandler);
return Objects.requireNonNull(bridgeHandler);
}
protected ThingRegistry getThingRegistry() {
assertNotNull(thingRegistry);
return Objects.requireNonNull(thingRegistry);
}
protected MieleWebservice getWebserviceMock() {
assertNotNull(webserviceMock);
return Objects.requireNonNull(webserviceMock);
}
protected AbstractMieleThingHandler getThingHandler() {
assertNotNull(thingHandler);
return Objects.requireNonNull(thingHandler);
}
protected ItemRegistry getItemRegistry() {
assertNotNull(itemRegistry);
return Objects.requireNonNull(itemRegistry);
}
private void setUpThingRegistry() {
thingRegistry = getService(ThingRegistry.class, ThingRegistry.class);
assertNotNull(thingRegistry, "Thing registry is missing");
}
private void setUpItemRegistry() {
itemRegistry = getService(ItemRegistry.class, ItemRegistry.class);
assertNotNull(itemRegistry);
}
private void setUpWebservice()
throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
webserviceMock = mock(MieleWebservice.class);
when(getWebserviceMock().hasAccessToken()).thenReturn(true);
MieleWebserviceFactory webserviceFactory = mock(MieleWebserviceFactory.class);
when(webserviceFactory.create(any())).thenReturn(getWebserviceMock());
MieleHandlerFactory factory = getService(ThingHandlerFactory.class, MieleHandlerFactory.class);
assertNotNull(factory);
setPrivate(Objects.requireNonNull(factory), "webserviceFactory", webserviceFactory);
}
private void setUpBridge() throws Exception {
AccessTokenResponse accessTokenResponse = new AccessTokenResponse();
accessTokenResponse.setAccessToken(ACCESS_TOKEN);
OAuthClientService oAuthClientService = mock(OAuthClientService.class);
when(oAuthClientService.getAccessTokenResponse()).thenReturn(accessTokenResponse);
OAuthFactory oAuthFactory = mock(OAuthFactory.class);
when(oAuthFactory
.getOAuthClientService(MieleCloudBindingIntegrationTestConstants.BRIDGE_THING_UID.getAsString()))
.thenReturn(oAuthClientService);
OpenHabOAuthTokenRefresher tokenRefresher = getService(OAuthTokenRefresher.class,
OpenHabOAuthTokenRefresher.class);
assertNotNull(tokenRefresher);
setPrivate(Objects.requireNonNull(tokenRefresher), "oauthFactory", oAuthFactory);
bridge = BridgeBuilder
.create(MieleCloudBindingConstants.THING_TYPE_BRIDGE,
MieleCloudBindingIntegrationTestConstants.BRIDGE_THING_UID)
.withLabel("Miele@home Account")
.withConfiguration(
new Configuration(Collections.singletonMap(MieleCloudBindingConstants.CONFIG_PARAM_EMAIL,
MieleCloudBindingIntegrationTestConstants.EMAIL)))
.build();
assertNotNull(bridge);
getThingRegistry().add(getBridge());
// then:
waitForAssert(() -> {
assertNotNull(getBridge().getHandler());
assertTrue(getBridge().getHandler() instanceof MieleBridgeHandler, "Handler type is wrong");
});
MieleBridgeHandler bridgeHandler = (MieleBridgeHandler) getBridge().getHandler();
assertNotNull(bridgeHandler);
waitForAssert(() -> {
assertNotNull(bridgeHandler.getThing());
});
bridgeHandler.initialize();
bridgeHandler.onConnectionAlive();
setPrivate(bridgeHandler, "discoveryService", null);
this.bridgeHandler = bridgeHandler;
}
protected AbstractMieleThingHandler createThingHandler(ThingTypeUID thingTypeUid, ThingUID thingUid,
Class<? extends AbstractMieleThingHandler> expectedHandlerClass, String deviceIdentifier) {
ThingRegistry registry = getThingRegistry();
List<Channel> channels = createChannelsForThingHandler(thingTypeUid, thingUid);
Thing thing = ThingBuilder.create(thingTypeUid, thingUid)
.withConfiguration(new Configuration(Collections
.singletonMap(MieleCloudBindingConstants.CONFIG_PARAM_DEVICE_IDENTIFIER, deviceIdentifier)))
.withBridge(getBridge().getUID()).withChannels(channels).withLabel("DA-6996").build();
assertNotNull(thing);
registry.add(thing);
waitForAssert(() -> {
ThingHandler handler = thing.getHandler();
assertNotNull(handler);
assertTrue(expectedHandlerClass.isAssignableFrom(handler.getClass()), "Handler type is wrong");
});
createItemsForChannels(thing);
linkChannelsToItems(thing);
ThingHandler handler = thing.getHandler();
assertNotNull(handler);
return (AbstractMieleThingHandler) Objects.requireNonNull(handler);
}
private List<Channel> createChannelsForThingHandler(ThingTypeUID thingTypeUid, ThingUID thingUid) {
ChannelTypeRegistry channelTypeRegistry = getService(ChannelTypeRegistry.class, ChannelTypeRegistry.class);
assertNotNull(channelTypeRegistry);
ThingTypeRegistry thingTypeRegistry = getService(ThingTypeRegistry.class, ThingTypeRegistry.class);
assertNotNull(thingTypeRegistry);
ThingType thingType = thingTypeRegistry.getThingType(thingTypeUid);
assertNotNull(thingType);
List<ChannelDefinition> channelDefinitions = thingType.getChannelDefinitions();
assertNotNull(channelDefinitions);
List<Channel> channels = new ArrayList<Channel>();
for (ChannelDefinition channelDefinition : channelDefinitions) {
ChannelTypeUID channelTypeUid = channelDefinition.getChannelTypeUID();
assertNotNull(channelTypeUid);
ChannelType channelType = channelTypeRegistry.getChannelType(channelTypeUid);
assertNotNull(channelType);
String acceptedItemType = channelType.getItemType();
assertNotNull(acceptedItemType);
String channelId = channelDefinition.getId();
assertNotNull(channelId);
ChannelUID channelUid = new ChannelUID(thingUid, channelId);
assertNotNull(channelUid);
Channel channel = ChannelBuilder.create(channelUid, acceptedItemType).build();
assertNotNull(channel);
channels.add(channel);
}
return channels;
}
private void createItemsForChannels(Thing thing) {
ItemBuilderFactory itemBuilderFactory = getService(ItemBuilderFactory.class);
assertNotNull(itemBuilderFactory);
for (Channel channel : thing.getChannels()) {
String acceptedItemType = channel.getAcceptedItemType();
assertNotNull(acceptedItemType);
ItemBuilder itemBuilder = itemBuilderFactory.newItemBuilder(Objects.requireNonNull(acceptedItemType),
channel.getUID().getId());
assertNotNull(itemBuilder);
Item item = itemBuilder.build();
assertNotNull(item);
getItemRegistry().add(item);
}
}
private void linkChannelsToItems(Thing thing) {
ItemChannelLinkRegistry itemChannelLinkRegistry = getService(ItemChannelLinkRegistry.class,
ItemChannelLinkRegistry.class);
assertNotNull(itemChannelLinkRegistry);
for (Channel channel : thing.getChannels()) {
String itemName = channel.getUID().getId();
assertNotNull(itemName);
ChannelUID channelUid = channel.getUID();
assertNotNull(channelUid);
ItemChannelLink link = itemChannelLinkRegistry.add(new ItemChannelLink(itemName, channelUid));
assertNotNull(link);
}
}
protected ChannelUID channel(String id) {
return new ChannelUID(getThingHandler().getThing().getUID(), id);
}
@BeforeEach
public void setUpAbstractMieleThingHandlerTest() throws Exception {
registerVolatileStorageService();
setUpThingRegistry();
setUpItemRegistry();
setUpWebservice();
setUpBridge();
thingHandler = setUpThingHandler();
}
private void assertThingStatusIs(Thing thing, ThingStatus expectedStatus, ThingStatusDetail expectedStatusDetail) {
assertThingStatusIs(thing, expectedStatus, expectedStatusDetail, null);
}
private void assertThingStatusIs(Thing thing, ThingStatus expectedStatus, ThingStatusDetail expectedStatusDetail,
@Nullable String expectedDescription) {
assertEquals(expectedStatus, thing.getStatus());
assertEquals(expectedStatusDetail, thing.getStatusInfo().getStatusDetail());
if (expectedDescription == null) {
assertNull(thing.getStatusInfo().getDescription());
} else {
assertEquals(expectedDescription, thing.getStatusInfo().getDescription());
}
}
protected State getChannelState(String channelUid) {
Item item = getItemRegistry().get(channelUid);
assertNotNull(item, "Item for channel UID " + channelUid + " is null.");
return item.getState();
}
/**
* Sets up the {@link ThingHandler} under test.
*
* @return The created {@link ThingHandler}.
*/
protected abstract AbstractMieleThingHandler setUpThingHandler();
@Test
public void testCachedStateIsQueriedOnInitialize() throws Exception {
// then:
verify(getWebserviceMock()).dispatchDeviceState(SERIAL_NUMBER);
}
@Test
public void testThingStatusIsOfflineWithDetailGoneAndDetailMessageWhenDeviceIsRemoved() {
// when:
getBridgeHandler().onDeviceRemoved(SERIAL_NUMBER);
// then:
Thing thing = getThingHandler().getThing();
assertThingStatusIs(thing, ThingStatus.OFFLINE, ThingStatusDetail.GONE,
"@text/mielecloud.thing.status.removed");
}
private DeviceState createDeviceStateMock(StateType stateType, String localizedState) {
DeviceState deviceState = mock(DeviceState.class);
when(deviceState.getDeviceIdentifier()).thenReturn(getThingHandler().getThing().getUID().getId());
when(deviceState.getRawType()).thenReturn(DeviceType.UNKNOWN);
when(deviceState.getStateType()).thenReturn(Optional.of(stateType));
when(deviceState.isInState(any())).thenCallRealMethod();
when(deviceState.getStatus()).thenReturn(Optional.of(localizedState));
return deviceState;
}
@Test
public void testStatusIsSetToOnlineWhenDeviceStateIsValid() {
// given:
DeviceState deviceState = createDeviceStateMock(StateType.ON, "On");
// when:
getBridgeHandler().onDeviceStateUpdated(deviceState);
// then:
assertThingStatusIs(getThingHandler().getThing(), ThingStatus.ONLINE, ThingStatusDetail.NONE);
}
@Test
public void testStatusIsSetToOfflineWhenDeviceIsNotConnected() {
// given:
DeviceState deviceState = createDeviceStateMock(StateType.NOT_CONNECTED, "Not connected");
// when:
getBridgeHandler().onDeviceStateUpdated(deviceState);
// then:
assertThingStatusIs(getThingHandler().getThing(), ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"@text/mielecloud.thing.status.disconnected");
}
@Test
public void testFailingPutProcessActionDoesNotSetTheDeviceToOffline() {
// given:
DeviceState deviceState = createDeviceStateMock(StateType.ON, "On");
getBridgeHandler().onDeviceStateUpdated(deviceState);
assertThingStatusIs(getThingHandler().getThing(), ThingStatus.ONLINE, ThingStatusDetail.NONE);
doThrow(MieleWebserviceException.class).when(getWebserviceMock()).putProcessAction(any(),
eq(ProcessAction.STOP));
// when:
getThingHandler().triggerProcessAction(ProcessAction.STOP);
// then:
assertThingStatusIs(getThingHandler().getThing(), ThingStatus.ONLINE, ThingStatusDetail.NONE);
}
@Test
public void testHandleCommandProgramStartToStartStopChannel() {
// when:
getThingHandler().handleCommand(channel(PROGRAM_START_STOP),
new StringType(ProgramStatus.PROGRAM_STARTED.getState()));
// then:
waitForAssert(() -> {
verify(getWebserviceMock()).putProcessAction(getThingHandler().getDeviceId(), ProcessAction.START);
});
}
@Test
public void testHandleCommandProgramStopToStartStopChannel() {
// when:
getThingHandler().handleCommand(channel(PROGRAM_START_STOP),
new StringType(ProgramStatus.PROGRAM_STOPPED.getState()));
// then:
waitForAssert(() -> {
verify(getWebserviceMock()).putProcessAction(getThingHandler().getDeviceId(), ProcessAction.STOP);
});
}
@Test
public void testHandleCommandProgramStartToStartStopPauseChannel() {
// when:
getThingHandler().handleCommand(channel(PROGRAM_START_STOP_PAUSE),
new StringType(ProgramStatus.PROGRAM_STARTED.getState()));
// then:
waitForAssert(() -> {
verify(getWebserviceMock()).putProcessAction(getThingHandler().getDeviceId(), ProcessAction.START);
});
}
@Test
public void testHandleCommandProgramStopToStartStopPauseChannel() {
// when:
getThingHandler().handleCommand(channel(PROGRAM_START_STOP_PAUSE),
new StringType(ProgramStatus.PROGRAM_STOPPED.getState()));
// then:
waitForAssert(() -> {
verify(getWebserviceMock()).putProcessAction(getThingHandler().getDeviceId(), ProcessAction.STOP);
});
}
@Test
public void testHandleCommandProgramPauseToStartStopPauseChannel() {
// when:
getThingHandler().handleCommand(channel(PROGRAM_START_STOP_PAUSE),
new StringType(ProgramStatus.PROGRAM_PAUSED.getState()));
// then:
waitForAssert(() -> {
verify(getWebserviceMock()).putProcessAction(getThingHandler().getDeviceId(), ProcessAction.PAUSE);
});
}
@Test
public void testFailingPutLightDoesNotSetTheDeviceToOffline() {
// given:
DeviceState deviceState = createDeviceStateMock(StateType.ON, "On");
getBridgeHandler().onDeviceStateUpdated(deviceState);
assertThingStatusIs(getThingHandler().getThing(), ThingStatus.ONLINE, ThingStatusDetail.NONE);
doThrow(MieleWebserviceException.class).when(getWebserviceMock()).putLight(any(), eq(true));
// when:
getThingHandler().triggerLight(true);
// then:
assertThingStatusIs(getThingHandler().getThing(), ThingStatus.ONLINE, ThingStatusDetail.NONE);
}
@Test
public void testHandleCommandLightOff() {
// when:
getThingHandler().handleCommand(channel(LIGHT_SWITCH), OnOffType.OFF);
// then:
waitForAssert(() -> {
verify(getWebserviceMock()).putLight(getThingHandler().getDeviceId(), false);
});
}
@Test
public void testHandleCommandLightOn() {
// when:
getThingHandler().handleCommand(channel(LIGHT_SWITCH), OnOffType.ON);
// then:
waitForAssert(() -> {
verify(getWebserviceMock()).putLight(getThingHandler().getDeviceId(), true);
});
}
@Test
public void testHandleCommandDoesNothingWhenCommandIsNotOfOnOffType() {
// when:
getThingHandler().handleCommand(channel(LIGHT_SWITCH), new DecimalType(0));
// then:
verify(getWebserviceMock(), never()).putLight(anyString(), anyBoolean());
}
@Test
public void testHandleCommandPowerOn() {
// when:
getThingHandler().handleCommand(channel(POWER_ON_OFF), OnOffType.ON);
// then:
waitForAssert(() -> {
verify(getWebserviceMock()).putPowerState(getThingHandler().getDeviceId(), true);
});
}
@Test
public void testHandleCommandPowerOff() {
// when:
getThingHandler().handleCommand(channel(POWER_ON_OFF), OnOffType.OFF);
// then:
waitForAssert(() -> {
verify(getWebserviceMock()).putPowerState(getThingHandler().getDeviceId(), false);
});
}
@Test
public void testHandleCommandDoesNothingWhenPowerCommandIsNotOfOnOffType() {
// when:
getThingHandler().handleCommand(channel(POWER_ON_OFF), new DecimalType(0));
// then:
verify(getWebserviceMock(), never()).putPowerState(anyString(), anyBoolean());
}
@Test
public void testMissingPropertiesAreSetWhenAStateUpdateIsReceivedFromTheCloud() {
// given:
assertFalse(getThingHandler().getThing().getProperties().containsKey(Thing.PROPERTY_SERIAL_NUMBER));
assertFalse(getThingHandler().getThing().getProperties().containsKey(Thing.PROPERTY_MODEL_ID));
var deviceState = mock(DeviceState.class);
when(deviceState.getRawType()).thenReturn(DeviceType.UNKNOWN);
when(deviceState.getDeviceIdentifier()).thenReturn(MieleCloudBindingIntegrationTestConstants.SERIAL_NUMBER);
when(deviceState.getFabNumber())
.thenReturn(Optional.of(MieleCloudBindingIntegrationTestConstants.SERIAL_NUMBER));
when(deviceState.getType()).thenReturn(Optional.of("Unknown device type"));
when(deviceState.getTechType()).thenReturn(Optional.of("UK-4567"));
// when:
getThingHandler().onDeviceStateUpdated(deviceState);
// then:
assertEquals(MieleCloudBindingIntegrationTestConstants.SERIAL_NUMBER,
getThingHandler().getThing().getProperties().get(Thing.PROPERTY_SERIAL_NUMBER));
assertEquals("Unknown device type UK-4567",
getThingHandler().getThing().getProperties().get(Thing.PROPERTY_MODEL_ID));
}
}

View File

@@ -0,0 +1,211 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.mielecloud.internal.handler;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
import static org.openhab.binding.mielecloud.internal.MieleCloudBindingConstants.Channels.*;
import static org.openhab.binding.mielecloud.internal.util.MieleCloudBindingIntegrationTestConstants.COFFEE_SYSTEM_THING_UID;
import java.util.Optional;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.Test;
import org.openhab.binding.mielecloud.internal.MieleCloudBindingConstants;
import org.openhab.binding.mielecloud.internal.util.MieleCloudBindingIntegrationTestConstants;
import org.openhab.binding.mielecloud.internal.webservice.api.ActionsState;
import org.openhab.binding.mielecloud.internal.webservice.api.DeviceState;
import org.openhab.binding.mielecloud.internal.webservice.api.PowerStatus;
import org.openhab.binding.mielecloud.internal.webservice.api.json.StateType;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.StringType;
/**
* @author Björn Lange - Initial contribution
* @author Benjamin Bolte - Add info state channel and map signal flags from API tests
* @author Björn Lange - Add elapsed time channel
*/
@NonNullByDefault
public class CoffeeDeviceThingHandlerTest extends AbstractMieleThingHandlerTest {
@Override
protected AbstractMieleThingHandler setUpThingHandler() {
return createThingHandler(MieleCloudBindingConstants.THING_TYPE_COFFEE_SYSTEM, COFFEE_SYSTEM_THING_UID,
CoffeeSystemThingHandler.class, MieleCloudBindingIntegrationTestConstants.SERIAL_NUMBER);
}
@Test
public void testChannelUpdatesForNullValues() {
// given:
DeviceState deviceState = mock(DeviceState.class);
when(deviceState.getDeviceIdentifier()).thenReturn(COFFEE_SYSTEM_THING_UID.getId());
when(deviceState.isRemoteControlEnabled()).thenReturn(Optional.empty());
when(deviceState.getSelectedProgram()).thenReturn(Optional.empty());
when(deviceState.getSelectedProgramId()).thenReturn(Optional.empty());
when(deviceState.getProgramPhase()).thenReturn(Optional.empty());
when(deviceState.getProgramPhaseRaw()).thenReturn(Optional.empty());
when(deviceState.getStateType()).thenReturn(Optional.empty());
when(deviceState.getStatus()).thenReturn(Optional.empty());
when(deviceState.getStatusRaw()).thenReturn(Optional.empty());
when(deviceState.getElapsedTime()).thenReturn(Optional.empty());
when(deviceState.getLightState()).thenReturn(Optional.empty());
// when:
getBridgeHandler().onDeviceStateUpdated(deviceState);
// then:
waitForAssert(() -> {
assertEquals(NULL_VALUE_STATE, getChannelState(PROGRAM_ACTIVE));
assertEquals(NULL_VALUE_STATE, getChannelState(PROGRAM_ACTIVE_RAW));
assertEquals(NULL_VALUE_STATE, getChannelState(PROGRAM_PHASE));
assertEquals(NULL_VALUE_STATE, getChannelState(PROGRAM_PHASE_RAW));
assertEquals(NULL_VALUE_STATE, getChannelState(OPERATION_STATE));
assertEquals(NULL_VALUE_STATE, getChannelState(OPERATION_STATE_RAW));
assertEquals(NULL_VALUE_STATE, getChannelState(PROGRAM_ELAPSED_TIME));
assertEquals(new StringType(PowerStatus.POWER_ON.getState()), getChannelState(POWER_ON_OFF));
assertEquals(NULL_VALUE_STATE, getChannelState(LIGHT_SWITCH));
});
}
@Test
public void testChannelUpdatesForValidValues() {
// given:
DeviceState deviceState = mock(DeviceState.class);
when(deviceState.getDeviceIdentifier()).thenReturn(COFFEE_SYSTEM_THING_UID.getId());
when(deviceState.isRemoteControlEnabled()).thenReturn(Optional.of(true));
when(deviceState.getSelectedProgram()).thenReturn(Optional.of("Latte Macchiato"));
when(deviceState.getSelectedProgramId()).thenReturn(Optional.of(5L));
when(deviceState.getProgramPhase()).thenReturn(Optional.of("Spühlen"));
when(deviceState.getProgramPhaseRaw()).thenReturn(Optional.of(1));
when(deviceState.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
when(deviceState.getStatus()).thenReturn(Optional.of("Running"));
when(deviceState.getStatusRaw()).thenReturn(Optional.of(StateType.RUNNING.getCode()));
when(deviceState.hasError()).thenReturn(false);
when(deviceState.hasInfo()).thenReturn(true);
when(deviceState.getLightState()).thenReturn(Optional.of(false));
when(deviceState.getElapsedTime()).thenReturn(Optional.of(3));
// when:
getBridgeHandler().onDeviceStateUpdated(deviceState);
// then:
waitForAssert(() -> {
assertEquals(new StringType("Latte Macchiato"), getChannelState(PROGRAM_ACTIVE));
assertEquals(new DecimalType(5), getChannelState(PROGRAM_ACTIVE_RAW));
assertEquals(new StringType("Spühlen"), getChannelState(PROGRAM_PHASE));
assertEquals(new DecimalType(1), getChannelState(PROGRAM_PHASE_RAW));
assertEquals(new StringType("Running"), getChannelState(OPERATION_STATE));
assertEquals(new DecimalType(StateType.RUNNING.getCode()), getChannelState(OPERATION_STATE_RAW));
assertEquals(new DecimalType(3), getChannelState(PROGRAM_ELAPSED_TIME));
assertEquals(new StringType(PowerStatus.POWER_ON.getState()), getChannelState(POWER_ON_OFF));
assertEquals(OnOffType.OFF, getChannelState(ERROR_STATE));
assertEquals(OnOffType.ON, getChannelState(INFO_STATE));
assertEquals(OnOffType.OFF, getChannelState(LIGHT_SWITCH));
});
}
@Test
public void testFinishStateChannelIsSetToOnWhenProgramHasFinished() {
// given:
DeviceState deviceStateBefore = mock(DeviceState.class);
when(deviceStateBefore.getDeviceIdentifier()).thenReturn(COFFEE_SYSTEM_THING_UID.getId());
when(deviceStateBefore.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
when(deviceStateBefore.isInState(any())).thenCallRealMethod();
getBridgeHandler().onDeviceStateUpdated(deviceStateBefore);
DeviceState deviceStateAfter = mock(DeviceState.class);
when(deviceStateAfter.getDeviceIdentifier()).thenReturn(COFFEE_SYSTEM_THING_UID.getId());
when(deviceStateAfter.getStateType()).thenReturn(Optional.of(StateType.END_PROGRAMMED));
when(deviceStateAfter.isInState(any())).thenCallRealMethod();
// when:
getBridgeHandler().onDeviceStateUpdated(deviceStateAfter);
// then:
waitForAssert(() -> {
assertEquals(OnOffType.ON, getChannelState(FINISH_STATE));
});
}
@Test
public void testTransitionChannelUpdatesForNullValues() {
// given:
DeviceState deviceStateBefore = mock(DeviceState.class);
when(deviceStateBefore.getDeviceIdentifier()).thenReturn(COFFEE_SYSTEM_THING_UID.getId());
when(deviceStateBefore.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
when(deviceStateBefore.isInState(any())).thenCallRealMethod();
when(deviceStateBefore.getRemainingTime()).thenReturn(Optional.empty());
getThingHandler().onDeviceStateUpdated(deviceStateBefore);
DeviceState deviceStateAfter = mock(DeviceState.class);
when(deviceStateAfter.getDeviceIdentifier()).thenReturn(COFFEE_SYSTEM_THING_UID.getId());
when(deviceStateAfter.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
when(deviceStateAfter.isInState(any())).thenCallRealMethod();
when(deviceStateAfter.getRemainingTime()).thenReturn(Optional.empty());
// when:
getThingHandler().onDeviceStateUpdated(deviceStateAfter);
waitForAssert(() -> {
assertEquals(NULL_VALUE_STATE, getChannelState(PROGRAM_REMAINING_TIME));
});
}
@Test
public void testTransitionChannelUpdatesForValidValues() {
// given:
DeviceState deviceStateBefore = mock(DeviceState.class);
when(deviceStateBefore.getDeviceIdentifier()).thenReturn(COFFEE_SYSTEM_THING_UID.getId());
when(deviceStateBefore.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
when(deviceStateBefore.isInState(any())).thenCallRealMethod();
when(deviceStateBefore.getRemainingTime()).thenReturn(Optional.of(10));
getThingHandler().onDeviceStateUpdated(deviceStateBefore);
DeviceState deviceStateAfter = mock(DeviceState.class);
when(deviceStateAfter.getDeviceIdentifier()).thenReturn(COFFEE_SYSTEM_THING_UID.getId());
when(deviceStateAfter.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
when(deviceStateAfter.isInState(any())).thenCallRealMethod();
when(deviceStateAfter.getRemainingTime()).thenReturn(Optional.of(10));
// when:
getThingHandler().onDeviceStateUpdated(deviceStateAfter);
waitForAssert(() -> {
assertEquals(new DecimalType(10), getChannelState(PROGRAM_REMAINING_TIME));
});
}
@Test
public void testActionsChannelUpdatesForValidValues() {
// given:
ActionsState actionsState = mock(ActionsState.class);
when(actionsState.getDeviceIdentifier()).thenReturn(COFFEE_SYSTEM_THING_UID.getId());
when(actionsState.canBeSwitchedOn()).thenReturn(true);
when(actionsState.canBeSwitchedOff()).thenReturn(false);
when(actionsState.canControlLight()).thenReturn(true);
// when:
getBridgeHandler().onProcessActionUpdated(actionsState);
// then:
waitForAssert(() -> {
assertEquals(OnOffType.ON, getChannelState(REMOTE_CONTROL_CAN_BE_SWITCHED_ON));
assertEquals(OnOffType.OFF, getChannelState(REMOTE_CONTROL_CAN_BE_SWITCHED_OFF));
assertEquals(OnOffType.ON, getChannelState(LIGHT_CAN_BE_CONTROLLED));
});
}
}

View File

@@ -0,0 +1,252 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.mielecloud.internal.handler;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import static org.openhab.binding.mielecloud.internal.MieleCloudBindingConstants.Channels.*;
import static org.openhab.binding.mielecloud.internal.util.MieleCloudBindingIntegrationTestConstants.FRIDGE_FREEZER_DEVICE_THING_UID;
import java.util.Optional;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.Test;
import org.openhab.binding.mielecloud.internal.MieleCloudBindingConstants;
import org.openhab.binding.mielecloud.internal.util.MieleCloudBindingIntegrationTestConstants;
import org.openhab.binding.mielecloud.internal.webservice.api.ActionsState;
import org.openhab.binding.mielecloud.internal.webservice.api.DeviceState;
import org.openhab.binding.mielecloud.internal.webservice.api.json.DeviceType;
import org.openhab.binding.mielecloud.internal.webservice.api.json.ProcessAction;
import org.openhab.binding.mielecloud.internal.webservice.api.json.StateType;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.library.unit.SIUnits;
/**
* @author Björn Lange - Initial contribution
* @author Benjamin Bolte - Add door state and door alarm
* @author Benjamin Bolte - Add info state channel and map signal flags from API tests
*/
@NonNullByDefault
public class CoolingDeviceThingHandlerTest extends AbstractMieleThingHandlerTest {
@Override
protected AbstractMieleThingHandler setUpThingHandler() {
return createThingHandler(MieleCloudBindingConstants.THING_TYPE_FRIDGE_FREEZER, FRIDGE_FREEZER_DEVICE_THING_UID,
CoolingDeviceThingHandler.class, MieleCloudBindingIntegrationTestConstants.SERIAL_NUMBER);
}
@Test
public void testChannelUpdatesForNullValues() {
// given:
DeviceState deviceState = mock(DeviceState.class);
when(deviceState.getDeviceIdentifier()).thenReturn(FRIDGE_FREEZER_DEVICE_THING_UID.getId());
when(deviceState.getRawType()).thenReturn(DeviceType.FRIDGE_FREEZER_COMBINATION);
when(deviceState.getStateType()).thenReturn(Optional.empty());
when(deviceState.isRemoteControlEnabled()).thenReturn(Optional.empty());
when(deviceState.getStatus()).thenReturn(Optional.empty());
when(deviceState.getStatusRaw()).thenReturn(Optional.empty());
when(deviceState.getTargetTemperature(0)).thenReturn(Optional.empty());
when(deviceState.getTargetTemperature(1)).thenReturn(Optional.empty());
when(deviceState.getTemperature(0)).thenReturn(Optional.empty());
when(deviceState.getTemperature(1)).thenReturn(Optional.empty());
when(deviceState.getDoorState()).thenReturn(Optional.empty());
when(deviceState.getDoorAlarm()).thenReturn(Optional.empty());
// when:
getBridgeHandler().onDeviceStateUpdated(deviceState);
// then:
waitForAssert(() -> {
assertEquals(NULL_VALUE_STATE, getChannelState(OPERATION_STATE));
assertEquals(NULL_VALUE_STATE, getChannelState(OPERATION_STATE_RAW));
assertEquals(NULL_VALUE_STATE, getChannelState(FRIDGE_SUPER_COOL));
assertEquals(NULL_VALUE_STATE, getChannelState(FREEZER_SUPER_FREEZE));
assertEquals(NULL_VALUE_STATE, getChannelState(FRIDGE_TEMPERATURE_TARGET));
assertEquals(NULL_VALUE_STATE, getChannelState(FREEZER_TEMPERATURE_TARGET));
assertEquals(NULL_VALUE_STATE, getChannelState(FRIDGE_TEMPERATURE_CURRENT));
assertEquals(NULL_VALUE_STATE, getChannelState(FREEZER_TEMPERATURE_CURRENT));
assertEquals(NULL_VALUE_STATE, getChannelState(DOOR_STATE));
assertEquals(NULL_VALUE_STATE, getChannelState(DOOR_ALARM));
});
}
@Test
public void testChannelUpdatesForValidValues() {
// given:
DeviceState deviceState = mock(DeviceState.class);
when(deviceState.getDeviceIdentifier()).thenReturn(FRIDGE_FREEZER_DEVICE_THING_UID.getId());
when(deviceState.getRawType()).thenReturn(DeviceType.FRIDGE_FREEZER_COMBINATION);
when(deviceState.getStateType()).thenReturn(Optional.of(StateType.SUPERCOOLING));
when(deviceState.isRemoteControlEnabled()).thenReturn(Optional.of(true));
when(deviceState.getStatus()).thenReturn(Optional.of("Super Cooling"));
when(deviceState.getStatusRaw()).thenReturn(Optional.of(StateType.SUPERCOOLING.getCode()));
when(deviceState.getTargetTemperature(0)).thenReturn(Optional.of(6));
when(deviceState.getTargetTemperature(1)).thenReturn(Optional.of(-18));
when(deviceState.getTemperature(0)).thenReturn(Optional.of(8));
when(deviceState.getTemperature(1)).thenReturn(Optional.of(-10));
when(deviceState.hasError()).thenReturn(false);
when(deviceState.getDoorState()).thenReturn(Optional.of(true));
when(deviceState.getDoorAlarm()).thenReturn(Optional.of(false));
// when:
getBridgeHandler().onDeviceStateUpdated(deviceState);
// then:
waitForAssert(() -> {
assertEquals(new StringType("Super Cooling"), getChannelState(OPERATION_STATE));
assertEquals(new DecimalType(StateType.SUPERCOOLING.getCode()), getChannelState(OPERATION_STATE_RAW));
assertEquals(OnOffType.ON, getChannelState(FRIDGE_SUPER_COOL));
assertEquals(OnOffType.OFF, getChannelState(FREEZER_SUPER_FREEZE));
assertEquals(new QuantityType<>(6, SIUnits.CELSIUS), getChannelState(FRIDGE_TEMPERATURE_TARGET));
assertEquals(new QuantityType<>(-18, SIUnits.CELSIUS), getChannelState(FREEZER_TEMPERATURE_TARGET));
assertEquals(new QuantityType<>(8, SIUnits.CELSIUS), getChannelState(FRIDGE_TEMPERATURE_CURRENT));
assertEquals(new QuantityType<>(-10, SIUnits.CELSIUS), getChannelState(FREEZER_TEMPERATURE_CURRENT));
assertEquals(OnOffType.OFF, getChannelState(ERROR_STATE));
assertEquals(OnOffType.ON, getChannelState(DOOR_STATE));
assertEquals(OnOffType.OFF, getChannelState(DOOR_ALARM));
});
}
@Test
public void testChannelUpdatesForSuperCooling() {
// given:
DeviceState deviceState = mock(DeviceState.class);
when(deviceState.getDeviceIdentifier()).thenReturn(FRIDGE_FREEZER_DEVICE_THING_UID.getId());
when(deviceState.getRawType()).thenReturn(DeviceType.FRIDGE_FREEZER_COMBINATION);
when(deviceState.getStateType()).thenReturn(Optional.of(StateType.SUPERCOOLING));
// when:
getBridgeHandler().onDeviceStateUpdated(deviceState);
// then:
waitForAssert(() -> {
assertEquals(OnOffType.ON, getChannelState(FRIDGE_SUPER_COOL));
assertEquals(OnOffType.OFF, getChannelState(FREEZER_SUPER_FREEZE));
});
}
@Test
public void testChannelUpdatesForSuperFreezing() {
// given:
DeviceState deviceState = mock(DeviceState.class);
when(deviceState.getDeviceIdentifier()).thenReturn(FRIDGE_FREEZER_DEVICE_THING_UID.getId());
when(deviceState.getRawType()).thenReturn(DeviceType.FRIDGE_FREEZER_COMBINATION);
when(deviceState.getStateType()).thenReturn(Optional.of(StateType.SUPERFREEZING));
// when:
getBridgeHandler().onDeviceStateUpdated(deviceState);
// then:
waitForAssert(() -> {
assertEquals(OnOffType.OFF, getChannelState(FRIDGE_SUPER_COOL));
assertEquals(OnOffType.ON, getChannelState(FREEZER_SUPER_FREEZE));
});
}
@Test
public void testChannelUpdatesForSuperCollingSuperFreezing() {
// given:
DeviceState deviceState = mock(DeviceState.class);
when(deviceState.getDeviceIdentifier()).thenReturn(FRIDGE_FREEZER_DEVICE_THING_UID.getId());
when(deviceState.getRawType()).thenReturn(DeviceType.FRIDGE_FREEZER_COMBINATION);
when(deviceState.getStateType()).thenReturn(Optional.of(StateType.SUPERCOOLING_SUPERFREEZING));
// when:
getBridgeHandler().onDeviceStateUpdated(deviceState);
// then:
waitForAssert(() -> {
assertEquals(OnOffType.ON, getChannelState(FRIDGE_SUPER_COOL));
assertEquals(OnOffType.ON, getChannelState(FREEZER_SUPER_FREEZE));
});
}
@Test
public void testActionsChannelUpdatesForValidValues() {
// given:
ActionsState actionsState = mock(ActionsState.class);
when(actionsState.getDeviceIdentifier()).thenReturn(FRIDGE_FREEZER_DEVICE_THING_UID.getId());
when(actionsState.canContolSupercooling()).thenReturn(true);
when(actionsState.canControlSuperfreezing()).thenReturn(false);
// when:
getBridgeHandler().onProcessActionUpdated(actionsState);
// then:
waitForAssert(() -> {
assertEquals(OnOffType.ON, getChannelState(SUPER_COOL_CAN_BE_CONTROLLED));
assertEquals(OnOffType.OFF, getChannelState(SUPER_FREEZE_CAN_BE_CONTROLLED));
});
}
@Override
@Test
public void testHandleCommandDoesNothingWhenCommandIsNotOfOnOffType() {
// when:
getThingHandler().handleCommand(channel(FRIDGE_SUPER_COOL), new DecimalType(50));
// then:
verify(getWebserviceMock(), never()).putProcessAction(anyString(), any());
}
@Test
public void testHandleCommandStartsSupercoolingWhenRequested() {
// when:
getThingHandler().handleCommand(channel(FRIDGE_SUPER_COOL), OnOffType.ON);
// then:
waitForAssert(() -> {
verify(getWebserviceMock()).putProcessAction(getThingHandler().getDeviceId(),
ProcessAction.START_SUPERCOOLING);
});
}
@Test
public void testHandleCommandStopsSupercoolingWhenRequested() {
// when:
getThingHandler().handleCommand(channel(FRIDGE_SUPER_COOL), OnOffType.OFF);
// then:
waitForAssert(() -> {
verify(getWebserviceMock()).putProcessAction(getThingHandler().getDeviceId(),
ProcessAction.STOP_SUPERCOOLING);
});
}
@Test
public void testHandleCommandStartsSuperfreezingWhenRequested() {
// when:
getThingHandler().handleCommand(channel(FREEZER_SUPER_FREEZE), OnOffType.ON);
// then:
waitForAssert(() -> {
verify(getWebserviceMock()).putProcessAction(getThingHandler().getDeviceId(),
ProcessAction.START_SUPERFREEZING);
});
}
@Test
public void testHandleCommandStopsSuperfreezingWhenRequested() {
// when:
getThingHandler().handleCommand(channel(FREEZER_SUPER_FREEZE), OnOffType.OFF);
// then:
waitForAssert(() -> {
verify(getWebserviceMock()).putProcessAction(getThingHandler().getDeviceId(),
ProcessAction.STOP_SUPERFREEZING);
});
}
}

View File

@@ -0,0 +1,232 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.mielecloud.internal.handler;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
import static org.openhab.binding.mielecloud.internal.MieleCloudBindingConstants.Channels.*;
import java.util.Optional;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.Test;
import org.openhab.binding.mielecloud.internal.MieleCloudBindingConstants;
import org.openhab.binding.mielecloud.internal.util.MieleCloudBindingIntegrationTestConstants;
import org.openhab.binding.mielecloud.internal.webservice.api.ActionsState;
import org.openhab.binding.mielecloud.internal.webservice.api.DeviceState;
import org.openhab.binding.mielecloud.internal.webservice.api.PowerStatus;
import org.openhab.binding.mielecloud.internal.webservice.api.json.StateType;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.library.unit.SIUnits;
/**
* @author Björn Lange - Initial contribution
*/
@NonNullByDefault
public class DishWarmerDeviceThingHandlerTest extends AbstractMieleThingHandlerTest {
@Override
protected AbstractMieleThingHandler setUpThingHandler() {
return createThingHandler(MieleCloudBindingConstants.THING_TYPE_DISH_WARMER,
MieleCloudBindingIntegrationTestConstants.DISH_WARMER_DEVICE_THING_UID,
DishWarmerDeviceThingHandler.class, MieleCloudBindingIntegrationTestConstants.SERIAL_NUMBER);
}
@Test
public void testChannelUpdatesForNullValues() {
// given:
DeviceState deviceState = mock(DeviceState.class);
when(deviceState.getDeviceIdentifier())
.thenReturn(MieleCloudBindingIntegrationTestConstants.DISH_WARMER_DEVICE_THING_UID.getId());
when(deviceState.getSelectedProgramId()).thenReturn(Optional.empty());
when(deviceState.getStatus()).thenReturn(Optional.empty());
when(deviceState.getStatusRaw()).thenReturn(Optional.empty());
when(deviceState.getStateType()).thenReturn(Optional.empty());
when(deviceState.getElapsedTime()).thenReturn(Optional.empty());
when(deviceState.getTargetTemperature(0)).thenReturn(Optional.empty());
when(deviceState.getTemperature(0)).thenReturn(Optional.empty());
when(deviceState.hasError()).thenReturn(false);
when(deviceState.hasInfo()).thenReturn(false);
when(deviceState.getDoorState()).thenReturn(Optional.empty());
// when:
getBridgeHandler().onDeviceStateUpdated(deviceState);
// then:
waitForAssert(() -> {
assertEquals(NULL_VALUE_STATE, getChannelState(DISH_WARMER_PROGRAM_ACTIVE));
assertEquals(NULL_VALUE_STATE, getChannelState(OPERATION_STATE));
assertEquals(NULL_VALUE_STATE, getChannelState(OPERATION_STATE_RAW));
assertEquals(new StringType(PowerStatus.POWER_ON.getState()), getChannelState(POWER_ON_OFF));
assertEquals(NULL_VALUE_STATE, getChannelState(PROGRAM_ELAPSED_TIME));
assertEquals(NULL_VALUE_STATE, getChannelState(TEMPERATURE_TARGET));
assertEquals(NULL_VALUE_STATE, getChannelState(TEMPERATURE_CURRENT));
assertEquals(OnOffType.OFF, getChannelState(ERROR_STATE));
assertEquals(OnOffType.OFF, getChannelState(INFO_STATE));
assertEquals(NULL_VALUE_STATE, getChannelState(DOOR_STATE));
});
}
@Test
public void testChannelUpdatesForValidValues() {
// given:
DeviceState deviceState = mock(DeviceState.class);
when(deviceState.getDeviceIdentifier())
.thenReturn(MieleCloudBindingIntegrationTestConstants.DISH_WARMER_DEVICE_THING_UID.getId());
when(deviceState.getSelectedProgramId()).thenReturn(Optional.of(2L));
when(deviceState.getStatus()).thenReturn(Optional.of("Running"));
when(deviceState.getStatusRaw()).thenReturn(Optional.of(StateType.RUNNING.getCode()));
when(deviceState.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
when(deviceState.getElapsedTime()).thenReturn(Optional.of(98));
when(deviceState.getTargetTemperature(0)).thenReturn(Optional.of(30));
when(deviceState.getTemperature(0)).thenReturn(Optional.of(29));
when(deviceState.hasError()).thenReturn(true);
when(deviceState.hasInfo()).thenReturn(true);
when(deviceState.getDoorState()).thenReturn(Optional.of(false));
// when:
getBridgeHandler().onDeviceStateUpdated(deviceState);
// then:
waitForAssert(() -> {
assertEquals(new StringType("2"), getChannelState(DISH_WARMER_PROGRAM_ACTIVE));
assertEquals(new StringType("Running"), getChannelState(OPERATION_STATE));
assertEquals(new StringType(PowerStatus.POWER_ON.getState()), getChannelState(POWER_ON_OFF));
assertEquals(new DecimalType(98), getChannelState(PROGRAM_ELAPSED_TIME));
assertEquals(new QuantityType<>(30, SIUnits.CELSIUS), getChannelState(TEMPERATURE_TARGET));
assertEquals(new QuantityType<>(29, SIUnits.CELSIUS), getChannelState(TEMPERATURE_CURRENT));
assertEquals(OnOffType.ON, getChannelState(ERROR_STATE));
assertEquals(OnOffType.ON, getChannelState(INFO_STATE));
assertEquals(OnOffType.OFF, getChannelState(DOOR_STATE));
});
}
@Test
public void testFinishStateChannelIsSetToOnWhenProgramHasFinished() {
// given:
DeviceState deviceStateBefore = mock(DeviceState.class);
when(deviceStateBefore.getDeviceIdentifier())
.thenReturn(MieleCloudBindingIntegrationTestConstants.DISH_WARMER_DEVICE_THING_UID.getId());
when(deviceStateBefore.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
when(deviceStateBefore.isInState(any())).thenCallRealMethod();
getBridgeHandler().onDeviceStateUpdated(deviceStateBefore);
DeviceState deviceStateAfter = mock(DeviceState.class);
when(deviceStateAfter.getDeviceIdentifier())
.thenReturn(MieleCloudBindingIntegrationTestConstants.DISH_WARMER_DEVICE_THING_UID.getId());
when(deviceStateAfter.getStateType()).thenReturn(Optional.of(StateType.END_PROGRAMMED));
when(deviceStateAfter.isInState(any())).thenCallRealMethod();
// when:
getBridgeHandler().onDeviceStateUpdated(deviceStateAfter);
// then:
waitForAssert(() -> {
assertEquals(OnOffType.ON, getChannelState(FINISH_STATE));
});
}
@Test
public void testTransitionChannelUpdatesForNullValues() {
// given:
DeviceState deviceStateBefore = mock(DeviceState.class);
when(deviceStateBefore.getDeviceIdentifier())
.thenReturn(MieleCloudBindingIntegrationTestConstants.DISH_WARMER_DEVICE_THING_UID.getId());
when(deviceStateBefore.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
when(deviceStateBefore.isInState(any())).thenCallRealMethod();
when(deviceStateBefore.getRemainingTime()).thenReturn(Optional.empty());
when(deviceStateBefore.getProgress()).thenReturn(Optional.empty());
getThingHandler().onDeviceStateUpdated(deviceStateBefore);
DeviceState deviceStateAfter = mock(DeviceState.class);
when(deviceStateAfter.getDeviceIdentifier())
.thenReturn(MieleCloudBindingIntegrationTestConstants.DISH_WARMER_DEVICE_THING_UID.getId());
when(deviceStateAfter.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
when(deviceStateAfter.isInState(any())).thenCallRealMethod();
when(deviceStateAfter.getRemainingTime()).thenReturn(Optional.empty());
when(deviceStateAfter.getProgress()).thenReturn(Optional.empty());
// when:
getThingHandler().onDeviceStateUpdated(deviceStateAfter);
waitForAssert(() -> {
assertEquals(NULL_VALUE_STATE, getChannelState(PROGRAM_REMAINING_TIME));
assertEquals(NULL_VALUE_STATE, getChannelState(PROGRAM_PROGRESS));
});
}
@Test
public void testTransitionChannelUpdatesForValidValues() {
// given:
DeviceState deviceStateBefore = mock(DeviceState.class);
when(deviceStateBefore.getDeviceIdentifier())
.thenReturn(MieleCloudBindingIntegrationTestConstants.DISH_WARMER_DEVICE_THING_UID.getId());
when(deviceStateBefore.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
when(deviceStateBefore.isInState(any())).thenCallRealMethod();
when(deviceStateBefore.getRemainingTime()).thenReturn(Optional.of(10));
when(deviceStateBefore.getProgress()).thenReturn(Optional.of(80));
getThingHandler().onDeviceStateUpdated(deviceStateBefore);
DeviceState deviceStateAfter = mock(DeviceState.class);
when(deviceStateAfter.getDeviceIdentifier())
.thenReturn(MieleCloudBindingIntegrationTestConstants.DISH_WARMER_DEVICE_THING_UID.getId());
when(deviceStateAfter.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
when(deviceStateAfter.isInState(any())).thenCallRealMethod();
when(deviceStateAfter.getRemainingTime()).thenReturn(Optional.of(10));
when(deviceStateAfter.getProgress()).thenReturn(Optional.of(80));
// when:
getThingHandler().onDeviceStateUpdated(deviceStateAfter);
waitForAssert(() -> {
assertEquals(new DecimalType(10), getChannelState(PROGRAM_REMAINING_TIME));
assertEquals(new DecimalType(80), getChannelState(PROGRAM_PROGRESS));
});
}
@Test
public void testActionsChannelUpdatesForValidValues() {
// given:
ActionsState actionsState = mock(ActionsState.class);
when(actionsState.getDeviceIdentifier())
.thenReturn(MieleCloudBindingIntegrationTestConstants.DISH_WARMER_DEVICE_THING_UID.getId());
when(actionsState.canBeSwitchedOn()).thenReturn(true);
when(actionsState.canBeSwitchedOff()).thenReturn(false);
// when:
getBridgeHandler().onProcessActionUpdated(actionsState);
// then:
waitForAssert(() -> {
assertEquals(OnOffType.ON, getChannelState(REMOTE_CONTROL_CAN_BE_SWITCHED_ON));
assertEquals(OnOffType.OFF, getChannelState(REMOTE_CONTROL_CAN_BE_SWITCHED_OFF));
});
}
@Test
public void testHandleCommandDishWarmerProgramActive() {
// when:
getThingHandler().handleCommand(channel(DISH_WARMER_PROGRAM_ACTIVE), new StringType("3"));
// then:
waitForAssert(() -> {
verify(getWebserviceMock()).putProgram(getThingHandler().getDeviceId(), 3);
});
}
}

View File

@@ -0,0 +1,227 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.mielecloud.internal.handler;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
import static org.openhab.binding.mielecloud.internal.MieleCloudBindingConstants.Channels.*;
import static org.openhab.binding.mielecloud.internal.util.MieleCloudBindingIntegrationTestConstants.DISHWASHER_DEVICE_THING_UID;
import java.util.Optional;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.Test;
import org.openhab.binding.mielecloud.internal.MieleCloudBindingConstants;
import org.openhab.binding.mielecloud.internal.util.MieleCloudBindingIntegrationTestConstants;
import org.openhab.binding.mielecloud.internal.webservice.api.ActionsState;
import org.openhab.binding.mielecloud.internal.webservice.api.DeviceState;
import org.openhab.binding.mielecloud.internal.webservice.api.PowerStatus;
import org.openhab.binding.mielecloud.internal.webservice.api.ProgramStatus;
import org.openhab.binding.mielecloud.internal.webservice.api.json.StateType;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.StringType;
/**
* @author Björn Lange - Initial contribution
* @author Benjamin Bolte - Add info state channel and map signal flags from API tests
* @author Björn Lange - Add elapsed time channel
*/
@NonNullByDefault
public class DishwasherDeviceThingHandlerTest extends AbstractMieleThingHandlerTest {
@Override
protected AbstractMieleThingHandler setUpThingHandler() {
return createThingHandler(MieleCloudBindingConstants.THING_TYPE_DISHWASHER, DISHWASHER_DEVICE_THING_UID,
DishwasherDeviceThingHandler.class, MieleCloudBindingIntegrationTestConstants.SERIAL_NUMBER);
}
@Test
public void testChannelUpdatesForNullValues() {
// given:
DeviceState deviceState = mock(DeviceState.class);
when(deviceState.getDeviceIdentifier()).thenReturn(DISHWASHER_DEVICE_THING_UID.getId());
when(deviceState.getStateType()).thenReturn(Optional.empty());
when(deviceState.isRemoteControlEnabled()).thenReturn(Optional.empty());
when(deviceState.getSelectedProgram()).thenReturn(Optional.empty());
when(deviceState.getSelectedProgramId()).thenReturn(Optional.empty());
when(deviceState.getProgramPhase()).thenReturn(Optional.empty());
when(deviceState.getProgramPhaseRaw()).thenReturn(Optional.empty());
when(deviceState.getStatus()).thenReturn(Optional.empty());
when(deviceState.getStatusRaw()).thenReturn(Optional.empty());
when(deviceState.getStartTime()).thenReturn(Optional.empty());
when(deviceState.getElapsedTime()).thenReturn(Optional.empty());
when(deviceState.getDoorState()).thenReturn(Optional.empty());
// when:
getBridgeHandler().onDeviceStateUpdated(deviceState);
// then:
waitForAssert(() -> {
assertEquals(NULL_VALUE_STATE, getChannelState(PROGRAM_ACTIVE));
assertEquals(NULL_VALUE_STATE, getChannelState(PROGRAM_ACTIVE_RAW));
assertEquals(NULL_VALUE_STATE, getChannelState(PROGRAM_PHASE));
assertEquals(NULL_VALUE_STATE, getChannelState(PROGRAM_PHASE_RAW));
assertEquals(NULL_VALUE_STATE, getChannelState(OPERATION_STATE));
assertEquals(NULL_VALUE_STATE, getChannelState(OPERATION_STATE_RAW));
assertEquals(new StringType(ProgramStatus.PROGRAM_STOPPED.getState()), getChannelState(PROGRAM_START_STOP));
assertEquals(new StringType(PowerStatus.POWER_ON.getState()), getChannelState(POWER_ON_OFF));
assertEquals(NULL_VALUE_STATE, getChannelState(DELAYED_START_TIME));
assertEquals(NULL_VALUE_STATE, getChannelState(PROGRAM_ELAPSED_TIME));
assertEquals(NULL_VALUE_STATE, getChannelState(DOOR_STATE));
});
}
@Test
public void testChannelUpdatesForValidValues() {
// given:
DeviceState deviceState = mock(DeviceState.class);
when(deviceState.isInState(any())).thenCallRealMethod();
when(deviceState.getDeviceIdentifier()).thenReturn(DISHWASHER_DEVICE_THING_UID.getId());
when(deviceState.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
when(deviceState.isRemoteControlEnabled()).thenReturn(Optional.of(true));
when(deviceState.getSelectedProgram()).thenReturn(Optional.of("Eco"));
when(deviceState.getSelectedProgramId()).thenReturn(Optional.of(4L));
when(deviceState.getProgramPhase()).thenReturn(Optional.of("Spülen"));
when(deviceState.getProgramPhaseRaw()).thenReturn(Optional.of(2));
when(deviceState.getStatus()).thenReturn(Optional.of("Running"));
when(deviceState.getStatusRaw()).thenReturn(Optional.of(StateType.RUNNING.getCode()));
when(deviceState.getStartTime()).thenReturn(Optional.of(3600));
when(deviceState.getElapsedTime()).thenReturn(Optional.of(4));
when(deviceState.hasError()).thenReturn(false);
when(deviceState.hasInfo()).thenReturn(true);
when(deviceState.getDoorState()).thenReturn(Optional.of(true));
// when:
getBridgeHandler().onDeviceStateUpdated(deviceState);
// then:
waitForAssert(() -> {
assertEquals(new StringType("Eco"), getChannelState(PROGRAM_ACTIVE));
assertEquals(new DecimalType(4), getChannelState(PROGRAM_ACTIVE_RAW));
assertEquals(new StringType("Spülen"), getChannelState(PROGRAM_PHASE));
assertEquals(new DecimalType(2), getChannelState(PROGRAM_PHASE_RAW));
assertEquals(new StringType("Running"), getChannelState(OPERATION_STATE));
assertEquals(new DecimalType(StateType.RUNNING.getCode()), getChannelState(OPERATION_STATE_RAW));
assertEquals(new StringType(ProgramStatus.PROGRAM_STARTED.getState()), getChannelState(PROGRAM_START_STOP));
assertEquals(new StringType(PowerStatus.POWER_ON.getState()), getChannelState(POWER_ON_OFF));
assertEquals(new DecimalType(3600), getChannelState(DELAYED_START_TIME));
assertEquals(new DecimalType(4), getChannelState(PROGRAM_ELAPSED_TIME));
assertEquals(OnOffType.OFF, getChannelState(ERROR_STATE));
assertEquals(OnOffType.ON, getChannelState(INFO_STATE));
assertEquals(OnOffType.ON, getChannelState(DOOR_STATE));
});
}
@Test
public void testFinishStateChannelIsSetToOnWhenProgramHasFinished() {
// given:
DeviceState deviceStateBefore = mock(DeviceState.class);
when(deviceStateBefore.getDeviceIdentifier()).thenReturn(DISHWASHER_DEVICE_THING_UID.getId());
when(deviceStateBefore.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
when(deviceStateBefore.isInState(any())).thenCallRealMethod();
getThingHandler().onDeviceStateUpdated(deviceStateBefore);
DeviceState deviceStateAfter = mock(DeviceState.class);
when(deviceStateAfter.getDeviceIdentifier()).thenReturn(DISHWASHER_DEVICE_THING_UID.getId());
when(deviceStateAfter.getStateType()).thenReturn(Optional.of(StateType.END_PROGRAMMED));
when(deviceStateAfter.isInState(any())).thenCallRealMethod();
// when:
getBridgeHandler().onDeviceStateUpdated(deviceStateAfter);
// then:
waitForAssert(() -> {
assertEquals(OnOffType.ON, getChannelState(FINISH_STATE));
});
}
@Test
public void testTransitionChannelUpdatesForNullValues() {
// given:
DeviceState deviceStateBefore = mock(DeviceState.class);
when(deviceStateBefore.getDeviceIdentifier()).thenReturn(DISHWASHER_DEVICE_THING_UID.getId());
when(deviceStateBefore.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
when(deviceStateBefore.isInState(any())).thenCallRealMethod();
when(deviceStateBefore.getRemainingTime()).thenReturn(Optional.empty());
when(deviceStateBefore.getProgress()).thenReturn(Optional.empty());
getThingHandler().onDeviceStateUpdated(deviceStateBefore);
DeviceState deviceStateAfter = mock(DeviceState.class);
when(deviceStateAfter.getDeviceIdentifier()).thenReturn(DISHWASHER_DEVICE_THING_UID.getId());
when(deviceStateAfter.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
when(deviceStateAfter.isInState(any())).thenCallRealMethod();
when(deviceStateAfter.getRemainingTime()).thenReturn(Optional.empty());
when(deviceStateAfter.getProgress()).thenReturn(Optional.empty());
// when:
getThingHandler().onDeviceStateUpdated(deviceStateAfter);
waitForAssert(() -> {
assertEquals(NULL_VALUE_STATE, getChannelState(PROGRAM_REMAINING_TIME));
assertEquals(NULL_VALUE_STATE, getChannelState(PROGRAM_PROGRESS));
});
}
@Test
public void testTransitionChannelUpdatesForValidValues() {
// given:
DeviceState deviceStateBefore = mock(DeviceState.class);
when(deviceStateBefore.getDeviceIdentifier()).thenReturn(DISHWASHER_DEVICE_THING_UID.getId());
when(deviceStateBefore.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
when(deviceStateBefore.isInState(any())).thenCallRealMethod();
when(deviceStateBefore.getRemainingTime()).thenReturn(Optional.of(10));
when(deviceStateBefore.getProgress()).thenReturn(Optional.of(80));
getThingHandler().onDeviceStateUpdated(deviceStateBefore);
DeviceState deviceStateAfter = mock(DeviceState.class);
when(deviceStateAfter.getDeviceIdentifier()).thenReturn(DISHWASHER_DEVICE_THING_UID.getId());
when(deviceStateAfter.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
when(deviceStateAfter.isInState(any())).thenCallRealMethod();
when(deviceStateAfter.getRemainingTime()).thenReturn(Optional.of(10));
when(deviceStateAfter.getProgress()).thenReturn(Optional.of(80));
// when:
getThingHandler().onDeviceStateUpdated(deviceStateAfter);
waitForAssert(() -> {
assertEquals(new DecimalType(10), getChannelState(PROGRAM_REMAINING_TIME));
assertEquals(new DecimalType(80), getChannelState(PROGRAM_PROGRESS));
});
}
@Test
public void testActionsChannelUpdatesForValidValues() {
// given:
ActionsState actionsState = mock(ActionsState.class);
when(actionsState.getDeviceIdentifier()).thenReturn(DISHWASHER_DEVICE_THING_UID.getId());
when(actionsState.canBeStarted()).thenReturn(true);
when(actionsState.canBeStopped()).thenReturn(false);
when(actionsState.canBeSwitchedOn()).thenReturn(true);
when(actionsState.canBeSwitchedOff()).thenReturn(false);
// when:
getBridgeHandler().onProcessActionUpdated(actionsState);
// then:
waitForAssert(() -> {
assertEquals(OnOffType.ON, getChannelState(REMOTE_CONTROL_CAN_BE_STARTED));
assertEquals(OnOffType.OFF, getChannelState(REMOTE_CONTROL_CAN_BE_STOPPED));
assertEquals(OnOffType.ON, getChannelState(REMOTE_CONTROL_CAN_BE_SWITCHED_ON));
assertEquals(OnOffType.OFF, getChannelState(REMOTE_CONTROL_CAN_BE_SWITCHED_OFF));
});
}
}

View File

@@ -0,0 +1,241 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.mielecloud.internal.handler;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
import static org.openhab.binding.mielecloud.internal.MieleCloudBindingConstants.Channels.*;
import static org.openhab.binding.mielecloud.internal.util.MieleCloudBindingIntegrationTestConstants.DRYER_DEVICE_THING_UID;
import java.util.Optional;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.Test;
import org.openhab.binding.mielecloud.internal.MieleCloudBindingConstants;
import org.openhab.binding.mielecloud.internal.util.MieleCloudBindingIntegrationTestConstants;
import org.openhab.binding.mielecloud.internal.webservice.api.ActionsState;
import org.openhab.binding.mielecloud.internal.webservice.api.DeviceState;
import org.openhab.binding.mielecloud.internal.webservice.api.PowerStatus;
import org.openhab.binding.mielecloud.internal.webservice.api.ProgramStatus;
import org.openhab.binding.mielecloud.internal.webservice.api.json.StateType;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.StringType;
/**
* @author Björn Lange - Initial contribution
* @author Benjamin Bolte - Add info state channel and map signal flags from API tests
* @author Björn Lange - Add elapsed time channel
*/
@NonNullByDefault
public class DryerDeviceThingHandlerTest extends AbstractMieleThingHandlerTest {
@Override
protected AbstractMieleThingHandler setUpThingHandler() {
return createThingHandler(MieleCloudBindingConstants.THING_TYPE_DRYER, DRYER_DEVICE_THING_UID,
DryerDeviceThingHandler.class, MieleCloudBindingIntegrationTestConstants.SERIAL_NUMBER);
}
@Test
public void testChannelUpdatesForNullValues() {
// given:
DeviceState deviceState = mock(DeviceState.class);
when(deviceState.getDeviceIdentifier()).thenReturn(DRYER_DEVICE_THING_UID.getId());
when(deviceState.getStateType()).thenReturn(Optional.empty());
when(deviceState.isRemoteControlEnabled()).thenReturn(Optional.empty());
when(deviceState.getSelectedProgram()).thenReturn(Optional.empty());
when(deviceState.getSelectedProgramId()).thenReturn(Optional.empty());
when(deviceState.getProgramPhase()).thenReturn(Optional.empty());
when(deviceState.getProgramPhaseRaw()).thenReturn(Optional.empty());
when(deviceState.getStatus()).thenReturn(Optional.empty());
when(deviceState.getStatusRaw()).thenReturn(Optional.empty());
when(deviceState.getStartTime()).thenReturn(Optional.empty());
when(deviceState.getElapsedTime()).thenReturn(Optional.empty());
when(deviceState.getDryingTarget()).thenReturn(Optional.empty());
when(deviceState.getDryingTargetRaw()).thenReturn(Optional.empty());
when(deviceState.getLightState()).thenReturn(Optional.empty());
when(deviceState.getDoorState()).thenReturn(Optional.empty());
// when:
getBridgeHandler().onDeviceStateUpdated(deviceState);
// then:
waitForAssert(() -> {
assertEquals(NULL_VALUE_STATE, getChannelState(PROGRAM_ACTIVE));
assertEquals(NULL_VALUE_STATE, getChannelState(PROGRAM_ACTIVE_RAW));
assertEquals(NULL_VALUE_STATE, getChannelState(PROGRAM_PHASE));
assertEquals(NULL_VALUE_STATE, getChannelState(PROGRAM_PHASE_RAW));
assertEquals(NULL_VALUE_STATE, getChannelState(OPERATION_STATE));
assertEquals(NULL_VALUE_STATE, getChannelState(OPERATION_STATE_RAW));
assertEquals(new StringType(ProgramStatus.PROGRAM_STOPPED.getState()), getChannelState(PROGRAM_START_STOP));
assertEquals(new StringType(PowerStatus.POWER_ON.getState()), getChannelState(POWER_ON_OFF));
assertEquals(NULL_VALUE_STATE, getChannelState(DELAYED_START_TIME));
assertEquals(NULL_VALUE_STATE, getChannelState(PROGRAM_ELAPSED_TIME));
assertEquals(NULL_VALUE_STATE, getChannelState(DRYING_TARGET));
assertEquals(NULL_VALUE_STATE, getChannelState(DRYING_TARGET_RAW));
assertEquals(NULL_VALUE_STATE, getChannelState(LIGHT_SWITCH));
assertEquals(NULL_VALUE_STATE, getChannelState(DOOR_STATE));
});
}
@Test
public void testChannelUpdatesForValidValues() {
// given:
DeviceState deviceState = mock(DeviceState.class);
when(deviceState.isInState(any())).thenCallRealMethod();
when(deviceState.getDeviceIdentifier()).thenReturn(DRYER_DEVICE_THING_UID.getId());
when(deviceState.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
when(deviceState.isRemoteControlEnabled()).thenReturn(Optional.of(true));
when(deviceState.getSelectedProgram()).thenReturn(Optional.of("Baumwolle"));
when(deviceState.getSelectedProgramId()).thenReturn(Optional.of(34L));
when(deviceState.getProgramPhase()).thenReturn(Optional.of("Schleudern"));
when(deviceState.getProgramPhaseRaw()).thenReturn(Optional.of(3));
when(deviceState.getStatus()).thenReturn(Optional.of("Running"));
when(deviceState.getStatusRaw()).thenReturn(Optional.of(StateType.RUNNING.getCode()));
when(deviceState.getStartTime()).thenReturn(Optional.of(3600));
when(deviceState.getElapsedTime()).thenReturn(Optional.of(61));
when(deviceState.getDryingTarget()).thenReturn(Optional.of("Schranktrocken"));
when(deviceState.getDryingTargetRaw()).thenReturn(Optional.of(3));
when(deviceState.hasError()).thenReturn(true);
when(deviceState.hasInfo()).thenReturn(true);
when(deviceState.getLightState()).thenReturn(Optional.of(false));
when(deviceState.getDoorState()).thenReturn(Optional.of(false));
// when:
getBridgeHandler().onDeviceStateUpdated(deviceState);
// then:
waitForAssert(() -> {
assertEquals(new StringType("Baumwolle"), getChannelState(PROGRAM_ACTIVE));
assertEquals(new DecimalType(34), getChannelState(PROGRAM_ACTIVE_RAW));
assertEquals(new StringType("Schleudern"), getChannelState(PROGRAM_PHASE));
assertEquals(new DecimalType(3), getChannelState(PROGRAM_PHASE_RAW));
assertEquals(new StringType("Running"), getChannelState(OPERATION_STATE));
assertEquals(new DecimalType(StateType.RUNNING.getCode()), getChannelState(OPERATION_STATE_RAW));
assertEquals(new StringType(ProgramStatus.PROGRAM_STARTED.getState()), getChannelState(PROGRAM_START_STOP));
assertEquals(new StringType(PowerStatus.POWER_ON.getState()), getChannelState(POWER_ON_OFF));
assertEquals(new DecimalType(3600), getChannelState(DELAYED_START_TIME));
assertEquals(new DecimalType(61), getChannelState(PROGRAM_ELAPSED_TIME));
assertEquals(new StringType("Schranktrocken"), getChannelState(DRYING_TARGET));
assertEquals(new DecimalType(3), getChannelState(DRYING_TARGET_RAW));
assertEquals(OnOffType.ON, getChannelState(ERROR_STATE));
assertEquals(OnOffType.ON, getChannelState(INFO_STATE));
assertEquals(OnOffType.OFF, getChannelState(LIGHT_SWITCH));
assertEquals(OnOffType.OFF, getChannelState(DOOR_STATE));
});
}
@Test
public void testFinishStateChannelIsSetToOnWhenProgramHasFinished() {
// given:
DeviceState deviceStateBefore = mock(DeviceState.class);
when(deviceStateBefore.getDeviceIdentifier()).thenReturn(DRYER_DEVICE_THING_UID.getId());
when(deviceStateBefore.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
when(deviceStateBefore.isInState(any())).thenCallRealMethod();
getThingHandler().onDeviceStateUpdated(deviceStateBefore);
DeviceState deviceStateAfter = mock(DeviceState.class);
when(deviceStateAfter.getDeviceIdentifier()).thenReturn(DRYER_DEVICE_THING_UID.getId());
when(deviceStateAfter.getStateType()).thenReturn(Optional.of(StateType.END_PROGRAMMED));
when(deviceStateAfter.isInState(any())).thenCallRealMethod();
// when:
getBridgeHandler().onDeviceStateUpdated(deviceStateAfter);
// then:
waitForAssert(() -> {
assertEquals(OnOffType.ON, getChannelState(FINISH_STATE));
});
}
@Test
public void testTransitionChannelUpdatesForNullValues() {
// given:
DeviceState deviceStateBefore = mock(DeviceState.class);
when(deviceStateBefore.getDeviceIdentifier()).thenReturn(DRYER_DEVICE_THING_UID.getId());
when(deviceStateBefore.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
when(deviceStateBefore.isInState(any())).thenCallRealMethod();
when(deviceStateBefore.getRemainingTime()).thenReturn(Optional.empty());
when(deviceStateBefore.getProgress()).thenReturn(Optional.empty());
getThingHandler().onDeviceStateUpdated(deviceStateBefore);
DeviceState deviceStateAfter = mock(DeviceState.class);
when(deviceStateAfter.getDeviceIdentifier()).thenReturn(DRYER_DEVICE_THING_UID.getId());
when(deviceStateAfter.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
when(deviceStateAfter.isInState(any())).thenCallRealMethod();
when(deviceStateAfter.getRemainingTime()).thenReturn(Optional.empty());
when(deviceStateAfter.getProgress()).thenReturn(Optional.empty());
// when:
getThingHandler().onDeviceStateUpdated(deviceStateAfter);
waitForAssert(() -> {
assertEquals(NULL_VALUE_STATE, getChannelState(PROGRAM_REMAINING_TIME));
assertEquals(NULL_VALUE_STATE, getChannelState(PROGRAM_PROGRESS));
});
}
@Test
public void testTransitionChannelUpdatesForValidValues() {
// given:
DeviceState deviceStateBefore = mock(DeviceState.class);
when(deviceStateBefore.getDeviceIdentifier()).thenReturn(DRYER_DEVICE_THING_UID.getId());
when(deviceStateBefore.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
when(deviceStateBefore.isInState(any())).thenCallRealMethod();
when(deviceStateBefore.getRemainingTime()).thenReturn(Optional.of(10));
when(deviceStateBefore.getProgress()).thenReturn(Optional.of(80));
getThingHandler().onDeviceStateUpdated(deviceStateBefore);
DeviceState deviceStateAfter = mock(DeviceState.class);
when(deviceStateAfter.getDeviceIdentifier()).thenReturn(DRYER_DEVICE_THING_UID.getId());
when(deviceStateAfter.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
when(deviceStateAfter.isInState(any())).thenCallRealMethod();
when(deviceStateAfter.getRemainingTime()).thenReturn(Optional.of(10));
when(deviceStateAfter.getProgress()).thenReturn(Optional.of(80));
// when:
getThingHandler().onDeviceStateUpdated(deviceStateAfter);
waitForAssert(() -> {
assertEquals(new DecimalType(10), getChannelState(PROGRAM_REMAINING_TIME));
assertEquals(new DecimalType(80), getChannelState(PROGRAM_PROGRESS));
});
}
@Test
public void testActionsChannelUpdatesForValidValues() {
// given:
ActionsState actionsState = mock(ActionsState.class);
when(actionsState.getDeviceIdentifier()).thenReturn(DRYER_DEVICE_THING_UID.getId());
when(actionsState.canBeStarted()).thenReturn(true);
when(actionsState.canBeStopped()).thenReturn(false);
when(actionsState.canBeSwitchedOn()).thenReturn(true);
when(actionsState.canBeSwitchedOff()).thenReturn(false);
when(actionsState.canControlLight()).thenReturn(true);
// when:
getBridgeHandler().onProcessActionUpdated(actionsState);
// then:
waitForAssert(() -> {
assertEquals(OnOffType.ON, getChannelState(REMOTE_CONTROL_CAN_BE_STARTED));
assertEquals(OnOffType.OFF, getChannelState(REMOTE_CONTROL_CAN_BE_STOPPED));
assertEquals(OnOffType.ON, getChannelState(REMOTE_CONTROL_CAN_BE_SWITCHED_ON));
assertEquals(OnOffType.OFF, getChannelState(REMOTE_CONTROL_CAN_BE_SWITCHED_OFF));
assertEquals(OnOffType.ON, getChannelState(LIGHT_CAN_BE_CONTROLLED));
});
}
}

View File

@@ -0,0 +1,122 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.mielecloud.internal.handler;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.*;
import static org.openhab.binding.mielecloud.internal.MieleCloudBindingConstants.Channels.*;
import static org.openhab.binding.mielecloud.internal.util.MieleCloudBindingIntegrationTestConstants.HOB_DEVICE_THING_UID;
import java.util.Optional;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.Test;
import org.openhab.binding.mielecloud.internal.MieleCloudBindingConstants;
import org.openhab.binding.mielecloud.internal.util.MieleCloudBindingIntegrationTestConstants;
import org.openhab.binding.mielecloud.internal.webservice.api.DeviceState;
import org.openhab.binding.mielecloud.internal.webservice.api.json.StateType;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.StringType;
/**
* @author Björn Lange - Initial contribution
* @author Benjamin Bolte - Add plate step
* @author Benjamin Bolte - Add info state channel and map signal flags from API tests
*/
@NonNullByDefault
public class HobDeviceThingHandlerTest extends AbstractMieleThingHandlerTest {
@Override
protected AbstractMieleThingHandler setUpThingHandler() {
return createThingHandler(MieleCloudBindingConstants.THING_TYPE_HOB, HOB_DEVICE_THING_UID,
HobDeviceThingHandler.class, MieleCloudBindingIntegrationTestConstants.SERIAL_NUMBER);
}
@Test
public void testChannelUpdatesForNullValues() {
// given:
DeviceState deviceState = mock(DeviceState.class);
when(deviceState.getDeviceIdentifier()).thenReturn(HOB_DEVICE_THING_UID.getId());
when(deviceState.getStateType()).thenReturn(Optional.empty());
when(deviceState.isRemoteControlEnabled()).thenReturn(Optional.empty());
when(deviceState.getStatus()).thenReturn(Optional.empty());
when(deviceState.getStatusRaw()).thenReturn(Optional.empty());
when(deviceState.getPlateStep(anyInt())).thenReturn(Optional.empty());
when(deviceState.getPlateStepRaw(anyInt())).thenReturn(Optional.empty());
// when:
getBridgeHandler().onDeviceStateUpdated(deviceState);
// then:
waitForAssert(() -> {
assertEquals(NULL_VALUE_STATE, getChannelState(OPERATION_STATE));
assertEquals(NULL_VALUE_STATE, getChannelState(OPERATION_STATE_RAW));
assertEquals(NULL_VALUE_STATE, getChannelState(PLATE_1_POWER_STEP));
assertEquals(NULL_VALUE_STATE, getChannelState(PLATE_1_POWER_STEP_RAW));
assertEquals(NULL_VALUE_STATE, getChannelState(PLATE_2_POWER_STEP));
assertEquals(NULL_VALUE_STATE, getChannelState(PLATE_2_POWER_STEP_RAW));
assertEquals(NULL_VALUE_STATE, getChannelState(PLATE_3_POWER_STEP));
assertEquals(NULL_VALUE_STATE, getChannelState(PLATE_3_POWER_STEP_RAW));
assertEquals(NULL_VALUE_STATE, getChannelState(PLATE_4_POWER_STEP));
assertEquals(NULL_VALUE_STATE, getChannelState(PLATE_4_POWER_STEP_RAW));
assertEquals(NULL_VALUE_STATE, getChannelState(PLATE_5_POWER_STEP));
assertEquals(NULL_VALUE_STATE, getChannelState(PLATE_5_POWER_STEP_RAW));
assertEquals(NULL_VALUE_STATE, getChannelState(PLATE_6_POWER_STEP));
assertEquals(NULL_VALUE_STATE, getChannelState(PLATE_6_POWER_STEP_RAW));
});
}
@Test
public void testChannelUpdatesForValidValues() {
// given:
DeviceState deviceState = mock(DeviceState.class);
when(deviceState.getDeviceIdentifier()).thenReturn(HOB_DEVICE_THING_UID.getId());
when(deviceState.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
when(deviceState.isRemoteControlEnabled()).thenReturn(Optional.of(false));
when(deviceState.getStatus()).thenReturn(Optional.of("Running"));
when(deviceState.getStatusRaw()).thenReturn(Optional.of(StateType.RUNNING.getCode()));
when(deviceState.hasError()).thenReturn(false);
when(deviceState.hasInfo()).thenReturn(true);
when(deviceState.getPlateStep(0)).thenReturn(Optional.of("1."));
when(deviceState.getPlateStepRaw(0)).thenReturn(Optional.of(2));
when(deviceState.getPlateStep(1)).thenReturn(Optional.empty());
when(deviceState.getPlateStep(2)).thenReturn(Optional.empty());
when(deviceState.getPlateStep(3)).thenReturn(Optional.empty());
when(deviceState.getPlateStep(4)).thenReturn(Optional.empty());
when(deviceState.getPlateStep(5)).thenReturn(Optional.empty());
// when:
getBridgeHandler().onDeviceStateUpdated(deviceState);
// then:
waitForAssert(() -> {
assertEquals(new StringType("Running"), getChannelState(OPERATION_STATE));
assertEquals(new DecimalType(StateType.RUNNING.getCode()), getChannelState(OPERATION_STATE_RAW));
assertEquals(OnOffType.OFF, getChannelState(ERROR_STATE));
assertEquals(OnOffType.ON, getChannelState(INFO_STATE));
assertEquals(new StringType("1."), getChannelState(PLATE_1_POWER_STEP));
assertEquals(new DecimalType(2), getChannelState(PLATE_1_POWER_STEP_RAW));
assertEquals(NULL_VALUE_STATE, getChannelState(PLATE_2_POWER_STEP));
assertEquals(NULL_VALUE_STATE, getChannelState(PLATE_2_POWER_STEP_RAW));
assertEquals(NULL_VALUE_STATE, getChannelState(PLATE_3_POWER_STEP));
assertEquals(NULL_VALUE_STATE, getChannelState(PLATE_3_POWER_STEP_RAW));
assertEquals(NULL_VALUE_STATE, getChannelState(PLATE_4_POWER_STEP));
assertEquals(NULL_VALUE_STATE, getChannelState(PLATE_4_POWER_STEP_RAW));
assertEquals(NULL_VALUE_STATE, getChannelState(PLATE_5_POWER_STEP));
assertEquals(NULL_VALUE_STATE, getChannelState(PLATE_5_POWER_STEP_RAW));
assertEquals(NULL_VALUE_STATE, getChannelState(PLATE_6_POWER_STEP));
assertEquals(NULL_VALUE_STATE, getChannelState(PLATE_6_POWER_STEP_RAW));
});
}
}

View File

@@ -0,0 +1,131 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.mielecloud.internal.handler;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.*;
import static org.openhab.binding.mielecloud.internal.MieleCloudBindingConstants.Channels.*;
import static org.openhab.binding.mielecloud.internal.util.MieleCloudBindingIntegrationTestConstants.HOOD_DEVICE_THING_UID;
import java.util.Optional;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.Test;
import org.openhab.binding.mielecloud.internal.MieleCloudBindingConstants;
import org.openhab.binding.mielecloud.internal.util.MieleCloudBindingIntegrationTestConstants;
import org.openhab.binding.mielecloud.internal.webservice.api.ActionsState;
import org.openhab.binding.mielecloud.internal.webservice.api.DeviceState;
import org.openhab.binding.mielecloud.internal.webservice.api.PowerStatus;
import org.openhab.binding.mielecloud.internal.webservice.api.json.StateType;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.StringType;
/**
* @author Björn Lange - Initial contribution
* @author Benjamin Bolte - Add info state channel and map signal flags from API tests
*/
@NonNullByDefault
public class HoodDeviceThingHandlerTest extends AbstractMieleThingHandlerTest {
@Override
protected AbstractMieleThingHandler setUpThingHandler() {
return createThingHandler(MieleCloudBindingConstants.THING_TYPE_HOOD, HOOD_DEVICE_THING_UID,
HoodDeviceThingHandler.class, MieleCloudBindingIntegrationTestConstants.SERIAL_NUMBER);
}
@Test
public void testChannelUpdatesForNullValues() {
// given:
DeviceState deviceState = mock(DeviceState.class);
when(deviceState.getDeviceIdentifier()).thenReturn(HOOD_DEVICE_THING_UID.getId());
when(deviceState.getStateType()).thenReturn(Optional.empty());
when(deviceState.isRemoteControlEnabled()).thenReturn(Optional.empty());
when(deviceState.getProgramPhase()).thenReturn(Optional.empty());
when(deviceState.getProgramPhaseRaw()).thenReturn(Optional.empty());
when(deviceState.getStatus()).thenReturn(Optional.empty());
when(deviceState.getStatusRaw()).thenReturn(Optional.empty());
when(deviceState.getVentilationStep()).thenReturn(Optional.empty());
when(deviceState.getVentilationStepRaw()).thenReturn(Optional.empty());
when(deviceState.getLightState()).thenReturn(Optional.empty());
// when:
getBridgeHandler().onDeviceStateUpdated(deviceState);
// then:
waitForAssert(() -> {
assertEquals(NULL_VALUE_STATE, getChannelState(PROGRAM_PHASE));
assertEquals(NULL_VALUE_STATE, getChannelState(PROGRAM_PHASE_RAW));
assertEquals(NULL_VALUE_STATE, getChannelState(OPERATION_STATE));
assertEquals(NULL_VALUE_STATE, getChannelState(OPERATION_STATE_RAW));
assertEquals(NULL_VALUE_STATE, getChannelState(VENTILATION_POWER));
assertEquals(NULL_VALUE_STATE, getChannelState(VENTILATION_POWER_RAW));
assertEquals(NULL_VALUE_STATE, getChannelState(LIGHT_SWITCH));
assertEquals(new StringType(PowerStatus.POWER_ON.getState()), getChannelState(POWER_ON_OFF));
});
}
@Test
public void testChannelUpdatesForValidValues() {
// given:
DeviceState deviceState = mock(DeviceState.class);
when(deviceState.getDeviceIdentifier()).thenReturn(HOOD_DEVICE_THING_UID.getId());
when(deviceState.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
when(deviceState.isRemoteControlEnabled()).thenReturn(Optional.of(false));
when(deviceState.getProgramPhase()).thenReturn(Optional.of("Kochen"));
when(deviceState.getProgramPhaseRaw()).thenReturn(Optional.of(5));
when(deviceState.getStatus()).thenReturn(Optional.of("Running"));
when(deviceState.getStatusRaw()).thenReturn(Optional.of(StateType.RUNNING.getCode()));
when(deviceState.getVentilationStep()).thenReturn(Optional.of("2"));
when(deviceState.getVentilationStepRaw()).thenReturn(Optional.of(2));
when(deviceState.hasError()).thenReturn(false);
when(deviceState.hasInfo()).thenReturn(true);
when(deviceState.getLightState()).thenReturn(Optional.of(false));
// when:
getBridgeHandler().onDeviceStateUpdated(deviceState);
// then:
waitForAssert(() -> {
assertEquals(new StringType("Kochen"), getChannelState(PROGRAM_PHASE));
assertEquals(new DecimalType(5), getChannelState(PROGRAM_PHASE_RAW));
assertEquals(new StringType("Running"), getChannelState(OPERATION_STATE));
assertEquals(new DecimalType(StateType.RUNNING.getCode()), getChannelState(OPERATION_STATE_RAW));
assertEquals(new StringType(PowerStatus.POWER_ON.getState()), getChannelState(POWER_ON_OFF));
assertEquals(new StringType("2"), getChannelState(VENTILATION_POWER));
assertEquals(new DecimalType(2), getChannelState(VENTILATION_POWER_RAW));
assertEquals(OnOffType.OFF, getChannelState(ERROR_STATE));
assertEquals(OnOffType.ON, getChannelState(INFO_STATE));
assertEquals(OnOffType.OFF, getChannelState(LIGHT_SWITCH));
});
}
@Test
public void testActionsChannelUpdatesForValidValues() {
// given:
ActionsState actionsState = mock(ActionsState.class);
when(actionsState.getDeviceIdentifier()).thenReturn(HOOD_DEVICE_THING_UID.getId());
when(actionsState.canBeSwitchedOn()).thenReturn(true);
when(actionsState.canBeSwitchedOff()).thenReturn(false);
when(actionsState.canControlLight()).thenReturn(true);
// when:
getBridgeHandler().onProcessActionUpdated(actionsState);
// then:
waitForAssert(() -> {
assertEquals(OnOffType.ON, getChannelState(REMOTE_CONTROL_CAN_BE_SWITCHED_ON));
assertEquals(OnOffType.OFF, getChannelState(REMOTE_CONTROL_CAN_BE_SWITCHED_OFF));
assertEquals(OnOffType.ON, getChannelState(LIGHT_CAN_BE_CONTROLLED));
});
}
}

View File

@@ -0,0 +1,562 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.mielecloud.internal.handler;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import static org.openhab.binding.mielecloud.internal.util.MieleCloudBindingIntegrationTestConstants.*;
import static org.openhab.binding.mielecloud.internal.util.ReflectionUtil.*;
import java.util.Collections;
import java.util.Objects;
import java.util.Optional;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.openhab.binding.mielecloud.internal.MieleCloudBindingConstants;
import org.openhab.binding.mielecloud.internal.MieleCloudBindingConstants.I18NKeys;
import org.openhab.binding.mielecloud.internal.auth.OAuthTokenRefresher;
import org.openhab.binding.mielecloud.internal.auth.OpenHabOAuthTokenRefresher;
import org.openhab.binding.mielecloud.internal.util.MieleCloudBindingIntegrationTestConstants;
import org.openhab.binding.mielecloud.internal.util.OpenHabOsgiTest;
import org.openhab.binding.mielecloud.internal.webservice.ConnectionError;
import org.openhab.binding.mielecloud.internal.webservice.MieleWebservice;
import org.openhab.binding.mielecloud.internal.webservice.MieleWebserviceFactory;
import org.openhab.binding.mielecloud.internal.webservice.language.CombiningLanguageProvider;
import org.openhab.core.auth.client.oauth2.AccessTokenResponse;
import org.openhab.core.auth.client.oauth2.OAuthClientService;
import org.openhab.core.auth.client.oauth2.OAuthFactory;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.binding.ThingHandlerFactory;
import org.openhab.core.thing.binding.builder.BridgeBuilder;
/**
* @author Björn Lange - Initial contribution
*/
@NonNullByDefault
public class MieleBridgeHandlerTest extends OpenHabOsgiTest {
private static final String SERVICE_HANDLE = MieleCloudBindingIntegrationTestConstants.EMAIL;
private static final String CONFIG_PARAM_LOCALE = "locale";
@Nullable
private MieleWebservice webserviceMock;
@Nullable
private String webserviceAccessToken;
@Nullable
private OAuthFactory oauthFactoryMock;
@Nullable
private OAuthClientService oauthClientServiceMock;
@Nullable
private Bridge bridge;
@Nullable
private MieleBridgeHandler handler;
private MieleWebservice getWebserviceMock() {
assertNotNull(webserviceMock);
return Objects.requireNonNull(webserviceMock);
}
private OAuthFactory getOAuthFactoryMock() {
assertNotNull(oauthFactoryMock);
return Objects.requireNonNull(oauthFactoryMock);
}
private OAuthClientService getOAuthClientServiceMock() {
OAuthClientService oauthClientServiceMock = this.oauthClientServiceMock;
assertNotNull(oauthClientServiceMock);
return Objects.requireNonNull(oauthClientServiceMock);
}
private Bridge getBridge() {
assertNotNull(bridge);
return Objects.requireNonNull(bridge);
}
private MieleBridgeHandler getHandler() {
assertNotNull(handler);
return Objects.requireNonNull(handler);
}
@BeforeEach
public void setUp() throws Exception {
setUpWebservice();
setUpBridgeThingAndHandler();
setUpOAuthFactory();
}
private void setUpWebservice() throws NoSuchFieldException, IllegalAccessException {
webserviceMock = mock(MieleWebservice.class);
doAnswer(invocation -> {
if (invocation != null) {
webserviceAccessToken = invocation.getArgument(0);
}
return null;
}).when(getWebserviceMock()).setAccessToken(anyString());
when(getWebserviceMock().hasAccessToken()).then(invocation -> webserviceAccessToken != null);
MieleWebserviceFactory webserviceFactory = mock(MieleWebserviceFactory.class);
when(webserviceFactory.create(any())).thenReturn(getWebserviceMock());
MieleHandlerFactory handlerFactory = getService(ThingHandlerFactory.class, MieleHandlerFactory.class);
assertNotNull(handlerFactory);
setPrivate(Objects.requireNonNull(handlerFactory), "webserviceFactory", webserviceFactory);
}
private void setUpBridgeThingAndHandler() {
when(getWebserviceMock().hasAccessToken()).thenReturn(false);
bridge = BridgeBuilder
.create(MieleCloudBindingConstants.THING_TYPE_BRIDGE,
MieleCloudBindingIntegrationTestConstants.BRIDGE_THING_UID)
.withConfiguration(
new Configuration(Collections.singletonMap(MieleCloudBindingConstants.CONFIG_PARAM_EMAIL,
MieleCloudBindingIntegrationTestConstants.EMAIL)))
.withLabel(MIELE_CLOUD_ACCOUNT_LABEL).build();
assertNotNull(bridge);
getThingRegistry().add(getBridge());
waitForAssert(() -> {
assertNotNull(getBridge().getHandler());
assertTrue(getBridge().getHandler() instanceof MieleBridgeHandler, "Handler type is wrong");
});
handler = (MieleBridgeHandler) getBridge().getHandler();
}
private void setUpOAuthFactory() throws Exception {
AccessTokenResponse accessTokenResponse = new AccessTokenResponse();
accessTokenResponse.setAccessToken(ACCESS_TOKEN);
oauthClientServiceMock = mock(OAuthClientService.class);
when(oauthClientServiceMock.getAccessTokenResponse()).thenReturn(accessTokenResponse);
OAuthFactory oAuthFactory = mock(OAuthFactory.class);
Mockito.when(oAuthFactory.getOAuthClientService(SERVICE_HANDLE)).thenReturn(getOAuthClientServiceMock());
oauthFactoryMock = oAuthFactory;
OpenHabOAuthTokenRefresher tokenRefresher = getService(OAuthTokenRefresher.class,
OpenHabOAuthTokenRefresher.class);
assertNotNull(tokenRefresher);
setPrivate(Objects.requireNonNull(tokenRefresher), "oauthFactory", oAuthFactory);
}
private void initializeBridgeWithTokens() {
getHandler().initialize();
assertThingStatusIs(ThingStatus.UNKNOWN);
}
private void assertThingStatusIs(ThingStatus expectedStatus) {
assertThingStatusIs(expectedStatus, ThingStatusDetail.NONE);
}
private void assertThingStatusIs(ThingStatus expectedStatus, ThingStatusDetail expectedStatusDetail) {
assertThingStatusIs(expectedStatus, expectedStatusDetail, null);
}
private void assertThingStatusIs(ThingStatus expectedStatus, ThingStatusDetail expectedStatusDetail,
@Nullable String expectedDescription) {
assertEquals(expectedStatus, getBridge().getStatus());
assertEquals(expectedStatusDetail, getBridge().getStatusInfo().getStatusDetail());
if (expectedDescription == null) {
assertNull(getBridge().getStatusInfo().getDescription());
} else {
assertEquals(expectedDescription, getBridge().getStatusInfo().getDescription());
}
}
@Test
public void testThingStatusIsSetToOfflineWithDetailConfigurationPendingAndDescriptionWhenTokensAreNotPassedViaInitialConfiguration()
throws Exception {
when(getOAuthClientServiceMock().getAccessTokenResponse()).thenReturn(null);
// when:
getHandler().initialize();
// then:
assertThingStatusIs(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_PENDING,
MieleCloudBindingConstants.I18NKeys.BRIDGE_STATUS_DESCRIPTION_ACCESS_TOKEN_NOT_CONFIGURED);
}
@Test
public void testThingStatusIsSetToOfflineWithDetailConfigurationErrorAndDescriptionWhenTheEmailAddressIsInvalid()
throws Exception {
// given:
getBridge().getConfiguration().setProperties(
Collections.singletonMap(MieleCloudBindingConstants.CONFIG_PARAM_EMAIL, "not!a!mail$address"));
// when:
getHandler().initialize();
// then:
assertThingStatusIs(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
MieleCloudBindingConstants.I18NKeys.BRIDGE_STATUS_DESCRIPTION_INVALID_EMAIL);
}
@Test
public void testThingStatusIsSetToOfflineWithDetailConfigurationErrorAndDescriptionWhenTheMieleAccountHasNotBeenAuthorized()
throws Exception {
// given:
OAuthFactory oAuthFactory = mock(OAuthFactory.class);
Mockito.when(oAuthFactory.getOAuthClientService(SERVICE_HANDLE)).thenReturn(null);
OpenHabOAuthTokenRefresher tokenRefresher = getService(OAuthTokenRefresher.class,
OpenHabOAuthTokenRefresher.class);
assertNotNull(tokenRefresher);
// Clear the setup configuration and use the failing one for this test.
setPrivate(Objects.requireNonNull(tokenRefresher), "oauthFactory", oAuthFactory);
// when:
getHandler().initialize();
// then:
assertThingStatusIs(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
MieleCloudBindingConstants.I18NKeys.BRIDGE_STATUS_DESCRIPTION_ACCOUNT_NOT_AUTHORIZED);
}
@Test
public void testThingStatusIsSetToUnknownAndThingWaitsForCloudConnectionWhenTheMieleAccountBecomesAuthorizedAfterTheBridgeWasInitialized()
throws Exception {
// given:
OAuthFactory oAuthFactory = mock(OAuthFactory.class);
Mockito.when(oAuthFactory.getOAuthClientService(SERVICE_HANDLE)).thenReturn(null);
OpenHabOAuthTokenRefresher tokenRefresher = getService(OAuthTokenRefresher.class,
OpenHabOAuthTokenRefresher.class);
assertNotNull(tokenRefresher);
// Clear the setup configuration and use the failing one for this test.
setPrivate(Objects.requireNonNull(tokenRefresher), "oauthFactory", oAuthFactory);
getHandler().initialize();
assertThingStatusIs(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
I18NKeys.BRIDGE_STATUS_DESCRIPTION_ACCOUNT_NOT_AUTHORIZED);
setUpOAuthFactory();
// when:
getHandler().dispose();
getHandler().initialize();
// then:
assertThingStatusIs(ThingStatus.UNKNOWN);
}
@Test
public void whenTheSseConnectionIsEstablishedThenTheThingStatusIsSetToOnline() throws Exception {
// given:
initializeBridgeWithTokens();
// when:
getHandler().onConnectionAlive();
// then:
assertThingStatusIs(ThingStatus.ONLINE);
}
@Test
public void whenAnAuthorizationFailedErrorIsReportedThenTheAccessTokenIsRefreshedAndTheSseConnectionRestored()
throws Exception {
// given:
AccessTokenResponse accessTokenResponse = new AccessTokenResponse();
accessTokenResponse.setAccessToken(ACCESS_TOKEN);
when(getOAuthClientServiceMock().refreshToken()).thenReturn(accessTokenResponse);
initializeBridgeWithTokens();
getHandler().onConnectionAlive();
// when:
getHandler().onConnectionError(ConnectionError.AUTHORIZATION_FAILED, 0);
// then:
verify(getOAuthClientServiceMock()).refreshToken();
verify(getWebserviceMock()).connectSse();
assertThingStatusIs(ThingStatus.ONLINE);
}
@Test
public void whenAnAuthorizationFailedErrorIsReportedAndTokenRefreshFailsThenSseConnectionIsTerminatedAndTheStatusSetToOfflineWithDetailConfigurationError()
throws Exception {
// given:
when(getOAuthClientServiceMock().refreshToken()).thenReturn(new AccessTokenResponse());
initializeBridgeWithTokens();
getHandler().onConnectionAlive();
// when:
getHandler().onConnectionError(ConnectionError.AUTHORIZATION_FAILED, 0);
// then:
verify(getOAuthClientServiceMock()).refreshToken();
verify(getWebserviceMock()).disconnectSse();
assertThingStatusIs(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
I18NKeys.BRIDGE_STATUS_DESCRIPTION_ACCESS_TOKEN_REFRESH_FAILED);
}
@Test
public void whenARequestExecutionFailedErrorIsReportedAndNoRetriesHaveBeenMadeThenItHasNoEffectOnTheThingStatus()
throws Exception {
// given:
initializeBridgeWithTokens();
getHandler().onConnectionAlive();
// when:
getHandler().onConnectionError(ConnectionError.REQUEST_EXECUTION_FAILED, 0);
// then:
assertThingStatusIs(ThingStatus.ONLINE);
}
@Test
public void whenARequestExecutionFailedErrorIsReportedWithSufficientRetriesThenTheThingStatusIsOfflineWithDetailCommunicationError()
throws Exception {
// given:
initializeBridgeWithTokens();
getHandler().onConnectionAlive();
// when:
getHandler().onConnectionError(ConnectionError.REQUEST_EXECUTION_FAILED, 10);
// then:
assertThingStatusIs(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
}
@Test
public void whenARequestExecutionFailedErrorIsReportedAndThingIsInStatusUnknownThenTheThingStatusIsOfflineWithDetailCommunicationError()
throws Exception {
// given:
initializeBridgeWithTokens();
// when:
getHandler().onConnectionError(ConnectionError.REQUEST_EXECUTION_FAILED, 0);
// then:
assertThingStatusIs(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
}
@Test
public void whenAServiceUnavailableErrorIsReportedWithSufficientRetriesThenTheThingStatusIsOfflineWithDetailCommunicationError()
throws Exception {
// given:
initializeBridgeWithTokens();
getHandler().onConnectionAlive();
// when:
getHandler().onConnectionError(ConnectionError.SERVICE_UNAVAILABLE, 10);
// then:
assertThingStatusIs(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
}
@Test
public void whenAResponseMalformedErrorIsReportedWithSufficientRetriesThenTheThingStatusIsOfflineWithDetailCommunicationError()
throws Exception {
// given:
initializeBridgeWithTokens();
// when:
getHandler().onConnectionError(ConnectionError.RESPONSE_MALFORMED, 10);
// then:
assertThingStatusIs(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
}
@Test
public void whenATimeoutErrorIsReportedWithSufficientRetriesThenTheThingStatusIsOfflineWithDetailCommunicationError()
throws Exception {
// given:
initializeBridgeWithTokens();
// when:
getHandler().onConnectionError(ConnectionError.TIMEOUT, 10);
// then:
assertThingStatusIs(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
}
@Test
public void whenATooManyRequestsErrorIsReportedWithSufficientRetriesThenTheThingStatusIsOfflineWithDetailCommunicationError()
throws Exception {
// given:
initializeBridgeWithTokens();
// when:
getHandler().onConnectionError(ConnectionError.TOO_MANY_RERQUESTS, 10);
// then:
assertThingStatusIs(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
}
@Test
public void whenAServerErrorIsReportedWithSufficientRetriesThenTheThingStatusIsOfflineWithDetailCommunicationError()
throws Exception {
// given:
initializeBridgeWithTokens();
// when:
getHandler().onConnectionError(ConnectionError.SERVER_ERROR, 10);
// then:
assertThingStatusIs(ThingStatus.OFFLINE, ThingStatusDetail.NONE,
I18NKeys.BRIDGE_STATUS_DESCRIPTION_TRANSIENT_HTTP_ERROR);
}
@Test
public void whenARequestInterruptedErrorIsReportedWithSufficientRetriesThenTheThingStatusIsOfflineWithDetailCommunicationError()
throws Exception {
// given:
initializeBridgeWithTokens();
// when:
getHandler().onConnectionError(ConnectionError.REQUEST_INTERRUPTED, 10);
// then:
assertThingStatusIs(ThingStatus.OFFLINE, ThingStatusDetail.NONE,
I18NKeys.BRIDGE_STATUS_DESCRIPTION_TRANSIENT_HTTP_ERROR);
}
@Test
public void whenSomeOtherHttpErrorIsReportedWithSufficientRetriesThenTheThingStatusIsOfflineWithDetailCommunicationError()
throws Exception {
// given:
initializeBridgeWithTokens();
// when:
getHandler().onConnectionError(ConnectionError.OTHER_HTTP_ERROR, 10);
// then:
assertThingStatusIs(ThingStatus.OFFLINE, ThingStatusDetail.NONE,
I18NKeys.BRIDGE_STATUS_DESCRIPTION_TRANSIENT_HTTP_ERROR);
}
@Test
public void whenARequestIsInterruptedDuringInitializationThenTheThingStatusIsNotModified() throws Exception {
// given:
initializeBridgeWithTokens();
// when:
getHandler().onConnectionError(ConnectionError.REQUEST_INTERRUPTED, 0);
// then:
assertThingStatusIs(ThingStatus.UNKNOWN);
}
@Test
public void whenTheAccessTokenWasRefreshedThenTheWebserviceIsSetIntoAnOperationalState()
throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
// given:
getHandler().initialize();
// when:
getHandler().onNewAccessToken(ACCESS_TOKEN);
// then:
verify(getWebserviceMock(), atLeast(1)).setAccessToken(ACCESS_TOKEN);
verify(getWebserviceMock(), atLeast(1)).connectSse();
}
@Test
public void whenTheHandlerIsDisposedThenTheSseConnectionIsDisconnectedAndTheLanguageProviderIsUnset()
throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
// given:
getHandler().initialize();
// when:
getHandler().dispose();
// then:
verify(getWebserviceMock()).disconnectSse();
CombiningLanguageProvider languageProvider = getPrivate(getHandler(), "languageProvider");
assertNull(getPrivate(languageProvider, "prioritizedLanguageProvider"));
}
@Test
public void testNoLanguageIsReturnedWhenTheConfigurationParameterIsNotSet() {
// when:
Optional<String> language = getHandler().getLanguage();
// then:
assertFalse(language.isPresent());
}
@Test
public void testNoLanguageIsReturnedWhenTheConfigurationParameterIsEmpty() {
// given:
getHandler().handleConfigurationUpdate(Collections.singletonMap(CONFIG_PARAM_LOCALE, ""));
// when:
Optional<String> language = getHandler().getLanguage();
// then:
assertFalse(language.isPresent());
}
@Test
public void testNoLanguageIsReturnedWhenTheConfigurationParameterIsNotAValidTwoLetterLanguageCode() {
// given:
getHandler().handleConfigurationUpdate(Collections.singletonMap(CONFIG_PARAM_LOCALE, "Deutsch"));
// when:
Optional<String> language = getHandler().getLanguage();
// then:
assertFalse(language.isPresent());
}
@Test
public void testAValidTwoLetterLanguageCodeIsReturnedWhenTheConfigurationParameterIsSetToTheTwoLetterLanguageCode() {
// given:
getHandler().handleConfigurationUpdate(Collections.singletonMap(CONFIG_PARAM_LOCALE, "DE"));
// when:
String language = getHandler().getLanguage().get();
// then:
assertEquals("DE", language);
}
@Test
public void testWhenTheThingIsRemovedThenTheWebserviceIsLoggedOut() throws Exception {
// given:
initializeBridgeWithTokens();
// when:
getThingRegistry().remove(getHandler().getThing().getUID());
// then:
waitForAssert(() -> {
verify(getWebserviceMock()).logout();
});
}
@Test
public void testWhenTheThingIsRemovedThenTheTokensAreRemovedFromTheStorage() throws Exception {
// given:
initializeBridgeWithTokens();
// when:
getThingRegistry().remove(getHandler().getThing().getUID());
// then:
waitForAssert(() -> {
verify(getOAuthFactoryMock()).deleteServiceAndAccessToken(SERVICE_HANDLE);
});
}
}

View File

@@ -0,0 +1,310 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.mielecloud.internal.handler;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
import static org.openhab.binding.mielecloud.internal.util.MieleCloudBindingIntegrationTestConstants.*;
import static org.openhab.binding.mielecloud.internal.util.ReflectionUtil.*;
import java.util.Collections;
import java.util.Objects;
import java.util.Optional;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.openhab.binding.mielecloud.internal.MieleCloudBindingConstants;
import org.openhab.binding.mielecloud.internal.auth.OAuthTokenRefresher;
import org.openhab.binding.mielecloud.internal.auth.OpenHabOAuthTokenRefresher;
import org.openhab.binding.mielecloud.internal.util.MieleCloudBindingIntegrationTestConstants;
import org.openhab.binding.mielecloud.internal.webservice.MieleWebservice;
import org.openhab.core.auth.client.oauth2.AccessTokenResponse;
import org.openhab.core.auth.client.oauth2.OAuthClientService;
import org.openhab.core.auth.client.oauth2.OAuthFactory;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.test.java.JavaOSGiTest;
import org.openhab.core.test.storage.VolatileStorageService;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingRegistry;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.ThingUID;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerFactory;
import org.openhab.core.thing.binding.builder.BridgeBuilder;
import org.openhab.core.thing.binding.builder.ThingBuilder;
/**
* @author Björn Lange - Initial contribution
*/
@NonNullByDefault
public class MieleHandlerFactoryTest extends JavaOSGiTest {
private static final String DEVICE_IDENTIFIER = "000124430016";
private static final ThingUID WASHING_MACHINE_TYPE = new ThingUID(
MieleCloudBindingConstants.THING_TYPE_WASHING_MACHINE, DEVICE_IDENTIFIER);
private static final ThingUID OVEN_DEVICE_TYPE = new ThingUID(MieleCloudBindingConstants.THING_TYPE_OVEN,
DEVICE_IDENTIFIER);
private static final ThingUID HOB_DEVICE_TYPE = new ThingUID(MieleCloudBindingConstants.THING_TYPE_HOB,
DEVICE_IDENTIFIER);
private static final ThingUID FRIDGE_FREEZER_DEVICE_TYPE = new ThingUID(
MieleCloudBindingConstants.THING_TYPE_FRIDGE_FREEZER, DEVICE_IDENTIFIER);
private static final ThingUID HOOD_DEVICE_TYPE = new ThingUID(MieleCloudBindingConstants.THING_TYPE_HOOD,
DEVICE_IDENTIFIER);
private static final ThingUID COFFEE_DEVICE_TYPE = new ThingUID(MieleCloudBindingConstants.THING_TYPE_COFFEE_SYSTEM,
DEVICE_IDENTIFIER);
private static final ThingUID WINE_STORAGE_DEVICE_TYPE = new ThingUID(
MieleCloudBindingConstants.THING_TYPE_WINE_STORAGE, DEVICE_IDENTIFIER);
private static final ThingUID DRYER_DEVICE_TYPE = new ThingUID(MieleCloudBindingConstants.THING_TYPE_DRYER,
DEVICE_IDENTIFIER);
private static final ThingUID DISHWASHER_DEVICE_TYPE = new ThingUID(
MieleCloudBindingConstants.THING_TYPE_DISHWASHER, DEVICE_IDENTIFIER);
private static final ThingUID DISH_WARMER_DEVICE_TYPE = new ThingUID(
MieleCloudBindingConstants.THING_TYPE_DISH_WARMER, DEVICE_IDENTIFIER);
private static final ThingUID ROBOTIC_VACUUM_CLEANER_DEVICE_TYPE = new ThingUID(
MieleCloudBindingConstants.THING_TYPE_ROBOTIC_VACUUM_CLEANER, DEVICE_IDENTIFIER);
@Nullable
private ThingRegistry thingRegistry;
private ThingRegistry getThingRegistry() {
assertNotNull(thingRegistry);
return Objects.requireNonNull(thingRegistry);
}
@BeforeEach
public void setUp() throws Exception {
registerVolatileStorageService();
thingRegistry = getService(ThingRegistry.class, ThingRegistry.class);
assertNotNull(thingRegistry, "Thing registry is missing");
// Ensure the MieleWebservice is not initialized.
MieleHandlerFactory factory = getService(ThingHandlerFactory.class, MieleHandlerFactory.class);
assertNotNull(factory);
// Assume an access token has already been stored
AccessTokenResponse accessTokenResponse = new AccessTokenResponse();
accessTokenResponse.setAccessToken(ACCESS_TOKEN);
OAuthClientService oAuthClientService = mock(OAuthClientService.class);
when(oAuthClientService.getAccessTokenResponse()).thenReturn(accessTokenResponse);
OAuthFactory oAuthFactory = mock(OAuthFactory.class);
when(oAuthFactory.getOAuthClientService(MieleCloudBindingIntegrationTestConstants.EMAIL))
.thenReturn(oAuthClientService);
OpenHabOAuthTokenRefresher tokenRefresher = getService(OAuthTokenRefresher.class,
OpenHabOAuthTokenRefresher.class);
assertNotNull(tokenRefresher);
setPrivate(Objects.requireNonNull(tokenRefresher), "oauthFactory", oAuthFactory);
}
@Test
public void testHandlerCanBeCreatedForGenesisBridge() throws Exception {
// when:
Bridge bridge = BridgeBuilder
.create(MieleCloudBindingConstants.THING_TYPE_BRIDGE,
MieleCloudBindingIntegrationTestConstants.BRIDGE_THING_UID)
.withConfiguration(
new Configuration(Collections.singletonMap(MieleCloudBindingConstants.CONFIG_PARAM_EMAIL,
MieleCloudBindingIntegrationTestConstants.EMAIL)))
.withLabel(MIELE_CLOUD_ACCOUNT_LABEL).build();
assertNotNull(bridge);
getThingRegistry().add(bridge);
// then:
waitForAssert(() -> {
assertNotNull(bridge.getHandler());
assertTrue(bridge.getHandler() instanceof MieleBridgeHandler, "Handler type is wrong");
});
MieleBridgeHandler handler = (MieleBridgeHandler) bridge.getHandler();
assertNotNull(handler);
}
@Test
public void testWebserviceIsInitializedOnHandlerInitialization() throws Exception {
// given:
Bridge bridge = BridgeBuilder
.create(MieleCloudBindingConstants.THING_TYPE_BRIDGE,
MieleCloudBindingIntegrationTestConstants.BRIDGE_THING_UID)
.withConfiguration(
new Configuration(Collections.singletonMap(MieleCloudBindingConstants.CONFIG_PARAM_EMAIL,
MieleCloudBindingIntegrationTestConstants.EMAIL)))
.withLabel(MIELE_CLOUD_ACCOUNT_LABEL).build();
assertNotNull(bridge);
getThingRegistry().add(bridge);
waitForAssert(() -> {
assertNotNull(bridge.getHandler());
assertTrue(bridge.getHandler() instanceof MieleBridgeHandler, "Handler type is wrong");
});
MieleBridgeHandler handler = (MieleBridgeHandler) bridge.getHandler();
assertNotNull(handler);
// when:
handler.initialize();
// then:
assertEquals(ACCESS_TOKEN,
handler.getThing().getProperties().get(MieleCloudBindingConstants.PROPERTY_ACCESS_TOKEN));
MieleWebservice webservice = getPrivate(handler, "webService");
assertNotNull(webservice);
Optional<String> accessToken = getPrivate(webservice, "accessToken");
assertEquals(Optional.of(ACCESS_TOKEN), accessToken);
}
private void verifyHandlerCreation(MieleWebservice webservice, Thing thing,
Class<? extends ThingHandler> expectedHandlerClass)
throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
getThingRegistry().add(thing);
// then:
waitForAssert(() -> {
ThingHandler handler = thing.getHandler();
assertNotNull(handler);
assertTrue(expectedHandlerClass.isAssignableFrom(handler.getClass()), "Handler type is wrong");
});
}
private void testHandlerCanBeCreatedForMieleDevice(ThingTypeUID thingTypeUid, ThingUID thingUid, String label,
Class<? extends ThingHandler> expectedHandlerClass)
throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
// given:
MieleWebservice webservice = mock(MieleWebservice.class);
MieleHandlerFactory factory = getService(ThingHandlerFactory.class, MieleHandlerFactory.class);
assertNotNull(factory);
// when:
Thing device = ThingBuilder.create(thingTypeUid, thingUid)
.withConfiguration(new Configuration(Collections
.singletonMap(MieleCloudBindingConstants.CONFIG_PARAM_DEVICE_IDENTIFIER, DEVICE_IDENTIFIER)))
.withLabel(label).build();
assertNotNull(device);
verifyHandlerCreation(webservice, device, expectedHandlerClass);
}
@Test
public void testHandlerCanBeCreatedForGenesisBridgeWithEmptyConfiguration() throws Exception {
// when:
Bridge bridge = BridgeBuilder
.create(MieleCloudBindingConstants.THING_TYPE_BRIDGE,
MieleCloudBindingIntegrationTestConstants.BRIDGE_THING_UID)
.withConfiguration(
new Configuration(Collections.singletonMap(MieleCloudBindingConstants.CONFIG_PARAM_EMAIL,
MieleCloudBindingIntegrationTestConstants.EMAIL)))
.withLabel(MIELE_CLOUD_ACCOUNT_LABEL).build();
assertNotNull(bridge);
getThingRegistry().add(bridge);
// then:
waitForAssert(() -> {
assertNotNull(bridge.getHandler());
assertTrue(bridge.getHandler() instanceof MieleBridgeHandler, "Handler type is wrong");
});
}
@Test
public void testHandlerCanBeCreatedForWashingDevice()
throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
testHandlerCanBeCreatedForMieleDevice(MieleCloudBindingConstants.THING_TYPE_WASHING_MACHINE,
WASHING_MACHINE_TYPE, "DA-6996", WashingDeviceThingHandler.class);
}
@Test
public void testHandlerCanBeCreatedForOvenDevice()
throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
testHandlerCanBeCreatedForMieleDevice(MieleCloudBindingConstants.THING_TYPE_OVEN, OVEN_DEVICE_TYPE, "OV-6887",
OvenDeviceThingHandler.class);
}
@Test
public void testHandlerCanBeCreatedForHobDevice()
throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
testHandlerCanBeCreatedForMieleDevice(MieleCloudBindingConstants.THING_TYPE_HOB, HOB_DEVICE_TYPE, "HB-3887",
HobDeviceThingHandler.class);
}
@Test
public void testHandlerCanBeCreatedForFridgeFreezerDevice()
throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
testHandlerCanBeCreatedForMieleDevice(MieleCloudBindingConstants.THING_TYPE_FRIDGE_FREEZER,
FRIDGE_FREEZER_DEVICE_TYPE, "CD-6097", CoolingDeviceThingHandler.class);
}
@Test
public void testHandlerCanBeCreatedForHoodDevice()
throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
testHandlerCanBeCreatedForMieleDevice(MieleCloudBindingConstants.THING_TYPE_HOOD, HOOD_DEVICE_TYPE, "HD-2097",
HoodDeviceThingHandler.class);
}
@Test
public void testHandlerCanBeCreatedForCoffeeDevice()
throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
testHandlerCanBeCreatedForMieleDevice(MieleCloudBindingConstants.THING_TYPE_COFFEE_SYSTEM, COFFEE_DEVICE_TYPE,
"DA-6997", CoffeeSystemThingHandler.class);
}
@Test
public void testHandlerCanBeCreatedForWineStorageDevice()
throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
testHandlerCanBeCreatedForMieleDevice(MieleCloudBindingConstants.THING_TYPE_WINE_STORAGE,
WINE_STORAGE_DEVICE_TYPE, "WS-6907", WineStorageDeviceThingHandler.class);
}
@Test
public void testHandlerCanBeCreatedForDryerDevice()
throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
testHandlerCanBeCreatedForMieleDevice(MieleCloudBindingConstants.THING_TYPE_DRYER, DRYER_DEVICE_TYPE, "DR-0907",
DryerDeviceThingHandler.class);
}
@Test
public void testHandlerCanBeCreatedForDishwasherDevice()
throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
testHandlerCanBeCreatedForMieleDevice(MieleCloudBindingConstants.THING_TYPE_DISHWASHER, DISHWASHER_DEVICE_TYPE,
"DR-0907", DishwasherDeviceThingHandler.class);
}
@Test
public void testHandlerCanBeCreatedForDishWarmerDevice()
throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
testHandlerCanBeCreatedForMieleDevice(MieleCloudBindingConstants.THING_TYPE_DISH_WARMER,
DISH_WARMER_DEVICE_TYPE, "DW-0907", DishWarmerDeviceThingHandler.class);
}
@Test
public void testHandlerCanBeCreatedForRoboticVacuumCleanerDevice()
throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
testHandlerCanBeCreatedForMieleDevice(MieleCloudBindingConstants.THING_TYPE_ROBOTIC_VACUUM_CLEANER,
ROBOTIC_VACUUM_CLEANER_DEVICE_TYPE, "RVC-0907", RoboticVacuumCleanerDeviceThingHandler.class);
}
/**
* Registers a volatile storage service.
*/
@Override
protected void registerVolatileStorageService() {
registerService(new VolatileStorageService());
}
}

View File

@@ -0,0 +1,248 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.mielecloud.internal.handler;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
import static org.openhab.binding.mielecloud.internal.MieleCloudBindingConstants.Channels.*;
import static org.openhab.binding.mielecloud.internal.util.MieleCloudBindingIntegrationTestConstants.OVEN_DEVICE_THING_UID;
import java.util.Optional;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.Test;
import org.openhab.binding.mielecloud.internal.MieleCloudBindingConstants;
import org.openhab.binding.mielecloud.internal.util.MieleCloudBindingIntegrationTestConstants;
import org.openhab.binding.mielecloud.internal.webservice.api.ActionsState;
import org.openhab.binding.mielecloud.internal.webservice.api.DeviceState;
import org.openhab.binding.mielecloud.internal.webservice.api.PowerStatus;
import org.openhab.binding.mielecloud.internal.webservice.api.ProgramStatus;
import org.openhab.binding.mielecloud.internal.webservice.api.json.StateType;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.library.unit.SIUnits;
/**
* @author Björn Lange - Initial contribution
* @author Benjamin Bolte - Add pre-heat finished channel
* @author Benjamin Bolte - Add info state channel and map signal flags from API tests
* @author Björn Lange - Add elapsed time channel
*/
@NonNullByDefault
public class OvenDeviceThingHandlerTest extends AbstractMieleThingHandlerTest {
@Override
protected AbstractMieleThingHandler setUpThingHandler() {
return createThingHandler(MieleCloudBindingConstants.THING_TYPE_OVEN, OVEN_DEVICE_THING_UID,
OvenDeviceThingHandler.class, MieleCloudBindingIntegrationTestConstants.SERIAL_NUMBER);
}
@Test
public void testChannelUpdatesForNullValues() {
// given:
DeviceState deviceState = mock(DeviceState.class);
when(deviceState.getDeviceIdentifier()).thenReturn(OVEN_DEVICE_THING_UID.getId());
when(deviceState.getStateType()).thenReturn(Optional.empty());
when(deviceState.isRemoteControlEnabled()).thenReturn(Optional.empty());
when(deviceState.getSelectedProgram()).thenReturn(Optional.empty());
when(deviceState.getSelectedProgramId()).thenReturn(Optional.empty());
when(deviceState.getProgramPhase()).thenReturn(Optional.empty());
when(deviceState.getProgramPhaseRaw()).thenReturn(Optional.empty());
when(deviceState.getStatus()).thenReturn(Optional.empty());
when(deviceState.getStatusRaw()).thenReturn(Optional.empty());
when(deviceState.getStartTime()).thenReturn(Optional.empty());
when(deviceState.getElapsedTime()).thenReturn(Optional.empty());
when(deviceState.hasPreHeatFinished()).thenReturn(Optional.empty());
when(deviceState.getTargetTemperature(0)).thenReturn(Optional.empty());
when(deviceState.getTemperature(0)).thenReturn(Optional.empty());
when(deviceState.getLightState()).thenReturn(Optional.empty());
when(deviceState.getDoorState()).thenReturn(Optional.empty());
// when:
getBridgeHandler().onDeviceStateUpdated(deviceState);
// then:
waitForAssert(() -> {
assertEquals(NULL_VALUE_STATE, getChannelState(PROGRAM_ACTIVE));
assertEquals(NULL_VALUE_STATE, getChannelState(PROGRAM_ACTIVE_RAW));
assertEquals(NULL_VALUE_STATE, getChannelState(PROGRAM_PHASE));
assertEquals(NULL_VALUE_STATE, getChannelState(PROGRAM_PHASE_RAW));
assertEquals(NULL_VALUE_STATE, getChannelState(OPERATION_STATE));
assertEquals(NULL_VALUE_STATE, getChannelState(OPERATION_STATE_RAW));
assertEquals(new StringType(ProgramStatus.PROGRAM_STOPPED.getState()), getChannelState(PROGRAM_START_STOP));
assertEquals(new StringType(PowerStatus.POWER_ON.getState()), getChannelState(POWER_ON_OFF));
assertEquals(NULL_VALUE_STATE, getChannelState(DELAYED_START_TIME));
assertEquals(NULL_VALUE_STATE, getChannelState(PROGRAM_ELAPSED_TIME));
assertEquals(NULL_VALUE_STATE, getChannelState(PRE_HEAT_FINISHED));
assertEquals(NULL_VALUE_STATE, getChannelState(TEMPERATURE_TARGET));
assertEquals(NULL_VALUE_STATE, getChannelState(TEMPERATURE_CURRENT));
assertEquals(NULL_VALUE_STATE, getChannelState(LIGHT_SWITCH));
assertEquals(NULL_VALUE_STATE, getChannelState(DOOR_STATE));
});
}
@Test
public void testChannelUpdatesForValidValues() {
// given:
DeviceState deviceState = mock(DeviceState.class);
when(deviceState.isInState(any())).thenCallRealMethod();
when(deviceState.getDeviceIdentifier()).thenReturn(OVEN_DEVICE_THING_UID.getId());
when(deviceState.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
when(deviceState.isRemoteControlEnabled()).thenReturn(Optional.of(false));
when(deviceState.getSelectedProgram()).thenReturn(Optional.of("Grill"));
when(deviceState.getSelectedProgramId()).thenReturn(Optional.of(6L));
when(deviceState.getProgramPhase()).thenReturn(Optional.of("Heat"));
when(deviceState.getProgramPhaseRaw()).thenReturn(Optional.of(6));
when(deviceState.getStatus()).thenReturn(Optional.of("Running"));
when(deviceState.getStatusRaw()).thenReturn(Optional.of(StateType.RUNNING.getCode()));
when(deviceState.getStartTime()).thenReturn(Optional.of(3600));
when(deviceState.getElapsedTime()).thenReturn(Optional.of(62));
when(deviceState.hasPreHeatFinished()).thenReturn(Optional.of(true));
when(deviceState.getTargetTemperature(0)).thenReturn(Optional.of(180));
when(deviceState.getTemperature(0)).thenReturn(Optional.of(181));
when(deviceState.hasError()).thenReturn(false);
when(deviceState.hasInfo()).thenReturn(true);
when(deviceState.getLightState()).thenReturn(Optional.of(false));
when(deviceState.getDoorState()).thenReturn(Optional.of(false));
// when:
getBridgeHandler().onDeviceStateUpdated(deviceState);
// then:
waitForAssert(() -> {
assertEquals(new StringType("Grill"), getChannelState(PROGRAM_ACTIVE));
assertEquals(new DecimalType(6), getChannelState(PROGRAM_ACTIVE_RAW));
assertEquals(new StringType("Heat"), getChannelState(PROGRAM_PHASE));
assertEquals(new DecimalType(6), getChannelState(PROGRAM_PHASE_RAW));
assertEquals(new StringType("Running"), getChannelState(OPERATION_STATE));
assertEquals(new DecimalType(StateType.RUNNING.getCode()), getChannelState(OPERATION_STATE_RAW));
assertEquals(new StringType(ProgramStatus.PROGRAM_STARTED.getState()), getChannelState(PROGRAM_START_STOP));
assertEquals(new StringType(PowerStatus.POWER_ON.getState()), getChannelState(POWER_ON_OFF));
assertEquals(new DecimalType(3600), getChannelState(DELAYED_START_TIME));
assertEquals(new DecimalType(62), getChannelState(PROGRAM_ELAPSED_TIME));
assertEquals(OnOffType.ON, getChannelState(PRE_HEAT_FINISHED));
assertEquals(new QuantityType<>(180, SIUnits.CELSIUS), getChannelState(TEMPERATURE_TARGET));
assertEquals(new QuantityType<>(181, SIUnits.CELSIUS), getChannelState(TEMPERATURE_CURRENT));
assertEquals(OnOffType.OFF, getChannelState(ERROR_STATE));
assertEquals(OnOffType.ON, getChannelState(INFO_STATE));
assertEquals(OnOffType.OFF, getChannelState(LIGHT_SWITCH));
assertEquals(OnOffType.OFF, getChannelState(DOOR_STATE));
});
}
@Test
public void testFinishStateChannelIsSetToOnWhenProgramHasFinished() {
// given:
DeviceState deviceStateBefore = mock(DeviceState.class);
when(deviceStateBefore.getDeviceIdentifier()).thenReturn(OVEN_DEVICE_THING_UID.getId());
when(deviceStateBefore.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
when(deviceStateBefore.isInState(any())).thenCallRealMethod();
getBridgeHandler().onDeviceStateUpdated(deviceStateBefore);
DeviceState deviceStateAfter = mock(DeviceState.class);
when(deviceStateAfter.getDeviceIdentifier()).thenReturn(OVEN_DEVICE_THING_UID.getId());
when(deviceStateAfter.getStateType()).thenReturn(Optional.of(StateType.END_PROGRAMMED));
when(deviceStateAfter.isInState(any())).thenCallRealMethod();
// when:
getBridgeHandler().onDeviceStateUpdated(deviceStateAfter);
// then:
waitForAssert(() -> {
assertEquals(OnOffType.ON, getChannelState(FINISH_STATE));
});
}
@Test
public void testTransitionChannelUpdatesForNullValues() {
// given:
DeviceState deviceStateBefore = mock(DeviceState.class);
when(deviceStateBefore.getDeviceIdentifier()).thenReturn(OVEN_DEVICE_THING_UID.getId());
when(deviceStateBefore.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
when(deviceStateBefore.isInState(any())).thenCallRealMethod();
when(deviceStateBefore.getRemainingTime()).thenReturn(Optional.empty());
when(deviceStateBefore.getProgress()).thenReturn(Optional.empty());
getThingHandler().onDeviceStateUpdated(deviceStateBefore);
DeviceState deviceStateAfter = mock(DeviceState.class);
when(deviceStateAfter.getDeviceIdentifier()).thenReturn(OVEN_DEVICE_THING_UID.getId());
when(deviceStateAfter.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
when(deviceStateAfter.isInState(any())).thenCallRealMethod();
when(deviceStateAfter.getRemainingTime()).thenReturn(Optional.empty());
when(deviceStateAfter.getProgress()).thenReturn(Optional.empty());
// when:
getThingHandler().onDeviceStateUpdated(deviceStateAfter);
waitForAssert(() -> {
assertEquals(NULL_VALUE_STATE, getChannelState(PROGRAM_REMAINING_TIME));
assertEquals(NULL_VALUE_STATE, getChannelState(PROGRAM_PROGRESS));
});
}
@Test
public void testTransitionChannelUpdatesForValidValues() {
// given:
DeviceState deviceStateBefore = mock(DeviceState.class);
when(deviceStateBefore.getDeviceIdentifier()).thenReturn(OVEN_DEVICE_THING_UID.getId());
when(deviceStateBefore.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
when(deviceStateBefore.isInState(any())).thenCallRealMethod();
when(deviceStateBefore.getRemainingTime()).thenReturn(Optional.of(10));
when(deviceStateBefore.getProgress()).thenReturn(Optional.of(80));
getThingHandler().onDeviceStateUpdated(deviceStateBefore);
DeviceState deviceStateAfter = mock(DeviceState.class);
when(deviceStateAfter.getDeviceIdentifier()).thenReturn(OVEN_DEVICE_THING_UID.getId());
when(deviceStateAfter.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
when(deviceStateAfter.isInState(any())).thenCallRealMethod();
when(deviceStateAfter.getRemainingTime()).thenReturn(Optional.of(10));
when(deviceStateAfter.getProgress()).thenReturn(Optional.of(80));
// when:
getThingHandler().onDeviceStateUpdated(deviceStateAfter);
waitForAssert(() -> {
assertEquals(new DecimalType(10), getChannelState(PROGRAM_REMAINING_TIME));
assertEquals(new DecimalType(80), getChannelState(PROGRAM_PROGRESS));
});
}
@Test
public void testActionsChannelUpdatesForValidValues() {
// given:
ActionsState actionsState = mock(ActionsState.class);
when(actionsState.getDeviceIdentifier()).thenReturn(OVEN_DEVICE_THING_UID.getId());
when(actionsState.canBeStarted()).thenReturn(true);
when(actionsState.canBeStopped()).thenReturn(false);
when(actionsState.canBeSwitchedOn()).thenReturn(true);
when(actionsState.canBeSwitchedOff()).thenReturn(false);
when(actionsState.canControlLight()).thenReturn(true);
// when:
getBridgeHandler().onProcessActionUpdated(actionsState);
// then:
waitForAssert(() -> {
assertEquals(OnOffType.ON, getChannelState(REMOTE_CONTROL_CAN_BE_STARTED));
assertEquals(OnOffType.OFF, getChannelState(REMOTE_CONTROL_CAN_BE_STOPPED));
assertEquals(OnOffType.ON, getChannelState(REMOTE_CONTROL_CAN_BE_SWITCHED_ON));
assertEquals(OnOffType.OFF, getChannelState(REMOTE_CONTROL_CAN_BE_SWITCHED_OFF));
assertEquals(OnOffType.ON, getChannelState(LIGHT_CAN_BE_CONTROLLED));
});
}
}

View File

@@ -0,0 +1,169 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.mielecloud.internal.handler;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
import static org.openhab.binding.mielecloud.internal.MieleCloudBindingConstants.Channels.*;
import java.util.Optional;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.Test;
import org.openhab.binding.mielecloud.internal.MieleCloudBindingConstants;
import org.openhab.binding.mielecloud.internal.util.MieleCloudBindingIntegrationTestConstants;
import org.openhab.binding.mielecloud.internal.webservice.api.ActionsState;
import org.openhab.binding.mielecloud.internal.webservice.api.DeviceState;
import org.openhab.binding.mielecloud.internal.webservice.api.PowerStatus;
import org.openhab.binding.mielecloud.internal.webservice.api.ProgramStatus;
import org.openhab.binding.mielecloud.internal.webservice.api.json.StateType;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.StringType;
/**
* @author Björn Lange - Initial contribution
*/
@NonNullByDefault
public class RoboticVacuumCleanerDeviceThingHandlerTest extends AbstractMieleThingHandlerTest {
@Override
protected AbstractMieleThingHandler setUpThingHandler() {
return createThingHandler(MieleCloudBindingConstants.THING_TYPE_ROBOTIC_VACUUM_CLEANER,
MieleCloudBindingIntegrationTestConstants.ROBOTIC_VACUUM_CLEANER_THING_UID,
RoboticVacuumCleanerDeviceThingHandler.class, MieleCloudBindingIntegrationTestConstants.SERIAL_NUMBER);
}
@Test
public void testChannelUpdatesForNullValues() {
// given:
DeviceState deviceState = mock(DeviceState.class);
when(deviceState.getDeviceIdentifier())
.thenReturn(MieleCloudBindingIntegrationTestConstants.ROBOTIC_VACUUM_CLEANER_THING_UID.getId());
when(deviceState.getSelectedProgramId()).thenReturn(Optional.empty());
when(deviceState.getStatus()).thenReturn(Optional.empty());
when(deviceState.getStatusRaw()).thenReturn(Optional.empty());
when(deviceState.getStateType()).thenReturn(Optional.empty());
when(deviceState.hasError()).thenReturn(false);
when(deviceState.hasInfo()).thenReturn(false);
when(deviceState.getBatteryLevel()).thenReturn(Optional.empty());
// when:
getBridgeHandler().onDeviceStateUpdated(deviceState);
// then:
waitForAssert(() -> {
assertEquals(NULL_VALUE_STATE, getChannelState(VACUUM_CLEANER_PROGRAM_ACTIVE));
assertEquals(NULL_VALUE_STATE, getChannelState(OPERATION_STATE));
assertEquals(NULL_VALUE_STATE, getChannelState(OPERATION_STATE_RAW));
assertEquals(new StringType(ProgramStatus.PROGRAM_STOPPED.getState()),
getChannelState(PROGRAM_START_STOP_PAUSE));
assertEquals(new StringType(PowerStatus.POWER_ON.getState()), getChannelState(POWER_ON_OFF));
assertEquals(OnOffType.OFF, getChannelState(ERROR_STATE));
assertEquals(OnOffType.OFF, getChannelState(INFO_STATE));
assertEquals(NULL_VALUE_STATE, getChannelState(BATTERY_LEVEL));
});
}
@Test
public void testChannelUpdatesForValidValues() {
// given:
DeviceState deviceState = mock(DeviceState.class);
when(deviceState.isInState(any())).thenCallRealMethod();
when(deviceState.getDeviceIdentifier())
.thenReturn(MieleCloudBindingIntegrationTestConstants.ROBOTIC_VACUUM_CLEANER_THING_UID.getId());
when(deviceState.getSelectedProgramId()).thenReturn(Optional.of(1L));
when(deviceState.getStatus()).thenReturn(Optional.of("Running"));
when(deviceState.getStatusRaw()).thenReturn(Optional.of(StateType.RUNNING.getCode()));
when(deviceState.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
when(deviceState.hasError()).thenReturn(true);
when(deviceState.hasInfo()).thenReturn(true);
when(deviceState.getBatteryLevel()).thenReturn(Optional.of(25));
// when:
getBridgeHandler().onDeviceStateUpdated(deviceState);
// then:
waitForAssert(() -> {
assertEquals(new StringType("1"), getChannelState(VACUUM_CLEANER_PROGRAM_ACTIVE));
assertEquals(new StringType("Running"), getChannelState(OPERATION_STATE));
assertEquals(new DecimalType(StateType.RUNNING.getCode()), getChannelState(OPERATION_STATE_RAW));
assertEquals(new StringType(ProgramStatus.PROGRAM_STARTED.getState()),
getChannelState(PROGRAM_START_STOP_PAUSE));
assertEquals(new StringType(PowerStatus.POWER_ON.getState()), getChannelState(POWER_ON_OFF));
assertEquals(OnOffType.ON, getChannelState(ERROR_STATE));
assertEquals(OnOffType.ON, getChannelState(INFO_STATE));
assertEquals(new DecimalType(25), getChannelState(BATTERY_LEVEL));
});
}
@Test
public void testFinishStateChannelIsSetToOnWhenProgramHasFinished() {
// given:
DeviceState deviceStateBefore = mock(DeviceState.class);
when(deviceStateBefore.getDeviceIdentifier())
.thenReturn(MieleCloudBindingIntegrationTestConstants.ROBOTIC_VACUUM_CLEANER_THING_UID.getId());
when(deviceStateBefore.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
when(deviceStateBefore.isInState(any())).thenCallRealMethod();
getThingHandler().onDeviceStateUpdated(deviceStateBefore);
DeviceState deviceStateAfter = mock(DeviceState.class);
when(deviceStateAfter.getDeviceIdentifier())
.thenReturn(MieleCloudBindingIntegrationTestConstants.ROBOTIC_VACUUM_CLEANER_THING_UID.getId());
when(deviceStateAfter.getStateType()).thenReturn(Optional.of(StateType.END_PROGRAMMED));
when(deviceStateAfter.isInState(any())).thenCallRealMethod();
// when:
getBridgeHandler().onDeviceStateUpdated(deviceStateAfter);
// then:
waitForAssert(() -> {
assertEquals(OnOffType.ON, getChannelState(FINISH_STATE));
});
}
@Test
public void testActionsChannelUpdatesForValidValues() {
// given:
ActionsState actionsState = mock(ActionsState.class);
when(actionsState.getDeviceIdentifier())
.thenReturn(MieleCloudBindingIntegrationTestConstants.ROBOTIC_VACUUM_CLEANER_THING_UID.getId());
when(actionsState.canBeStarted()).thenReturn(true);
when(actionsState.canBeStopped()).thenReturn(false);
when(actionsState.canBePaused()).thenReturn(true);
when(actionsState.canSetActiveProgramId()).thenReturn(false);
// when:
getBridgeHandler().onProcessActionUpdated(actionsState);
// then:
waitForAssert(() -> {
assertEquals(OnOffType.ON, getChannelState(REMOTE_CONTROL_CAN_BE_STARTED));
assertEquals(OnOffType.OFF, getChannelState(REMOTE_CONTROL_CAN_BE_STOPPED));
assertEquals(OnOffType.ON, getChannelState(REMOTE_CONTROL_CAN_BE_PAUSED));
assertEquals(OnOffType.OFF, getChannelState(REMOTE_CONTROL_CAN_SET_PROGRAM_ACTIVE));
});
}
@Test
public void testHandleCommandVacuumCleanerProgramActive() {
// when:
getThingHandler().handleCommand(channel(VACUUM_CLEANER_PROGRAM_ACTIVE), new StringType("1"));
// then:
waitForAssert(() -> {
verify(getWebserviceMock()).putProgram(getThingHandler().getDeviceId(), 1);
});
}
}

View File

@@ -0,0 +1,248 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.mielecloud.internal.handler;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
import static org.openhab.binding.mielecloud.internal.MieleCloudBindingConstants.Channels.*;
import static org.openhab.binding.mielecloud.internal.util.MieleCloudBindingIntegrationTestConstants.WASHING_MACHINE_THING_UID;
import java.util.Optional;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.Test;
import org.openhab.binding.mielecloud.internal.MieleCloudBindingConstants;
import org.openhab.binding.mielecloud.internal.util.MieleCloudBindingIntegrationTestConstants;
import org.openhab.binding.mielecloud.internal.webservice.api.ActionsState;
import org.openhab.binding.mielecloud.internal.webservice.api.DeviceState;
import org.openhab.binding.mielecloud.internal.webservice.api.PowerStatus;
import org.openhab.binding.mielecloud.internal.webservice.api.ProgramStatus;
import org.openhab.binding.mielecloud.internal.webservice.api.json.StateType;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.library.unit.SIUnits;
/**
* @author Björn Lange - Initial contribution
* @author Benjamin Bolte - Add info state channel and map signal flags from API tests
* @author Björn Lange - Add elapsed time channel
*/
@NonNullByDefault
public class WashingDeviceThingHandlerTest extends AbstractMieleThingHandlerTest {
@Override
protected AbstractMieleThingHandler setUpThingHandler() {
return createThingHandler(MieleCloudBindingConstants.THING_TYPE_WASHING_MACHINE, WASHING_MACHINE_THING_UID,
WashingDeviceThingHandler.class, MieleCloudBindingIntegrationTestConstants.SERIAL_NUMBER);
}
@Test
public void testChannelUpdatesForNullValues() {
// given:
DeviceState deviceState = mock(DeviceState.class);
when(deviceState.getDeviceIdentifier()).thenReturn(WASHING_MACHINE_THING_UID.getId());
when(deviceState.getStateType()).thenReturn(Optional.empty());
when(deviceState.isRemoteControlEnabled()).thenReturn(Optional.empty());
when(deviceState.getSpinningSpeed()).thenReturn(Optional.empty());
when(deviceState.getSpinningSpeedRaw()).thenReturn(Optional.empty());
when(deviceState.getSelectedProgram()).thenReturn(Optional.empty());
when(deviceState.getSelectedProgramId()).thenReturn(Optional.empty());
when(deviceState.getProgramPhase()).thenReturn(Optional.empty());
when(deviceState.getProgramPhaseRaw()).thenReturn(Optional.empty());
when(deviceState.getStatus()).thenReturn(Optional.empty());
when(deviceState.getStatusRaw()).thenReturn(Optional.empty());
when(deviceState.getStartTime()).thenReturn(Optional.empty());
when(deviceState.getElapsedTime()).thenReturn(Optional.empty());
when(deviceState.getTargetTemperature(0)).thenReturn(Optional.empty());
when(deviceState.getLightState()).thenReturn(Optional.empty());
when(deviceState.getDoorState()).thenReturn(Optional.empty());
// when:
getBridgeHandler().onDeviceStateUpdated(deviceState);
// then:
waitForAssert(() -> {
assertEquals(NULL_VALUE_STATE, getChannelState(SPINNING_SPEED));
assertEquals(NULL_VALUE_STATE, getChannelState(SPINNING_SPEED_RAW));
assertEquals(NULL_VALUE_STATE, getChannelState(PROGRAM_ACTIVE));
assertEquals(NULL_VALUE_STATE, getChannelState(PROGRAM_ACTIVE_RAW));
assertEquals(NULL_VALUE_STATE, getChannelState(PROGRAM_PHASE));
assertEquals(NULL_VALUE_STATE, getChannelState(PROGRAM_PHASE_RAW));
assertEquals(NULL_VALUE_STATE, getChannelState(OPERATION_STATE));
assertEquals(NULL_VALUE_STATE, getChannelState(OPERATION_STATE_RAW));
assertEquals(new StringType(ProgramStatus.PROGRAM_STOPPED.getState()), getChannelState(PROGRAM_START_STOP));
assertEquals(new StringType(PowerStatus.POWER_ON.getState()), getChannelState(POWER_ON_OFF));
assertEquals(NULL_VALUE_STATE, getChannelState(DELAYED_START_TIME));
assertEquals(NULL_VALUE_STATE, getChannelState(PROGRAM_ELAPSED_TIME));
assertEquals(NULL_VALUE_STATE, getChannelState(TEMPERATURE_TARGET));
assertEquals(NULL_VALUE_STATE, getChannelState(LIGHT_SWITCH));
assertEquals(NULL_VALUE_STATE, getChannelState(DOOR_STATE));
});
}
@Test
public void testChannelUpdatesForValidValues() {
// given:
DeviceState deviceState = mock(DeviceState.class);
when(deviceState.isInState(any())).thenCallRealMethod();
when(deviceState.getDeviceIdentifier()).thenReturn(WASHING_MACHINE_THING_UID.getId());
when(deviceState.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
when(deviceState.isRemoteControlEnabled()).thenReturn(Optional.of(true));
when(deviceState.getSpinningSpeed()).thenReturn(Optional.of("1200"));
when(deviceState.getSpinningSpeedRaw()).thenReturn(Optional.of(1200));
when(deviceState.getSelectedProgram()).thenReturn(Optional.of("Buntwäsche"));
when(deviceState.getSelectedProgramId()).thenReturn(Optional.of(1L));
when(deviceState.getProgramPhase()).thenReturn(Optional.of("Waschen"));
when(deviceState.getProgramPhaseRaw()).thenReturn(Optional.of(7));
when(deviceState.getStatus()).thenReturn(Optional.of("Läuft"));
when(deviceState.getStatusRaw()).thenReturn(Optional.of(StateType.RUNNING.getCode()));
when(deviceState.getStartTime()).thenReturn(Optional.of(3600));
when(deviceState.getElapsedTime()).thenReturn(Optional.of(63));
when(deviceState.getTargetTemperature(0)).thenReturn(Optional.of(30));
when(deviceState.hasError()).thenReturn(true);
when(deviceState.hasInfo()).thenReturn(true);
when(deviceState.getLightState()).thenReturn(Optional.of(false));
when(deviceState.getDoorState()).thenReturn(Optional.of(true));
// when:
getBridgeHandler().onDeviceStateUpdated(deviceState);
// then:
waitForAssert(() -> {
assertEquals(new StringType("1200"), getChannelState(SPINNING_SPEED));
assertEquals(new DecimalType(1200), getChannelState(SPINNING_SPEED_RAW));
assertEquals(new StringType("Buntwäsche"), getChannelState(PROGRAM_ACTIVE));
assertEquals(new DecimalType(1), getChannelState(PROGRAM_ACTIVE_RAW));
assertEquals(new StringType("Waschen"), getChannelState(PROGRAM_PHASE));
assertEquals(new DecimalType(7), getChannelState(PROGRAM_PHASE_RAW));
assertEquals(new StringType("Läuft"), getChannelState(OPERATION_STATE));
assertEquals(new DecimalType(StateType.RUNNING.getCode()), getChannelState(OPERATION_STATE_RAW));
assertEquals(new StringType(ProgramStatus.PROGRAM_STARTED.getState()), getChannelState(PROGRAM_START_STOP));
assertEquals(new StringType(PowerStatus.POWER_ON.getState()), getChannelState(POWER_ON_OFF));
assertEquals(new DecimalType(3600), getChannelState(DELAYED_START_TIME));
assertEquals(new DecimalType(63), getChannelState(PROGRAM_ELAPSED_TIME));
assertEquals(new QuantityType<>(30, SIUnits.CELSIUS), getChannelState(TEMPERATURE_TARGET));
assertEquals(OnOffType.ON, getChannelState(ERROR_STATE));
assertEquals(OnOffType.ON, getChannelState(INFO_STATE));
assertEquals(OnOffType.OFF, getChannelState(LIGHT_SWITCH));
assertEquals(OnOffType.ON, getChannelState(DOOR_STATE));
});
}
@Test
public void testFinishStateChannelIsSetToOnWhenProgramHasFinished() {
// given:
DeviceState deviceStateBefore = mock(DeviceState.class);
when(deviceStateBefore.getDeviceIdentifier()).thenReturn(WASHING_MACHINE_THING_UID.getId());
when(deviceStateBefore.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
when(deviceStateBefore.isInState(any())).thenCallRealMethod();
getBridgeHandler().onDeviceStateUpdated(deviceStateBefore);
DeviceState deviceStateAfter = mock(DeviceState.class);
when(deviceStateAfter.getDeviceIdentifier()).thenReturn(WASHING_MACHINE_THING_UID.getId());
when(deviceStateAfter.getStateType()).thenReturn(Optional.of(StateType.END_PROGRAMMED));
when(deviceStateAfter.isInState(any())).thenCallRealMethod();
// when:
getBridgeHandler().onDeviceStateUpdated(deviceStateAfter);
// then:
waitForAssert(() -> {
assertEquals(OnOffType.ON, getChannelState(FINISH_STATE));
});
}
@Test
public void testTransitionChannelUpdatesForNullValues() {
// given:
DeviceState deviceStateBefore = mock(DeviceState.class);
when(deviceStateBefore.getDeviceIdentifier()).thenReturn(WASHING_MACHINE_THING_UID.getId());
when(deviceStateBefore.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
when(deviceStateBefore.isInState(any())).thenCallRealMethod();
when(deviceStateBefore.getRemainingTime()).thenReturn(Optional.empty());
when(deviceStateBefore.getProgress()).thenReturn(Optional.empty());
getThingHandler().onDeviceStateUpdated(deviceStateBefore);
DeviceState deviceStateAfter = mock(DeviceState.class);
when(deviceStateAfter.getDeviceIdentifier()).thenReturn(WASHING_MACHINE_THING_UID.getId());
when(deviceStateAfter.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
when(deviceStateAfter.isInState(any())).thenCallRealMethod();
when(deviceStateAfter.getRemainingTime()).thenReturn(Optional.empty());
when(deviceStateAfter.getProgress()).thenReturn(Optional.empty());
// when:
getThingHandler().onDeviceStateUpdated(deviceStateAfter);
waitForAssert(() -> {
assertEquals(NULL_VALUE_STATE, getChannelState(PROGRAM_REMAINING_TIME));
assertEquals(NULL_VALUE_STATE, getChannelState(PROGRAM_PROGRESS));
});
}
@Test
public void testTransitionChannelUpdatesForValidValues() {
// given:
DeviceState deviceStateBefore = mock(DeviceState.class);
when(deviceStateBefore.getDeviceIdentifier()).thenReturn(WASHING_MACHINE_THING_UID.getId());
when(deviceStateBefore.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
when(deviceStateBefore.isInState(any())).thenCallRealMethod();
when(deviceStateBefore.getRemainingTime()).thenReturn(Optional.of(10));
when(deviceStateBefore.getProgress()).thenReturn(Optional.of(80));
getThingHandler().onDeviceStateUpdated(deviceStateBefore);
DeviceState deviceStateAfter = mock(DeviceState.class);
when(deviceStateAfter.getDeviceIdentifier()).thenReturn(WASHING_MACHINE_THING_UID.getId());
when(deviceStateAfter.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
when(deviceStateAfter.isInState(any())).thenCallRealMethod();
when(deviceStateAfter.getRemainingTime()).thenReturn(Optional.of(10));
when(deviceStateAfter.getProgress()).thenReturn(Optional.of(80));
// when:
getThingHandler().onDeviceStateUpdated(deviceStateAfter);
waitForAssert(() -> {
assertEquals(new DecimalType(10), getChannelState(PROGRAM_REMAINING_TIME));
assertEquals(new DecimalType(80), getChannelState(PROGRAM_PROGRESS));
});
}
@Test
public void testActionsChannelUpdatesForValidValues() {
// given:
ActionsState actionsState = mock(ActionsState.class);
when(actionsState.getDeviceIdentifier()).thenReturn(WASHING_MACHINE_THING_UID.getId());
when(actionsState.canBeStarted()).thenReturn(true);
when(actionsState.canBeStopped()).thenReturn(false);
when(actionsState.canBeSwitchedOn()).thenReturn(true);
when(actionsState.canBeSwitchedOff()).thenReturn(false);
when(actionsState.canControlLight()).thenReturn(false);
// when:
getBridgeHandler().onProcessActionUpdated(actionsState);
// then:
waitForAssert(() -> {
assertEquals(OnOffType.ON, getChannelState(REMOTE_CONTROL_CAN_BE_STARTED));
assertEquals(OnOffType.OFF, getChannelState(REMOTE_CONTROL_CAN_BE_STOPPED));
assertEquals(OnOffType.ON, getChannelState(REMOTE_CONTROL_CAN_BE_SWITCHED_ON));
assertEquals(OnOffType.OFF, getChannelState(REMOTE_CONTROL_CAN_BE_SWITCHED_OFF));
assertEquals(OnOffType.OFF, getChannelState(LIGHT_CAN_BE_CONTROLLED));
});
}
}

View File

@@ -0,0 +1,142 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.mielecloud.internal.handler;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.*;
import static org.openhab.binding.mielecloud.internal.MieleCloudBindingConstants.Channels.*;
import static org.openhab.binding.mielecloud.internal.util.MieleCloudBindingIntegrationTestConstants.WINE_STORAGE_DEVICE_THING_UID;
import java.util.Optional;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.Test;
import org.openhab.binding.mielecloud.internal.MieleCloudBindingConstants;
import org.openhab.binding.mielecloud.internal.util.MieleCloudBindingIntegrationTestConstants;
import org.openhab.binding.mielecloud.internal.webservice.api.ActionsState;
import org.openhab.binding.mielecloud.internal.webservice.api.DeviceState;
import org.openhab.binding.mielecloud.internal.webservice.api.PowerStatus;
import org.openhab.binding.mielecloud.internal.webservice.api.json.DeviceType;
import org.openhab.binding.mielecloud.internal.webservice.api.json.StateType;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.library.unit.SIUnits;
/**
* @author Björn Lange - Initial contribution
* @author Benjamin Bolte - Add info state channel and map signal flags from API tests
*/
@NonNullByDefault
public class WineStorageDeviceThingHandlerTest extends AbstractMieleThingHandlerTest {
@Override
protected AbstractMieleThingHandler setUpThingHandler() {
return createThingHandler(MieleCloudBindingConstants.THING_TYPE_WINE_STORAGE, WINE_STORAGE_DEVICE_THING_UID,
WineStorageDeviceThingHandler.class, MieleCloudBindingIntegrationTestConstants.SERIAL_NUMBER);
}
@Test
public void testChannelUpdatesForNullValues() {
// given:
DeviceState deviceState = mock(DeviceState.class);
when(deviceState.getDeviceIdentifier()).thenReturn(WINE_STORAGE_DEVICE_THING_UID.getId());
when(deviceState.getRawType()).thenReturn(DeviceType.WINE_CONDITIONING_UNIT);
when(deviceState.getStateType()).thenReturn(Optional.empty());
when(deviceState.isRemoteControlEnabled()).thenReturn(Optional.empty());
when(deviceState.getStatus()).thenReturn(Optional.empty());
when(deviceState.getStatusRaw()).thenReturn(Optional.empty());
when(deviceState.getTargetTemperature(0)).thenReturn(Optional.empty());
when(deviceState.getTemperature(0)).thenReturn(Optional.empty());
when(deviceState.getTargetTemperature(1)).thenReturn(Optional.empty());
when(deviceState.getTemperature(1)).thenReturn(Optional.empty());
when(deviceState.getTargetTemperature(2)).thenReturn(Optional.empty());
when(deviceState.getTemperature(2)).thenReturn(Optional.empty());
// when:
getBridgeHandler().onDeviceStateUpdated(deviceState);
// then:
waitForAssert(() -> {
assertEquals(NULL_VALUE_STATE, getChannelState(OPERATION_STATE));
assertEquals(NULL_VALUE_STATE, getChannelState(OPERATION_STATE_RAW));
assertEquals(NULL_VALUE_STATE, getChannelState(TEMPERATURE_TARGET));
assertEquals(NULL_VALUE_STATE, getChannelState(TEMPERATURE_CURRENT));
assertEquals(NULL_VALUE_STATE, getChannelState(TOP_TEMPERATURE_TARGET));
assertEquals(NULL_VALUE_STATE, getChannelState(TOP_TEMPERATURE_CURRENT));
assertEquals(NULL_VALUE_STATE, getChannelState(MIDDLE_TEMPERATURE_TARGET));
assertEquals(NULL_VALUE_STATE, getChannelState(MIDDLE_TEMPERATURE_CURRENT));
assertEquals(NULL_VALUE_STATE, getChannelState(BOTTOM_TEMPERATURE_TARGET));
assertEquals(NULL_VALUE_STATE, getChannelState(BOTTOM_TEMPERATURE_CURRENT));
assertEquals(new StringType(PowerStatus.POWER_ON.getState()), getChannelState(POWER_ON_OFF));
});
}
@Test
public void testChannelUpdatesForValidValues() {
// given:
DeviceState deviceState = mock(DeviceState.class);
when(deviceState.getDeviceIdentifier()).thenReturn(WINE_STORAGE_DEVICE_THING_UID.getId());
when(deviceState.getRawType()).thenReturn(DeviceType.WINE_CONDITIONING_UNIT);
when(deviceState.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
when(deviceState.isRemoteControlEnabled()).thenReturn(Optional.empty());
when(deviceState.getStatus()).thenReturn(Optional.of("Im Betrieb"));
when(deviceState.getStatusRaw()).thenReturn(Optional.of(StateType.RUNNING.getCode()));
when(deviceState.getTargetTemperature(0)).thenReturn(Optional.of(8));
when(deviceState.getTemperature(0)).thenReturn(Optional.of(9));
when(deviceState.getTargetTemperature(1)).thenReturn(Optional.of(10));
when(deviceState.getTemperature(1)).thenReturn(Optional.of(11));
when(deviceState.getTargetTemperature(2)).thenReturn(Optional.of(12));
when(deviceState.getTemperature(2)).thenReturn(Optional.of(14));
when(deviceState.hasError()).thenReturn(true);
when(deviceState.hasInfo()).thenReturn(true);
// when:
getBridgeHandler().onDeviceStateUpdated(deviceState);
// then:
waitForAssert(() -> {
assertEquals(new StringType("Im Betrieb"), getChannelState(OPERATION_STATE));
assertEquals(new DecimalType(StateType.RUNNING.getCode()), getChannelState(OPERATION_STATE_RAW));
assertEquals(NULL_VALUE_STATE, getChannelState(TEMPERATURE_TARGET));
assertEquals(NULL_VALUE_STATE, getChannelState(TEMPERATURE_CURRENT));
assertEquals(new QuantityType<>(8, SIUnits.CELSIUS), getChannelState(TOP_TEMPERATURE_TARGET));
assertEquals(new QuantityType<>(9, SIUnits.CELSIUS), getChannelState(TOP_TEMPERATURE_CURRENT));
assertEquals(new QuantityType<>(10, SIUnits.CELSIUS), getChannelState(MIDDLE_TEMPERATURE_TARGET));
assertEquals(new QuantityType<>(11, SIUnits.CELSIUS), getChannelState(MIDDLE_TEMPERATURE_CURRENT));
assertEquals(new QuantityType<>(12, SIUnits.CELSIUS), getChannelState(BOTTOM_TEMPERATURE_TARGET));
assertEquals(new QuantityType<>(14, SIUnits.CELSIUS), getChannelState(BOTTOM_TEMPERATURE_CURRENT));
assertEquals(new StringType(PowerStatus.POWER_ON.getState()), getChannelState(POWER_ON_OFF));
assertEquals(OnOffType.ON, getChannelState(ERROR_STATE));
assertEquals(OnOffType.ON, getChannelState(INFO_STATE));
});
}
@Test
public void testActionsChannelUpdatesForValidValues() {
// given:
ActionsState actionsState = mock(ActionsState.class);
when(actionsState.getDeviceIdentifier()).thenReturn(WINE_STORAGE_DEVICE_THING_UID.getId());
when(actionsState.canBeSwitchedOn()).thenReturn(true);
when(actionsState.canBeSwitchedOff()).thenReturn(false);
// when:
getBridgeHandler().onProcessActionUpdated(actionsState);
// then:
waitForAssert(() -> {
assertEquals(OnOffType.ON, getChannelState(REMOTE_CONTROL_CAN_BE_SWITCHED_ON));
assertEquals(OnOffType.OFF, getChannelState(REMOTE_CONTROL_CAN_BE_SWITCHED_OFF));
});
}
}

View File

@@ -0,0 +1,113 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.mielecloud.internal.util;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import java.util.Objects;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.junit.jupiter.api.BeforeEach;
import org.openhab.binding.mielecloud.internal.config.MieleCloudConfigService;
import org.openhab.binding.mielecloud.internal.config.servlet.AccountOverviewServlet;
import org.openhab.binding.mielecloud.internal.config.servlet.CreateBridgeServlet;
import org.openhab.binding.mielecloud.internal.config.servlet.ForwardToLoginServlet;
import org.openhab.binding.mielecloud.internal.config.servlet.ResultServlet;
import org.openhab.binding.mielecloud.internal.config.servlet.SuccessServlet;
import org.openhab.core.io.net.http.HttpClientFactory;
/**
* Common base class for all config flow tests.
*
* @author Björn Lange - Initial Contribution
*/
@NonNullByDefault
public abstract class AbstractConfigFlowTest extends OpenHabOsgiTest {
@Nullable
private WebsiteCrawler crawler;
@Nullable
private AccountOverviewServlet accountOverviewServlet;
@Nullable
private ForwardToLoginServlet forwardToLoginServlet;
@Nullable
private ResultServlet resultServlet;
@Nullable
private SuccessServlet successServlet;
@Nullable
private CreateBridgeServlet createBridgeServlet;
protected final WebsiteCrawler getCrawler() {
final WebsiteCrawler crawler = this.crawler;
assertNotNull(crawler);
return Objects.requireNonNull(crawler);
}
protected final AccountOverviewServlet getAccountOverviewServlet() {
final AccountOverviewServlet accountOverviewServlet = this.accountOverviewServlet;
assertNotNull(accountOverviewServlet);
return Objects.requireNonNull(accountOverviewServlet);
}
protected final ForwardToLoginServlet getForwardToLoginServlet() {
final ForwardToLoginServlet forwardToLoginServlet = this.forwardToLoginServlet;
assertNotNull(forwardToLoginServlet);
return Objects.requireNonNull(forwardToLoginServlet);
}
protected final ResultServlet getResultServlet() {
final ResultServlet resultServlet = this.resultServlet;
assertNotNull(resultServlet);
return Objects.requireNonNull(resultServlet);
}
protected final SuccessServlet getSuccessServlet() {
final SuccessServlet successServlet = this.successServlet;
assertNotNull(successServlet);
return Objects.requireNonNull(successServlet);
}
protected final CreateBridgeServlet getCreateBridgeServlet() {
final CreateBridgeServlet createBridgeServlet = this.createBridgeServlet;
assertNotNull(createBridgeServlet);
return Objects.requireNonNull(createBridgeServlet);
}
@BeforeEach
public final void setUpConfigFlowTest() {
setUpCrawler();
setUpServlets();
}
private void setUpCrawler() {
HttpClientFactory clientFactory = getService(HttpClientFactory.class);
assertNotNull(clientFactory);
crawler = new WebsiteCrawler(Objects.requireNonNull(clientFactory));
}
private void setUpServlets() {
MieleCloudConfigService configService = getService(MieleCloudConfigService.class);
assertNotNull(configService);
accountOverviewServlet = configService.getAccountOverviewServlet();
forwardToLoginServlet = configService.getForwardToLoginServlet();
resultServlet = configService.getResultServlet();
successServlet = configService.getSuccessServlet();
createBridgeServlet = configService.getCreateBridgeServlet();
}
}

View File

@@ -0,0 +1,72 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.mielecloud.internal.util;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.mielecloud.internal.MieleCloudBindingConstants;
import org.openhab.core.thing.ThingUID;
/**
* The {@link MieleCloudBindingIntegrationTestConstants} class holds common constants used in integration tests.
*
* @author Björn Lange - Initial contribution
*/
@NonNullByDefault
public final class MieleCloudBindingIntegrationTestConstants {
private MieleCloudBindingIntegrationTestConstants() {
}
public static final String SERIAL_NUMBER = "000124430017";
public static final String BRIDGE_ID = "genesis";
public static final ThingUID BRIDGE_THING_UID = new ThingUID(MieleCloudBindingConstants.THING_TYPE_BRIDGE,
BRIDGE_ID);
public static final ThingUID WASHING_MACHINE_THING_UID = new ThingUID(
MieleCloudBindingConstants.THING_TYPE_WASHING_MACHINE, BRIDGE_THING_UID, SERIAL_NUMBER);
public static final ThingUID OVEN_DEVICE_THING_UID = new ThingUID(MieleCloudBindingConstants.THING_TYPE_OVEN,
BRIDGE_THING_UID, SERIAL_NUMBER);
public static final ThingUID HOB_DEVICE_THING_UID = new ThingUID(MieleCloudBindingConstants.THING_TYPE_HOB,
BRIDGE_THING_UID, SERIAL_NUMBER);
public static final ThingUID FRIDGE_FREEZER_DEVICE_THING_UID = new ThingUID(
MieleCloudBindingConstants.THING_TYPE_FRIDGE_FREEZER, BRIDGE_THING_UID, SERIAL_NUMBER);
public static final ThingUID HOOD_DEVICE_THING_UID = new ThingUID(MieleCloudBindingConstants.THING_TYPE_HOOD,
BRIDGE_THING_UID, SERIAL_NUMBER);
public static final ThingUID COFFEE_SYSTEM_THING_UID = new ThingUID(
MieleCloudBindingConstants.THING_TYPE_COFFEE_SYSTEM, BRIDGE_THING_UID, SERIAL_NUMBER);
public static final ThingUID WINE_STORAGE_DEVICE_THING_UID = new ThingUID(
MieleCloudBindingConstants.THING_TYPE_WINE_STORAGE, BRIDGE_THING_UID, SERIAL_NUMBER);
public static final ThingUID DRYER_DEVICE_THING_UID = new ThingUID(MieleCloudBindingConstants.THING_TYPE_DRYER,
BRIDGE_THING_UID, SERIAL_NUMBER);
public static final ThingUID DISHWASHER_DEVICE_THING_UID = new ThingUID(
MieleCloudBindingConstants.THING_TYPE_DISHWASHER, BRIDGE_THING_UID, SERIAL_NUMBER);
public static final ThingUID DISH_WARMER_DEVICE_THING_UID = new ThingUID(
MieleCloudBindingConstants.THING_TYPE_DISH_WARMER, BRIDGE_THING_UID, SERIAL_NUMBER);
public static final ThingUID ROBOTIC_VACUUM_CLEANER_THING_UID = new ThingUID(
MieleCloudBindingConstants.THING_TYPE_ROBOTIC_VACUUM_CLEANER, BRIDGE_THING_UID, SERIAL_NUMBER);
public static final String MIELE_CLOUD_ACCOUNT_LABEL = "Miele Cloud Account";
public static final String CONFIG_PARAM_REFRESH_TOKEN = "refreshToken";
public static final String ACCESS_TOKEN = "DE_ABCDE";
public static final String ALTERNATIVE_ACCESS_TOKEN = "DE_01234";
public static final String REFRESH_TOKEN = "AT_12345";
public static final String CLIENT_ID = "01234567-890a-bcde-f012-34567890abcd";
public static final String CLIENT_SECRET = "0123456789abcdefghijklmnopqrstiu";
public static final String AUTHORIZATION_CODE = "0123456789";
public static final String EMAIL = "openhab@openhab.org";
}

View File

@@ -0,0 +1,102 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.mielecloud.internal.util;
import static org.junit.jupiter.api.Assertions.*;
import static org.openhab.binding.mielecloud.internal.util.MieleCloudBindingIntegrationTestConstants.MIELE_CLOUD_ACCOUNT_LABEL;
import java.util.Collections;
import java.util.Objects;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.junit.jupiter.api.BeforeEach;
import org.openhab.binding.mielecloud.internal.MieleCloudBindingConstants;
import org.openhab.binding.mielecloud.internal.handler.MieleBridgeHandler;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.config.discovery.inbox.Inbox;
import org.openhab.core.test.java.JavaOSGiTest;
import org.openhab.core.test.storage.VolatileStorageService;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.ManagedThingProvider;
import org.openhab.core.thing.ThingRegistry;
import org.openhab.core.thing.binding.builder.BridgeBuilder;
/**
* Parent class for openHAB OSGi tests offering helper methods for common interactions with the openHAB runtime and its
* services.
*
* @author Björn Lange - Initial contribution
*/
@NonNullByDefault
public abstract class OpenHabOsgiTest extends JavaOSGiTest {
@Nullable
private Inbox inbox;
@Nullable
private ThingRegistry thingRegistry;
protected Inbox getInbox() {
assertNotNull(inbox);
return Objects.requireNonNull(inbox);
}
protected ThingRegistry getThingRegistry() {
assertNotNull(thingRegistry);
return Objects.requireNonNull(thingRegistry);
}
@BeforeEach
public void setUpEshOsgiTest() {
registerVolatileStorageService();
inbox = getService(Inbox.class);
setUpThingRegistry();
}
private void setUpThingRegistry() {
thingRegistry = getService(ThingRegistry.class, ThingRegistry.class);
assertNotNull(thingRegistry, "Thing registry is missing");
}
/**
* Sets up a {@link Bridge} with an attached {@link MieleBridgeHandler} and registers it with the
* {@link ManagedThingProvider} and {@link ThingRegistry}.
*/
public void setUpBridge() {
ManagedThingProvider managedThingProvider = getService(ManagedThingProvider.class);
assertNotNull(managedThingProvider);
Bridge bridge = BridgeBuilder
.create(MieleCloudBindingConstants.THING_TYPE_BRIDGE,
MieleCloudBindingIntegrationTestConstants.BRIDGE_THING_UID)
.withConfiguration(
new Configuration(Collections.singletonMap(MieleCloudBindingConstants.CONFIG_PARAM_EMAIL,
MieleCloudBindingIntegrationTestConstants.EMAIL)))
.withLabel(MIELE_CLOUD_ACCOUNT_LABEL).build();
assertNotNull(bridge);
managedThingProvider.add(bridge);
waitForAssert(() -> {
assertNotNull(bridge.getHandler());
assertTrue(bridge.getHandler() instanceof MieleBridgeHandler, "Handler type is wrong");
});
}
/**
* Registers a volatile storage service.
*/
@Override
protected void registerVolatileStorageService() {
registerService(new VolatileStorageService());
}
}

View File

@@ -0,0 +1,167 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.mielecloud.internal.util;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
/**
* Utility class for reflection operations such as accessing private fields or methods.
*
* @author Björn Lange - Initial contribution
*/
@NonNullByDefault
public final class ReflectionUtil {
private ReflectionUtil() {
}
/**
* Gets a private attribute.
*
* @param object The object to get the attribute from.
* @param fieldName The name of the field to get.
* @return The obtained value.
* @throws SecurityException if the operation is not allowed.
* @throws NoSuchFieldException if no field with the given name exists.
* @throws IllegalAccessException if the field is enforcing Java language access control and is inaccessible.
* @throws IllegalArgumentException if one of the passed parameters is invalid.
*/
@SuppressWarnings("unchecked")
public static <T> T getPrivate(Object object, String fieldName)
throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
Field field = getFieldFromClassHierarchy(object.getClass(), fieldName);
field.setAccessible(true);
return (T) field.get(object);
}
private static Field getFieldFromClassHierarchy(Class<?> clazz, String fieldName)
throws NoSuchFieldException, SecurityException {
Class<?> iteratedClass = clazz;
do {
try {
return iteratedClass.getDeclaredField(fieldName);
} catch (NoSuchFieldException e) {
}
iteratedClass = iteratedClass.getSuperclass();
} while (iteratedClass != null);
throw new NoSuchFieldException();
}
/**
* Sets a private attribute.
*
* @param object The object to set the attribute on.
* @param fieldName The name of the field to set.
* @param value The value to set.
* @throws SecurityException if the operation is not allowed.
* @throws NoSuchFieldException if no field with the given name exists.
* @throws IllegalAccessException if the field is enforcing Java language access control and is inaccessible.
* @throws IllegalArgumentException if one of the passed parameters is invalid.
*/
public static void setPrivate(Object object, String fieldName, @Nullable Object value)
throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
Field field = object.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(object, value);
}
/**
* Sets an attribute declared as {@code private static final}.
*
* @param clazz The class owning the static attribute.
* @param fieldName The name of the attribute.
* @param value The new value.
* @throws NoSuchFieldException if no field with the given name exists.
* @throws SecurityException if the operation is not allowed.
* @throws IllegalArgumentException if one of the passed parameters is invalid.
* @throws IllegalAccessException if the field is enforcing Java language access control and is inaccessible.
*/
public static void setPrivateStaticFinal(Class<?> clazz, String fieldName, @Nullable Object value)
throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
field.set(null, value);
}
/**
* Invokes a private method on an object.
*
* @param object The object to invoke the method on.
* @param methodName The name of the method to invoke.
* @param parameters The parameters of the method invocation.
* @return The method call's return value.
* @throws NoSuchMethodException if no method with the given parameters or name exists.
* @throws SecurityException if the operation is not allowed.
* @throws IllegalAccessException if the method is enforcing Java language access control and is inaccessible.
* @throws IllegalArgumentException if one of the passed parameters is invalid.
* @throws InvocationTargetException if the invoked method throws an exception.
*/
public static <T> T invokePrivate(Object object, String methodName, Object... parameters)
throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException {
Class<?>[] parameterTypes = new Class[parameters.length];
for (int i = 0; i < parameters.length; i++) {
parameterTypes[i] = parameters[i].getClass();
}
return invokePrivate(object, methodName, parameterTypes, parameters);
}
/**
* Invokes a private method on an object.
*
* @param object The object to invoke the method on.
* @param methodName The name of the method to invoke.
* @param parameterTypes The types of the parameters.
* @param parameters The parameters of the method invocation.
* @return The method call's return value.
* @throws NoSuchMethodException if no method with the given parameters or name exists.
* @throws SecurityException if the operation is not allowed.
* @throws IllegalAccessException if the method is enforcing Java language access control and is inaccessible.
* @throws IllegalArgumentException if one of the passed parameters is invalid.
* @throws InvocationTargetException if the invoked method throws an exception.
*/
@SuppressWarnings("unchecked")
public static <T> T invokePrivate(Object object, String methodName, Class<?>[] parameterTypes, Object... parameters)
throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException {
Method method = getMethodFromClassHierarchy(object.getClass(), methodName, parameterTypes);
method.setAccessible(true);
try {
return (T) method.invoke(object, parameters);
} catch (InvocationTargetException e) {
throw new IllegalStateException(e.getCause());
}
}
private static Method getMethodFromClassHierarchy(Class<?> clazz, String methodName, Class<?>[] parameterTypes)
throws NoSuchMethodException {
Class<?> iteratedClass = clazz;
do {
try {
return iteratedClass.getDeclaredMethod(methodName, parameterTypes);
} catch (NoSuchMethodException e) {
}
iteratedClass = iteratedClass.getSuperclass();
} while (iteratedClass != null);
throw new NoSuchMethodException();
}
}

View File

@@ -0,0 +1,113 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.mielecloud.internal.util;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* Helper class for testing websites. Allows for easy access to the document contents.
*
* @author Björn Lange - Initial Contribution
*/
@NonNullByDefault
public final class Website {
private String content;
protected Website(String content) {
this.content = content;
}
/**
* Gets the part of the content representing the element that surrounds the given text.
*/
private String getElementSurrounding(String text) {
int index = content.indexOf(text);
if (index == -1) {
throw new IllegalStateException("Could not find \"" + text + "\" in \"" + content + "\"");
}
int elementBegin = content.lastIndexOf('<', index);
if (elementBegin == -1) {
throw new IllegalStateException("\"" + text + "\" is not contained in \"" + content + "\"");
}
int elementEnd = content.indexOf('>', index);
if (elementEnd == -1) {
throw new IllegalStateException("Malformatted HTML content: " + content);
}
return content.substring(elementBegin, elementEnd + 1);
}
/**
* Gets the value of an attribute from an element.
*/
private String getAttributeFromElement(String element, String attribute) {
int valueStart = element.indexOf(attribute + "=\"");
if (valueStart == -1) {
throw new IllegalStateException("Element \"" + element + "\" has no " + attribute);
}
int valueEnd = element.indexOf('\"', valueStart + attribute.length() + 2);
if (valueEnd == -1) {
throw new IllegalStateException("Malformatted HTML content in element: " + element);
}
return element.substring(valueStart + attribute.length() + 2, valueEnd);
}
/**
* Gets the value of the input field with the given name.
*
* @param inputName Name of the input field.
* @return The value of the input field.
*/
public String getValueOfInput(String inputName) {
return getAttributeFromElement(getElementSurrounding("name=\"" + inputName + "\""), "value");
}
/**
* Gets the value of the href attribute of the link with the given title text.
*/
public String getTargetOfLink(String linkTitle) {
return getAttributeFromElement(getElementSurrounding(linkTitle), "href");
}
/**
* Checks whether the given raw text is contained in the raw website code.
*/
public boolean contains(String expectedContent) {
return this.content.contains(expectedContent);
}
/**
* Gets the value of the action attribute of the first form found in the website body.
*/
public String getFormAction() {
int formActionStart = content.indexOf("<form action=\"");
if (formActionStart == -1) {
throw new IllegalStateException("Could not find a form in \"" + content + "\"");
}
int formActionEnd = content.indexOf('\"', formActionStart + 15);
if (formActionEnd == -1) {
throw new IllegalStateException("Malformatted HTML content in form: " + content);
}
return content.substring(formActionStart + 14, formActionEnd);
}
public String getContent() {
return content;
}
}

View File

@@ -0,0 +1,47 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.mielecloud.internal.util;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.ContentResponse;
import org.openhab.core.io.net.http.HttpClientFactory;
/**
* Allows for requesting website content from URLs.
*
* @author Björn Lange - Initial Contribution
*/
@NonNullByDefault
public final class WebsiteCrawler {
private HttpClient httpClient;
public WebsiteCrawler(HttpClientFactory httpClientFactory) {
this.httpClient = httpClientFactory.getCommonHttpClient();
}
/**
* Gets a website relative to the address of the openHAB installation running in test mode during integration tests.
*
* @param relativeUrl The relative URL.
* @return The website.
* @throws Exception if anything goes wrong.
*/
public Website doGetRelative(String relativeUrl) throws Exception {
ContentResponse response = httpClient.GET("http://127.0.0.1:8080" + relativeUrl);
assertEquals(200, response.getStatus());
return new Website(response.getContentAsString());
}
}