[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:
13
itests/org.openhab.binding.mielecloud.tests/NOTICE
Normal file
13
itests/org.openhab.binding.mielecloud.tests/NOTICE
Normal 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
|
||||
88
itests/org.openhab.binding.mielecloud.tests/itest.bndrun
Normal file
88
itests/org.openhab.binding.mielecloud.tests/itest.bndrun
Normal 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)'
|
||||
25
itests/org.openhab.binding.mielecloud.tests/pom.xml
Normal file
25
itests/org.openhab.binding.mielecloud.tests/pom.xml
Normal 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>
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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\">"));
|
||||
}
|
||||
}
|
||||
@@ -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."));
|
||||
}
|
||||
}
|
||||
@@ -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"));
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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."));
|
||||
}
|
||||
}
|
||||
@@ -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."));
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user