[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:
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* 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;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* @author Björn Lange - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public final class MieleCloudBindingTestConstants {
|
||||
private MieleCloudBindingTestConstants() {
|
||||
throw new IllegalStateException("MieleCloudTestConstants must not be instantiated");
|
||||
}
|
||||
|
||||
public static final String BRIDGE_ID = "genesis";
|
||||
|
||||
public static final String SERVICE_HANDLE = MieleCloudBindingConstants.THING_TYPE_BRIDGE.getAsString() + ":"
|
||||
+ BRIDGE_ID;
|
||||
}
|
||||
@@ -0,0 +1,334 @@
|
||||
/**
|
||||
* 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.auth;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
import org.openhab.binding.mielecloud.internal.MieleCloudBindingTestConstants;
|
||||
import org.openhab.binding.mielecloud.internal.util.ReflectionUtil;
|
||||
import org.openhab.core.auth.client.oauth2.AccessTokenRefreshListener;
|
||||
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.auth.client.oauth2.OAuthResponseException;
|
||||
|
||||
/**
|
||||
* @author Björn Lange - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class OpenHabOAuthTokenRefresherTest {
|
||||
private static final String ACCESS_TOKEN = "DE_0123456789abcdef0123456789abcdef";
|
||||
|
||||
private boolean hasAccessTokenRefreshListenerForServiceHandle(OpenHabOAuthTokenRefresher refresher,
|
||||
String serviceHandle)
|
||||
throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
|
||||
return ReflectionUtil
|
||||
.<Map<String, @Nullable AccessTokenRefreshListener>> getPrivate(refresher, "listenerByServiceHandle")
|
||||
.get(MieleCloudBindingTestConstants.SERVICE_HANDLE) != null;
|
||||
}
|
||||
|
||||
private AccessTokenRefreshListener getAccessTokenRefreshListenerByServiceHandle(
|
||||
OpenHabOAuthTokenRefresher refresher, String serviceHandle)
|
||||
throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
|
||||
AccessTokenRefreshListener listener = ReflectionUtil
|
||||
.<Map<String, @Nullable AccessTokenRefreshListener>> getPrivate(refresher, "listenerByServiceHandle")
|
||||
.get(MieleCloudBindingTestConstants.SERVICE_HANDLE);
|
||||
assertNotNull(listener);
|
||||
return Objects.requireNonNull(listener);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenTheAccountWasNotConfiguredPriorToTheThingInitializingThenNoRefreshListenerCanBeRegistered() {
|
||||
// given:
|
||||
OAuthFactory oauthFactory = mock(OAuthFactory.class);
|
||||
OpenHabOAuthTokenRefresher refresher = new OpenHabOAuthTokenRefresher(oauthFactory);
|
||||
OAuthTokenRefreshListener listener = mock(OAuthTokenRefreshListener.class);
|
||||
|
||||
// when:
|
||||
assertThrows(OAuthException.class, () -> {
|
||||
refresher.setRefreshListener(listener, MieleCloudBindingTestConstants.SERVICE_HANDLE);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenARefreshListenerIsRegisteredThenAListenerIsRegisteredAtTheClientService()
|
||||
throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
|
||||
// given:
|
||||
OAuthClientService oauthClientService = mock(OAuthClientService.class);
|
||||
|
||||
OAuthFactory oauthFactory = mock(OAuthFactory.class);
|
||||
when(oauthFactory.getOAuthClientService(MieleCloudBindingTestConstants.SERVICE_HANDLE))
|
||||
.thenReturn(oauthClientService);
|
||||
|
||||
OpenHabOAuthTokenRefresher refresher = new OpenHabOAuthTokenRefresher(oauthFactory);
|
||||
|
||||
OAuthTokenRefreshListener listener = mock(OAuthTokenRefreshListener.class);
|
||||
|
||||
// when:
|
||||
refresher.setRefreshListener(listener, MieleCloudBindingTestConstants.SERVICE_HANDLE);
|
||||
|
||||
// then:
|
||||
verify(oauthClientService).addAccessTokenRefreshListener(any());
|
||||
assertNotNull(
|
||||
getAccessTokenRefreshListenerByServiceHandle(refresher, MieleCloudBindingTestConstants.SERVICE_HANDLE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenTokenIsRefreshedThenTheListenerIsCalledWithTheNewAccessToken()
|
||||
throws org.openhab.core.auth.client.oauth2.OAuthException, IOException, OAuthResponseException {
|
||||
// given:
|
||||
AccessTokenResponse accessTokenResponse = new AccessTokenResponse();
|
||||
accessTokenResponse.setAccessToken(ACCESS_TOKEN);
|
||||
|
||||
OAuthClientService oauthClientService = mock(OAuthClientService.class);
|
||||
|
||||
OAuthFactory oauthFactory = mock(OAuthFactory.class);
|
||||
when(oauthFactory.getOAuthClientService(MieleCloudBindingTestConstants.SERVICE_HANDLE))
|
||||
.thenReturn(oauthClientService);
|
||||
|
||||
OpenHabOAuthTokenRefresher refresher = new OpenHabOAuthTokenRefresher(oauthFactory);
|
||||
when(oauthClientService.refreshToken()).thenAnswer(new Answer<@Nullable AccessTokenResponse>() {
|
||||
@Override
|
||||
@Nullable
|
||||
public AccessTokenResponse answer(@Nullable InvocationOnMock invocation) throws Throwable {
|
||||
getAccessTokenRefreshListenerByServiceHandle(refresher, MieleCloudBindingTestConstants.SERVICE_HANDLE)
|
||||
.onAccessTokenResponse(accessTokenResponse);
|
||||
return accessTokenResponse;
|
||||
}
|
||||
});
|
||||
|
||||
OAuthTokenRefreshListener listener = mock(OAuthTokenRefreshListener.class);
|
||||
refresher.setRefreshListener(listener, MieleCloudBindingTestConstants.SERVICE_HANDLE);
|
||||
|
||||
// when:
|
||||
refresher.refreshToken(MieleCloudBindingTestConstants.SERVICE_HANDLE);
|
||||
|
||||
// then:
|
||||
verify(listener).onNewAccessToken(ACCESS_TOKEN);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenTokenIsRefreshedAndNoAccessTokenIsProvidedThenTheListenerIsNotNotified()
|
||||
throws org.openhab.core.auth.client.oauth2.OAuthException, IOException, OAuthResponseException {
|
||||
// given:
|
||||
AccessTokenResponse accessTokenResponse = new AccessTokenResponse();
|
||||
|
||||
OAuthClientService oauthClientService = mock(OAuthClientService.class);
|
||||
|
||||
OAuthFactory oauthFactory = mock(OAuthFactory.class);
|
||||
when(oauthFactory.getOAuthClientService(MieleCloudBindingTestConstants.SERVICE_HANDLE))
|
||||
.thenReturn(oauthClientService);
|
||||
|
||||
OpenHabOAuthTokenRefresher refresher = new OpenHabOAuthTokenRefresher(oauthFactory);
|
||||
when(oauthClientService.refreshToken()).thenAnswer(new Answer<@Nullable AccessTokenResponse>() {
|
||||
@Override
|
||||
@Nullable
|
||||
public AccessTokenResponse answer(@Nullable InvocationOnMock invocation) throws Throwable {
|
||||
getAccessTokenRefreshListenerByServiceHandle(refresher, MieleCloudBindingTestConstants.SERVICE_HANDLE)
|
||||
.onAccessTokenResponse(accessTokenResponse);
|
||||
return accessTokenResponse;
|
||||
}
|
||||
});
|
||||
|
||||
OAuthTokenRefreshListener listener = mock(OAuthTokenRefreshListener.class);
|
||||
refresher.setRefreshListener(listener, MieleCloudBindingTestConstants.SERVICE_HANDLE);
|
||||
|
||||
// when:
|
||||
assertThrows(OAuthException.class, () -> {
|
||||
try {
|
||||
refresher.refreshToken(MieleCloudBindingTestConstants.SERVICE_HANDLE);
|
||||
} catch (OAuthException e) {
|
||||
verifyNoInteractions(listener);
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenTokenRefreshFailsWithOAuthExceptionThenTheListenerIsNotNotified()
|
||||
throws org.openhab.core.auth.client.oauth2.OAuthException, IOException, OAuthResponseException {
|
||||
// given:
|
||||
OAuthClientService oauthClientService = mock(OAuthClientService.class);
|
||||
when(oauthClientService.refreshToken()).thenThrow(new org.openhab.core.auth.client.oauth2.OAuthException());
|
||||
|
||||
OAuthFactory oauthFactory = mock(OAuthFactory.class);
|
||||
when(oauthFactory.getOAuthClientService(MieleCloudBindingTestConstants.SERVICE_HANDLE))
|
||||
.thenReturn(oauthClientService);
|
||||
|
||||
OpenHabOAuthTokenRefresher refresher = new OpenHabOAuthTokenRefresher(oauthFactory);
|
||||
|
||||
OAuthTokenRefreshListener listener = mock(OAuthTokenRefreshListener.class);
|
||||
refresher.setRefreshListener(listener, MieleCloudBindingTestConstants.SERVICE_HANDLE);
|
||||
|
||||
// when:
|
||||
assertThrows(OAuthException.class, () -> {
|
||||
try {
|
||||
refresher.refreshToken(MieleCloudBindingTestConstants.SERVICE_HANDLE);
|
||||
} catch (OAuthException e) {
|
||||
verifyNoInteractions(listener);
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenTokenRefreshFailsDueToNetworkErrorThenTheListenerIsNotNotified()
|
||||
throws org.openhab.core.auth.client.oauth2.OAuthException, IOException, OAuthResponseException {
|
||||
// given:
|
||||
OAuthClientService oauthClientService = mock(OAuthClientService.class);
|
||||
when(oauthClientService.refreshToken()).thenThrow(new IOException());
|
||||
|
||||
OAuthFactory oauthFactory = mock(OAuthFactory.class);
|
||||
when(oauthFactory.getOAuthClientService(MieleCloudBindingTestConstants.SERVICE_HANDLE))
|
||||
.thenReturn(oauthClientService);
|
||||
|
||||
OpenHabOAuthTokenRefresher refresher = new OpenHabOAuthTokenRefresher(oauthFactory);
|
||||
|
||||
OAuthTokenRefreshListener listener = mock(OAuthTokenRefreshListener.class);
|
||||
refresher.setRefreshListener(listener, MieleCloudBindingTestConstants.SERVICE_HANDLE);
|
||||
|
||||
// when:
|
||||
assertThrows(OAuthException.class, () -> {
|
||||
try {
|
||||
refresher.refreshToken(MieleCloudBindingTestConstants.SERVICE_HANDLE);
|
||||
} catch (OAuthException e) {
|
||||
verifyNoInteractions(listener);
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenTokenRefreshFailsDueToAnIllegalResponseThenTheListenerIsNotNotified()
|
||||
throws org.openhab.core.auth.client.oauth2.OAuthException, IOException, OAuthResponseException {
|
||||
// given:
|
||||
OAuthClientService oauthClientService = mock(OAuthClientService.class);
|
||||
when(oauthClientService.refreshToken()).thenThrow(new OAuthResponseException());
|
||||
|
||||
OAuthFactory oauthFactory = mock(OAuthFactory.class);
|
||||
when(oauthFactory.getOAuthClientService(MieleCloudBindingTestConstants.SERVICE_HANDLE))
|
||||
.thenReturn(oauthClientService);
|
||||
|
||||
OpenHabOAuthTokenRefresher refresher = new OpenHabOAuthTokenRefresher(oauthFactory);
|
||||
|
||||
OAuthTokenRefreshListener listener = mock(OAuthTokenRefreshListener.class);
|
||||
refresher.setRefreshListener(listener, MieleCloudBindingTestConstants.SERVICE_HANDLE);
|
||||
|
||||
// when:
|
||||
assertThrows(OAuthException.class, () -> {
|
||||
try {
|
||||
refresher.refreshToken(MieleCloudBindingTestConstants.SERVICE_HANDLE);
|
||||
} catch (OAuthException e) {
|
||||
verifyNoInteractions(listener);
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenTheRefreshListenerIsUnsetAndWasNotRegisteredBeforeThenNothingHappens()
|
||||
throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
|
||||
// given:
|
||||
OAuthFactory oauthFactory = mock(OAuthFactory.class);
|
||||
OpenHabOAuthTokenRefresher refresher = new OpenHabOAuthTokenRefresher(oauthFactory);
|
||||
|
||||
// when:
|
||||
refresher.unsetRefreshListener(MieleCloudBindingTestConstants.SERVICE_HANDLE);
|
||||
|
||||
// then:
|
||||
assertFalse(hasAccessTokenRefreshListenerForServiceHandle(refresher,
|
||||
MieleCloudBindingTestConstants.SERVICE_HANDLE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenTheRefreshListenerIsUnsetAndTheClientServiceIsNotAvailableThenTheListenerIsCleared()
|
||||
throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
|
||||
// given:
|
||||
OAuthClientService oauthClientService = mock(OAuthClientService.class);
|
||||
|
||||
OAuthFactory oauthFactory = mock(OAuthFactory.class);
|
||||
when(oauthFactory.getOAuthClientService(MieleCloudBindingTestConstants.SERVICE_HANDLE))
|
||||
.thenReturn(oauthClientService);
|
||||
|
||||
OpenHabOAuthTokenRefresher refresher = new OpenHabOAuthTokenRefresher(oauthFactory);
|
||||
|
||||
OAuthTokenRefreshListener listener = mock(OAuthTokenRefreshListener.class);
|
||||
|
||||
refresher.setRefreshListener(listener, MieleCloudBindingTestConstants.SERVICE_HANDLE);
|
||||
|
||||
// when:
|
||||
refresher.unsetRefreshListener(MieleCloudBindingTestConstants.SERVICE_HANDLE);
|
||||
|
||||
// then:
|
||||
assertFalse(hasAccessTokenRefreshListenerForServiceHandle(refresher,
|
||||
MieleCloudBindingTestConstants.SERVICE_HANDLE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenTheRefreshListenerIsUnsetThenTheListenerIsClearedAndRemovedFromTheClientService()
|
||||
throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
|
||||
// given:
|
||||
OAuthClientService oauthClientService = mock(OAuthClientService.class);
|
||||
|
||||
OAuthFactory oauthFactory = mock(OAuthFactory.class);
|
||||
when(oauthFactory.getOAuthClientService(MieleCloudBindingTestConstants.SERVICE_HANDLE))
|
||||
.thenReturn(oauthClientService);
|
||||
|
||||
OpenHabOAuthTokenRefresher refresher = new OpenHabOAuthTokenRefresher(oauthFactory);
|
||||
|
||||
OAuthTokenRefreshListener listener = mock(OAuthTokenRefreshListener.class);
|
||||
|
||||
refresher.setRefreshListener(listener, MieleCloudBindingTestConstants.SERVICE_HANDLE);
|
||||
|
||||
// when:
|
||||
refresher.unsetRefreshListener(MieleCloudBindingTestConstants.SERVICE_HANDLE);
|
||||
|
||||
// then:
|
||||
verify(oauthClientService).removeAccessTokenRefreshListener(any());
|
||||
assertFalse(hasAccessTokenRefreshListenerForServiceHandle(refresher,
|
||||
MieleCloudBindingTestConstants.SERVICE_HANDLE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenTokensAreRemovedThenTheRuntimeIsRequestedToDeleteServiceAndAccessToken()
|
||||
throws org.openhab.core.auth.client.oauth2.OAuthException, IOException, OAuthResponseException {
|
||||
// given:
|
||||
OAuthClientService oauthClientService = mock(OAuthClientService.class);
|
||||
|
||||
OAuthFactory oauthFactory = mock(OAuthFactory.class);
|
||||
when(oauthFactory.getOAuthClientService(MieleCloudBindingTestConstants.SERVICE_HANDLE))
|
||||
.thenReturn(oauthClientService);
|
||||
|
||||
OpenHabOAuthTokenRefresher refresher = new OpenHabOAuthTokenRefresher(oauthFactory);
|
||||
|
||||
OAuthTokenRefreshListener listener = mock(OAuthTokenRefreshListener.class);
|
||||
refresher.setRefreshListener(listener, MieleCloudBindingTestConstants.SERVICE_HANDLE);
|
||||
|
||||
// when:
|
||||
refresher.removeTokensFromStorage(MieleCloudBindingTestConstants.SERVICE_HANDLE);
|
||||
|
||||
// then:
|
||||
verify(oauthFactory).deleteServiceAndAccessToken(MieleCloudBindingTestConstants.SERVICE_HANDLE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,358 @@
|
||||
/**
|
||||
* 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.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.openhab.binding.mielecloud.internal.util.ReflectionUtil.getPrivate;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
|
||||
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.ArgumentMatchers;
|
||||
import org.openhab.binding.mielecloud.internal.MieleCloudBindingConstants;
|
||||
import org.openhab.binding.mielecloud.internal.MieleCloudBindingTestConstants;
|
||||
import org.openhab.binding.mielecloud.internal.auth.OAuthException;
|
||||
import org.openhab.binding.mielecloud.internal.config.exception.NoOngoingAuthorizationException;
|
||||
import org.openhab.binding.mielecloud.internal.config.exception.OngoingAuthorizationException;
|
||||
import org.openhab.core.auth.client.oauth2.OAuthClientService;
|
||||
import org.openhab.core.auth.client.oauth2.OAuthFactory;
|
||||
import org.openhab.core.auth.client.oauth2.OAuthResponseException;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
|
||||
/**
|
||||
* @author Björn Lange - Initial Contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public final class OAuthAuthorizationHandlerImplTest {
|
||||
private static final String CLIENT_ID = "01234567-890a-bcde-f012-34567890abcd";
|
||||
private static final String CLIENT_SECRET = "0123456789abcdefghijklmnopqrstiu";
|
||||
private static final String REDIRECT_URL = "http://127.0.0.1:8080/mielecloud/result";
|
||||
private static final String AUTH_CODE = "abcdef";
|
||||
private static final ThingUID BRIDGE_UID = new ThingUID(MieleCloudBindingConstants.THING_TYPE_BRIDGE,
|
||||
MieleCloudBindingTestConstants.BRIDGE_ID);
|
||||
private static final String EMAIL = "openhab@openhab.org";
|
||||
|
||||
@Nullable
|
||||
private OAuthClientService clientService;
|
||||
@Nullable
|
||||
private ScheduledFuture<?> timer;
|
||||
@Nullable
|
||||
private Runnable scheduledRunnable;
|
||||
@Nullable
|
||||
private OAuthAuthorizationHandler authorizationHandler;
|
||||
|
||||
private OAuthClientService getClientService() {
|
||||
final OAuthClientService clientService = this.clientService;
|
||||
assertNotNull(clientService);
|
||||
return Objects.requireNonNull(clientService);
|
||||
}
|
||||
|
||||
private ScheduledFuture<?> getTimer() {
|
||||
final ScheduledFuture<?> timer = this.timer;
|
||||
assertNotNull(timer);
|
||||
return Objects.requireNonNull(timer);
|
||||
}
|
||||
|
||||
private Runnable getScheduledRunnable() {
|
||||
final Runnable scheduledRunnable = this.scheduledRunnable;
|
||||
assertNotNull(scheduledRunnable);
|
||||
return Objects.requireNonNull(scheduledRunnable);
|
||||
}
|
||||
|
||||
private OAuthAuthorizationHandler getAuthorizationHandler() {
|
||||
final OAuthAuthorizationHandler authorizationHandler = this.authorizationHandler;
|
||||
assertNotNull(authorizationHandler);
|
||||
return Objects.requireNonNull(authorizationHandler);
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
OAuthClientService clientService = mock(OAuthClientService.class);
|
||||
|
||||
OAuthFactory oauthFactory = mock(OAuthFactory.class);
|
||||
when(oauthFactory.createOAuthClientService(anyString(), anyString(), anyString(), anyString(), anyString(),
|
||||
isNull(), any())).thenReturn(clientService);
|
||||
|
||||
ScheduledFuture<?> timer = mock(ScheduledFuture.class);
|
||||
when(timer.isDone()).thenReturn(false);
|
||||
|
||||
ScheduledExecutorService scheduler = mock(ScheduledExecutorService.class);
|
||||
when(scheduler.schedule(ArgumentMatchers.<Runnable> any(), anyLong(), any())).thenAnswer(invocation -> {
|
||||
scheduledRunnable = invocation.getArgument(0);
|
||||
return timer;
|
||||
});
|
||||
|
||||
OAuthAuthorizationHandler authorizationHandler = new OAuthAuthorizationHandlerImpl(oauthFactory, scheduler);
|
||||
|
||||
this.clientService = clientService;
|
||||
this.timer = timer;
|
||||
this.scheduledRunnable = null;
|
||||
this.authorizationHandler = authorizationHandler;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenTheAuthorizationIsCompletedInTimeThenTheTimerIsCancelledAndAllResourcesAreCleanedUp()
|
||||
throws Exception {
|
||||
// given:
|
||||
getAuthorizationHandler().beginAuthorization(CLIENT_ID, CLIENT_SECRET, BRIDGE_UID, EMAIL);
|
||||
getAuthorizationHandler().getAuthorizationUrl(REDIRECT_URL);
|
||||
|
||||
// when:
|
||||
getAuthorizationHandler().completeAuthorization("http://127.0.0.1:8080/mielecloud/result?code=abc&state=def");
|
||||
|
||||
// then:
|
||||
assertNull(getPrivate(getAuthorizationHandler(), "timer"));
|
||||
verify(getTimer()).cancel(false);
|
||||
|
||||
assertNull(getPrivate(getAuthorizationHandler(), "oauthClientService"));
|
||||
assertNull(getPrivate(getAuthorizationHandler(), "bridgeUid"));
|
||||
assertNull(getPrivate(getAuthorizationHandler(), "redirectUri"));
|
||||
assertNull(getPrivate(getAuthorizationHandler(), "email"));
|
||||
|
||||
verify(getClientService()).extractAuthCodeFromAuthResponse(anyString());
|
||||
verify(getClientService()).getAccessTokenResponseByAuthorizationCode(isNull(), anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenTheAuthorizationTimesOutThenTheOngoingAuthorizationIsCancelled() throws Exception {
|
||||
// given:
|
||||
getAuthorizationHandler().beginAuthorization(CLIENT_ID, CLIENT_SECRET, BRIDGE_UID, EMAIL);
|
||||
getAuthorizationHandler().getAuthorizationUrl(REDIRECT_URL);
|
||||
|
||||
// when:
|
||||
getScheduledRunnable().run();
|
||||
|
||||
// then:
|
||||
assertNull(getPrivate(getAuthorizationHandler(), "oauthClientService"));
|
||||
assertNull(getPrivate(getAuthorizationHandler(), "bridgeUid"));
|
||||
assertNull(getPrivate(getAuthorizationHandler(), "redirectUri"));
|
||||
assertNull(getPrivate(getAuthorizationHandler(), "email"));
|
||||
assertNull(getPrivate(getAuthorizationHandler(), "timer"));
|
||||
verify(getTimer()).cancel(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenTheAuthorizationCompletesAfterItTimedOutThenAnNoOngoingAuthorizationExceptionIsThrown()
|
||||
throws Exception {
|
||||
// given:
|
||||
getAuthorizationHandler().beginAuthorization(CLIENT_ID, CLIENT_SECRET, BRIDGE_UID, EMAIL);
|
||||
getAuthorizationHandler().getAuthorizationUrl(REDIRECT_URL);
|
||||
|
||||
getScheduledRunnable().run();
|
||||
|
||||
// when:
|
||||
assertThrows(NoOngoingAuthorizationException.class, () -> {
|
||||
getAuthorizationHandler()
|
||||
.completeAuthorization("http://127.0.0.1:8080/mielecloud/result?code=abc&state=def");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenASecondAuthorizationIsBegunWhileAnotherIsStillOngoingThenAnOngoingAuthorizationExceptionIsThrown() {
|
||||
// given:
|
||||
getAuthorizationHandler().beginAuthorization(CLIENT_ID, CLIENT_SECRET, BRIDGE_UID, EMAIL);
|
||||
|
||||
// when:
|
||||
assertThrows(OngoingAuthorizationException.class, () -> {
|
||||
getAuthorizationHandler().beginAuthorization(CLIENT_ID, CLIENT_SECRET, BRIDGE_UID, EMAIL);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenNoAuthorizationIsOngoingAndTheAuthorizationUrlIsRequestedThenAnNoOngoingAuthorizationExceptionIsThrown() {
|
||||
// when:
|
||||
assertThrows(NoOngoingAuthorizationException.class, () -> {
|
||||
getAuthorizationHandler().getAuthorizationUrl(REDIRECT_URL);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenGetAuthorizationUrlFromTheFrameworkFailsThenTheOngoingAuthorizationIsAborted()
|
||||
throws org.openhab.core.auth.client.oauth2.OAuthException, IllegalArgumentException, IllegalAccessException,
|
||||
NoSuchFieldException, SecurityException {
|
||||
// given:
|
||||
when(getClientService().getAuthorizationUrl(anyString(), isNull(), isNull()))
|
||||
.thenThrow(new org.openhab.core.auth.client.oauth2.OAuthException());
|
||||
|
||||
getAuthorizationHandler().beginAuthorization(CLIENT_ID, CLIENT_SECRET, BRIDGE_UID, EMAIL);
|
||||
|
||||
// when:
|
||||
assertThrows(OAuthException.class, () -> {
|
||||
try {
|
||||
getAuthorizationHandler().getAuthorizationUrl(REDIRECT_URL);
|
||||
} catch (OAuthException e) {
|
||||
assertNull(getPrivate(getAuthorizationHandler(), "timer"));
|
||||
assertNull(getPrivate(getAuthorizationHandler(), "oauthClientService"));
|
||||
assertNull(getPrivate(getAuthorizationHandler(), "bridgeUid"));
|
||||
assertNull(getPrivate(getAuthorizationHandler(), "redirectUri"));
|
||||
assertNull(getPrivate(getAuthorizationHandler(), "email"));
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenExtractingTheAuthCodeFromTheResponseFailsThenAnOAuthExceptionIsThrownAndAllResourcesAreCleanedUp()
|
||||
throws Exception {
|
||||
// given:
|
||||
when(getClientService().extractAuthCodeFromAuthResponse(anyString()))
|
||||
.thenThrow(new org.openhab.core.auth.client.oauth2.OAuthException());
|
||||
|
||||
getAuthorizationHandler().beginAuthorization(CLIENT_ID, CLIENT_SECRET, BRIDGE_UID, EMAIL);
|
||||
getAuthorizationHandler().getAuthorizationUrl(REDIRECT_URL);
|
||||
|
||||
// when:
|
||||
assertThrows(OAuthException.class, () -> {
|
||||
try {
|
||||
getAuthorizationHandler()
|
||||
.completeAuthorization("http://127.0.0.1:8080/mielecloud/result?code=abc&state=def");
|
||||
} catch (OAuthException e) {
|
||||
assertNull(getPrivate(getAuthorizationHandler(), "timer"));
|
||||
assertNull(getPrivate(getAuthorizationHandler(), "oauthClientService"));
|
||||
assertNull(getPrivate(getAuthorizationHandler(), "bridgeUid"));
|
||||
assertNull(getPrivate(getAuthorizationHandler(), "email"));
|
||||
assertNull(getPrivate(getAuthorizationHandler(), "redirectUri"));
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenRetrievingTheAccessTokenFailsDueToANetworkErrorThenAnOAuthExceptionIsThrownAndAllResourcesAreCleanedUp()
|
||||
throws Exception {
|
||||
// given:
|
||||
when(getClientService().extractAuthCodeFromAuthResponse(anyString())).thenReturn(AUTH_CODE);
|
||||
when(getClientService().getAccessTokenResponseByAuthorizationCode(anyString(), anyString()))
|
||||
.thenThrow(new IOException());
|
||||
|
||||
getAuthorizationHandler().beginAuthorization(CLIENT_ID, CLIENT_SECRET, BRIDGE_UID, EMAIL);
|
||||
getAuthorizationHandler().getAuthorizationUrl(REDIRECT_URL);
|
||||
|
||||
// when:
|
||||
assertThrows(OAuthException.class, () -> {
|
||||
try {
|
||||
getAuthorizationHandler()
|
||||
.completeAuthorization("http://127.0.0.1:8080/mielecloud/result?code=abc&state=def");
|
||||
} catch (OAuthException e) {
|
||||
assertNull(getPrivate(getAuthorizationHandler(), "timer"));
|
||||
assertNull(getPrivate(getAuthorizationHandler(), "oauthClientService"));
|
||||
assertNull(getPrivate(getAuthorizationHandler(), "bridgeUid"));
|
||||
assertNull(getPrivate(getAuthorizationHandler(), "email"));
|
||||
assertNull(getPrivate(getAuthorizationHandler(), "redirectUri"));
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenRetrievingTheAccessTokenFailsDueToAnIllegalAnswerFromTheMieleServiceThenAnOAuthExceptionIsThrownAndAllResourcesAreCleanedUp()
|
||||
throws Exception {
|
||||
// given:
|
||||
when(getClientService().extractAuthCodeFromAuthResponse(anyString())).thenReturn(AUTH_CODE);
|
||||
when(getClientService().getAccessTokenResponseByAuthorizationCode(anyString(), anyString()))
|
||||
.thenThrow(new OAuthResponseException());
|
||||
|
||||
getAuthorizationHandler().beginAuthorization(CLIENT_ID, CLIENT_SECRET, BRIDGE_UID, EMAIL);
|
||||
getAuthorizationHandler().getAuthorizationUrl(REDIRECT_URL);
|
||||
|
||||
// when:
|
||||
assertThrows(OAuthException.class, () -> {
|
||||
try {
|
||||
getAuthorizationHandler()
|
||||
.completeAuthorization("http://127.0.0.1:8080/mielecloud/result?code=abc&state=def");
|
||||
} catch (OAuthException e) {
|
||||
assertNull(getPrivate(getAuthorizationHandler(), "timer"));
|
||||
assertNull(getPrivate(getAuthorizationHandler(), "oauthClientService"));
|
||||
assertNull(getPrivate(getAuthorizationHandler(), "bridgeUid"));
|
||||
assertNull(getPrivate(getAuthorizationHandler(), "email"));
|
||||
assertNull(getPrivate(getAuthorizationHandler(), "redirectUri"));
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenRetrievingTheAccessTokenFailsWhileProcessingTheResponseThenAnOAuthExceptionIsThrownAndAllResourcesAreCleanedUp()
|
||||
throws Exception {
|
||||
// given:
|
||||
when(getClientService().extractAuthCodeFromAuthResponse(anyString())).thenReturn(AUTH_CODE);
|
||||
when(getClientService().getAccessTokenResponseByAuthorizationCode(anyString(), anyString()))
|
||||
.thenThrow(new org.openhab.core.auth.client.oauth2.OAuthException());
|
||||
|
||||
getAuthorizationHandler().beginAuthorization(CLIENT_ID, CLIENT_SECRET, BRIDGE_UID, EMAIL);
|
||||
getAuthorizationHandler().getAuthorizationUrl(REDIRECT_URL);
|
||||
|
||||
// when:
|
||||
assertThrows(OAuthException.class, () -> {
|
||||
try {
|
||||
getAuthorizationHandler()
|
||||
.completeAuthorization("http://127.0.0.1:8080/mielecloud/result?code=abc&state=def");
|
||||
} catch (OAuthException e) {
|
||||
assertNull(getPrivate(getAuthorizationHandler(), "timer"));
|
||||
assertNull(getPrivate(getAuthorizationHandler(), "oauthClientService"));
|
||||
assertNull(getPrivate(getAuthorizationHandler(), "bridgeUid"));
|
||||
assertNull(getPrivate(getAuthorizationHandler(), "email"));
|
||||
assertNull(getPrivate(getAuthorizationHandler(), "redirectUri"));
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenNoAuthorizationIsOngoingThenGetBridgeUidThrowsNoOngoingAuthorizationException() {
|
||||
// when:
|
||||
assertThrows(NoOngoingAuthorizationException.class, () -> {
|
||||
getAuthorizationHandler().getBridgeUid();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenNoAuthorizationIsOngoingThenGetEmailThrowsNoOngoingAuthorizationException() {
|
||||
// when:
|
||||
assertThrows(NoOngoingAuthorizationException.class, () -> {
|
||||
getAuthorizationHandler().getEmail();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenAnAuthorizationIsOngoingThenGetBridgeUidReturnsTheUidOfTheBridgeBeingAuthorized() {
|
||||
// given:
|
||||
getAuthorizationHandler().beginAuthorization(CLIENT_ID, CLIENT_SECRET, BRIDGE_UID, EMAIL);
|
||||
|
||||
// when:
|
||||
ThingUID bridgeUid = getAuthorizationHandler().getBridgeUid();
|
||||
|
||||
// then:
|
||||
assertEquals(BRIDGE_UID, bridgeUid);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenAnAuthorizationIsOngoingThenGetEmailReturnsTheEmailBeingAuthorized() {
|
||||
// given:
|
||||
getAuthorizationHandler().beginAuthorization(CLIENT_ID, CLIENT_SECRET, BRIDGE_UID, EMAIL);
|
||||
|
||||
// when:
|
||||
String email = getAuthorizationHandler().getEmail();
|
||||
|
||||
// then:
|
||||
assertEquals(EMAIL, email);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,270 @@
|
||||
/**
|
||||
* 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.assertEquals;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openhab.binding.mielecloud.internal.MieleCloudBindingConstants;
|
||||
import org.openhab.binding.mielecloud.internal.MieleCloudBindingTestConstants;
|
||||
import org.openhab.core.config.core.Configuration;
|
||||
import org.openhab.core.config.discovery.DiscoveryResult;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
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 ThingsTemplateGeneratorTest {
|
||||
private static final String BRIDGE_ID = "genesis";
|
||||
private static final String ALTERNATIVE_BRIDGE_ID = "mielebridge";
|
||||
|
||||
private static final String LOCALE = "en";
|
||||
private static final String ALTERNATIVE_LOCALE = "de";
|
||||
|
||||
private static final String EMAIL = "openhab@openhab.org";
|
||||
private static final String ALTERNATIVE_EMAIL = "everyone@openhab.org";
|
||||
|
||||
@Test
|
||||
public void whenBridgeIdAndAccessTokenAndLocaleAreProvidedThenAValidBridgeConfigurationTemplateIsGenerated() {
|
||||
// given:
|
||||
ThingsTemplateGenerator templateGenerator = new ThingsTemplateGenerator();
|
||||
|
||||
// when:
|
||||
String template = templateGenerator.createBridgeConfigurationTemplate(BRIDGE_ID, EMAIL, LOCALE);
|
||||
|
||||
// then:
|
||||
assertEquals("Bridge mielecloud:account:genesis [ email=\"openhab@openhab.org\", locale=\"en\" ]", template);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenAnAlternativeBridgeIdIsProvidedThenAValidBridgeConfigurationTemplateWithThatIdIsGenerated() {
|
||||
// given:
|
||||
ThingsTemplateGenerator templateGenerator = new ThingsTemplateGenerator();
|
||||
|
||||
// when:
|
||||
String template = templateGenerator.createBridgeConfigurationTemplate(ALTERNATIVE_BRIDGE_ID, EMAIL, LOCALE);
|
||||
|
||||
// then:
|
||||
assertEquals("Bridge mielecloud:account:mielebridge [ email=\"openhab@openhab.org\", locale=\"en\" ]",
|
||||
template);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenAnAlternativeAccessTokenIsProvidedThenAValidBridgeConfigurationTemplateWithThatAccessTokenIsGenerated() {
|
||||
// given:
|
||||
ThingsTemplateGenerator templateGenerator = new ThingsTemplateGenerator();
|
||||
|
||||
// when:
|
||||
String template = templateGenerator.createBridgeConfigurationTemplate(BRIDGE_ID, EMAIL, LOCALE);
|
||||
|
||||
// then:
|
||||
assertEquals("Bridge mielecloud:account:genesis [ email=\"openhab@openhab.org\", locale=\"en\" ]", template);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenAnAlternativeLocaleIsProvidedThenAValidBridgeConfigurationTemplateWithThatLocaleIsGenerated() {
|
||||
// given:
|
||||
ThingsTemplateGenerator templateGenerator = new ThingsTemplateGenerator();
|
||||
|
||||
// when:
|
||||
String template = templateGenerator.createBridgeConfigurationTemplate(BRIDGE_ID, EMAIL, ALTERNATIVE_LOCALE);
|
||||
|
||||
// then:
|
||||
assertEquals("Bridge mielecloud:account:genesis [ email=\"openhab@openhab.org\", locale=\"de\" ]", template);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenAnAlternativeEmailIsProvidedThenAValidBridgeConfigurationTemplateWithThatEmailIsGenerated() {
|
||||
// given:
|
||||
ThingsTemplateGenerator templateGenerator = new ThingsTemplateGenerator();
|
||||
|
||||
// when:
|
||||
String template = templateGenerator.createBridgeConfigurationTemplate(BRIDGE_ID, ALTERNATIVE_EMAIL, LOCALE);
|
||||
|
||||
// then:
|
||||
assertEquals("Bridge mielecloud:account:genesis [ email=\"everyone@openhab.org\", locale=\"en\" ]", template);
|
||||
}
|
||||
|
||||
private Bridge createBridgeMock(String id, String locale, String email) {
|
||||
Configuration configuration = mock(Configuration.class);
|
||||
when(configuration.get(MieleCloudBindingConstants.CONFIG_PARAM_LOCALE)).thenReturn(locale);
|
||||
when(configuration.get(MieleCloudBindingConstants.CONFIG_PARAM_EMAIL)).thenReturn(email);
|
||||
|
||||
Bridge bridge = mock(Bridge.class);
|
||||
when(bridge.getUID()).thenReturn(new ThingUID(MieleCloudBindingConstants.THING_TYPE_BRIDGE, id));
|
||||
when(bridge.getConfiguration()).thenReturn(configuration);
|
||||
|
||||
return bridge;
|
||||
}
|
||||
|
||||
private Thing createThingMock(ThingTypeUID thingTypeUid, String deviceIdentifier, @Nullable String label,
|
||||
String bridgeId) {
|
||||
Configuration configuration = mock(Configuration.class);
|
||||
when(configuration.get(MieleCloudBindingConstants.CONFIG_PARAM_DEVICE_IDENTIFIER)).thenReturn(deviceIdentifier);
|
||||
|
||||
Thing thing = mock(Thing.class);
|
||||
when(thing.getThingTypeUID()).thenReturn(thingTypeUid);
|
||||
when(thing.getUID()).thenReturn(new ThingUID(thingTypeUid, deviceIdentifier, bridgeId));
|
||||
when(thing.getLabel()).thenReturn(label);
|
||||
when(thing.getConfiguration()).thenReturn(configuration);
|
||||
return thing;
|
||||
}
|
||||
|
||||
private DiscoveryResult createDiscoveryResultMock(ThingTypeUID thingTypeUid, String id, String label,
|
||||
String bridgeId) {
|
||||
DiscoveryResult discoveryResult = mock(DiscoveryResult.class);
|
||||
when(discoveryResult.getLabel()).thenReturn(label);
|
||||
when(discoveryResult.getThingTypeUID()).thenReturn(thingTypeUid);
|
||||
when(discoveryResult.getThingUID()).thenReturn(new ThingUID(thingTypeUid, id, bridgeId));
|
||||
when(discoveryResult.getProperties())
|
||||
.thenReturn(Collections.singletonMap(MieleCloudBindingConstants.CONFIG_PARAM_DEVICE_IDENTIFIER, id));
|
||||
return discoveryResult;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenNoThingsArePairedAndNoInboxEntriesAreAvailableThenAnEmptyConfigurationTemplateIsGenerated() {
|
||||
// given:
|
||||
ThingsTemplateGenerator templateGenerator = new ThingsTemplateGenerator();
|
||||
|
||||
Bridge bridge = createBridgeMock(MieleCloudBindingTestConstants.BRIDGE_ID, LOCALE, EMAIL);
|
||||
|
||||
// when:
|
||||
String template = templateGenerator.createBridgeAndThingConfigurationTemplate(bridge, Collections.emptyList(),
|
||||
Collections.emptyList());
|
||||
|
||||
// then:
|
||||
assertEquals("Bridge mielecloud:account:genesis [ email=\"openhab@openhab.org\", locale=\"en\" ] {\n}",
|
||||
template);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenPairedThingsArePresentThenTheyArePresentInTheConfigurationTemplate() {
|
||||
// given:
|
||||
ThingsTemplateGenerator templateGenerator = new ThingsTemplateGenerator();
|
||||
|
||||
Bridge bridge = createBridgeMock(ALTERNATIVE_BRIDGE_ID, ALTERNATIVE_LOCALE, ALTERNATIVE_EMAIL);
|
||||
|
||||
Thing thing1 = createThingMock(MieleCloudBindingConstants.THING_TYPE_OVEN, "000137439123", "Oven H7860XY",
|
||||
ALTERNATIVE_BRIDGE_ID);
|
||||
Thing thing2 = createThingMock(MieleCloudBindingConstants.THING_TYPE_HOB, "000160106123", null,
|
||||
ALTERNATIVE_BRIDGE_ID);
|
||||
|
||||
List<Thing> pairedThings = Arrays.asList(thing1, thing2);
|
||||
|
||||
// when:
|
||||
String template = templateGenerator.createBridgeAndThingConfigurationTemplate(bridge, pairedThings,
|
||||
Collections.emptyList());
|
||||
|
||||
// then:
|
||||
assertEquals("Bridge mielecloud:account:mielebridge [ email=\"everyone@openhab.org\", locale=\"de\" ] {\n"
|
||||
+ " Thing oven 000137439123 \"Oven H7860XY\" [ deviceIdentifier=\"000137439123\" ]\n"
|
||||
+ " Thing hob 000160106123 [ deviceIdentifier=\"000160106123\" ]\n}", template);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenDiscoveryResultsAreInTheInboxThenTheyArePresentInTheConfigurationTemplate() {
|
||||
// given:
|
||||
ThingsTemplateGenerator templateGenerator = new ThingsTemplateGenerator();
|
||||
|
||||
Bridge bridge = createBridgeMock(ALTERNATIVE_BRIDGE_ID, ALTERNATIVE_LOCALE, ALTERNATIVE_EMAIL);
|
||||
|
||||
DiscoveryResult discoveryResult1 = createDiscoveryResultMock(
|
||||
MieleCloudBindingConstants.THING_TYPE_FRIDGE_FREEZER, "000154106123", "Fridge-Freezer Kitchen",
|
||||
ALTERNATIVE_BRIDGE_ID);
|
||||
DiscoveryResult discoveryResult2 = createDiscoveryResultMock(
|
||||
MieleCloudBindingConstants.THING_TYPE_WASHING_MACHINE, "000189106123", "Washing Machine",
|
||||
ALTERNATIVE_BRIDGE_ID);
|
||||
|
||||
List<DiscoveryResult> discoveredThings = Arrays.asList(discoveryResult1, discoveryResult2);
|
||||
|
||||
// when:
|
||||
String template = templateGenerator.createBridgeAndThingConfigurationTemplate(bridge, Collections.emptyList(),
|
||||
discoveredThings);
|
||||
|
||||
// then:
|
||||
assertEquals("Bridge mielecloud:account:mielebridge [ email=\"everyone@openhab.org\", locale=\"de\" ] {\n"
|
||||
+ " Thing fridge_freezer 000154106123 \"Fridge-Freezer Kitchen\" [ deviceIdentifier=\"000154106123\" ]\n"
|
||||
+ " Thing washing_machine 000189106123 \"Washing Machine\" [ deviceIdentifier=\"000189106123\" ]\n}",
|
||||
template);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenThingsArePresentAndDiscoveryResultsAreInTheInboxThenTheyArePresentInTheConfigurationTemplate() {
|
||||
// given:
|
||||
ThingsTemplateGenerator templateGenerator = new ThingsTemplateGenerator();
|
||||
|
||||
Bridge bridge = createBridgeMock(ALTERNATIVE_BRIDGE_ID, ALTERNATIVE_LOCALE, EMAIL);
|
||||
|
||||
Thing thing1 = createThingMock(MieleCloudBindingConstants.THING_TYPE_OVEN, "000137439123", "Oven H7860XY",
|
||||
ALTERNATIVE_BRIDGE_ID);
|
||||
Thing thing2 = createThingMock(MieleCloudBindingConstants.THING_TYPE_HOB, "000160106123", null,
|
||||
ALTERNATIVE_BRIDGE_ID);
|
||||
|
||||
List<Thing> pairedThings = Arrays.asList(thing1, thing2);
|
||||
|
||||
DiscoveryResult discoveryResult1 = createDiscoveryResultMock(
|
||||
MieleCloudBindingConstants.THING_TYPE_FRIDGE_FREEZER, "000154106123", "Fridge-Freezer Kitchen",
|
||||
ALTERNATIVE_BRIDGE_ID);
|
||||
DiscoveryResult discoveryResult2 = createDiscoveryResultMock(
|
||||
MieleCloudBindingConstants.THING_TYPE_WASHING_MACHINE, "000189106123", "Washing Machine",
|
||||
ALTERNATIVE_BRIDGE_ID);
|
||||
|
||||
List<DiscoveryResult> discoveredThings = Arrays.asList(discoveryResult1, discoveryResult2);
|
||||
|
||||
// when:
|
||||
String template = templateGenerator.createBridgeAndThingConfigurationTemplate(bridge, pairedThings,
|
||||
discoveredThings);
|
||||
|
||||
// then:
|
||||
assertEquals("Bridge mielecloud:account:mielebridge [ email=\"openhab@openhab.org\", locale=\"de\" ] {\n"
|
||||
+ " Thing oven 000137439123 \"Oven H7860XY\" [ deviceIdentifier=\"000137439123\" ]\n"
|
||||
+ " Thing hob 000160106123 [ deviceIdentifier=\"000160106123\" ]\n"
|
||||
+ " Thing fridge_freezer 000154106123 \"Fridge-Freezer Kitchen\" [ deviceIdentifier=\"000154106123\" ]\n"
|
||||
+ " Thing washing_machine 000189106123 \"Washing Machine\" [ deviceIdentifier=\"000189106123\" ]\n}",
|
||||
template);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenNoLocaleIsConfiguredThenTheDefaultIsUsed() {
|
||||
// given:
|
||||
ThingsTemplateGenerator templateGenerator = new ThingsTemplateGenerator();
|
||||
|
||||
Configuration configuration = mock(Configuration.class);
|
||||
when(configuration.get(MieleCloudBindingConstants.CONFIG_PARAM_LOCALE)).thenReturn(null);
|
||||
when(configuration.get(MieleCloudBindingConstants.CONFIG_PARAM_EMAIL)).thenReturn(EMAIL);
|
||||
|
||||
Bridge bridge = mock(Bridge.class);
|
||||
when(bridge.getUID()).thenReturn(
|
||||
new ThingUID(MieleCloudBindingConstants.THING_TYPE_BRIDGE, MieleCloudBindingTestConstants.BRIDGE_ID));
|
||||
when(bridge.getConfiguration()).thenReturn(configuration);
|
||||
|
||||
// when:
|
||||
String template = templateGenerator.createBridgeAndThingConfigurationTemplate(bridge, Collections.emptyList(),
|
||||
Collections.emptyList());
|
||||
|
||||
// then:
|
||||
assertEquals("Bridge mielecloud:account:genesis [ email=\"openhab@openhab.org\", locale=\"en\" ] {\n}",
|
||||
template);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
* 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.assertEquals;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.CsvSource;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.openhab.binding.mielecloud.internal.MieleCloudBindingConstants;
|
||||
import org.openhab.binding.mielecloud.internal.webservice.api.DeviceState;
|
||||
import org.openhab.binding.mielecloud.internal.webservice.api.json.DeviceType;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
|
||||
/**
|
||||
* @author Björn Lange - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ThingInformationExtractorTest {
|
||||
private static Stream<Arguments> extractedPropertiesContainSerialNumberAndModelIdParameterSource() {
|
||||
return Stream.of(
|
||||
Arguments.of(MieleCloudBindingConstants.THING_TYPE_HOOD, DeviceType.HOOD, "000124430018",
|
||||
Optional.of("000124430017"), Optional.of("Ventilation Hood"), Optional.of("DA-6996"),
|
||||
"000124430017", "Ventilation Hood DA-6996", "000124430018"),
|
||||
Arguments.of(MieleCloudBindingConstants.THING_TYPE_COFFEE_SYSTEM, DeviceType.COFFEE_SYSTEM,
|
||||
"000124431235", Optional.of("000124431234"), Optional.of("Coffee Machine"),
|
||||
Optional.of("CM-1234"), "000124431234", "Coffee Machine CM-1234", "000124431235"),
|
||||
Arguments.of(MieleCloudBindingConstants.THING_TYPE_HOOD, DeviceType.HOOD, "000124430018",
|
||||
Optional.empty(), Optional.of("Ventilation Hood"), Optional.of("DA-6996"), "000124430018",
|
||||
"Ventilation Hood DA-6996", "000124430018"),
|
||||
Arguments.of(MieleCloudBindingConstants.THING_TYPE_HOOD, DeviceType.HOOD, "000124430018",
|
||||
Optional.empty(), Optional.empty(), Optional.of("DA-6996"), "000124430018", "DA-6996",
|
||||
"000124430018"),
|
||||
Arguments.of(MieleCloudBindingConstants.THING_TYPE_HOOD, DeviceType.HOOD, "000124430018",
|
||||
Optional.empty(), Optional.of("Ventilation Hood"), Optional.empty(), "000124430018",
|
||||
"Ventilation Hood", "000124430018"),
|
||||
Arguments.of(MieleCloudBindingConstants.THING_TYPE_HOOD, DeviceType.HOOD, "000124430018",
|
||||
Optional.empty(), Optional.empty(), Optional.empty(), "000124430018", "Unknown",
|
||||
"000124430018"));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("extractedPropertiesContainSerialNumberAndModelIdParameterSource")
|
||||
void extractedPropertiesContainSerialNumberAndModelId(ThingTypeUID thingTypeUid, DeviceType deviceType,
|
||||
String deviceIdentifier, Optional<String> fabNumber, Optional<String> type, Optional<String> techType,
|
||||
String expectedSerialNumber, String expectedModelId, String expectedDeviceIdentifier) {
|
||||
// given:
|
||||
var deviceState = mock(DeviceState.class);
|
||||
when(deviceState.getRawType()).thenReturn(deviceType);
|
||||
when(deviceState.getDeviceIdentifier()).thenReturn(deviceIdentifier);
|
||||
when(deviceState.getFabNumber()).thenReturn(fabNumber);
|
||||
when(deviceState.getType()).thenReturn(type);
|
||||
when(deviceState.getTechType()).thenReturn(techType);
|
||||
|
||||
// when:
|
||||
var properties = ThingInformationExtractor.extractProperties(thingTypeUid, deviceState);
|
||||
|
||||
// then:
|
||||
assertEquals(3, properties.size());
|
||||
assertEquals(expectedSerialNumber, properties.get(Thing.PROPERTY_SERIAL_NUMBER));
|
||||
assertEquals(expectedModelId, properties.get(Thing.PROPERTY_MODEL_ID));
|
||||
assertEquals(expectedDeviceIdentifier,
|
||||
properties.get(MieleCloudBindingConstants.CONFIG_PARAM_DEVICE_IDENTIFIER));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@CsvSource({ "2,2", "4,4" })
|
||||
void propertiesForHobContainPlateCount(int plateCount, String expectedPlateCountPropertyValue) {
|
||||
// given:
|
||||
var deviceState = mock(DeviceState.class);
|
||||
when(deviceState.getRawType()).thenReturn(DeviceType.HOB_INDUCTION);
|
||||
when(deviceState.getDeviceIdentifier()).thenReturn("000124430019");
|
||||
when(deviceState.getFabNumber()).thenReturn(Optional.of("000124430019"));
|
||||
when(deviceState.getType()).thenReturn(Optional.of("Induction Hob"));
|
||||
when(deviceState.getTechType()).thenReturn(Optional.of("IH-7890"));
|
||||
when(deviceState.getPlateStepCount()).thenReturn(Optional.of(plateCount));
|
||||
|
||||
// when:
|
||||
var properties = ThingInformationExtractor.extractProperties(MieleCloudBindingConstants.THING_TYPE_HOB,
|
||||
deviceState);
|
||||
|
||||
// then:
|
||||
assertEquals(4, properties.size());
|
||||
assertEquals("000124430019", properties.get(Thing.PROPERTY_SERIAL_NUMBER));
|
||||
assertEquals("Induction Hob IH-7890", properties.get(Thing.PROPERTY_MODEL_ID));
|
||||
assertEquals("000124430019", properties.get(MieleCloudBindingConstants.CONFIG_PARAM_DEVICE_IDENTIFIER));
|
||||
assertEquals(expectedPlateCountPropertyValue, properties.get(MieleCloudBindingConstants.PROPERTY_PLATE_COUNT));
|
||||
}
|
||||
}
|
||||
@@ -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 static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* @author Björn Lange - Initial Contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class LocaleValidatorTest {
|
||||
@Test
|
||||
public void enIsAValidLanguage() {
|
||||
// given:
|
||||
String language = "en";
|
||||
|
||||
// when:
|
||||
boolean valid = LocaleValidator.isValidLanguage(language);
|
||||
|
||||
// then:
|
||||
assertTrue(valid);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deIsAValidLanguage() {
|
||||
// given:
|
||||
String language = "de";
|
||||
|
||||
// when:
|
||||
boolean valid = LocaleValidator.isValidLanguage(language);
|
||||
|
||||
// then:
|
||||
assertTrue(valid);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aFullLocaleIsNotAValidLanguage() {
|
||||
// given:
|
||||
String language = "en_us";
|
||||
|
||||
// when:
|
||||
boolean valid = LocaleValidator.isValidLanguage(language);
|
||||
|
||||
// then:
|
||||
assertFalse(valid);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void textIsNotAValidLanguage() {
|
||||
// given:
|
||||
String language = "Hello World!";
|
||||
|
||||
// when:
|
||||
boolean valid = LocaleValidator.isValidLanguage(language);
|
||||
|
||||
// then:
|
||||
assertFalse(valid);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/**
|
||||
* 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.fail;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.eclipse.jetty.client.api.Request;
|
||||
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.Ident;
|
||||
|
||||
/**
|
||||
* Utility class for creating common mocks.
|
||||
*
|
||||
* @author Björn Lange - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public final class MockUtil {
|
||||
private MockUtil() {
|
||||
}
|
||||
|
||||
public static Device mockDevice(String fabNumber) {
|
||||
DeviceIdentLabel deviceIdentLabel = mock(DeviceIdentLabel.class);
|
||||
when(deviceIdentLabel.getFabNumber()).thenReturn(Optional.of(fabNumber));
|
||||
|
||||
Ident ident = mock(Ident.class);
|
||||
when(ident.getDeviceIdentLabel()).thenReturn(Optional.of(deviceIdentLabel));
|
||||
|
||||
Device device = mock(Device.class);
|
||||
when(device.getIdent()).thenReturn(Optional.of(ident));
|
||||
|
||||
return device;
|
||||
}
|
||||
|
||||
public static <T> T requireNonNull(@Nullable T obj) {
|
||||
if (obj == null) {
|
||||
throw new IllegalArgumentException("Object must not be null");
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a mock for {@link HttpClient} circumventing the problem that {@link HttpClient#start()} is {@code final}
|
||||
* and {@link HttpClient#doStart()} {@code protected} and unaccessible when mocking with Mockito.
|
||||
*/
|
||||
public static HttpClient mockHttpClient() {
|
||||
return new HttpClient() {
|
||||
@Override
|
||||
protected void doStart() throws Exception {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a mock for {@link HttpClient} circumventing the problem that {@link HttpClient#start()} is {@code final}
|
||||
* and {@link HttpClient#doStart()} {@code protected} and unaccessible when mocking with Mockito.
|
||||
*
|
||||
* @param newRequestUri {@code uri} parameter of {@link HttpClient#newRequest(String)} to mock.
|
||||
* @param newRequestReturnValue Return value of {@link HttpClient#newRequest(String)} to mock.
|
||||
*/
|
||||
public static HttpClient mockHttpClient(String newRequestUri, Request newRequestReturnValue) {
|
||||
return new HttpClient() {
|
||||
@Override
|
||||
protected void doStart() throws Exception {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Request newRequest(@Nullable String uri) {
|
||||
if (newRequestUri.equals(uri)) {
|
||||
return newRequestReturnValue;
|
||||
} else {
|
||||
fail();
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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.util;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* 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, Object value)
|
||||
throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
|
||||
Field field = object.getClass().getDeclaredField(fieldName);
|
||||
field.setAccessible(true);
|
||||
field.set(object, 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,46 @@
|
||||
/**
|
||||
* 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.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* Utility class for handling test resources.
|
||||
*
|
||||
* @author Björn Lange - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public final class ResourceUtil {
|
||||
private ResourceUtil() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the contents of a resource file as {@link String}.
|
||||
*
|
||||
* @param resourceName The resource name (path inside the resources source folder).
|
||||
* @return The file contents.
|
||||
* @throws IOException if reading the resource fails or it cannot be found.
|
||||
*/
|
||||
public static String getResourceAsString(String resourceName) throws IOException {
|
||||
InputStream inputStream = ResourceUtil.class.getResourceAsStream(resourceName);
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
|
||||
return reader.lines().collect(Collectors.joining("\n"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
/**
|
||||
* 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.webservice;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentMatchers;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
import org.openhab.binding.mielecloud.internal.util.MockUtil;
|
||||
import org.openhab.binding.mielecloud.internal.webservice.api.DeviceState;
|
||||
import org.openhab.binding.mielecloud.internal.webservice.api.json.StateType;
|
||||
import org.openhab.binding.mielecloud.internal.webservice.exception.AuthorizationFailedException;
|
||||
import org.openhab.binding.mielecloud.internal.webservice.exception.MieleWebserviceException;
|
||||
import org.openhab.binding.mielecloud.internal.webservice.exception.TooManyRequestsException;
|
||||
|
||||
/**
|
||||
* @author Björn Lange - Initial Contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ActionStateFetcherTest {
|
||||
private ScheduledExecutorService mockImmediatelyExecutingExecutorService() {
|
||||
ScheduledExecutorService scheduler = mock(ScheduledExecutorService.class);
|
||||
when(scheduler.submit(ArgumentMatchers.<Runnable> any()))
|
||||
.thenAnswer(new Answer<@Nullable ScheduledFuture<?>>() {
|
||||
@Override
|
||||
@Nullable
|
||||
public ScheduledFuture<?> answer(@Nullable InvocationOnMock invocation) throws Throwable {
|
||||
((Runnable) MockUtil.requireNonNull(invocation).getArgument(0)).run();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
return scheduler;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFetchActionsIsInvokedWhenInitialDeviceStateIsSet() {
|
||||
// given:
|
||||
ScheduledExecutorService scheduler = mockImmediatelyExecutingExecutorService();
|
||||
|
||||
MieleWebservice webservice = mock(MieleWebservice.class);
|
||||
DeviceState deviceState = mock(DeviceState.class);
|
||||
DeviceState newDeviceState = mock(DeviceState.class);
|
||||
ActionStateFetcher actionsfetcher = new ActionStateFetcher(() -> webservice, scheduler);
|
||||
|
||||
when(deviceState.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
|
||||
when(newDeviceState.getStateType()).thenReturn(Optional.of(StateType.END_PROGRAMMED));
|
||||
|
||||
// when:
|
||||
actionsfetcher.onDeviceStateUpdated(deviceState);
|
||||
|
||||
// then:
|
||||
verify(webservice).fetchActions(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFetchActionsIsInvokedOnStateTransition() {
|
||||
// given:
|
||||
ScheduledExecutorService scheduler = mockImmediatelyExecutingExecutorService();
|
||||
|
||||
MieleWebservice webservice = mock(MieleWebservice.class);
|
||||
DeviceState deviceState = mock(DeviceState.class);
|
||||
DeviceState newDeviceState = mock(DeviceState.class);
|
||||
ActionStateFetcher actionsfetcher = new ActionStateFetcher(() -> webservice, scheduler);
|
||||
|
||||
when(deviceState.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
|
||||
when(newDeviceState.getStateType()).thenReturn(Optional.of(StateType.END_PROGRAMMED));
|
||||
|
||||
actionsfetcher.onDeviceStateUpdated(deviceState);
|
||||
|
||||
// when:
|
||||
actionsfetcher.onDeviceStateUpdated(newDeviceState);
|
||||
|
||||
// then:
|
||||
verify(webservice, times(2)).fetchActions(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFetchActionsIsNotInvokedWhenNoStateTransitionOccurrs() {
|
||||
// given:
|
||||
ScheduledExecutorService scheduler = mockImmediatelyExecutingExecutorService();
|
||||
|
||||
MieleWebservice webservice = mock(MieleWebservice.class);
|
||||
DeviceState deviceState = mock(DeviceState.class);
|
||||
DeviceState newDeviceState = mock(DeviceState.class);
|
||||
ActionStateFetcher actionsfetcher = new ActionStateFetcher(() -> webservice, scheduler);
|
||||
|
||||
when(deviceState.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
|
||||
when(newDeviceState.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
|
||||
|
||||
actionsfetcher.onDeviceStateUpdated(deviceState);
|
||||
|
||||
// when:
|
||||
actionsfetcher.onDeviceStateUpdated(newDeviceState);
|
||||
|
||||
// then:
|
||||
verify(webservice, times(1)).fetchActions(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenFetchActionsFailsWithAMieleWebserviceExceptionThenNoExceptionIsThrown() {
|
||||
// given:
|
||||
ScheduledExecutorService scheduler = mockImmediatelyExecutingExecutorService();
|
||||
|
||||
MieleWebservice webservice = mock(MieleWebservice.class);
|
||||
doThrow(new MieleWebserviceException("It went wrong", ConnectionError.REQUEST_EXECUTION_FAILED))
|
||||
.when(webservice).fetchActions(any());
|
||||
|
||||
DeviceState deviceState = mock(DeviceState.class);
|
||||
when(deviceState.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
|
||||
|
||||
ActionStateFetcher actionsfetcher = new ActionStateFetcher(() -> webservice, scheduler);
|
||||
|
||||
// when:
|
||||
actionsfetcher.onDeviceStateUpdated(deviceState);
|
||||
|
||||
// then:
|
||||
verify(webservice, times(1)).fetchActions(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenFetchActionsFailsWithAnAuthorizationFailedExceptionThenNoExceptionIsThrown() {
|
||||
// given:
|
||||
ScheduledExecutorService scheduler = mockImmediatelyExecutingExecutorService();
|
||||
|
||||
MieleWebservice webservice = mock(MieleWebservice.class);
|
||||
doThrow(new AuthorizationFailedException("Authorization failed")).when(webservice).fetchActions(any());
|
||||
|
||||
DeviceState deviceState = mock(DeviceState.class);
|
||||
when(deviceState.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
|
||||
|
||||
ActionStateFetcher actionsfetcher = new ActionStateFetcher(() -> webservice, scheduler);
|
||||
|
||||
// when:
|
||||
actionsfetcher.onDeviceStateUpdated(deviceState);
|
||||
|
||||
// then:
|
||||
verify(webservice, times(1)).fetchActions(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenFetchActionsFailsWithATooManyRequestsExceptionThenNoExceptionIsThrown() {
|
||||
// given:
|
||||
ScheduledExecutorService scheduler = mockImmediatelyExecutingExecutorService();
|
||||
|
||||
MieleWebservice webservice = mock(MieleWebservice.class);
|
||||
doThrow(new TooManyRequestsException("Too many requests", null)).when(webservice).fetchActions(any());
|
||||
|
||||
DeviceState deviceState = mock(DeviceState.class);
|
||||
when(deviceState.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
|
||||
|
||||
ActionStateFetcher actionsfetcher = new ActionStateFetcher(() -> webservice, scheduler);
|
||||
|
||||
// when:
|
||||
actionsfetcher.onDeviceStateUpdated(deviceState);
|
||||
|
||||
// then:
|
||||
verify(webservice, times(1)).fetchActions(any());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,742 @@
|
||||
/**
|
||||
* 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.webservice;
|
||||
|
||||
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.getPrivate;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.eclipse.jetty.client.api.ContentResponse;
|
||||
import org.eclipse.jetty.client.api.Request;
|
||||
import org.eclipse.jetty.http.HttpFields;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openhab.binding.mielecloud.internal.MieleCloudBindingTestConstants;
|
||||
import org.openhab.binding.mielecloud.internal.auth.OAuthTokenRefresher;
|
||||
import org.openhab.binding.mielecloud.internal.util.MockUtil;
|
||||
import org.openhab.binding.mielecloud.internal.webservice.api.json.ProcessAction;
|
||||
import org.openhab.binding.mielecloud.internal.webservice.exception.AuthorizationFailedException;
|
||||
import org.openhab.binding.mielecloud.internal.webservice.exception.MieleWebserviceException;
|
||||
import org.openhab.binding.mielecloud.internal.webservice.exception.MieleWebserviceTransientException;
|
||||
import org.openhab.binding.mielecloud.internal.webservice.exception.TooManyRequestsException;
|
||||
import org.openhab.binding.mielecloud.internal.webservice.language.LanguageProvider;
|
||||
import org.openhab.binding.mielecloud.internal.webservice.request.RequestFactory;
|
||||
import org.openhab.binding.mielecloud.internal.webservice.retry.AuthorizationFailedRetryStrategy;
|
||||
import org.openhab.binding.mielecloud.internal.webservice.retry.NTimesRetryStrategy;
|
||||
import org.openhab.binding.mielecloud.internal.webservice.retry.RetryStrategy;
|
||||
import org.openhab.binding.mielecloud.internal.webservice.retry.RetryStrategyCombiner;
|
||||
import org.openhab.core.io.net.http.HttpClientFactory;
|
||||
|
||||
/**
|
||||
* @author Björn Lange - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class DefaultMieleWebserviceTest {
|
||||
private static final String MESSAGE_INTERNAL_SERVER_ERROR = "{\"message\": \"Internal Server Error\"}";
|
||||
private static final String MESSAGE_SERVICE_UNAVAILABLE = "{\"message\": \"unavailable\"}";
|
||||
private static final String MESSAGE_INVALID_JSON = "{\"abc123: \"äfgh\"}";
|
||||
|
||||
private static final String DEVICE_IDENTIFIER = "000124430016";
|
||||
|
||||
private static final String SERVER_ADDRESS = "https://api.mcs3.miele.com";
|
||||
private static final String ENDPOINT_DEVICES = SERVER_ADDRESS + "/v1/devices/";
|
||||
private static final String ENDPOINT_ACTIONS = ENDPOINT_DEVICES + DEVICE_IDENTIFIER + "/actions";
|
||||
private static final String ENDPOINT_LOGOUT = SERVER_ADDRESS + "/thirdparty/logout";
|
||||
|
||||
private static final String ACCESS_TOKEN = "DE_0123456789abcdef0123456789abcdef";
|
||||
|
||||
private final RetryStrategy retryStrategy = new UncatchedRetryStrategy();
|
||||
private final Request request = mock(Request.class);
|
||||
|
||||
@Test
|
||||
public void testDefaultRetryStrategyIsCombinationOfOneTimeRetryStrategyAndAuthorizationFailedStrategy()
|
||||
throws Exception {
|
||||
// given:
|
||||
HttpClientFactory httpClientFactory = mock(HttpClientFactory.class);
|
||||
when(httpClientFactory.createHttpClient(anyString())).thenReturn(MockUtil.mockHttpClient());
|
||||
LanguageProvider languageProvider = mock(LanguageProvider.class);
|
||||
OAuthTokenRefresher tokenRefresher = mock(OAuthTokenRefresher.class);
|
||||
ScheduledExecutorService scheduler = mock(ScheduledExecutorService.class);
|
||||
|
||||
// when:
|
||||
DefaultMieleWebservice webservice = new DefaultMieleWebservice(MieleWebserviceConfiguration.builder()
|
||||
.withHttpClientFactory(httpClientFactory).withLanguageProvider(languageProvider)
|
||||
.withTokenRefresher(tokenRefresher).withServiceHandle(MieleCloudBindingTestConstants.SERVICE_HANDLE)
|
||||
.withScheduler(scheduler).build());
|
||||
|
||||
// then:
|
||||
RetryStrategy retryStrategy = getPrivate(webservice, "retryStrategy");
|
||||
assertTrue(retryStrategy instanceof RetryStrategyCombiner);
|
||||
|
||||
RetryStrategy first = getPrivate(retryStrategy, "first");
|
||||
assertTrue(first instanceof NTimesRetryStrategy);
|
||||
int numberOfRetries = getPrivate(first, "numberOfRetries");
|
||||
assertEquals(1, numberOfRetries);
|
||||
|
||||
RetryStrategy second = getPrivate(retryStrategy, "second");
|
||||
assertTrue(second instanceof AuthorizationFailedRetryStrategy);
|
||||
OAuthTokenRefresher internalTokenRefresher = getPrivate(second, "tokenRefresher");
|
||||
assertEquals(tokenRefresher, internalTokenRefresher);
|
||||
}
|
||||
|
||||
private ContentResponse createContentResponseMock(int errorCode, String content) {
|
||||
ContentResponse response = mock(ContentResponse.class);
|
||||
when(response.getStatus()).thenReturn(errorCode);
|
||||
when(response.getContentAsString()).thenReturn(content);
|
||||
return response;
|
||||
}
|
||||
|
||||
private void performFetchActions() throws Exception {
|
||||
RequestFactory requestFactory = mock(RequestFactory.class);
|
||||
when(requestFactory.createGetRequest(ENDPOINT_ACTIONS, ACCESS_TOKEN)).thenReturn(request);
|
||||
|
||||
ScheduledExecutorService scheduler = mock(ScheduledExecutorService.class);
|
||||
|
||||
try (DefaultMieleWebservice webservice = new DefaultMieleWebservice(requestFactory, retryStrategy,
|
||||
new DeviceStateDispatcher(), scheduler)) {
|
||||
webservice.setAccessToken(ACCESS_TOKEN);
|
||||
|
||||
webservice.fetchActions(DEVICE_IDENTIFIER);
|
||||
}
|
||||
}
|
||||
|
||||
private void performFetchActionsExpectingFailure(ConnectionError expectedError) throws Exception {
|
||||
try {
|
||||
performFetchActions();
|
||||
} catch (MieleWebserviceException e) {
|
||||
assertEquals(expectedError, e.getConnectionError());
|
||||
throw e;
|
||||
} catch (MieleWebserviceTransientException e) {
|
||||
assertEquals(expectedError, e.getConnectionError());
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTimeoutExceptionWhilePerformingFetchActionsRequest() throws Exception {
|
||||
// given:
|
||||
when(request.send()).thenThrow(TimeoutException.class);
|
||||
|
||||
// when:
|
||||
assertThrows(MieleWebserviceTransientException.class, () -> {
|
||||
performFetchActionsExpectingFailure(ConnectionError.TIMEOUT);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test500InternalServerErrorWhilePerformingFetchActionsRequest() throws Exception {
|
||||
// given:
|
||||
ContentResponse contentResponse = createContentResponseMock(500, MESSAGE_INTERNAL_SERVER_ERROR);
|
||||
when(request.send()).thenReturn(contentResponse);
|
||||
|
||||
// when:
|
||||
assertThrows(MieleWebserviceTransientException.class, () -> {
|
||||
performFetchActionsExpectingFailure(ConnectionError.SERVER_ERROR);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test503ServiceUnavailableWhilePerformingFetchActionsRequest() throws Exception {
|
||||
// given:
|
||||
ContentResponse contentResponse = createContentResponseMock(503, MESSAGE_SERVICE_UNAVAILABLE);
|
||||
when(request.send()).thenReturn(contentResponse);
|
||||
|
||||
// when:
|
||||
assertThrows(MieleWebserviceTransientException.class, () -> {
|
||||
performFetchActionsExpectingFailure(ConnectionError.SERVICE_UNAVAILABLE);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidJsonWhilePerformingFetchActionsRequest() throws Exception {
|
||||
// given:
|
||||
ContentResponse contentResponse = createContentResponseMock(200, MESSAGE_INVALID_JSON);
|
||||
when(request.send()).thenReturn(contentResponse);
|
||||
|
||||
// when:
|
||||
assertThrows(MieleWebserviceTransientException.class, () -> {
|
||||
performFetchActionsExpectingFailure(ConnectionError.RESPONSE_MALFORMED);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInterruptedExceptionWhilePerformingFetchActionsRequest() throws Exception {
|
||||
// given:
|
||||
when(request.send()).thenThrow(InterruptedException.class);
|
||||
|
||||
// when:
|
||||
assertThrows(MieleWebserviceException.class, () -> {
|
||||
performFetchActionsExpectingFailure(ConnectionError.REQUEST_INTERRUPTED);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExecutionExceptionWhilePerformingFetchActionsRequest() throws Exception {
|
||||
// given:
|
||||
when(request.send()).thenThrow(ExecutionException.class);
|
||||
|
||||
// when:
|
||||
assertThrows(MieleWebserviceException.class, () -> {
|
||||
performFetchActionsExpectingFailure(ConnectionError.REQUEST_EXECUTION_FAILED);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test400BadRequestWhilePerformingFetchActionsRequest() throws Exception {
|
||||
// given:
|
||||
ContentResponse response = createContentResponseMock(400, "{\"message\": \"grant_type is invalid\"}");
|
||||
when(request.send()).thenReturn(response);
|
||||
|
||||
// when:
|
||||
assertThrows(MieleWebserviceException.class, () -> {
|
||||
performFetchActionsExpectingFailure(ConnectionError.OTHER_HTTP_ERROR);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test401UnauthorizedWhilePerformingFetchActionsRequest() throws Exception {
|
||||
// given:
|
||||
ContentResponse response = createContentResponseMock(401, "{\"message\": \"Unauthorized\"}");
|
||||
when(request.send()).thenReturn(response);
|
||||
|
||||
// when:
|
||||
assertThrows(AuthorizationFailedException.class, () -> {
|
||||
performFetchActions();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test404NotFoundWhilePerformingFetchActionsRequest() throws Exception {
|
||||
// given:
|
||||
ContentResponse response = createContentResponseMock(404, "{\"message\": \"Not found\"}");
|
||||
when(request.send()).thenReturn(response);
|
||||
|
||||
// when:
|
||||
assertThrows(MieleWebserviceException.class, () -> {
|
||||
performFetchActionsExpectingFailure(ConnectionError.OTHER_HTTP_ERROR);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test405MethodNotAllowedWhilePerformingFetchActionsRequest() throws Exception {
|
||||
// given:
|
||||
ContentResponse response = createContentResponseMock(405, "{\"message\": \"HTTP 405 Method Not Allowed\"}");
|
||||
when(request.send()).thenReturn(response);
|
||||
|
||||
// when:
|
||||
assertThrows(MieleWebserviceException.class, () -> {
|
||||
performFetchActionsExpectingFailure(ConnectionError.OTHER_HTTP_ERROR);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test429TooManyRequestsWhilePerformingFetchActionsRequest() throws Exception {
|
||||
// given:
|
||||
HttpFields headerFields = mock(HttpFields.class);
|
||||
when(headerFields.containsKey(anyString())).thenReturn(false);
|
||||
|
||||
ContentResponse response = createContentResponseMock(429, "{\"message\": \"Too Many Requests\"}");
|
||||
when(response.getHeaders()).thenReturn(headerFields);
|
||||
|
||||
when(request.send()).thenReturn(response);
|
||||
|
||||
// when:
|
||||
assertThrows(TooManyRequestsException.class, () -> {
|
||||
performFetchActions();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test502BadGatewayWhilePerforminggFetchActionsRequest() throws Exception {
|
||||
// given:
|
||||
ContentResponse response = createContentResponseMock(502, "{\"message\": \"Bad Gateway\"}");
|
||||
when(request.send()).thenReturn(response);
|
||||
|
||||
// when:
|
||||
assertThrows(MieleWebserviceException.class, () -> {
|
||||
performFetchActionsExpectingFailure(ConnectionError.OTHER_HTTP_ERROR);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMalformatedBodyWhilePerforminggFetchActionsRequest() throws Exception {
|
||||
// given:
|
||||
ContentResponse response = createContentResponseMock(502, "{\"message \"Bad Gateway\"}");
|
||||
when(request.send()).thenReturn(response);
|
||||
|
||||
// when:
|
||||
assertThrows(MieleWebserviceException.class, () -> {
|
||||
performFetchActionsExpectingFailure(ConnectionError.OTHER_HTTP_ERROR);
|
||||
});
|
||||
}
|
||||
|
||||
private void fillRequestMockWithDefaultContent() throws InterruptedException, TimeoutException, ExecutionException {
|
||||
ContentResponse response = createContentResponseMock(200,
|
||||
"{\"000124430016\":{\"ident\": {\"deviceName\": \"MyFancyHood\", \"deviceIdentLabel\": {\"fabNumber\": \"000124430016\"}}}}");
|
||||
when(request.send()).thenReturn(response);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddDeviceStateListenerIsDelegatedToDeviceStateDispatcher() throws Exception {
|
||||
// given:
|
||||
RequestFactory requestFactory = mock(RequestFactory.class);
|
||||
DeviceStateDispatcher dispatcher = mock(DeviceStateDispatcher.class);
|
||||
ScheduledExecutorService scheduler = mock(ScheduledExecutorService.class);
|
||||
|
||||
try (DefaultMieleWebservice webservice = new DefaultMieleWebservice(requestFactory, new NTimesRetryStrategy(0),
|
||||
dispatcher, scheduler)) {
|
||||
DeviceStateListener listener = mock(DeviceStateListener.class);
|
||||
|
||||
// when:
|
||||
webservice.addDeviceStateListener(listener);
|
||||
|
||||
// then:
|
||||
verify(dispatcher).addListener(listener);
|
||||
verifyNoMoreInteractions(dispatcher);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFetchActionsDelegatesDeviceStateListenerDispatchingToDeviceStateDispatcher() throws Exception {
|
||||
// given:
|
||||
fillRequestMockWithDefaultContent();
|
||||
|
||||
RequestFactory requestFactory = mock(RequestFactory.class);
|
||||
when(requestFactory.createGetRequest(ENDPOINT_ACTIONS, ACCESS_TOKEN)).thenReturn(request);
|
||||
|
||||
DeviceStateDispatcher dispatcher = mock(DeviceStateDispatcher.class);
|
||||
|
||||
ScheduledExecutorService scheduler = mock(ScheduledExecutorService.class);
|
||||
|
||||
try (DefaultMieleWebservice webservice = new DefaultMieleWebservice(requestFactory, retryStrategy, dispatcher,
|
||||
scheduler)) {
|
||||
webservice.setAccessToken(ACCESS_TOKEN);
|
||||
|
||||
// when:
|
||||
webservice.fetchActions(DEVICE_IDENTIFIER);
|
||||
|
||||
// then:
|
||||
verify(dispatcher).dispatchActionStateUpdates(any(), any());
|
||||
verifyNoMoreInteractions(dispatcher);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFetchActionsThrowsMieleWebserviceTransientExceptionWhenRequestContentIsMalformatted()
|
||||
throws Exception {
|
||||
// given:
|
||||
ContentResponse response = createContentResponseMock(200, "{\"}");
|
||||
when(request.send()).thenReturn(response);
|
||||
|
||||
RequestFactory requestFactory = mock(RequestFactory.class);
|
||||
when(requestFactory.createGetRequest(ENDPOINT_ACTIONS, ACCESS_TOKEN)).thenReturn(request);
|
||||
|
||||
ScheduledExecutorService scheduler = mock(ScheduledExecutorService.class);
|
||||
|
||||
try (DefaultMieleWebservice webservice = new DefaultMieleWebservice(requestFactory, retryStrategy,
|
||||
new DeviceStateDispatcher(), scheduler)) {
|
||||
webservice.setAccessToken(ACCESS_TOKEN);
|
||||
|
||||
// when:
|
||||
assertThrows(MieleWebserviceTransientException.class, () -> {
|
||||
webservice.fetchActions(DEVICE_IDENTIFIER);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPutProcessActionSendsRequestWithCorrectJsonContent() throws Exception {
|
||||
// given:
|
||||
ContentResponse response = createContentResponseMock(204, "");
|
||||
when(request.send()).thenReturn(response);
|
||||
|
||||
RequestFactory requestFactory = mock(RequestFactory.class);
|
||||
when(requestFactory.createPutRequest(ENDPOINT_ACTIONS, ACCESS_TOKEN, "{\"processAction\":1}"))
|
||||
.thenReturn(request);
|
||||
|
||||
ScheduledExecutorService scheduler = mock(ScheduledExecutorService.class);
|
||||
|
||||
try (DefaultMieleWebservice webservice = new DefaultMieleWebservice(requestFactory, new NTimesRetryStrategy(0),
|
||||
new DeviceStateDispatcher(), scheduler)) {
|
||||
webservice.setAccessToken(ACCESS_TOKEN);
|
||||
|
||||
// when:
|
||||
webservice.putProcessAction(DEVICE_IDENTIFIER, ProcessAction.START);
|
||||
|
||||
// then:
|
||||
verify(request).send();
|
||||
verifyNoMoreInteractions(request);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPutProcessActionThrowsIllegalArgumentExceptionWhenProcessActionIsUnknown() throws Exception {
|
||||
// given:
|
||||
RequestFactory requestFactory = mock(RequestFactory.class);
|
||||
|
||||
ScheduledExecutorService scheduler = mock(ScheduledExecutorService.class);
|
||||
|
||||
try (DefaultMieleWebservice webservice = new DefaultMieleWebservice(requestFactory, retryStrategy,
|
||||
new DeviceStateDispatcher(), scheduler)) {
|
||||
|
||||
// when:
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
webservice.putProcessAction(DEVICE_IDENTIFIER, ProcessAction.UNKNOWN);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPutProcessActionThrowsTooManyRequestsExceptionWhenHttpResponseCodeIs429() throws Exception {
|
||||
// given:
|
||||
HttpFields responseHeaders = mock(HttpFields.class);
|
||||
when(responseHeaders.containsKey(anyString())).thenReturn(false);
|
||||
|
||||
ContentResponse response = createContentResponseMock(429, "{\"message\":\"Too many requests\"}");
|
||||
when(response.getHeaders()).thenReturn(responseHeaders);
|
||||
|
||||
when(request.send()).thenReturn(response);
|
||||
|
||||
RequestFactory requestFactory = mock(RequestFactory.class);
|
||||
when(requestFactory.createPutRequest(ENDPOINT_ACTIONS, ACCESS_TOKEN, "{\"processAction\":1}"))
|
||||
.thenReturn(request);
|
||||
|
||||
ScheduledExecutorService scheduler = mock(ScheduledExecutorService.class);
|
||||
|
||||
try (DefaultMieleWebservice webservice = new DefaultMieleWebservice(requestFactory, new NTimesRetryStrategy(0),
|
||||
new DeviceStateDispatcher(), scheduler)) {
|
||||
webservice.setAccessToken(ACCESS_TOKEN);
|
||||
|
||||
// when:
|
||||
assertThrows(TooManyRequestsException.class, () -> {
|
||||
webservice.putProcessAction(DEVICE_IDENTIFIER, ProcessAction.START);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPutLightSendsRequestWithCorrectJsonContentWhenTurningTheLightOn() throws Exception {
|
||||
// given:
|
||||
ContentResponse response = createContentResponseMock(204, "");
|
||||
when(request.send()).thenReturn(response);
|
||||
|
||||
RequestFactory requestFactory = mock(RequestFactory.class);
|
||||
when(requestFactory.createPutRequest(ENDPOINT_ACTIONS, ACCESS_TOKEN, "{\"light\":1}")).thenReturn(request);
|
||||
|
||||
ScheduledExecutorService scheduler = mock(ScheduledExecutorService.class);
|
||||
|
||||
try (DefaultMieleWebservice webservice = new DefaultMieleWebservice(requestFactory, new NTimesRetryStrategy(0),
|
||||
new DeviceStateDispatcher(), scheduler)) {
|
||||
webservice.setAccessToken(ACCESS_TOKEN);
|
||||
|
||||
// when:
|
||||
webservice.putLight(DEVICE_IDENTIFIER, true);
|
||||
|
||||
// then:
|
||||
verify(request).send();
|
||||
verifyNoMoreInteractions(request);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPutLightSendsRequestWithCorrectJsonContentWhenTurningTheLightOff() throws Exception {
|
||||
// given:
|
||||
ContentResponse response = createContentResponseMock(204, "");
|
||||
when(request.send()).thenReturn(response);
|
||||
|
||||
RequestFactory requestFactory = mock(RequestFactory.class);
|
||||
when(requestFactory.createPutRequest(ENDPOINT_ACTIONS, ACCESS_TOKEN, "{\"light\":2}")).thenReturn(request);
|
||||
|
||||
ScheduledExecutorService scheduler = mock(ScheduledExecutorService.class);
|
||||
|
||||
try (DefaultMieleWebservice webservice = new DefaultMieleWebservice(requestFactory, new NTimesRetryStrategy(0),
|
||||
new DeviceStateDispatcher(), scheduler)) {
|
||||
webservice.setAccessToken(ACCESS_TOKEN);
|
||||
|
||||
// when:
|
||||
webservice.putLight(DEVICE_IDENTIFIER, false);
|
||||
|
||||
// then:
|
||||
verify(request).send();
|
||||
verifyNoMoreInteractions(request);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPutLightThrowsTooManyRequestsExceptionWhenHttpResponseCodeIs429() throws Exception {
|
||||
// given:
|
||||
HttpFields responseHeaders = mock(HttpFields.class);
|
||||
when(responseHeaders.containsKey(anyString())).thenReturn(false);
|
||||
|
||||
ContentResponse response = createContentResponseMock(429, "{\"message\":\"Too many requests\"}");
|
||||
when(response.getHeaders()).thenReturn(responseHeaders);
|
||||
|
||||
when(request.send()).thenReturn(response);
|
||||
|
||||
RequestFactory requestFactory = mock(RequestFactory.class);
|
||||
when(requestFactory.createPutRequest(ENDPOINT_ACTIONS, ACCESS_TOKEN, "{\"light\":2}")).thenReturn(request);
|
||||
|
||||
ScheduledExecutorService scheduler = mock(ScheduledExecutorService.class);
|
||||
|
||||
try (DefaultMieleWebservice webservice = new DefaultMieleWebservice(requestFactory, new NTimesRetryStrategy(0),
|
||||
new DeviceStateDispatcher(), scheduler)) {
|
||||
webservice.setAccessToken(ACCESS_TOKEN);
|
||||
|
||||
// when:
|
||||
assertThrows(TooManyRequestsException.class, () -> {
|
||||
webservice.putLight(DEVICE_IDENTIFIER, false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLogoutInvalidatesAccessTokenOnSuccess() throws Exception {
|
||||
// given:
|
||||
ContentResponse response = createContentResponseMock(204, "");
|
||||
when(request.send()).thenReturn(response);
|
||||
|
||||
RequestFactory requestFactory = mock(RequestFactory.class);
|
||||
when(requestFactory.createPostRequest(ENDPOINT_LOGOUT, ACCESS_TOKEN)).thenReturn(request);
|
||||
|
||||
ScheduledExecutorService scheduler = mock(ScheduledExecutorService.class);
|
||||
|
||||
try (DefaultMieleWebservice webservice = new DefaultMieleWebservice(requestFactory, retryStrategy,
|
||||
new DeviceStateDispatcher(), scheduler)) {
|
||||
webservice.setAccessToken(ACCESS_TOKEN);
|
||||
|
||||
// when:
|
||||
webservice.logout();
|
||||
|
||||
// then:
|
||||
assertFalse(webservice.hasAccessToken());
|
||||
verify(request).send();
|
||||
verifyNoMoreInteractions(request);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLogoutThrowsMieleWebserviceExceptionWhenMieleWebserviceTransientExceptionIsThrownInternally()
|
||||
throws Exception {
|
||||
// given:
|
||||
when(request.send()).thenThrow(TimeoutException.class);
|
||||
|
||||
RequestFactory requestFactory = mock(RequestFactory.class);
|
||||
when(requestFactory.createPostRequest(ENDPOINT_LOGOUT, ACCESS_TOKEN)).thenReturn(request);
|
||||
|
||||
ScheduledExecutorService scheduler = mock(ScheduledExecutorService.class);
|
||||
|
||||
try (DefaultMieleWebservice webservice = new DefaultMieleWebservice(requestFactory, retryStrategy,
|
||||
new DeviceStateDispatcher(), scheduler)) {
|
||||
webservice.setAccessToken(ACCESS_TOKEN);
|
||||
|
||||
// when:
|
||||
assertThrows(MieleWebserviceException.class, () -> {
|
||||
webservice.logout();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLogoutInvalidatesAccessTokenWhenOperationFails() throws Exception {
|
||||
// given:
|
||||
when(request.send()).thenThrow(TimeoutException.class);
|
||||
|
||||
RequestFactory requestFactory = mock(RequestFactory.class);
|
||||
when(requestFactory.createPostRequest(ENDPOINT_LOGOUT, ACCESS_TOKEN)).thenReturn(request);
|
||||
|
||||
ScheduledExecutorService scheduler = mock(ScheduledExecutorService.class);
|
||||
|
||||
try (DefaultMieleWebservice webservice = new DefaultMieleWebservice(requestFactory, retryStrategy,
|
||||
new DeviceStateDispatcher(), scheduler)) {
|
||||
webservice.setAccessToken(ACCESS_TOKEN);
|
||||
|
||||
// when:
|
||||
try {
|
||||
webservice.logout();
|
||||
} catch (MieleWebserviceException e) {
|
||||
}
|
||||
|
||||
// then:
|
||||
assertFalse(webservice.hasAccessToken());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveDeviceStateListenerIsDelegatedToDeviceStateDispatcher() throws Exception {
|
||||
// given:
|
||||
RequestFactory requestFactory = mock(RequestFactory.class);
|
||||
|
||||
DeviceStateDispatcher dispatcher = mock(DeviceStateDispatcher.class);
|
||||
|
||||
ScheduledExecutorService scheduler = mock(ScheduledExecutorService.class);
|
||||
|
||||
try (DefaultMieleWebservice webservice = new DefaultMieleWebservice(requestFactory, new NTimesRetryStrategy(0),
|
||||
dispatcher, scheduler)) {
|
||||
DeviceStateListener listener = mock(DeviceStateListener.class);
|
||||
webservice.addDeviceStateListener(listener);
|
||||
|
||||
// when:
|
||||
webservice.removeDeviceStateListener(listener);
|
||||
|
||||
// then:
|
||||
verify(dispatcher).addListener(listener);
|
||||
verify(dispatcher).removeListener(listener);
|
||||
verifyNoMoreInteractions(dispatcher);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPutPowerStateSendsRequestWithCorrectJsonContentWhenSwitchingTheDeviceOn() throws Exception {
|
||||
// given:
|
||||
ContentResponse response = createContentResponseMock(204, "");
|
||||
when(request.send()).thenReturn(response);
|
||||
|
||||
RequestFactory requestFactory = mock(RequestFactory.class);
|
||||
when(requestFactory.createPutRequest(ENDPOINT_ACTIONS, ACCESS_TOKEN, "{\"powerOn\":true}")).thenReturn(request);
|
||||
|
||||
ScheduledExecutorService scheduler = mock(ScheduledExecutorService.class);
|
||||
|
||||
try (DefaultMieleWebservice webservice = new DefaultMieleWebservice(requestFactory, new NTimesRetryStrategy(0),
|
||||
new DeviceStateDispatcher(), scheduler)) {
|
||||
webservice.setAccessToken(ACCESS_TOKEN);
|
||||
|
||||
// when:
|
||||
webservice.putPowerState(DEVICE_IDENTIFIER, true);
|
||||
|
||||
// then:
|
||||
verify(request).send();
|
||||
verifyNoMoreInteractions(request);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPutPowerStateSendsRequestWithCorrectJsonContentWhenDeviceIsSwitchedOff() throws Exception {
|
||||
// given:
|
||||
ContentResponse response = createContentResponseMock(204, "");
|
||||
when(request.send()).thenReturn(response);
|
||||
|
||||
RequestFactory requestFactory = mock(RequestFactory.class);
|
||||
when(requestFactory.createPutRequest(ENDPOINT_ACTIONS, ACCESS_TOKEN, "{\"powerOff\":true}"))
|
||||
.thenReturn(request);
|
||||
|
||||
ScheduledExecutorService scheduler = mock(ScheduledExecutorService.class);
|
||||
|
||||
try (DefaultMieleWebservice webservice = new DefaultMieleWebservice(requestFactory, new NTimesRetryStrategy(0),
|
||||
new DeviceStateDispatcher(), scheduler)) {
|
||||
webservice.setAccessToken(ACCESS_TOKEN);
|
||||
|
||||
// when:
|
||||
webservice.putPowerState(DEVICE_IDENTIFIER, false);
|
||||
|
||||
// then:
|
||||
verify(request).send();
|
||||
verifyNoMoreInteractions(request);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPutPowerStateThrowsTooManyRequestsExceptionWhenHttpResponseCodeIs429() throws Exception {
|
||||
// given:
|
||||
HttpFields responseHeaders = mock(HttpFields.class);
|
||||
when(responseHeaders.containsKey(anyString())).thenReturn(false);
|
||||
|
||||
ContentResponse response = createContentResponseMock(429, "{\"message\":\"Too many requests\"}");
|
||||
when(response.getHeaders()).thenReturn(responseHeaders);
|
||||
|
||||
when(request.send()).thenReturn(response);
|
||||
|
||||
RequestFactory requestFactory = mock(RequestFactory.class);
|
||||
when(requestFactory.createPutRequest(ENDPOINT_ACTIONS, ACCESS_TOKEN, "{\"powerOff\":true}"))
|
||||
.thenReturn(request);
|
||||
|
||||
ScheduledExecutorService scheduler = mock(ScheduledExecutorService.class);
|
||||
|
||||
try (DefaultMieleWebservice webservice = new DefaultMieleWebservice(requestFactory, new NTimesRetryStrategy(0),
|
||||
new DeviceStateDispatcher(), scheduler)) {
|
||||
webservice.setAccessToken(ACCESS_TOKEN);
|
||||
|
||||
// when:
|
||||
assertThrows(TooManyRequestsException.class, () -> {
|
||||
webservice.putPowerState(DEVICE_IDENTIFIER, false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPutProgramResultsInARequestWithCorrectJson() throws Exception {
|
||||
// given:
|
||||
ContentResponse response = createContentResponseMock(204, "");
|
||||
when(request.send()).thenReturn(response);
|
||||
RequestFactory requestFactory = mock(RequestFactory.class);
|
||||
when(requestFactory.createPutRequest(ENDPOINT_ACTIONS, ACCESS_TOKEN, "{\"programId\":1}")).thenReturn(request);
|
||||
|
||||
ScheduledExecutorService scheduler = mock(ScheduledExecutorService.class);
|
||||
|
||||
try (DefaultMieleWebservice webservice = new DefaultMieleWebservice(requestFactory, new NTimesRetryStrategy(0),
|
||||
new DeviceStateDispatcher(), scheduler)) {
|
||||
webservice.setAccessToken(ACCESS_TOKEN);
|
||||
|
||||
// when:
|
||||
webservice.putProgram(DEVICE_IDENTIFIER, 1);
|
||||
|
||||
// then:
|
||||
verify(request).send();
|
||||
verifyNoMoreInteractions(request);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDispatchDeviceStateIsDelegatedToDeviceStateDispatcher() throws Exception {
|
||||
// given:
|
||||
RequestFactory requestFactory = mock(RequestFactory.class);
|
||||
DeviceStateDispatcher dispatcher = mock(DeviceStateDispatcher.class);
|
||||
ScheduledExecutorService scheduler = mock(ScheduledExecutorService.class);
|
||||
|
||||
try (DefaultMieleWebservice webservice = new DefaultMieleWebservice(requestFactory, new NTimesRetryStrategy(0),
|
||||
dispatcher, scheduler)) {
|
||||
// when:
|
||||
webservice.dispatchDeviceState(DEVICE_IDENTIFIER);
|
||||
|
||||
// then:
|
||||
verify(dispatcher).dispatchDeviceState(DEVICE_IDENTIFIER);
|
||||
verifyNoMoreInteractions(dispatcher);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link RetryStrategy} for testing purposes. No exceptions will be catched.
|
||||
*
|
||||
* @author Roland Edelhoff - Initial contribution.
|
||||
*/
|
||||
private static class UncatchedRetryStrategy implements RetryStrategy {
|
||||
|
||||
@Override
|
||||
public <@Nullable T> T performRetryableOperation(Supplier<T> operation,
|
||||
Consumer<Exception> onTransientException) {
|
||||
return operation.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performRetryableOperation(Runnable operation, Consumer<Exception> onTransientException) {
|
||||
operation.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/**
|
||||
* 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.webservice;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openhab.binding.mielecloud.internal.webservice.api.json.Device;
|
||||
import org.openhab.binding.mielecloud.internal.webservice.api.json.DeviceCollection;
|
||||
|
||||
/**
|
||||
* @author Björn Lange - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class DeviceCacheTest {
|
||||
private static final String FIRST_DEVICE_IDENTIFIER = "000124430016";
|
||||
private static final String SECOND_DEVICE_IDENTIFIER = "000124430017";
|
||||
private static final String THIRD_DEVICE_IDENTIFIER = "400124430017";
|
||||
|
||||
private final Device firstDevice = mock(Device.class);
|
||||
private final Device secondDevice = mock(Device.class);
|
||||
private final Device thirdDevice = mock(Device.class);
|
||||
|
||||
private final DeviceCache deviceCache = new DeviceCache();
|
||||
|
||||
@Test
|
||||
public void testCacheIsEmptyAfterConstruction() {
|
||||
// then:
|
||||
assertEquals(0, deviceCache.getDeviceIds().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReplaceAllDevicesClearsTheCacheAndPutsAllNewDevicesIntoTheCache() {
|
||||
// given:
|
||||
DeviceCollection deviceCollection = mock(DeviceCollection.class);
|
||||
when(deviceCollection.getDeviceIdentifiers())
|
||||
.thenReturn(new HashSet<>(Arrays.asList(FIRST_DEVICE_IDENTIFIER, SECOND_DEVICE_IDENTIFIER)));
|
||||
when(deviceCollection.getDevice(FIRST_DEVICE_IDENTIFIER)).thenReturn(firstDevice);
|
||||
when(deviceCollection.getDevice(SECOND_DEVICE_IDENTIFIER)).thenReturn(secondDevice);
|
||||
|
||||
// when:
|
||||
deviceCache.replaceAllDevices(deviceCollection);
|
||||
|
||||
// then:
|
||||
assertEquals(new HashSet<>(Arrays.asList(FIRST_DEVICE_IDENTIFIER, SECOND_DEVICE_IDENTIFIER)),
|
||||
deviceCache.getDeviceIds());
|
||||
assertEquals(firstDevice, deviceCache.getDevice(FIRST_DEVICE_IDENTIFIER).get());
|
||||
assertEquals(secondDevice, deviceCache.getDevice(SECOND_DEVICE_IDENTIFIER).get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReplaceAllDevicesClearsTheCachePriorToCachingThePassedDevices() {
|
||||
// given:
|
||||
testReplaceAllDevicesClearsTheCacheAndPutsAllNewDevicesIntoTheCache();
|
||||
|
||||
DeviceCollection deviceCollection = mock(DeviceCollection.class);
|
||||
when(deviceCollection.getDeviceIdentifiers()).thenReturn(new HashSet<>(Arrays.asList(THIRD_DEVICE_IDENTIFIER)));
|
||||
when(deviceCollection.getDevice(THIRD_DEVICE_IDENTIFIER)).thenReturn(thirdDevice);
|
||||
|
||||
// when:
|
||||
deviceCache.replaceAllDevices(deviceCollection);
|
||||
|
||||
// then:
|
||||
assertEquals(new HashSet<>(Arrays.asList(THIRD_DEVICE_IDENTIFIER)), deviceCache.getDeviceIds());
|
||||
assertEquals(thirdDevice, deviceCache.getDevice(THIRD_DEVICE_IDENTIFIER).get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClearClearsTheCachedDevices() {
|
||||
// given:
|
||||
testReplaceAllDevicesClearsTheCacheAndPutsAllNewDevicesIntoTheCache();
|
||||
|
||||
// when:
|
||||
deviceCache.clear();
|
||||
|
||||
// then:
|
||||
assertEquals(0, deviceCache.getDeviceIds().size());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,257 @@
|
||||
/**
|
||||
* 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.webservice;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.openhab.binding.mielecloud.internal.util.MockUtil.mockDevice;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
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.webservice.api.ActionsState;
|
||||
import org.openhab.binding.mielecloud.internal.webservice.api.DeviceState;
|
||||
import org.openhab.binding.mielecloud.internal.webservice.api.json.Actions;
|
||||
import org.openhab.binding.mielecloud.internal.webservice.api.json.Device;
|
||||
import org.openhab.binding.mielecloud.internal.webservice.api.json.DeviceCollection;
|
||||
|
||||
/**
|
||||
* @author Björn Lange - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class DeviceStateDispatcherTest {
|
||||
private static final String FIRST_DEVICE_IDENTIFIER = "000124430016";
|
||||
private static final String SECOND_DEVICE_IDENTIFIER = "000124430017";
|
||||
private static final String UNKNOWN_DEVICE_IDENTIFIER = "100124430016";
|
||||
|
||||
@Nullable
|
||||
private Device firstDevice;
|
||||
@Nullable
|
||||
private Device secondDevice;
|
||||
@Nullable
|
||||
private DeviceCollection devices;
|
||||
|
||||
private Device getFirstDevice() {
|
||||
assertNotNull(firstDevice);
|
||||
return Objects.requireNonNull(firstDevice);
|
||||
}
|
||||
|
||||
private Device getSecondDevice() {
|
||||
assertNotNull(secondDevice);
|
||||
return Objects.requireNonNull(secondDevice);
|
||||
}
|
||||
|
||||
private DeviceCollection getDevices() {
|
||||
assertNotNull(devices);
|
||||
return Objects.requireNonNull(devices);
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
firstDevice = mockDevice(FIRST_DEVICE_IDENTIFIER);
|
||||
secondDevice = mockDevice(SECOND_DEVICE_IDENTIFIER);
|
||||
|
||||
devices = mock(DeviceCollection.class);
|
||||
when(getDevices().getDeviceIdentifiers())
|
||||
.thenReturn(new HashSet<String>(Arrays.asList(FIRST_DEVICE_IDENTIFIER, SECOND_DEVICE_IDENTIFIER)));
|
||||
when(getDevices().getDevice(FIRST_DEVICE_IDENTIFIER)).thenReturn(getFirstDevice());
|
||||
when(getDevices().getDevice(SECOND_DEVICE_IDENTIFIER)).thenReturn(getSecondDevice());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddListenerDispatchesStateUpdatesToPassedListenerForCachedDevices()
|
||||
throws InterruptedException, TimeoutException, ExecutionException {
|
||||
// given:
|
||||
DeviceStateListener listener = mock(DeviceStateListener.class);
|
||||
|
||||
DeviceStateDispatcher dispatcher = new DeviceStateDispatcher();
|
||||
dispatcher.dispatchDeviceStateUpdates(getDevices());
|
||||
|
||||
// when:
|
||||
dispatcher.addListener(listener);
|
||||
|
||||
// then:
|
||||
verify(listener).onDeviceStateUpdated(new DeviceState(FIRST_DEVICE_IDENTIFIER, firstDevice));
|
||||
verify(listener).onDeviceStateUpdated(new DeviceState(SECOND_DEVICE_IDENTIFIER, secondDevice));
|
||||
verifyNoMoreInteractions(listener);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeviceStateUpdatesAreNotDispatchedToRemovedListeners() {
|
||||
// given:
|
||||
DeviceStateListener listener = mock(DeviceStateListener.class);
|
||||
|
||||
DeviceStateDispatcher dispatcher = new DeviceStateDispatcher();
|
||||
dispatcher.addListener(listener);
|
||||
|
||||
// when:
|
||||
dispatcher.removeListener(listener);
|
||||
dispatcher.dispatchDeviceStateUpdates(getDevices());
|
||||
|
||||
// then:
|
||||
verifyNoMoreInteractions(listener);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClearCachePreventsDeviceStateUpdateDispatchingOnListenerRegistration() {
|
||||
// given:
|
||||
DeviceStateListener listener = mock(DeviceStateListener.class);
|
||||
|
||||
DeviceStateDispatcher dispatcher = new DeviceStateDispatcher();
|
||||
dispatcher.dispatchDeviceStateUpdates(getDevices());
|
||||
|
||||
// when:
|
||||
dispatcher.clearCache();
|
||||
dispatcher.addListener(listener);
|
||||
|
||||
// then:
|
||||
verifyNoMoreInteractions(listener);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeviceStateUpdatesAreDispatchedToSubscribedListeners() {
|
||||
// given:
|
||||
DeviceStateListener listener = mock(DeviceStateListener.class);
|
||||
|
||||
DeviceStateDispatcher dispatcher = new DeviceStateDispatcher();
|
||||
dispatcher.addListener(listener);
|
||||
|
||||
// when:
|
||||
dispatcher.dispatchDeviceStateUpdates(getDevices());
|
||||
|
||||
// then:
|
||||
verify(listener).onDeviceStateUpdated(new DeviceState(FIRST_DEVICE_IDENTIFIER, firstDevice));
|
||||
verify(listener).onDeviceStateUpdated(new DeviceState(SECOND_DEVICE_IDENTIFIER, secondDevice));
|
||||
verifyNoMoreInteractions(listener);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemovalEventsAreDispatchedToSubscribedListeners()
|
||||
throws InterruptedException, TimeoutException, ExecutionException {
|
||||
// given:
|
||||
DeviceStateListener listener = mock(DeviceStateListener.class);
|
||||
|
||||
Device deviceWithUnknownIdentifier = mockDevice(UNKNOWN_DEVICE_IDENTIFIER);
|
||||
DeviceCollection devicesWithUnknownDevice = mock(DeviceCollection.class);
|
||||
when(devicesWithUnknownDevice.getDeviceIdentifiers())
|
||||
.thenReturn(new HashSet<String>(Arrays.asList(UNKNOWN_DEVICE_IDENTIFIER)));
|
||||
when(devicesWithUnknownDevice.getDevice(UNKNOWN_DEVICE_IDENTIFIER)).thenReturn(deviceWithUnknownIdentifier);
|
||||
|
||||
DeviceStateDispatcher dispatcher = new DeviceStateDispatcher();
|
||||
dispatcher.dispatchDeviceStateUpdates(devicesWithUnknownDevice);
|
||||
dispatcher.clearCache();
|
||||
dispatcher.addListener(listener);
|
||||
|
||||
// when:
|
||||
dispatcher.dispatchDeviceStateUpdates(getDevices());
|
||||
|
||||
// then:
|
||||
verify(listener).onDeviceRemoved(UNKNOWN_DEVICE_IDENTIFIER);
|
||||
verify(listener, times(2)).onDeviceStateUpdated(any());
|
||||
verifyNoMoreInteractions(listener);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemovalEventsAreDispatchedToSubscribedListenersMatchingAllDeviceIds()
|
||||
throws InterruptedException, TimeoutException, ExecutionException {
|
||||
// given:
|
||||
DeviceStateListener listener = mock(DeviceStateListener.class);
|
||||
|
||||
Device deviceWithUnknownIdentifier = mockDevice(UNKNOWN_DEVICE_IDENTIFIER);
|
||||
DeviceCollection devicesWithUnknownDevice = mock(DeviceCollection.class);
|
||||
when(devicesWithUnknownDevice.getDeviceIdentifiers())
|
||||
.thenReturn(new HashSet<String>(Arrays.asList(UNKNOWN_DEVICE_IDENTIFIER)));
|
||||
when(devicesWithUnknownDevice.getDevice(UNKNOWN_DEVICE_IDENTIFIER)).thenReturn(deviceWithUnknownIdentifier);
|
||||
|
||||
DeviceCollection emptyDevices = mock(DeviceCollection.class);
|
||||
when(emptyDevices.getDeviceIdentifiers()).thenReturn(new HashSet<String>());
|
||||
|
||||
DeviceStateDispatcher dispatcher = new DeviceStateDispatcher();
|
||||
dispatcher.dispatchDeviceStateUpdates(devicesWithUnknownDevice);
|
||||
dispatcher.clearCache();
|
||||
dispatcher.addListener(listener);
|
||||
|
||||
// when:
|
||||
dispatcher.dispatchDeviceStateUpdates(emptyDevices);
|
||||
|
||||
// then:
|
||||
verify(listener).onDeviceRemoved(UNKNOWN_DEVICE_IDENTIFIER);
|
||||
verifyNoMoreInteractions(listener);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeviceEventDispatchingForSubscribedListenersWithAnyDeviceIdFilter()
|
||||
throws InterruptedException, TimeoutException, ExecutionException {
|
||||
// given:
|
||||
DeviceStateListener listener = mock(DeviceStateListener.class);
|
||||
|
||||
DeviceStateDispatcher dispatcher = new DeviceStateDispatcher();
|
||||
dispatcher.addListener(listener);
|
||||
|
||||
// when:
|
||||
dispatcher.dispatchDeviceStateUpdates(getDevices());
|
||||
|
||||
// then:
|
||||
verify(listener).onDeviceStateUpdated(new DeviceState(FIRST_DEVICE_IDENTIFIER, firstDevice));
|
||||
verify(listener).onDeviceStateUpdated(new DeviceState(SECOND_DEVICE_IDENTIFIER, secondDevice));
|
||||
verifyNoMoreInteractions(listener);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testActionsEventDispatchingForSubscribedListeners()
|
||||
throws InterruptedException, TimeoutException, ExecutionException {
|
||||
// given:
|
||||
DeviceStateListener listener = mock(DeviceStateListener.class);
|
||||
Actions actions = mock(Actions.class);
|
||||
|
||||
DeviceStateDispatcher dispatcher = new DeviceStateDispatcher();
|
||||
dispatcher.addListener(listener);
|
||||
|
||||
// when:
|
||||
dispatcher.dispatchActionStateUpdates(FIRST_DEVICE_IDENTIFIER, actions);
|
||||
|
||||
// then:
|
||||
verify(listener).onProcessActionUpdated(new ActionsState(FIRST_DEVICE_IDENTIFIER, actions));
|
||||
verifyNoMoreInteractions(listener);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeviceStateDispatcherDispatchesDeviceStatesAndActions() {
|
||||
// given:
|
||||
DeviceStateListener listener = mock(DeviceStateListener.class);
|
||||
Actions actions = mock(Actions.class);
|
||||
|
||||
DeviceStateDispatcher dispatcher = new DeviceStateDispatcher();
|
||||
dispatcher.addListener(listener);
|
||||
|
||||
dispatcher.dispatchDeviceStateUpdates(getDevices());
|
||||
dispatcher.dispatchActionStateUpdates(FIRST_DEVICE_IDENTIFIER, actions);
|
||||
|
||||
// when:
|
||||
dispatcher.dispatchDeviceState(FIRST_DEVICE_IDENTIFIER);
|
||||
|
||||
// then:
|
||||
verify(listener, times(2)).onDeviceStateUpdated(new DeviceState(FIRST_DEVICE_IDENTIFIER, firstDevice));
|
||||
verify(listener).onDeviceStateUpdated(new DeviceState(SECOND_DEVICE_IDENTIFIER, secondDevice));
|
||||
verify(listener).onProcessActionUpdated(new ActionsState(FIRST_DEVICE_IDENTIFIER, actions));
|
||||
verifyNoMoreInteractions(listener);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* 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.webservice;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jetty.client.api.Response;
|
||||
import org.eclipse.jetty.http.HttpFields;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openhab.binding.mielecloud.internal.webservice.exception.TooManyRequestsException;
|
||||
|
||||
/**
|
||||
* @author Björn Lange - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class HttpUtilTest {
|
||||
@Test
|
||||
public void whenTheResponseHasARetryAfterHeaderThenItIsParsedAndPassedWithTheException() {
|
||||
// given:
|
||||
HttpFields httpFields = mock(HttpFields.class);
|
||||
when(httpFields.containsKey("Retry-After")).thenReturn(true);
|
||||
when(httpFields.get("Retry-After")).thenReturn("100");
|
||||
|
||||
Response response = mock(Response.class);
|
||||
when(response.getStatus()).thenReturn(429);
|
||||
when(response.getReason()).thenReturn("Too many requests!");
|
||||
when(response.getHeaders()).thenReturn(httpFields);
|
||||
|
||||
// when:
|
||||
try {
|
||||
HttpUtil.checkHttpSuccess(response);
|
||||
fail();
|
||||
} catch (TooManyRequestsException e) {
|
||||
// then:
|
||||
assertEquals(100L, e.getSecondsUntilRetry());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.webservice;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.eclipse.jetty.client.api.Request;
|
||||
import org.eclipse.jetty.client.util.StringContentProvider;
|
||||
import org.eclipse.jetty.http.HttpMethod;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openhab.binding.mielecloud.internal.util.MockUtil;
|
||||
import org.openhab.binding.mielecloud.internal.webservice.language.LanguageProvider;
|
||||
import org.openhab.binding.mielecloud.internal.webservice.request.RequestFactoryImpl;
|
||||
import org.openhab.core.io.net.http.HttpClientFactory;
|
||||
|
||||
/**
|
||||
* @author Björn Lange - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class RequestFactoryImplTest {
|
||||
private static final String URL = "https://www.openhab.org/";
|
||||
private static final String ACCESS_TOKEN = "DE_0123456789abcdef0123456789abcdef";
|
||||
private static final String JSON_CONTENT = "{ \"update\": 1 }";
|
||||
|
||||
private static final String LANGUAGE = "de";
|
||||
|
||||
private static final long REQUEST_TIMEOUT = 5;
|
||||
private static final long EXTENDED_REQUEST_TIMEOUT = 10;
|
||||
private static final TimeUnit REQUEST_TIMEOUT_UNIT = TimeUnit.SECONDS;
|
||||
|
||||
@Nullable
|
||||
private String contentString;
|
||||
@Nullable
|
||||
private String contentType;
|
||||
|
||||
private final LanguageProvider defaultLanguageProvider = new LanguageProvider() {
|
||||
@Override
|
||||
public Optional<String> getLanguage() {
|
||||
return Optional.of(LANGUAGE);
|
||||
}
|
||||
};
|
||||
private final LanguageProvider emptyStringLanguageProvider = new LanguageProvider() {
|
||||
@Override
|
||||
public Optional<String> getLanguage() {
|
||||
return Optional.of("");
|
||||
}
|
||||
};
|
||||
|
||||
private Request getRequestMock() {
|
||||
Request requestMock = mock(Request.class);
|
||||
when(requestMock.header(anyString(), anyString())).thenReturn(requestMock);
|
||||
when(requestMock.timeout(anyLong(), any())).thenReturn(requestMock);
|
||||
when(requestMock.method(any(HttpMethod.class))).thenReturn(requestMock);
|
||||
when(requestMock.param(anyString(), anyString())).thenReturn(requestMock);
|
||||
when(requestMock.content(any())).thenAnswer(i -> {
|
||||
StringContentProvider provider = i.getArgument(0);
|
||||
List<Byte> rawData = new ArrayList<Byte>();
|
||||
provider.forEach(b -> {
|
||||
b.rewind();
|
||||
while (b.hasRemaining()) {
|
||||
rawData.add(b.get());
|
||||
}
|
||||
});
|
||||
byte[] data = new byte[rawData.size()];
|
||||
for (int j = 0; j < data.length; j++) {
|
||||
data[j] = rawData.get(j);
|
||||
}
|
||||
contentString = new String(data, StandardCharsets.UTF_8);
|
||||
contentType = provider.getContentType();
|
||||
return requestMock;
|
||||
});
|
||||
return requestMock;
|
||||
}
|
||||
|
||||
private RequestFactoryImpl createRequestFactoryImpl(Request requestMock, LanguageProvider languageProvider) {
|
||||
HttpClient httpClient = MockUtil.mockHttpClient(URL, requestMock);
|
||||
|
||||
HttpClientFactory httpClientFactory = mock(HttpClientFactory.class);
|
||||
when(httpClientFactory.createHttpClient(anyString())).thenReturn(httpClient);
|
||||
|
||||
return new RequestFactoryImpl(httpClientFactory, languageProvider);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateGetRequestReturnsRequestWithExpectedHeaders() {
|
||||
// given:
|
||||
Request requestMock = getRequestMock();
|
||||
RequestFactoryImpl requestFactory = createRequestFactoryImpl(requestMock, defaultLanguageProvider);
|
||||
|
||||
// when:
|
||||
Request request = requestFactory.createGetRequest(URL, ACCESS_TOKEN);
|
||||
|
||||
// then:
|
||||
assertEquals(requestMock, request);
|
||||
verify(request).header("Content-type", "application/json");
|
||||
verify(request).header("Accept", "*/*");
|
||||
verify(request).header("Authorization", "Bearer " + ACCESS_TOKEN);
|
||||
verify(request).timeout(REQUEST_TIMEOUT, REQUEST_TIMEOUT_UNIT);
|
||||
verify(request).method(HttpMethod.GET);
|
||||
verify(request).param("language", LANGUAGE);
|
||||
verifyNoMoreInteractions(request);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreatePutRequestReturnsRequestWithExpectedHeadersAndContent() {
|
||||
Request requestMock = getRequestMock();
|
||||
RequestFactoryImpl requestFactory = createRequestFactoryImpl(requestMock, defaultLanguageProvider);
|
||||
|
||||
// when:
|
||||
Request request = requestFactory.createPutRequest(URL, ACCESS_TOKEN, JSON_CONTENT);
|
||||
|
||||
// then:
|
||||
assertEquals(requestMock, request);
|
||||
verify(request).header("Content-type", "application/json");
|
||||
verify(request).header("Accept", "*/*");
|
||||
verify(request).header("Authorization", "Bearer " + ACCESS_TOKEN);
|
||||
verify(request).timeout(EXTENDED_REQUEST_TIMEOUT, REQUEST_TIMEOUT_UNIT);
|
||||
verify(request).method(HttpMethod.PUT);
|
||||
verify(request).content(any());
|
||||
verify(request).param("language", LANGUAGE);
|
||||
assertEquals(JSON_CONTENT, contentString);
|
||||
assertEquals("application/json", contentType);
|
||||
verifyNoMoreInteractions(request);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreatePostRequestReturnsRequestWithExpectedHeaders() {
|
||||
Request requestMock = getRequestMock();
|
||||
RequestFactoryImpl requestFactory = createRequestFactoryImpl(requestMock, defaultLanguageProvider);
|
||||
|
||||
// when:
|
||||
Request request = requestFactory.createPostRequest(URL, ACCESS_TOKEN);
|
||||
|
||||
// then:
|
||||
assertEquals(requestMock, request);
|
||||
verify(request).header("Content-type", "application/json");
|
||||
verify(request).header("Accept", "*/*");
|
||||
verify(request).header("Authorization", "Bearer " + ACCESS_TOKEN);
|
||||
verify(request).timeout(REQUEST_TIMEOUT, REQUEST_TIMEOUT_UNIT);
|
||||
verify(request).method(HttpMethod.POST);
|
||||
verify(request).param("language", LANGUAGE);
|
||||
verifyNoMoreInteractions(request);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateRequestWithoutSuppliedLangugeCreatesNoLanguageParameter() {
|
||||
// given:
|
||||
Request requestMock = getRequestMock();
|
||||
RequestFactoryImpl requestFactory = createRequestFactoryImpl(requestMock, new LanguageProvider() {
|
||||
@Override
|
||||
public Optional<String> getLanguage() {
|
||||
return Optional.empty();
|
||||
}
|
||||
});
|
||||
|
||||
// when:
|
||||
Request request = requestFactory.createGetRequest(URL, ACCESS_TOKEN);
|
||||
|
||||
// then:
|
||||
assertEquals(requestMock, request);
|
||||
verify(request).header("Content-type", "application/json");
|
||||
verify(request).header("Accept", "*/*");
|
||||
verify(request).header("Authorization", "Bearer " + ACCESS_TOKEN);
|
||||
verify(request).timeout(REQUEST_TIMEOUT, REQUEST_TIMEOUT_UNIT);
|
||||
verify(request).method(HttpMethod.GET);
|
||||
verifyNoMoreInteractions(request);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateRequestWithEmptyLanguageCreatesNoLanguageParameter() {
|
||||
// given:
|
||||
Request requestMock = getRequestMock();
|
||||
RequestFactoryImpl requestFactory = createRequestFactoryImpl(requestMock, emptyStringLanguageProvider);
|
||||
|
||||
// when:
|
||||
Request request = requestFactory.createGetRequest(URL, ACCESS_TOKEN);
|
||||
|
||||
// then:
|
||||
assertEquals(requestMock, request);
|
||||
verify(request).header("Content-type", "application/json");
|
||||
verify(request).header("Accept", "*/*");
|
||||
verify(request).header("Authorization", "Bearer " + ACCESS_TOKEN);
|
||||
verify(request).timeout(REQUEST_TIMEOUT, REQUEST_TIMEOUT_UNIT);
|
||||
verify(request).method(HttpMethod.GET);
|
||||
verifyNoMoreInteractions(request);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenAnSseRequestIsCreatedWithoutLanguageThenTheRequiredParametersAreSet() {
|
||||
Request requestMock = getRequestMock();
|
||||
RequestFactoryImpl requestFactory = createRequestFactoryImpl(requestMock, emptyStringLanguageProvider);
|
||||
|
||||
// when:
|
||||
Request request = requestFactory.createSseRequest(URL, ACCESS_TOKEN);
|
||||
|
||||
// then:
|
||||
assertEquals(requestMock, request);
|
||||
verify(request).header("Content-type", "application/json");
|
||||
verify(request).header("Accept", "text/event-stream");
|
||||
verify(request).header("Authorization", "Bearer " + ACCESS_TOKEN);
|
||||
verifyNoMoreInteractions(request);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenAnSseRequestIsCreatedWithLanguageThenTheAcceptLanguageHeaderIsSet() {
|
||||
Request requestMock = getRequestMock();
|
||||
RequestFactoryImpl requestFactory = createRequestFactoryImpl(requestMock, defaultLanguageProvider);
|
||||
|
||||
// when:
|
||||
Request request = requestFactory.createSseRequest(URL, ACCESS_TOKEN);
|
||||
|
||||
// then:
|
||||
assertEquals(requestMock, request);
|
||||
verify(request).header("Content-type", "application/json");
|
||||
verify(request).header("Accept", "text/event-stream");
|
||||
verify(request).header("Authorization", "Bearer " + ACCESS_TOKEN);
|
||||
verify(request).header("Accept-Language", LANGUAGE);
|
||||
verifyNoMoreInteractions(request);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,299 @@
|
||||
/**
|
||||
* 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.webservice.api;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openhab.binding.mielecloud.internal.webservice.api.json.Actions;
|
||||
import org.openhab.binding.mielecloud.internal.webservice.api.json.Light;
|
||||
import org.openhab.binding.mielecloud.internal.webservice.api.json.ProcessAction;
|
||||
|
||||
/**
|
||||
* @author Björn Lange - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ActionsStateTest {
|
||||
private static final String DEVICE_IDENTIFIER = "003458276345";
|
||||
|
||||
@Test
|
||||
public void testGetDeviceIdentifierReturnsDeviceIdentifier() {
|
||||
// given:
|
||||
Actions actions = mock(Actions.class);
|
||||
ActionsState actionsState = new ActionsState(DEVICE_IDENTIFIER, actions);
|
||||
|
||||
// when:
|
||||
String deviceId = actionsState.getDeviceIdentifier();
|
||||
|
||||
// then:
|
||||
assertEquals(DEVICE_IDENTIFIER, deviceId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReturnValuesWhenActionsIsNull() {
|
||||
// given:
|
||||
ActionsState actionState = new ActionsState(DEVICE_IDENTIFIER, null);
|
||||
|
||||
// when:
|
||||
boolean canBeStarted = actionState.canBeStarted();
|
||||
boolean canBeStopped = actionState.canBeStopped();
|
||||
boolean canBePaused = actionState.canBePaused();
|
||||
boolean canStartSupercooling = actionState.canStartSupercooling();
|
||||
boolean canStopSupercooling = actionState.canStopSupercooling();
|
||||
boolean canContolSupercooling = actionState.canContolSupercooling();
|
||||
boolean canStartSuperfreezing = actionState.canStartSuperfreezing();
|
||||
boolean canStopSuperfreezing = actionState.canStopSuperfreezing();
|
||||
boolean canControlSuperfreezing = actionState.canControlSuperfreezing();
|
||||
boolean canEnableLight = actionState.canEnableLight();
|
||||
boolean canDisableLight = actionState.canDisableLight();
|
||||
|
||||
// then:
|
||||
assertFalse(canBeStarted);
|
||||
assertFalse(canBeStopped);
|
||||
assertFalse(canBePaused);
|
||||
assertFalse(canStartSupercooling);
|
||||
assertFalse(canStopSupercooling);
|
||||
assertFalse(canContolSupercooling);
|
||||
assertFalse(canStartSuperfreezing);
|
||||
assertFalse(canStopSuperfreezing);
|
||||
assertFalse(canControlSuperfreezing);
|
||||
assertFalse(canEnableLight);
|
||||
assertFalse(canDisableLight);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReturnValuesWhenProcessActionIsEmpty() {
|
||||
// given:
|
||||
Actions actions = mock(Actions.class);
|
||||
ActionsState actionState = new ActionsState(DEVICE_IDENTIFIER, null);
|
||||
when(actions.getProcessAction()).thenReturn(Collections.emptyList());
|
||||
|
||||
// when:
|
||||
boolean canBeStarted = actionState.canBeStarted();
|
||||
boolean canBeStopped = actionState.canBeStopped();
|
||||
boolean canBePaused = actionState.canBePaused();
|
||||
boolean canStartSupercooling = actionState.canStartSupercooling();
|
||||
boolean canStopSupercooling = actionState.canStopSupercooling();
|
||||
boolean canStartSuperfreezing = actionState.canStartSuperfreezing();
|
||||
boolean canStopSuperfreezing = actionState.canStopSuperfreezing();
|
||||
|
||||
// then:
|
||||
assertFalse(canBeStarted);
|
||||
assertFalse(canBeStopped);
|
||||
assertFalse(canBePaused);
|
||||
assertFalse(canStartSupercooling);
|
||||
assertFalse(canStopSupercooling);
|
||||
assertFalse(canStartSuperfreezing);
|
||||
assertFalse(canStopSuperfreezing);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReturnValuesWhenLightIsEmpty() {
|
||||
// given:
|
||||
Actions actions = mock(Actions.class);
|
||||
ActionsState actionState = new ActionsState(DEVICE_IDENTIFIER, null);
|
||||
when(actions.getLight()).thenReturn(Collections.emptyList());
|
||||
|
||||
// when:
|
||||
boolean canEnableLight = actionState.canEnableLight();
|
||||
boolean canDisableLight = actionState.canDisableLight();
|
||||
|
||||
// then:
|
||||
assertFalse(canEnableLight);
|
||||
assertFalse(canDisableLight);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReturnValueWhenProcessActionStartIsAvailable() {
|
||||
// given:
|
||||
Actions actions = mock(Actions.class);
|
||||
ActionsState actionState = new ActionsState(DEVICE_IDENTIFIER, actions);
|
||||
when(actions.getProcessAction()).thenReturn(Collections.singletonList(ProcessAction.START));
|
||||
|
||||
// when:
|
||||
boolean canBeStarted = actionState.canBeStarted();
|
||||
|
||||
// then:
|
||||
assertTrue(canBeStarted);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReturnValueWhenProcessActionStopIsAvailable() {
|
||||
// given:
|
||||
Actions actions = mock(Actions.class);
|
||||
ActionsState actionState = new ActionsState(DEVICE_IDENTIFIER, actions);
|
||||
when(actions.getProcessAction()).thenReturn(Collections.singletonList(ProcessAction.STOP));
|
||||
|
||||
// when:
|
||||
boolean canBeStopped = actionState.canBeStopped();
|
||||
|
||||
// then:
|
||||
assertTrue(canBeStopped);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReturnValueWhenProcessActionStartSupercoolIsAvailable() {
|
||||
// given:
|
||||
Actions actions = mock(Actions.class);
|
||||
ActionsState actionState = new ActionsState(DEVICE_IDENTIFIER, actions);
|
||||
when(actions.getProcessAction()).thenReturn(Collections.singletonList(ProcessAction.START_SUPERCOOLING));
|
||||
|
||||
// when:
|
||||
boolean canStartSupercooling = actionState.canStartSupercooling();
|
||||
boolean canContolSupercooling = actionState.canContolSupercooling();
|
||||
|
||||
// then:
|
||||
assertTrue(canStartSupercooling);
|
||||
assertTrue(canContolSupercooling);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReturnValueWhenProcessActionStartSuperfreezeIsAvailable() {
|
||||
// given:
|
||||
Actions actions = mock(Actions.class);
|
||||
ActionsState actionState = new ActionsState(DEVICE_IDENTIFIER, actions);
|
||||
when(actions.getProcessAction()).thenReturn(Collections.singletonList(ProcessAction.START_SUPERFREEZING));
|
||||
|
||||
// when:
|
||||
boolean canStartSuperfreezing = actionState.canStartSuperfreezing();
|
||||
boolean canControlSuperfreezing = actionState.canControlSuperfreezing();
|
||||
|
||||
// then:
|
||||
assertTrue(canStartSuperfreezing);
|
||||
assertTrue(canControlSuperfreezing);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReturnValueWhenLightEnableIsAvailable() {
|
||||
// given:
|
||||
Actions actions = mock(Actions.class);
|
||||
ActionsState actionState = new ActionsState(DEVICE_IDENTIFIER, actions);
|
||||
when(actions.getLight()).thenReturn(Collections.singletonList(Light.ENABLE));
|
||||
|
||||
// when:
|
||||
boolean canEnableLight = actionState.canEnableLight();
|
||||
|
||||
// then:
|
||||
assertTrue(canEnableLight);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReturnValueWhenLightDisableIsAvailable() {
|
||||
// given:
|
||||
Actions actions = mock(Actions.class);
|
||||
ActionsState actionState = new ActionsState(DEVICE_IDENTIFIER, actions);
|
||||
when(actions.getLight()).thenReturn(Collections.singletonList(Light.DISABLE));
|
||||
|
||||
// when:
|
||||
boolean canDisableLight = actionState.canDisableLight();
|
||||
|
||||
// then:
|
||||
assertTrue(canDisableLight);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCanControlLightReturnsTrueWhenLightCanBeEnabled() {
|
||||
// given:
|
||||
Actions actions = mock(Actions.class);
|
||||
when(actions.getLight()).thenReturn(Collections.singletonList(Light.ENABLE));
|
||||
|
||||
ActionsState actionState = new ActionsState(DEVICE_IDENTIFIER, actions);
|
||||
|
||||
// when:
|
||||
boolean canControlLight = actionState.canControlLight();
|
||||
|
||||
// then:
|
||||
assertTrue(canControlLight);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCanControlLightReturnsTrueWhenLightCanBeDisabled() {
|
||||
// given:
|
||||
Actions actions = mock(Actions.class);
|
||||
when(actions.getLight()).thenReturn(Collections.singletonList(Light.DISABLE));
|
||||
|
||||
ActionsState actionState = new ActionsState(DEVICE_IDENTIFIER, actions);
|
||||
|
||||
// when:
|
||||
boolean canControlLight = actionState.canControlLight();
|
||||
|
||||
// then:
|
||||
assertTrue(canControlLight);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCanControlLightReturnsTrueWhenLightCanBeEnabledAndDisabled() {
|
||||
// given:
|
||||
Actions actions = mock(Actions.class);
|
||||
when(actions.getLight()).thenReturn(Arrays.asList(Light.ENABLE, Light.DISABLE));
|
||||
|
||||
ActionsState actionState = new ActionsState(DEVICE_IDENTIFIER, actions);
|
||||
|
||||
// when:
|
||||
boolean canControlLight = actionState.canControlLight();
|
||||
|
||||
// then:
|
||||
assertTrue(canControlLight);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCanControlLightReturnsFalseWhenNoLightOptionIsAvailable() {
|
||||
// given:
|
||||
Actions actions = mock(Actions.class);
|
||||
when(actions.getLight()).thenReturn(new LinkedList<Light>());
|
||||
|
||||
ActionsState actionState = new ActionsState(DEVICE_IDENTIFIER, actions);
|
||||
|
||||
// when:
|
||||
boolean canControlLight = actionState.canControlLight();
|
||||
|
||||
// then:
|
||||
assertFalse(canControlLight);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoProgramCanBeSetWhenNoProgramIdIsPresent() {
|
||||
// given:
|
||||
Actions actions = mock(Actions.class);
|
||||
when(actions.getProgramId()).thenReturn(Collections.emptyList());
|
||||
|
||||
ActionsState actionState = new ActionsState(DEVICE_IDENTIFIER, actions);
|
||||
|
||||
// when:
|
||||
boolean canSetActiveProgram = actionState.canSetActiveProgramId();
|
||||
|
||||
// then:
|
||||
assertFalse(canSetActiveProgram);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProgramIdCanBeSetWhenProgramIdIsPresent() {
|
||||
// given:
|
||||
Actions actions = mock(Actions.class);
|
||||
when(actions.getProgramId()).thenReturn(Collections.singletonList(1));
|
||||
|
||||
ActionsState actionState = new ActionsState(DEVICE_IDENTIFIER, actions);
|
||||
|
||||
// when:
|
||||
boolean canSetActiveProgram = actionState.canSetActiveProgramId();
|
||||
|
||||
// then:
|
||||
assertTrue(canSetActiveProgram);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
/**
|
||||
* 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.webservice.api;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
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.webservice.api.json.DeviceType;
|
||||
|
||||
/**
|
||||
* @author Björn Lange - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class CoolingDeviceTemperatureStateTest {
|
||||
private static final Integer TEMPERATURE_0 = 8;
|
||||
private static final Integer TEMPERATURE_1 = -10;
|
||||
|
||||
private static final Integer TARGET_TEMPERATURE_0 = 5;
|
||||
private static final Integer TARGET_TEMPERATURE_1 = -18;
|
||||
|
||||
@Nullable
|
||||
private DeviceState deviceState;
|
||||
|
||||
private DeviceState getDeviceState() {
|
||||
assertNotNull(deviceState);
|
||||
return Objects.requireNonNull(deviceState);
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
deviceState = mock(DeviceState.class);
|
||||
when(getDeviceState().getTemperature(0)).thenReturn(Optional.of(TEMPERATURE_0));
|
||||
when(getDeviceState().getTemperature(1)).thenReturn(Optional.of(TEMPERATURE_1));
|
||||
when(getDeviceState().getTargetTemperature(0)).thenReturn(Optional.of(TARGET_TEMPERATURE_0));
|
||||
when(getDeviceState().getTargetTemperature(1)).thenReturn(Optional.of(TARGET_TEMPERATURE_1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetFridgeTemperaturesForFridge() {
|
||||
// given:
|
||||
when(getDeviceState().getRawType()).thenReturn(DeviceType.FRIDGE);
|
||||
CoolingDeviceTemperatureState state = new CoolingDeviceTemperatureState(getDeviceState());
|
||||
|
||||
// when:
|
||||
Integer current = state.getFridgeTemperature().get();
|
||||
Integer target = state.getFridgeTargetTemperature().get();
|
||||
|
||||
// then:
|
||||
assertEquals(TEMPERATURE_0, current);
|
||||
assertEquals(TARGET_TEMPERATURE_0, target);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetFridgeTemperaturesForFridgeFreezerCombination() {
|
||||
// given:
|
||||
when(getDeviceState().getRawType()).thenReturn(DeviceType.FRIDGE_FREEZER_COMBINATION);
|
||||
CoolingDeviceTemperatureState state = new CoolingDeviceTemperatureState(getDeviceState());
|
||||
|
||||
// when:
|
||||
Integer current = state.getFridgeTemperature().get();
|
||||
Integer target = state.getFridgeTargetTemperature().get();
|
||||
|
||||
// then:
|
||||
assertEquals(TEMPERATURE_0, current);
|
||||
assertEquals(TARGET_TEMPERATURE_0, target);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetFridgeTemperaturesForFreezer() {
|
||||
// given:
|
||||
when(getDeviceState().getRawType()).thenReturn(DeviceType.FREEZER);
|
||||
CoolingDeviceTemperatureState state = new CoolingDeviceTemperatureState(getDeviceState());
|
||||
|
||||
// when:
|
||||
Optional<Integer> current = state.getFridgeTemperature();
|
||||
Optional<Integer> target = state.getFridgeTargetTemperature();
|
||||
|
||||
// then:
|
||||
assertFalse(current.isPresent());
|
||||
assertFalse(target.isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetFreezerTemperaturesForFridge() {
|
||||
// given:
|
||||
when(getDeviceState().getRawType()).thenReturn(DeviceType.FRIDGE);
|
||||
CoolingDeviceTemperatureState state = new CoolingDeviceTemperatureState(getDeviceState());
|
||||
|
||||
// when:
|
||||
Optional<Integer> current = state.getFreezerTemperature();
|
||||
Optional<Integer> target = state.getFreezerTargetTemperature();
|
||||
|
||||
// then:
|
||||
assertFalse(current.isPresent());
|
||||
assertFalse(target.isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetFreezerTemperaturesForFridgeFreezerCombination() {
|
||||
// given:
|
||||
when(getDeviceState().getRawType()).thenReturn(DeviceType.FRIDGE_FREEZER_COMBINATION);
|
||||
CoolingDeviceTemperatureState state = new CoolingDeviceTemperatureState(getDeviceState());
|
||||
|
||||
// when:
|
||||
Integer current = state.getFreezerTemperature().get();
|
||||
Integer target = state.getFreezerTargetTemperature().get();
|
||||
|
||||
// then:
|
||||
assertEquals(TEMPERATURE_1, current);
|
||||
assertEquals(TARGET_TEMPERATURE_1, target);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetFreezerTemperaturesForFreezer() {
|
||||
// given:
|
||||
when(getDeviceState().getRawType()).thenReturn(DeviceType.FREEZER);
|
||||
CoolingDeviceTemperatureState state = new CoolingDeviceTemperatureState(getDeviceState());
|
||||
|
||||
// when:
|
||||
Integer current = state.getFreezerTemperature().get();
|
||||
Integer target = state.getFreezerTargetTemperature().get();
|
||||
|
||||
// then:
|
||||
assertEquals(TEMPERATURE_0, current);
|
||||
assertEquals(TARGET_TEMPERATURE_0, target);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,576 @@
|
||||
/**
|
||||
* 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.webservice.api;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openhab.binding.mielecloud.internal.webservice.api.json.StateType;
|
||||
|
||||
/**
|
||||
* @author Björn Lange - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class TransitionStateTest {
|
||||
private final DeviceState historic = mock(DeviceState.class);
|
||||
private final DeviceState previous = mock(DeviceState.class);
|
||||
private final DeviceState next = mock(DeviceState.class);
|
||||
|
||||
@Test
|
||||
public void testHasFinishedChangedReturnsTrueWhenPreviousStateIsNull() {
|
||||
// given:
|
||||
when(next.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
|
||||
|
||||
TransitionState transitionState = new TransitionState(null, next);
|
||||
|
||||
// when:
|
||||
boolean hasFinishedChanged = transitionState.hasFinishedChanged();
|
||||
|
||||
// then:
|
||||
assertTrue(hasFinishedChanged);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHasFinishedChangedReturnsTrueWhenPreviousStateIsUnknown() {
|
||||
// given:
|
||||
when(previous.getStateType()).thenReturn(Optional.empty());
|
||||
when(previous.isInState(any())).thenCallRealMethod();
|
||||
when(next.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
|
||||
when(next.isInState(any())).thenCallRealMethod();
|
||||
|
||||
TransitionState transitionState = new TransitionState(new TransitionState(null, previous), next);
|
||||
|
||||
// when:
|
||||
boolean hasFinishedChanged = transitionState.hasFinishedChanged();
|
||||
|
||||
// then:
|
||||
assertTrue(hasFinishedChanged);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHasFinishedChangedReturnsFalseWhenNoStateTransitionOccurred() {
|
||||
// given:
|
||||
when(previous.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
|
||||
when(next.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
|
||||
|
||||
TransitionState transitionState = new TransitionState(new TransitionState(null, previous), next);
|
||||
|
||||
// when:
|
||||
boolean hasFinishedChanged = transitionState.hasFinishedChanged();
|
||||
|
||||
// then:
|
||||
assertFalse(hasFinishedChanged);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHasFinishedChangedReturnsTrueWhenStateChangedFromRunningToEndProgrammed() {
|
||||
// given:
|
||||
when(previous.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
|
||||
when(previous.isInState(any())).thenCallRealMethod();
|
||||
when(next.getStateType()).thenReturn(Optional.of(StateType.END_PROGRAMMED));
|
||||
when(next.isInState(any())).thenCallRealMethod();
|
||||
|
||||
TransitionState transitionState = new TransitionState(new TransitionState(null, previous), next);
|
||||
|
||||
// when:
|
||||
boolean hasFinishedChanged = transitionState.hasFinishedChanged();
|
||||
|
||||
// then:
|
||||
assertTrue(hasFinishedChanged);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHasFinishedChangedReturnsTrueWhenStateChangedFromRunningToProgrammed() {
|
||||
// given:
|
||||
when(previous.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
|
||||
when(previous.isInState(any())).thenCallRealMethod();
|
||||
when(next.getStateType()).thenReturn(Optional.of(StateType.PROGRAMMED));
|
||||
when(next.isInState(any())).thenCallRealMethod();
|
||||
|
||||
TransitionState transitionState = new TransitionState(new TransitionState(null, previous), next);
|
||||
|
||||
// when:
|
||||
boolean hasFinishedChanged = transitionState.hasFinishedChanged();
|
||||
|
||||
// then:
|
||||
assertTrue(hasFinishedChanged);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHasFinishedChangedReturnsFalseWhenStateChangedFromRunningToPause() {
|
||||
// given:
|
||||
when(previous.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
|
||||
when(next.getStateType()).thenReturn(Optional.of(StateType.PAUSE));
|
||||
|
||||
TransitionState transitionState = new TransitionState(new TransitionState(null, previous), next);
|
||||
|
||||
// when:
|
||||
boolean hasFinishedChanged = transitionState.hasFinishedChanged();
|
||||
|
||||
// then:
|
||||
assertFalse(hasFinishedChanged);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHasFinishedChangedReturnsTrueWhenStateChangedFromProgrammedWaitingToStartToRunning() {
|
||||
// given:
|
||||
when(previous.getStateType()).thenReturn(Optional.of(StateType.PROGRAMMED_WAITING_TO_START));
|
||||
when(previous.isInState(any())).thenCallRealMethod();
|
||||
when(next.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
|
||||
when(next.isInState(any())).thenCallRealMethod();
|
||||
|
||||
TransitionState transitionState = new TransitionState(new TransitionState(null, previous), next);
|
||||
|
||||
// when:
|
||||
boolean hasFinishedChanged = transitionState.hasFinishedChanged();
|
||||
|
||||
// then:
|
||||
assertTrue(hasFinishedChanged);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHasFinishedChangedReturnsFalseWhenStateRemainsProgrammedWaitingToStart() {
|
||||
// given:
|
||||
when(previous.getStateType()).thenReturn(Optional.of(StateType.PROGRAMMED_WAITING_TO_START));
|
||||
when(next.getStateType()).thenReturn(Optional.of(StateType.PROGRAMMED_WAITING_TO_START));
|
||||
|
||||
TransitionState transitionState = new TransitionState(new TransitionState(null, previous), next);
|
||||
|
||||
// when:
|
||||
boolean hasFinishedChanged = transitionState.hasFinishedChanged();
|
||||
|
||||
// then:
|
||||
assertFalse(hasFinishedChanged);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHasFinishedChangedReturnsFalseWhenStateChangedFromPauseToRunning() {
|
||||
// given:
|
||||
when(previous.getStateType()).thenReturn(Optional.of(StateType.PAUSE));
|
||||
when(next.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
|
||||
|
||||
TransitionState transitionState = new TransitionState(new TransitionState(null, previous), next);
|
||||
|
||||
// when:
|
||||
boolean hasFinishedChanged = transitionState.hasFinishedChanged();
|
||||
|
||||
// then:
|
||||
assertFalse(hasFinishedChanged);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHasFinishedChangedReturnsTrueWhenStateChangedFromEndProgrammedToOff() {
|
||||
// given:
|
||||
when(previous.getStateType()).thenReturn(Optional.of(StateType.END_PROGRAMMED));
|
||||
when(previous.isInState(any())).thenCallRealMethod();
|
||||
when(next.getStateType()).thenReturn(Optional.of(StateType.OFF));
|
||||
when(next.isInState(any())).thenCallRealMethod();
|
||||
|
||||
TransitionState transitionState = new TransitionState(new TransitionState(null, previous), next);
|
||||
|
||||
// when:
|
||||
boolean hasFinishedChanged = transitionState.hasFinishedChanged();
|
||||
|
||||
// then:
|
||||
assertTrue(hasFinishedChanged);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHasFinishedChangedReturnsFalseWhenStateChangedFromRunningToFailure() {
|
||||
// given:
|
||||
when(previous.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
|
||||
when(next.getStateType()).thenReturn(Optional.of(StateType.FAILURE));
|
||||
|
||||
TransitionState transitionState = new TransitionState(new TransitionState(null, previous), next);
|
||||
|
||||
// when:
|
||||
boolean hasFinishedChanged = transitionState.hasFinishedChanged();
|
||||
|
||||
// then:
|
||||
assertFalse(hasFinishedChanged);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHasFinishedChangedReturnsFalseWhenStateChangedFromPauseToFailure() {
|
||||
// given:
|
||||
when(previous.getStateType()).thenReturn(Optional.of(StateType.PAUSE));
|
||||
when(next.getStateType()).thenReturn(Optional.of(StateType.FAILURE));
|
||||
|
||||
TransitionState transitionState = new TransitionState(new TransitionState(null, previous), next);
|
||||
|
||||
// when:
|
||||
boolean hasFinishedChanged = transitionState.hasFinishedChanged();
|
||||
|
||||
// then:
|
||||
assertFalse(hasFinishedChanged);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsFinishedReturnsTrueWhenStateChangedFromRunningToEndProgrammed() {
|
||||
// given:
|
||||
when(previous.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
|
||||
when(next.getStateType()).thenReturn(Optional.of(StateType.END_PROGRAMMED));
|
||||
|
||||
TransitionState transitionState = new TransitionState(new TransitionState(null, previous), next);
|
||||
|
||||
// when:
|
||||
Boolean isFinished = transitionState.isFinished().get();
|
||||
|
||||
// then:
|
||||
assertTrue(isFinished);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsFinishedReturnsTrueWhenStateChangedFromRunningToProgrammed() {
|
||||
// given:
|
||||
when(previous.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
|
||||
when(next.getStateType()).thenReturn(Optional.of(StateType.PROGRAMMED));
|
||||
|
||||
TransitionState transitionState = new TransitionState(new TransitionState(null, previous), next);
|
||||
|
||||
// when:
|
||||
Boolean isFinished = transitionState.isFinished().get();
|
||||
|
||||
// then:
|
||||
assertTrue(isFinished);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsFinishedReturnsFalseWhenStateChangedFromProgrammedWaitingToStartToRunning() {
|
||||
// given:
|
||||
when(previous.getStateType()).thenReturn(Optional.of(StateType.PROGRAMMED_WAITING_TO_START));
|
||||
when(previous.isInState(any())).thenCallRealMethod();
|
||||
when(next.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
|
||||
when(next.isInState(any())).thenCallRealMethod();
|
||||
|
||||
TransitionState transitionState = new TransitionState(new TransitionState(null, previous), next);
|
||||
|
||||
// when:
|
||||
Boolean isFinished = transitionState.isFinished().get();
|
||||
|
||||
// then:
|
||||
assertFalse(isFinished);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsFinishedReturnsFalseWhenStateChangedFromRunningToFailure() {
|
||||
// given:
|
||||
when(previous.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
|
||||
when(previous.isInState(any())).thenCallRealMethod();
|
||||
when(next.getStateType()).thenReturn(Optional.of(StateType.FAILURE));
|
||||
when(next.isInState(any())).thenCallRealMethod();
|
||||
|
||||
TransitionState transitionState = new TransitionState(new TransitionState(null, previous), next);
|
||||
|
||||
// when:
|
||||
Boolean isFinished = transitionState.isFinished().get();
|
||||
|
||||
// then:
|
||||
assertFalse(isFinished);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsFinishedReturnsFalseWhenStateChangedFromPauseToFailure() {
|
||||
// given:
|
||||
when(previous.getStateType()).thenReturn(Optional.of(StateType.PAUSE));
|
||||
when(previous.isInState(any())).thenCallRealMethod();
|
||||
when(next.getStateType()).thenReturn(Optional.of(StateType.FAILURE));
|
||||
when(next.isInState(any())).thenCallRealMethod();
|
||||
|
||||
TransitionState transitionState = new TransitionState(new TransitionState(null, previous), next);
|
||||
|
||||
// when:
|
||||
Boolean isFinished = transitionState.isFinished().get();
|
||||
|
||||
// then:
|
||||
assertFalse(isFinished);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsFinishedReturnsTrueWhenStateChangedFromEndProgrammedToOff() {
|
||||
// given:
|
||||
when(previous.getStateType()).thenReturn(Optional.of(StateType.END_PROGRAMMED));
|
||||
when(previous.isInState(any())).thenCallRealMethod();
|
||||
when(next.getStateType()).thenReturn(Optional.of(StateType.OFF));
|
||||
when(next.isInState(any())).thenCallRealMethod();
|
||||
|
||||
TransitionState transitionState = new TransitionState(new TransitionState(null, previous), next);
|
||||
|
||||
// when:
|
||||
Boolean isFinished = transitionState.isFinished().get();
|
||||
|
||||
// then:
|
||||
assertFalse(isFinished);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsFinishedReturnsNullWhenPreviousStateIsNull() {
|
||||
// given:
|
||||
when(next.getStateType()).thenReturn(Optional.of(StateType.IDLE));
|
||||
|
||||
TransitionState transitionState = new TransitionState(null, next);
|
||||
|
||||
// when:
|
||||
Optional<Boolean> isFinished = transitionState.isFinished();
|
||||
|
||||
// then:
|
||||
assertFalse(isFinished.isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsFinishedReturnsNullWhenPreviousStateIsUnknown() {
|
||||
// given:
|
||||
when(previous.getStateType()).thenReturn(Optional.empty());
|
||||
when(next.getStateType()).thenReturn(Optional.of(StateType.IDLE));
|
||||
|
||||
TransitionState transitionState = new TransitionState(new TransitionState(null, previous), next);
|
||||
|
||||
// when:
|
||||
Optional<Boolean> isFinished = transitionState.isFinished();
|
||||
|
||||
// then:
|
||||
assertFalse(isFinished.isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProgramStartedWithZeroRemainingTimeShowsNoRemainingTimeAndProgress() {
|
||||
// given:
|
||||
when(previous.isInState(any())).thenCallRealMethod();
|
||||
when(previous.getStateType()).thenReturn(Optional.of(StateType.PROGRAMMED_WAITING_TO_START));
|
||||
|
||||
when(next.isInState(any())).thenCallRealMethod();
|
||||
when(next.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
|
||||
when(next.getRemainingTime()).thenReturn(Optional.of(0));
|
||||
when(next.getProgress()).thenReturn(Optional.of(100));
|
||||
|
||||
TransitionState transitionState = new TransitionState(new TransitionState(null, previous), next);
|
||||
|
||||
// when:
|
||||
Optional<Integer> remainingTime = transitionState.getRemainingTime();
|
||||
Optional<Integer> progress = transitionState.getProgress();
|
||||
|
||||
// then:
|
||||
assertFalse(remainingTime.isPresent());
|
||||
assertFalse(progress.isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProgramStartetdWithRemainingTimeShowsRemainingTimeAndProgress() {
|
||||
// given:
|
||||
when(previous.isInState(any())).thenCallRealMethod();
|
||||
when(previous.getStateType()).thenReturn(Optional.of(StateType.PROGRAMMED_WAITING_TO_START));
|
||||
|
||||
when(next.isInState(any())).thenCallRealMethod();
|
||||
when(next.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
|
||||
when(next.getRemainingTime()).thenReturn(Optional.of(2));
|
||||
when(next.getProgress()).thenReturn(Optional.of(50));
|
||||
|
||||
TransitionState transitionState = new TransitionState(new TransitionState(null, previous), next);
|
||||
|
||||
// when:
|
||||
int remainingTime = transitionState.getRemainingTime().get();
|
||||
int progress = transitionState.getProgress().get();
|
||||
|
||||
// then:
|
||||
assertEquals(2, remainingTime);
|
||||
assertEquals(50, progress);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProgramCountingDownRemainingTimeToZeroShowsRemainingTimeAndProgress() {
|
||||
// given:
|
||||
when(previous.isInState(any())).thenCallRealMethod();
|
||||
when(previous.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
|
||||
when(previous.getRemainingTime()).thenReturn(Optional.of(1));
|
||||
|
||||
when(next.isInState(any())).thenCallRealMethod();
|
||||
when(next.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
|
||||
when(next.getRemainingTime()).thenReturn(Optional.of(0));
|
||||
when(next.getProgress()).thenReturn(Optional.of(100));
|
||||
|
||||
TransitionState transitionState = new TransitionState(new TransitionState(null, previous), next);
|
||||
|
||||
// when:
|
||||
int remainingTime = transitionState.getRemainingTime().get();
|
||||
int progress = transitionState.getProgress().get();
|
||||
|
||||
// then:
|
||||
assertEquals(0, remainingTime);
|
||||
assertEquals(100, progress);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDevicePairedWhileRunningWithZeroRemainingTimeShowsNoRemainingTimeAndProgress() {
|
||||
// given:
|
||||
when(next.isInState(any())).thenCallRealMethod();
|
||||
when(next.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
|
||||
when(next.getRemainingTime()).thenReturn(Optional.of(0));
|
||||
when(next.getProgress()).thenReturn(Optional.of(100));
|
||||
|
||||
TransitionState transitionState = new TransitionState(null, next);
|
||||
|
||||
// when:
|
||||
Optional<Integer> remainingTime = transitionState.getRemainingTime();
|
||||
Optional<Integer> progress = transitionState.getProgress();
|
||||
|
||||
// then:
|
||||
assertFalse(remainingTime.isPresent());
|
||||
assertFalse(progress.isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDevicePairedWhileRunningWithRemainingTimeShowsRemainingTimeAndProgress() {
|
||||
// given:
|
||||
when(next.isInState(any())).thenCallRealMethod();
|
||||
when(next.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
|
||||
when(next.getRemainingTime()).thenReturn(Optional.of(3));
|
||||
when(next.getProgress()).thenReturn(Optional.of(80));
|
||||
|
||||
TransitionState transitionState = new TransitionState(null, next);
|
||||
|
||||
// when:
|
||||
int remainingTime = transitionState.getRemainingTime().get();
|
||||
int progress = transitionState.getProgress().get();
|
||||
|
||||
// then:
|
||||
assertEquals(3, remainingTime);
|
||||
assertEquals(80, progress);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWhenNoRemainingTimeIsSetWhileProgramIsRunningThenNoRemainingTimeAndProgressIsShown() {
|
||||
// given:
|
||||
when(historic.isInState(any())).thenCallRealMethod();
|
||||
when(historic.getStateType()).thenReturn(Optional.of(StateType.PROGRAMMED_WAITING_TO_START));
|
||||
|
||||
when(previous.isInState(any())).thenCallRealMethod();
|
||||
when(previous.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
|
||||
when(previous.getRemainingTime()).thenReturn(Optional.of(0));
|
||||
|
||||
when(next.isInState(any())).thenCallRealMethod();
|
||||
when(next.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
|
||||
when(next.getRemainingTime()).thenReturn(Optional.of(0));
|
||||
when(next.getProgress()).thenReturn(Optional.of(100));
|
||||
|
||||
TransitionState transitionState = new TransitionState(
|
||||
new TransitionState(new TransitionState(null, historic), previous), next);
|
||||
|
||||
// when:
|
||||
Optional<Integer> remainingTime = transitionState.getRemainingTime();
|
||||
Optional<Integer> progress = transitionState.getProgress();
|
||||
|
||||
// then:
|
||||
assertFalse(remainingTime.isPresent());
|
||||
assertFalse(progress.isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemainingTimeIsSetWhileRunningShowsRemainingTimeAndProgress() {
|
||||
// given:
|
||||
when(historic.isInState(any())).thenCallRealMethod();
|
||||
when(historic.getStateType()).thenReturn(Optional.of(StateType.PROGRAMMED_WAITING_TO_START));
|
||||
|
||||
when(previous.isInState(any())).thenCallRealMethod();
|
||||
when(previous.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
|
||||
when(previous.getRemainingTime()).thenReturn(Optional.of(0));
|
||||
|
||||
when(next.isInState(any())).thenCallRealMethod();
|
||||
when(next.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
|
||||
when(next.getRemainingTime()).thenReturn(Optional.of(100));
|
||||
when(next.getProgress()).thenReturn(Optional.of(10));
|
||||
|
||||
TransitionState transitionState = new TransitionState(
|
||||
new TransitionState(new TransitionState(null, historic), previous), next);
|
||||
|
||||
// when:
|
||||
int remainingTime = transitionState.getRemainingTime().get();
|
||||
int progress = transitionState.getProgress().get();
|
||||
|
||||
// then:
|
||||
assertEquals(100, remainingTime);
|
||||
assertEquals(10, progress);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPreviousProgramDoesNotAffectHandlingOfRemainingTimeAndProgressForNextProgramCase1() {
|
||||
// given:
|
||||
DeviceState beforeHistoric = mock(DeviceState.class);
|
||||
when(beforeHistoric.isInState(any())).thenCallRealMethod();
|
||||
when(beforeHistoric.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
|
||||
when(beforeHistoric.getRemainingTime()).thenReturn(Optional.of(1));
|
||||
|
||||
when(historic.isInState(any())).thenCallRealMethod();
|
||||
when(historic.getStateType()).thenReturn(Optional.of(StateType.END_PROGRAMMED));
|
||||
when(historic.getRemainingTime()).thenReturn(Optional.of(0));
|
||||
|
||||
when(previous.isInState(any())).thenCallRealMethod();
|
||||
when(previous.getStateType()).thenReturn(Optional.of(StateType.PROGRAMMED_WAITING_TO_START));
|
||||
when(previous.getRemainingTime()).thenReturn(Optional.of(0));
|
||||
|
||||
when(next.isInState(any())).thenCallRealMethod();
|
||||
when(next.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
|
||||
when(next.getRemainingTime()).thenReturn(Optional.of(0));
|
||||
when(next.getProgress()).thenReturn(Optional.of(100));
|
||||
|
||||
TransitionState transitionState = new TransitionState(
|
||||
new TransitionState(new TransitionState(new TransitionState(null, beforeHistoric), historic), previous),
|
||||
next);
|
||||
|
||||
// when:
|
||||
Optional<Integer> remainingTime = transitionState.getRemainingTime();
|
||||
Optional<Integer> progress = transitionState.getProgress();
|
||||
|
||||
// then:
|
||||
assertFalse(remainingTime.isPresent());
|
||||
assertFalse(progress.isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPreviousProgramDoesNotAffectHandlingOfRemainingTimeAndProgressForNextProgramCase2() {
|
||||
// given:
|
||||
DeviceState beforeHistoric = mock(DeviceState.class);
|
||||
when(beforeHistoric.isInState(any())).thenCallRealMethod();
|
||||
when(beforeHistoric.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
|
||||
when(beforeHistoric.getRemainingTime()).thenReturn(Optional.of(1));
|
||||
|
||||
when(historic.isInState(any())).thenCallRealMethod();
|
||||
when(historic.getStateType()).thenReturn(Optional.of(StateType.END_PROGRAMMED));
|
||||
when(historic.getRemainingTime()).thenReturn(Optional.of(0));
|
||||
|
||||
when(previous.isInState(any())).thenCallRealMethod();
|
||||
when(previous.getStateType()).thenReturn(Optional.of(StateType.PROGRAMMED_WAITING_TO_START));
|
||||
when(previous.getRemainingTime()).thenReturn(Optional.of(0));
|
||||
|
||||
when(next.isInState(any())).thenCallRealMethod();
|
||||
when(next.getStateType()).thenReturn(Optional.of(StateType.RUNNING));
|
||||
when(next.getRemainingTime()).thenReturn(Optional.of(10));
|
||||
when(next.getProgress()).thenReturn(Optional.of(60));
|
||||
|
||||
TransitionState transitionState = new TransitionState(
|
||||
new TransitionState(new TransitionState(new TransitionState(null, beforeHistoric), historic), previous),
|
||||
next);
|
||||
|
||||
// when:
|
||||
int remainingTime = transitionState.getRemainingTime().get();
|
||||
int progress = transitionState.getProgress().get();
|
||||
|
||||
// then:
|
||||
assertEquals(10, remainingTime);
|
||||
assertEquals(60, progress);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,455 @@
|
||||
/**
|
||||
* 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.webservice.api;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
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.Test;
|
||||
import org.openhab.binding.mielecloud.internal.webservice.api.json.DeviceType;
|
||||
|
||||
/**
|
||||
* @author Björn Lange - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class WineStorageDeviceTemperatureStateTest {
|
||||
private static final Integer TEMPERATURE_0 = 8;
|
||||
private static final Integer TEMPERATURE_1 = 10;
|
||||
private static final Integer TEMPERATURE_2 = 12;
|
||||
|
||||
private static final Integer TARGET_TEMPERATURE_0 = 5;
|
||||
private static final Integer TARGET_TEMPERATURE_1 = 9;
|
||||
private static final Integer TARGET_TEMPERATURE_2 = 11;
|
||||
|
||||
@Nullable
|
||||
private DeviceState deviceState;
|
||||
|
||||
private DeviceState getDeviceState() {
|
||||
assertNotNull(deviceState);
|
||||
return Objects.requireNonNull(deviceState);
|
||||
}
|
||||
|
||||
private void setUpDeviceStateMock(int numberOfTemperatures) {
|
||||
deviceState = mock(DeviceState.class);
|
||||
if (numberOfTemperatures > 0) {
|
||||
when(getDeviceState().getTemperature(0)).thenReturn(Optional.of(TEMPERATURE_0));
|
||||
when(getDeviceState().getTargetTemperature(0)).thenReturn(Optional.of(TARGET_TEMPERATURE_0));
|
||||
} else {
|
||||
when(getDeviceState().getTemperature(0)).thenReturn(Optional.empty());
|
||||
when(getDeviceState().getTargetTemperature(0)).thenReturn(Optional.empty());
|
||||
}
|
||||
if (numberOfTemperatures > 1) {
|
||||
when(getDeviceState().getTemperature(1)).thenReturn(Optional.of(TEMPERATURE_1));
|
||||
when(getDeviceState().getTargetTemperature(1)).thenReturn(Optional.of(TARGET_TEMPERATURE_1));
|
||||
} else {
|
||||
when(getDeviceState().getTemperature(1)).thenReturn(Optional.empty());
|
||||
when(getDeviceState().getTargetTemperature(1)).thenReturn(Optional.empty());
|
||||
}
|
||||
if (numberOfTemperatures > 2) {
|
||||
when(getDeviceState().getTemperature(2)).thenReturn(Optional.of(TEMPERATURE_2));
|
||||
when(getDeviceState().getTargetTemperature(2)).thenReturn(Optional.of(TARGET_TEMPERATURE_2));
|
||||
} else {
|
||||
when(getDeviceState().getTemperature(2)).thenReturn(Optional.empty());
|
||||
when(getDeviceState().getTargetTemperature(2)).thenReturn(Optional.empty());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetTemperaturesForWineCabinetWithThreeCompartments() {
|
||||
// given:
|
||||
setUpDeviceStateMock(3);
|
||||
when(getDeviceState().getRawType()).thenReturn(DeviceType.WINE_CABINET);
|
||||
WineStorageDeviceTemperatureState state = new WineStorageDeviceTemperatureState(getDeviceState());
|
||||
|
||||
// when:
|
||||
Optional<Integer> temperature = state.getTemperature();
|
||||
Optional<Integer> targetTemperature = state.getTargetTemperature();
|
||||
|
||||
// then:
|
||||
assertFalse(temperature.isPresent());
|
||||
assertFalse(targetTemperature.isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetTemperaturesForWineCabinetWithTwoCompartments() {
|
||||
// given:
|
||||
setUpDeviceStateMock(2);
|
||||
when(getDeviceState().getRawType()).thenReturn(DeviceType.WINE_CABINET);
|
||||
WineStorageDeviceTemperatureState state = new WineStorageDeviceTemperatureState(getDeviceState());
|
||||
|
||||
// when:
|
||||
Optional<Integer> temperature = state.getTemperature();
|
||||
Optional<Integer> targetTemperature = state.getTargetTemperature();
|
||||
|
||||
// then:
|
||||
assertFalse(temperature.isPresent());
|
||||
assertFalse(targetTemperature.isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetTemperaturesForWineCabinetWithOneCompartment() {
|
||||
// given:
|
||||
setUpDeviceStateMock(1);
|
||||
when(getDeviceState().getRawType()).thenReturn(DeviceType.WINE_CABINET);
|
||||
WineStorageDeviceTemperatureState state = new WineStorageDeviceTemperatureState(getDeviceState());
|
||||
|
||||
// when:
|
||||
Integer temperature = state.getTemperature().get();
|
||||
Integer targetTemperature = state.getTargetTemperature().get();
|
||||
|
||||
// then:
|
||||
assertEquals(TEMPERATURE_0, temperature);
|
||||
assertEquals(TARGET_TEMPERATURE_0, targetTemperature);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetTemperaturesForWineCabinetFreezerCombination() {
|
||||
// given:
|
||||
setUpDeviceStateMock(2);
|
||||
when(getDeviceState().getRawType()).thenReturn(DeviceType.WINE_CABINET_FREEZER_COMBINATION);
|
||||
WineStorageDeviceTemperatureState state = new WineStorageDeviceTemperatureState(getDeviceState());
|
||||
|
||||
// when:
|
||||
Optional<Integer> temperature = state.getTemperature();
|
||||
Optional<Integer> targetTemperature = state.getTargetTemperature();
|
||||
|
||||
// then:
|
||||
assertFalse(temperature.isPresent());
|
||||
assertFalse(targetTemperature.isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetTemperaturesForOtherDeviceWithOneTemperature() {
|
||||
// given:
|
||||
setUpDeviceStateMock(1);
|
||||
when(getDeviceState().getRawType()).thenReturn(DeviceType.OVEN);
|
||||
WineStorageDeviceTemperatureState state = new WineStorageDeviceTemperatureState(getDeviceState());
|
||||
|
||||
// when:
|
||||
Optional<Integer> temperature = state.getTemperature();
|
||||
Optional<Integer> targetTemperature = state.getTargetTemperature();
|
||||
|
||||
// then:
|
||||
assertFalse(temperature.isPresent());
|
||||
assertFalse(targetTemperature.isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetTemperaturesWhenNoTemperaturesAreAvailable() {
|
||||
// given:
|
||||
setUpDeviceStateMock(0);
|
||||
when(getDeviceState().getRawType()).thenReturn(DeviceType.WINE_CABINET);
|
||||
WineStorageDeviceTemperatureState state = new WineStorageDeviceTemperatureState(getDeviceState());
|
||||
|
||||
// when:
|
||||
Optional<Integer> temperature = state.getTemperature();
|
||||
Optional<Integer> targetTemperature = state.getTargetTemperature();
|
||||
|
||||
// then:
|
||||
assertFalse(temperature.isPresent());
|
||||
assertFalse(targetTemperature.isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetTopTemperaturesForWineCabinetWithThreeCompartments() {
|
||||
// given:
|
||||
setUpDeviceStateMock(3);
|
||||
when(getDeviceState().getRawType()).thenReturn(DeviceType.WINE_CABINET);
|
||||
WineStorageDeviceTemperatureState state = new WineStorageDeviceTemperatureState(getDeviceState());
|
||||
|
||||
// when:
|
||||
Integer temperature = state.getTopTemperature().get();
|
||||
Integer targetTemperature = state.getTopTargetTemperature().get();
|
||||
|
||||
// then:
|
||||
assertEquals(TEMPERATURE_0, temperature);
|
||||
assertEquals(TARGET_TEMPERATURE_0, targetTemperature);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetTopTemperaturesForWineCabinetWithTwoCompartments() {
|
||||
// given:
|
||||
setUpDeviceStateMock(2);
|
||||
when(getDeviceState().getRawType()).thenReturn(DeviceType.WINE_CABINET);
|
||||
WineStorageDeviceTemperatureState state = new WineStorageDeviceTemperatureState(getDeviceState());
|
||||
|
||||
// when:
|
||||
Integer temperature = state.getTopTemperature().get();
|
||||
Integer targetTemperature = state.getTopTargetTemperature().get();
|
||||
|
||||
// then:
|
||||
assertEquals(TEMPERATURE_0, temperature);
|
||||
assertEquals(TARGET_TEMPERATURE_0, targetTemperature);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetTopTemperaturesForWineCabinetWithOneCompartment() {
|
||||
// given:
|
||||
setUpDeviceStateMock(1);
|
||||
when(getDeviceState().getRawType()).thenReturn(DeviceType.WINE_CABINET);
|
||||
WineStorageDeviceTemperatureState state = new WineStorageDeviceTemperatureState(getDeviceState());
|
||||
|
||||
// when:
|
||||
Optional<Integer> temperature = state.getTopTemperature();
|
||||
Optional<Integer> targetTemperature = state.getTopTargetTemperature();
|
||||
|
||||
// then:
|
||||
assertFalse(temperature.isPresent());
|
||||
assertFalse(targetTemperature.isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetTopTemperaturesForWineCabinetFreezerCombination() {
|
||||
// given:
|
||||
setUpDeviceStateMock(2);
|
||||
when(getDeviceState().getRawType()).thenReturn(DeviceType.WINE_CABINET_FREEZER_COMBINATION);
|
||||
WineStorageDeviceTemperatureState state = new WineStorageDeviceTemperatureState(getDeviceState());
|
||||
|
||||
// when:
|
||||
Integer temperature = state.getTopTemperature().get();
|
||||
Integer targetTemperature = state.getTopTargetTemperature().get();
|
||||
|
||||
// then:
|
||||
assertEquals(TEMPERATURE_0, temperature);
|
||||
assertEquals(TARGET_TEMPERATURE_0, targetTemperature);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetTopTemperaturesForOtherDeviceWithTwoTemperatures() {
|
||||
// given:
|
||||
setUpDeviceStateMock(2);
|
||||
when(getDeviceState().getRawType()).thenReturn(DeviceType.OVEN);
|
||||
WineStorageDeviceTemperatureState state = new WineStorageDeviceTemperatureState(getDeviceState());
|
||||
|
||||
// when:
|
||||
Optional<Integer> temperature = state.getTopTemperature();
|
||||
Optional<Integer> targetTemperature = state.getTopTargetTemperature();
|
||||
|
||||
// then:
|
||||
assertFalse(temperature.isPresent());
|
||||
assertFalse(targetTemperature.isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetTopTemperaturesWhenNoTemperaturesAreAvailable() {
|
||||
// given:
|
||||
setUpDeviceStateMock(0);
|
||||
when(getDeviceState().getRawType()).thenReturn(DeviceType.WINE_CABINET);
|
||||
WineStorageDeviceTemperatureState state = new WineStorageDeviceTemperatureState(getDeviceState());
|
||||
|
||||
// when:
|
||||
Optional<Integer> temperature = state.getTopTemperature();
|
||||
Optional<Integer> targetTemperature = state.getTopTargetTemperature();
|
||||
|
||||
// then:
|
||||
assertFalse(temperature.isPresent());
|
||||
assertFalse(targetTemperature.isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetMiddleTemperaturesForWineCabinetWithThreeCompartments() {
|
||||
// given:
|
||||
setUpDeviceStateMock(3);
|
||||
when(getDeviceState().getRawType()).thenReturn(DeviceType.WINE_CABINET);
|
||||
WineStorageDeviceTemperatureState state = new WineStorageDeviceTemperatureState(getDeviceState());
|
||||
|
||||
// when:
|
||||
Integer temperature = state.getMiddleTemperature().get();
|
||||
Integer targetTemperature = state.getMiddleTargetTemperature().get();
|
||||
|
||||
// then:
|
||||
assertEquals(TEMPERATURE_1, temperature);
|
||||
assertEquals(TARGET_TEMPERATURE_1, targetTemperature);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetMiddleTemperaturesForWineCabinetWithTwoCompartments() {
|
||||
// given:
|
||||
setUpDeviceStateMock(2);
|
||||
when(getDeviceState().getRawType()).thenReturn(DeviceType.WINE_CABINET);
|
||||
WineStorageDeviceTemperatureState state = new WineStorageDeviceTemperatureState(getDeviceState());
|
||||
|
||||
// when:
|
||||
Optional<Integer> temperature = state.getMiddleTemperature();
|
||||
Optional<Integer> targetTemperature = state.getMiddleTargetTemperature();
|
||||
|
||||
// then:
|
||||
assertFalse(temperature.isPresent());
|
||||
assertFalse(targetTemperature.isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetMiddleTemperaturesForWineCabinetWithOneCompartment() {
|
||||
// given:
|
||||
setUpDeviceStateMock(1);
|
||||
when(getDeviceState().getRawType()).thenReturn(DeviceType.WINE_CABINET);
|
||||
WineStorageDeviceTemperatureState state = new WineStorageDeviceTemperatureState(getDeviceState());
|
||||
|
||||
// when:
|
||||
Optional<Integer> temperature = state.getMiddleTemperature();
|
||||
Optional<Integer> targetTemperature = state.getMiddleTargetTemperature();
|
||||
|
||||
// then:
|
||||
assertFalse(temperature.isPresent());
|
||||
assertFalse(targetTemperature.isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetMiddleTemperaturesForWineCabinetFreezerCombination() {
|
||||
// given:
|
||||
setUpDeviceStateMock(2);
|
||||
when(getDeviceState().getRawType()).thenReturn(DeviceType.WINE_CABINET_FREEZER_COMBINATION);
|
||||
WineStorageDeviceTemperatureState state = new WineStorageDeviceTemperatureState(getDeviceState());
|
||||
|
||||
// when:
|
||||
Optional<Integer> temperature = state.getMiddleTemperature();
|
||||
Optional<Integer> targetTemperature = state.getMiddleTargetTemperature();
|
||||
|
||||
// then:
|
||||
assertFalse(temperature.isPresent());
|
||||
assertFalse(targetTemperature.isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetMiddleTemperaturesForOtherDeviceWithTwoTemperatures() {
|
||||
// given:
|
||||
setUpDeviceStateMock(2);
|
||||
when(getDeviceState().getRawType()).thenReturn(DeviceType.OVEN);
|
||||
WineStorageDeviceTemperatureState state = new WineStorageDeviceTemperatureState(getDeviceState());
|
||||
|
||||
// when:
|
||||
Optional<Integer> temperature = state.getMiddleTemperature();
|
||||
Optional<Integer> targetTemperature = state.getMiddleTargetTemperature();
|
||||
|
||||
// then:
|
||||
assertFalse(temperature.isPresent());
|
||||
assertFalse(targetTemperature.isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetMiddleTemperaturesWhenNoTemperaturesAreAvailable() {
|
||||
// given:
|
||||
setUpDeviceStateMock(0);
|
||||
when(getDeviceState().getRawType()).thenReturn(DeviceType.WINE_CABINET);
|
||||
WineStorageDeviceTemperatureState state = new WineStorageDeviceTemperatureState(getDeviceState());
|
||||
|
||||
// when:
|
||||
Optional<Integer> temperature = state.getMiddleTemperature();
|
||||
Optional<Integer> targetTemperature = state.getMiddleTargetTemperature();
|
||||
|
||||
// then:
|
||||
assertFalse(temperature.isPresent());
|
||||
assertFalse(targetTemperature.isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBottomTemperaturesForWineCabinetWithThreeCompartments() {
|
||||
// given:
|
||||
setUpDeviceStateMock(3);
|
||||
when(getDeviceState().getRawType()).thenReturn(DeviceType.WINE_CABINET);
|
||||
WineStorageDeviceTemperatureState state = new WineStorageDeviceTemperatureState(getDeviceState());
|
||||
|
||||
// when:
|
||||
Integer temperature = state.getBottomTemperature().get();
|
||||
Integer targetTemperature = state.getBottomTargetTemperature().get();
|
||||
|
||||
// then:
|
||||
assertEquals(TEMPERATURE_2, temperature);
|
||||
assertEquals(TARGET_TEMPERATURE_2, targetTemperature);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBottomTemperaturesForWineCabinetWithTwoCompartments() {
|
||||
// given:
|
||||
setUpDeviceStateMock(2);
|
||||
when(getDeviceState().getRawType()).thenReturn(DeviceType.WINE_CABINET);
|
||||
WineStorageDeviceTemperatureState state = new WineStorageDeviceTemperatureState(getDeviceState());
|
||||
|
||||
// when:
|
||||
Integer temperature = state.getBottomTemperature().get();
|
||||
Integer targetTemperature = state.getBottomTargetTemperature().get();
|
||||
|
||||
// then:
|
||||
assertEquals(TEMPERATURE_1, temperature);
|
||||
assertEquals(TARGET_TEMPERATURE_1, targetTemperature);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBottomTemperaturesForWineCabinetWithOneCompartment() {
|
||||
// given:
|
||||
setUpDeviceStateMock(1);
|
||||
when(getDeviceState().getRawType()).thenReturn(DeviceType.WINE_CABINET);
|
||||
WineStorageDeviceTemperatureState state = new WineStorageDeviceTemperatureState(getDeviceState());
|
||||
|
||||
// when:
|
||||
Optional<Integer> temperature = state.getBottomTemperature();
|
||||
Optional<Integer> targetTemperature = state.getBottomTargetTemperature();
|
||||
|
||||
// then:
|
||||
assertFalse(temperature.isPresent());
|
||||
assertFalse(targetTemperature.isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBottomTemperaturesForWineCabinetFreezerCombination() {
|
||||
// given:
|
||||
setUpDeviceStateMock(2);
|
||||
when(getDeviceState().getRawType()).thenReturn(DeviceType.WINE_CABINET_FREEZER_COMBINATION);
|
||||
WineStorageDeviceTemperatureState state = new WineStorageDeviceTemperatureState(getDeviceState());
|
||||
|
||||
// when:
|
||||
Integer temperature = state.getBottomTemperature().get();
|
||||
Integer targetTemperature = state.getBottomTargetTemperature().get();
|
||||
|
||||
// then:
|
||||
assertEquals(TEMPERATURE_1, temperature);
|
||||
assertEquals(TARGET_TEMPERATURE_1, targetTemperature);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBottomTemperaturesForOtherDeviceWithTwoTemperatures() {
|
||||
// given:
|
||||
setUpDeviceStateMock(2);
|
||||
when(getDeviceState().getRawType()).thenReturn(DeviceType.OVEN);
|
||||
WineStorageDeviceTemperatureState state = new WineStorageDeviceTemperatureState(getDeviceState());
|
||||
|
||||
// when:
|
||||
Optional<Integer> temperature = state.getBottomTemperature();
|
||||
Optional<Integer> targetTemperature = state.getBottomTargetTemperature();
|
||||
|
||||
// then:
|
||||
assertFalse(temperature.isPresent());
|
||||
assertFalse(targetTemperature.isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBottomTemperaturesWhenNoTemperaturesAreAvailable() {
|
||||
// given:
|
||||
setUpDeviceStateMock(0);
|
||||
when(getDeviceState().getRawType()).thenReturn(DeviceType.WINE_CABINET);
|
||||
WineStorageDeviceTemperatureState state = new WineStorageDeviceTemperatureState(getDeviceState());
|
||||
|
||||
// when:
|
||||
Optional<Integer> temperature = state.getBottomTemperature();
|
||||
Optional<Integer> targetTemperature = state.getBottomTargetTemperature();
|
||||
|
||||
// then:
|
||||
assertFalse(temperature.isPresent());
|
||||
assertFalse(targetTemperature.isPresent());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/**
|
||||
* 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.webservice.api.json;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
/**
|
||||
* @author Björn Lange - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ActionsTest {
|
||||
@Test
|
||||
public void testNullProcessActionInJsonIsConvertedToEmptyList() throws IOException {
|
||||
// given:
|
||||
String json = "{ \"processAction\": null, \"light\": [1], \"startTime\": [ [0, 0],[23,59] ] }";
|
||||
|
||||
// when:
|
||||
Actions actions = new Gson().fromJson(json, Actions.class);
|
||||
|
||||
// then:
|
||||
assertNotNull(actions.getProcessAction());
|
||||
assertTrue(actions.getProcessAction().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNullLightInJsonIsConvertedToEmptyList() throws IOException {
|
||||
// given:
|
||||
String json = "{ \"processAction\": [1], \"light\": null, \"startTime\": [ [0, 0],[23,59] ] }";
|
||||
|
||||
// when:
|
||||
Actions actions = new Gson().fromJson(json, Actions.class);
|
||||
|
||||
// then:
|
||||
assertNotNull(actions.getLight());
|
||||
assertTrue(actions.getLight().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNullStartTimeInJsonIsReturnedAsNull() throws IOException {
|
||||
// given:
|
||||
String json = "{ \"processAction\": [1], \"light\": [1], \"startTime\": null }";
|
||||
|
||||
// when:
|
||||
Actions actions = new Gson().fromJson(json, Actions.class);
|
||||
|
||||
// then:
|
||||
assertFalse(actions.getStartTime().isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIdListIsEmptyWhenProgramIdFieldIsMissing() {
|
||||
// given:
|
||||
String json = "{ \"processAction\": [1] }";
|
||||
|
||||
// when:
|
||||
Actions actions = new Gson().fromJson(json, Actions.class);
|
||||
|
||||
// then:
|
||||
assertTrue(actions.getProgramId().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIdListIsEmptyWhenProgramIdFieldIsNull() {
|
||||
// given:
|
||||
String json = "{ \"programId\": null }";
|
||||
|
||||
// when:
|
||||
Actions actions = new Gson().fromJson(json, Actions.class);
|
||||
|
||||
// then:
|
||||
assertTrue(actions.getProgramId().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIdListContainsEntriesWhenProgramIdFieldIsPresent() {
|
||||
// given:
|
||||
String json = "{ \"programId\": [1,2,3,4] }";
|
||||
|
||||
// when:
|
||||
Actions actions = new Gson().fromJson(json, Actions.class);
|
||||
|
||||
// then:
|
||||
assertEquals(Arrays.asList(1, 2, 3, 4), actions.getProgramId());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,290 @@
|
||||
/**
|
||||
* 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.webservice.api.json;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.openhab.binding.mielecloud.internal.util.ResourceUtil.getResourceAsString;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* @author Björn Lange - Initial contribution
|
||||
* @author Benjamin Bolte - Add plate step
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class DeviceCollectionTest {
|
||||
@Test
|
||||
public void testCreateDeviceCollection() throws IOException {
|
||||
// given:
|
||||
String json = getResourceAsString(
|
||||
"/org/openhab/binding/mielecloud/internal/webservice/api/json/deviceCollection.json");
|
||||
|
||||
// when:
|
||||
DeviceCollection collection = DeviceCollection.fromJson(json);
|
||||
|
||||
// then:
|
||||
assertEquals(1, collection.getDeviceIdentifiers().size());
|
||||
Device device = collection.getDevice(collection.getDeviceIdentifiers().iterator().next());
|
||||
|
||||
Ident ident = device.getIdent().get();
|
||||
Type type = ident.getType().get();
|
||||
assertEquals("Devicetype", type.getKeyLocalized().get());
|
||||
assertEquals(DeviceType.HOOD, type.getValueRaw());
|
||||
assertEquals("Ventilation Hood", type.getValueLocalized().get());
|
||||
|
||||
assertEquals("My Hood", ident.getDeviceName().get());
|
||||
|
||||
DeviceIdentLabel deviceIdentLabel = ident.getDeviceIdentLabel().get();
|
||||
assertEquals("000124430017", deviceIdentLabel.getFabNumber().get());
|
||||
assertEquals("00", deviceIdentLabel.getFabIndex().get());
|
||||
assertEquals("DA-6996", deviceIdentLabel.getTechType().get());
|
||||
assertEquals("10101010", deviceIdentLabel.getMatNumber().get());
|
||||
assertEquals(Arrays.asList("4164", "20380", "25226"), deviceIdentLabel.getSwids());
|
||||
|
||||
XkmIdentLabel xkmIdentLabel = ident.getXkmIdentLabel().get();
|
||||
assertEquals("EK039W", xkmIdentLabel.getTechType().get());
|
||||
assertEquals("02.31", xkmIdentLabel.getReleaseVersion().get());
|
||||
|
||||
State state = device.getState().get();
|
||||
Status status = state.getStatus().get();
|
||||
assertEquals(Integer.valueOf(StateType.RUNNING.getCode()), status.getValueRaw().get());
|
||||
assertEquals("In use", status.getValueLocalized().get());
|
||||
assertEquals("State", status.getKeyLocalized().get());
|
||||
|
||||
ProgramType programType = state.getProgramType().get();
|
||||
assertEquals(Integer.valueOf(0), programType.getValueRaw().get());
|
||||
assertEquals("", programType.getValueLocalized().get());
|
||||
assertEquals("Programme", programType.getKeyLocalized().get());
|
||||
|
||||
ProgramPhase programPhase = state.getProgramPhase().get();
|
||||
assertEquals(Integer.valueOf(4609), programPhase.getValueRaw().get());
|
||||
assertEquals("", programPhase.getValueLocalized().get());
|
||||
assertEquals("Phase", programPhase.getKeyLocalized().get());
|
||||
|
||||
assertEquals(Arrays.asList(0, 0), state.getRemainingTime().get());
|
||||
assertEquals(Arrays.asList(0, 0), state.getStartTime().get());
|
||||
|
||||
assertEquals(1, state.getTargetTemperature().size());
|
||||
Temperature targetTemperature = state.getTargetTemperature().get(0);
|
||||
assertNotNull(targetTemperature);
|
||||
assertEquals(Integer.valueOf(-32768), targetTemperature.getValueRaw().get());
|
||||
assertFalse(targetTemperature.getValueLocalized().isPresent());
|
||||
assertEquals("Celsius", targetTemperature.getUnit().get());
|
||||
|
||||
assertEquals(3, state.getTemperature().size());
|
||||
Temperature temperature0 = state.getTemperature().get(0);
|
||||
assertNotNull(temperature0);
|
||||
assertEquals(Integer.valueOf(-32768), temperature0.getValueRaw().get());
|
||||
assertFalse(temperature0.getValueLocalized().isPresent());
|
||||
assertEquals("Celsius", temperature0.getUnit().get());
|
||||
Temperature temperature1 = state.getTemperature().get(1);
|
||||
assertNotNull(temperature1);
|
||||
assertEquals(Integer.valueOf(-32768), temperature1.getValueRaw().get());
|
||||
assertFalse(temperature1.getValueLocalized().isPresent());
|
||||
assertEquals("Celsius", temperature1.getUnit().get());
|
||||
Temperature temperature2 = state.getTemperature().get(2);
|
||||
assertNotNull(temperature2);
|
||||
assertEquals(Integer.valueOf(-32768), temperature2.getValueRaw().get());
|
||||
assertFalse(temperature2.getValueLocalized().isPresent());
|
||||
assertEquals("Celsius", temperature2.getUnit().get());
|
||||
|
||||
assertEquals(false, state.getSignalInfo().get());
|
||||
assertEquals(false, state.getSignalFailure().get());
|
||||
assertEquals(false, state.getSignalDoor().get());
|
||||
|
||||
RemoteEnable remoteEnable = state.getRemoteEnable().get();
|
||||
assertEquals(false, remoteEnable.getFullRemoteControl().get());
|
||||
assertEquals(false, remoteEnable.getSmartGrid().get());
|
||||
|
||||
assertEquals(Light.ENABLE, state.getLight());
|
||||
assertEquals(new ArrayList<Object>(), state.getElapsedTime().get());
|
||||
|
||||
SpinningSpeed spinningSpeed = state.getSpinningSpeed().get();
|
||||
assertEquals(Integer.valueOf(1200), spinningSpeed.getValueRaw().get());
|
||||
assertEquals("1200", spinningSpeed.getValueLocalized().get());
|
||||
assertEquals("rpm", spinningSpeed.getUnit().get());
|
||||
|
||||
DryingStep dryingStep = state.getDryingStep().get();
|
||||
assertFalse(dryingStep.getValueRaw().isPresent());
|
||||
assertEquals("", dryingStep.getValueLocalized().get());
|
||||
assertEquals("Drying level", dryingStep.getKeyLocalized().get());
|
||||
|
||||
VentilationStep ventilationStep = state.getVentilationStep().get();
|
||||
assertEquals(Integer.valueOf(2), ventilationStep.getValueRaw().get());
|
||||
assertEquals("2", ventilationStep.getValueLocalized().get());
|
||||
assertEquals("Power Level", ventilationStep.getKeyLocalized().get());
|
||||
|
||||
List<PlateStep> plateStep = state.getPlateStep();
|
||||
assertEquals(4, plateStep.size());
|
||||
assertEquals(Integer.valueOf(0), plateStep.get(0).getValueRaw().get());
|
||||
assertEquals("0", plateStep.get(0).getValueLocalized().get());
|
||||
assertEquals("Plate Step", plateStep.get(0).getKeyLocalized().get());
|
||||
assertEquals(Integer.valueOf(1), plateStep.get(1).getValueRaw().get());
|
||||
assertEquals("1", plateStep.get(1).getValueLocalized().get());
|
||||
assertEquals("Plate Step", plateStep.get(1).getKeyLocalized().get());
|
||||
assertEquals(Integer.valueOf(2), plateStep.get(2).getValueRaw().get());
|
||||
assertEquals("1.", plateStep.get(2).getValueLocalized().get());
|
||||
assertEquals("Plate Step", plateStep.get(2).getKeyLocalized().get());
|
||||
assertEquals(Integer.valueOf(3), plateStep.get(3).getValueRaw().get());
|
||||
assertEquals("2", plateStep.get(3).getValueLocalized().get());
|
||||
assertEquals("Plate Step", plateStep.get(3).getKeyLocalized().get());
|
||||
|
||||
assertEquals(Integer.valueOf(20), state.getBatteryLevel().get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateDeviceCollectionFromInvalidJsonThrowsMieleSyntaxException() throws IOException {
|
||||
// given:
|
||||
String invalidJson = getResourceAsString(
|
||||
"/org/openhab/binding/mielecloud/internal/webservice/api/json/invalidDeviceCollection.json");
|
||||
|
||||
// when:
|
||||
assertThrows(MieleSyntaxException.class, () -> {
|
||||
DeviceCollection.fromJson(invalidJson);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateDeviceCollectionWithLargeProgramID() throws IOException {
|
||||
// given:
|
||||
String json = getResourceAsString(
|
||||
"/org/openhab/binding/mielecloud/internal/webservice/api/json/deviceCollectionWithLargeProgramID.json");
|
||||
|
||||
// when:
|
||||
DeviceCollection collection = DeviceCollection.fromJson(json);
|
||||
|
||||
// then:
|
||||
assertEquals(1, collection.getDeviceIdentifiers().size());
|
||||
Device device = collection.getDevice(collection.getDeviceIdentifiers().iterator().next());
|
||||
|
||||
Ident ident = device.getIdent().get();
|
||||
Type type = ident.getType().get();
|
||||
assertEquals("Devicetype", type.getKeyLocalized().get());
|
||||
assertEquals(DeviceType.UNKNOWN, type.getValueRaw());
|
||||
assertEquals("", type.getValueLocalized().get());
|
||||
|
||||
assertEquals("Some Devicename", ident.getDeviceName().get());
|
||||
|
||||
DeviceIdentLabel deviceIdentLabel = ident.getDeviceIdentLabel().get();
|
||||
assertEquals("", deviceIdentLabel.getFabNumber().get());
|
||||
assertEquals("", deviceIdentLabel.getFabIndex().get());
|
||||
assertEquals("", deviceIdentLabel.getTechType().get());
|
||||
assertEquals("", deviceIdentLabel.getMatNumber().get());
|
||||
assertEquals(Arrays.asList(), deviceIdentLabel.getSwids());
|
||||
|
||||
XkmIdentLabel xkmIdentLabel = ident.getXkmIdentLabel().get();
|
||||
assertEquals("", xkmIdentLabel.getTechType().get());
|
||||
assertEquals("", xkmIdentLabel.getReleaseVersion().get());
|
||||
|
||||
State state = device.getState().get();
|
||||
ProgramId programId = state.getProgramId().get();
|
||||
assertEquals(Long.valueOf(2499805184L), programId.getValueRaw().get());
|
||||
assertEquals("", programId.getValueLocalized().get());
|
||||
assertEquals("Program Id", programId.getKeyLocalized().get());
|
||||
|
||||
Status status = state.getStatus().get();
|
||||
assertEquals(Integer.valueOf(StateType.RUNNING.getCode()), status.getValueRaw().get());
|
||||
assertEquals("In use", status.getValueLocalized().get());
|
||||
assertEquals("State", status.getKeyLocalized().get());
|
||||
|
||||
ProgramType programType = state.getProgramType().get();
|
||||
assertEquals(Integer.valueOf(0), programType.getValueRaw().get());
|
||||
assertEquals("Operation mode", programType.getValueLocalized().get());
|
||||
assertEquals("Program type", programType.getKeyLocalized().get());
|
||||
|
||||
ProgramPhase programPhase = state.getProgramPhase().get();
|
||||
assertEquals(Integer.valueOf(0), programPhase.getValueRaw().get());
|
||||
assertEquals("", programPhase.getValueLocalized().get());
|
||||
assertEquals("Phase", programPhase.getKeyLocalized().get());
|
||||
|
||||
assertEquals(Arrays.asList(0, 0), state.getRemainingTime().get());
|
||||
assertEquals(Arrays.asList(0, 0), state.getStartTime().get());
|
||||
|
||||
assertTrue(state.getTargetTemperature().isEmpty());
|
||||
assertTrue(state.getTemperature().isEmpty());
|
||||
|
||||
assertEquals(false, state.getSignalInfo().get());
|
||||
assertEquals(false, state.getSignalFailure().get());
|
||||
assertEquals(false, state.getSignalDoor().get());
|
||||
|
||||
RemoteEnable remoteEnable = state.getRemoteEnable().get();
|
||||
assertEquals(true, remoteEnable.getFullRemoteControl().get());
|
||||
assertEquals(false, remoteEnable.getSmartGrid().get());
|
||||
|
||||
assertEquals(Light.NOT_SUPPORTED, state.getLight());
|
||||
assertEquals(new ArrayList<Object>(), state.getElapsedTime().get());
|
||||
|
||||
DryingStep dryingStep = state.getDryingStep().get();
|
||||
assertFalse(dryingStep.getValueRaw().isPresent());
|
||||
assertEquals("", dryingStep.getValueLocalized().get());
|
||||
assertEquals("Drying level", dryingStep.getKeyLocalized().get());
|
||||
|
||||
VentilationStep ventilationStep = state.getVentilationStep().get();
|
||||
assertFalse(ventilationStep.getValueRaw().isPresent());
|
||||
assertEquals("", ventilationStep.getValueLocalized().get());
|
||||
assertEquals("Power Level", ventilationStep.getKeyLocalized().get());
|
||||
|
||||
List<PlateStep> plateStep = state.getPlateStep();
|
||||
assertEquals(0, plateStep.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateDeviceCollectionWithSpinningSpeedObject() throws IOException {
|
||||
// given:
|
||||
String json = getResourceAsString(
|
||||
"/org/openhab/binding/mielecloud/internal/webservice/api/json/deviceCollectionWithSpinningSpeedObject.json");
|
||||
|
||||
// when:
|
||||
DeviceCollection collection = DeviceCollection.fromJson(json);
|
||||
|
||||
// then:
|
||||
assertEquals(1, collection.getDeviceIdentifiers().size());
|
||||
Device device = collection.getDevice(collection.getDeviceIdentifiers().iterator().next());
|
||||
|
||||
State state = device.getState().get();
|
||||
SpinningSpeed spinningSpeed = state.getSpinningSpeed().get();
|
||||
assertNotNull(spinningSpeed);
|
||||
assertEquals(Integer.valueOf(1600), spinningSpeed.getValueRaw().get());
|
||||
assertEquals("1600", spinningSpeed.getValueLocalized().get());
|
||||
assertEquals("U/min", spinningSpeed.getUnit().get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateDeviceCollectionWithFloatingPointTemperature() throws IOException {
|
||||
// given:
|
||||
String json = getResourceAsString(
|
||||
"/org/openhab/binding/mielecloud/internal/webservice/api/json/deviceCollectionWithFloatingPointTargetTemperature.json");
|
||||
|
||||
// when:
|
||||
DeviceCollection collection = DeviceCollection.fromJson(json);
|
||||
|
||||
// then:
|
||||
assertEquals(1, collection.getDeviceIdentifiers().size());
|
||||
Device device = collection.getDevice(collection.getDeviceIdentifiers().iterator().next());
|
||||
|
||||
State state = device.getState().get();
|
||||
List<Temperature> targetTemperatures = state.getTargetTemperature();
|
||||
assertEquals(1, targetTemperatures.size());
|
||||
|
||||
Temperature targetTemperature = targetTemperatures.get(0);
|
||||
assertEquals(Integer.valueOf(80), targetTemperature.getValueRaw().get());
|
||||
assertEquals(Integer.valueOf(0), targetTemperature.getValueLocalized().get());
|
||||
assertEquals("Celsius", targetTemperature.getUnit().get());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* 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.webservice.api.json;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
/**
|
||||
* @author Björn Lange - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class DeviceIdentLabelTest {
|
||||
@Test
|
||||
public void testNullSwidsInJsonAreConvertedToEmptyList() throws IOException {
|
||||
// given:
|
||||
String json = "{ \"swids\": null }";
|
||||
|
||||
// when:
|
||||
DeviceIdentLabel deviceIdentLabel = new Gson().fromJson(json, DeviceIdentLabel.class);
|
||||
|
||||
// then:
|
||||
assertNotNull(deviceIdentLabel.getSwids());
|
||||
assertTrue(deviceIdentLabel.getSwids().isEmpty());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* 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.webservice.api.json;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* @author Björn Lange - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ErrorMessageTest {
|
||||
|
||||
@Test
|
||||
public void testErrorMessageCanBeCreated() {
|
||||
// given:
|
||||
String json = "{\"message\": \"Unauthorized\"}";
|
||||
|
||||
// when:
|
||||
ErrorMessage errorMessage = ErrorMessage.fromJson(json);
|
||||
|
||||
// then:
|
||||
assertEquals("Unauthorized", errorMessage.getMessage().get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testErrorMessageCreationThrowsMieleSyntaxExceptionWhenJsonIsInvalid() {
|
||||
// given:
|
||||
String json = "\"message\": \"Unauthorized}";
|
||||
|
||||
// when:
|
||||
assertThrows(MieleSyntaxException.class, () -> {
|
||||
ErrorMessage.fromJson(json);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* 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.webservice.api.json;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* @author Björn Lange - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class LightTest {
|
||||
@Test
|
||||
public void testFromNullId() {
|
||||
// when:
|
||||
Light light = Light.fromId(null);
|
||||
|
||||
// then:
|
||||
assertEquals(Light.UNKNOWN, light);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFromNotSupportedId() {
|
||||
// when:
|
||||
Light light = Light.fromId(0);
|
||||
|
||||
// then:
|
||||
assertEquals(Light.NOT_SUPPORTED, light);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFromNotSupportedAlternativeId() {
|
||||
// when:
|
||||
Light light = Light.fromId(255);
|
||||
|
||||
// then:
|
||||
assertEquals(Light.NOT_SUPPORTED, light);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFromEnabledId() {
|
||||
// when:
|
||||
Light light = Light.fromId(1);
|
||||
|
||||
// then:
|
||||
assertEquals(Light.ENABLE, light);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFromDisabledId() {
|
||||
// when:
|
||||
Light light = Light.fromId(2);
|
||||
|
||||
// then:
|
||||
assertEquals(Light.DISABLE, light);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* 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.webservice.api.json;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
/**
|
||||
* @author Björn Lange - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class StateTest {
|
||||
@Test
|
||||
public void testNullRemainingTimeInJsonCausesRemainingTimeListToBeNull() throws IOException {
|
||||
// given:
|
||||
String json = "{ \"remainingTime\": null, \"startTime\": [0, 0], \"targetTemperature\": [{}], \"temperature\": [{}], \"elapsedTime\": [0, 0] }";
|
||||
|
||||
// when:
|
||||
State state = new Gson().fromJson(json, State.class);
|
||||
|
||||
// then:
|
||||
assertFalse(state.getRemainingTime().isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNullStartTimeInJsonCausesStartTimeListToBeNull() throws IOException {
|
||||
// given:
|
||||
String json = "{ \"remainingTime\": [0, 0], \"startTime\": null, \"targetTemperature\": [{}], \"temperature\": [{}], \"elapsedTime\": [0, 0] }";
|
||||
|
||||
// when:
|
||||
State state = new Gson().fromJson(json, State.class);
|
||||
|
||||
// then:
|
||||
assertFalse(state.getStartTime().isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNullElapsedTimeInJsonCausesElapsedTimeListToBeNull() throws IOException {
|
||||
// given:
|
||||
String json = "{ \"remainingTime\": [0, 0], \"startTime\": [0, 0], \"targetTemperature\": [{}], \"temperature\": [{}], \"elapsedTime\": null }";
|
||||
|
||||
// when:
|
||||
State state = new Gson().fromJson(json, State.class);
|
||||
|
||||
// then:
|
||||
assertFalse(state.getElapsedTime().isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNullTargetTemperatureInJsonIsConvertedToEmptyList() throws IOException {
|
||||
// given:
|
||||
String json = "{ \"remainingTime\": [0, 0], \"startTime\": [0, 0], \"targetTemperature\": null, \"temperature\": [{}], \"elapsedTime\": [0, 0] }";
|
||||
|
||||
// when:
|
||||
State state = new Gson().fromJson(json, State.class);
|
||||
|
||||
// then:
|
||||
assertNotNull(state.getTargetTemperature());
|
||||
assertTrue(state.getTargetTemperature().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNullTemperatureInJsonIsConvertedToEmptyList() throws IOException {
|
||||
// given:
|
||||
String json = "{ \"remainingTime\": [0, 0], \"startTime\": [0, 0], \"targetTemperature\": [{}], \"temperature\": null, \"elapsedTime\": [0, 0] }";
|
||||
|
||||
// when:
|
||||
State state = new Gson().fromJson(json, State.class);
|
||||
|
||||
// then:
|
||||
assertNotNull(state.getTemperature());
|
||||
assertTrue(state.getTemperature().isEmpty());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* 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.webservice.api.json;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
/**
|
||||
* @author Björn Lange - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class StatusTest {
|
||||
@Test
|
||||
public void testParseStatusWithUnknownRawValue() {
|
||||
// given:
|
||||
String json = "{ \"key_localized\": \"State\", \"value_raw\": 99, \"value_localized\": \"Booting\" }";
|
||||
|
||||
// when:
|
||||
Status status = new Gson().fromJson(json, Status.class);
|
||||
|
||||
// then:
|
||||
assertNotNull(status);
|
||||
assertEquals("State", status.getKeyLocalized().get());
|
||||
assertEquals(Integer.valueOf(99), status.getValueRaw().get());
|
||||
assertEquals("Booting", status.getValueLocalized().get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseStatusWithKnownRawValue() {
|
||||
// given:
|
||||
String json = "{ \"key_localized\": \"State\", \"value_raw\": 1, \"value_localized\": \"Off\" }";
|
||||
|
||||
// when:
|
||||
Status status = new Gson().fromJson(json, Status.class);
|
||||
|
||||
// then:
|
||||
assertNotNull(status);
|
||||
assertEquals("State", status.getKeyLocalized().get());
|
||||
assertEquals(Integer.valueOf(StateType.OFF.getCode()), status.getValueRaw().get());
|
||||
assertEquals("Off", status.getValueLocalized().get());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* 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.webservice.api.json;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
/**
|
||||
* @author Björn Lange - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class TypeTest {
|
||||
@Test
|
||||
public void testParseTypeWithUnknownRawValue() {
|
||||
// given:
|
||||
String json = "{ \"key_localized\": \"Devicetype\", \"value_raw\": 99, \"value_localized\": \"Car Vaccuum Robot\" }";
|
||||
|
||||
// when:
|
||||
Type type = new Gson().fromJson(json, Type.class);
|
||||
|
||||
// then:
|
||||
assertNotNull(type);
|
||||
assertEquals("Devicetype", type.getKeyLocalized().get());
|
||||
assertEquals(DeviceType.UNKNOWN, type.getValueRaw());
|
||||
assertEquals("Car Vaccuum Robot", type.getValueLocalized().get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseTypeWithKnownRawValue() {
|
||||
// given:
|
||||
String json = "{ \"key_localized\": \"Devicetype\", \"value_raw\": 1, \"value_localized\": \"Washing Machine\" }";
|
||||
|
||||
// when:
|
||||
Type type = new Gson().fromJson(json, Type.class);
|
||||
|
||||
// then:
|
||||
assertNotNull(type);
|
||||
assertEquals("Devicetype", type.getKeyLocalized().get());
|
||||
assertEquals(DeviceType.WASHING_MACHINE, type.getValueRaw());
|
||||
assertEquals("Washing Machine", type.getValueLocalized().get());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
/**
|
||||
* 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.webservice.exception;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* @author Björn Lange - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class TooManyRequestsExceptionTest {
|
||||
@Test
|
||||
public void testHasRetryAfterHintReturnsFalseWhenNoRetryAfterWasPassedToConstructor() {
|
||||
// given:
|
||||
TooManyRequestsException exception = new TooManyRequestsException("", null);
|
||||
|
||||
// when:
|
||||
boolean result = exception.hasRetryAfterHint();
|
||||
|
||||
// then:
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHasRetryAfterHintReturnsTrueWhenRetryAfterWasPassedToConstructor() {
|
||||
// given:
|
||||
TooManyRequestsException exception = new TooManyRequestsException("", "25");
|
||||
|
||||
// when:
|
||||
boolean result = exception.hasRetryAfterHint();
|
||||
|
||||
// then:
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSecondsUntilRetryReturnsMinusOneWhenNoRetryAfterHintIsPresent() {
|
||||
// given:
|
||||
TooManyRequestsException exception = new TooManyRequestsException("", null);
|
||||
|
||||
// when:
|
||||
long result = exception.getSecondsUntilRetry();
|
||||
|
||||
// then:
|
||||
assertEquals(-1L, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSecondsUntilRetryParsesNumber() {
|
||||
// given:
|
||||
TooManyRequestsException exception = new TooManyRequestsException("", "30");
|
||||
|
||||
// when:
|
||||
long result = exception.getSecondsUntilRetry();
|
||||
|
||||
// then:
|
||||
assertEquals(30L, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSecondsUntilRetryParsesDate() {
|
||||
// given:
|
||||
TooManyRequestsException exception = new TooManyRequestsException("", "Thu, 12 Jan 5015 15:02:30 GMT");
|
||||
|
||||
// when:
|
||||
long result = exception.getSecondsUntilRetry();
|
||||
|
||||
// then:
|
||||
assertNotEquals(0L, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSecondsUntilRetryParsesDateFromThePast() {
|
||||
// given:
|
||||
TooManyRequestsException exception = new TooManyRequestsException("", "Wed, 21 Oct 2015 07:28:00 GMT");
|
||||
|
||||
// when:
|
||||
long result = exception.getSecondsUntilRetry();
|
||||
|
||||
// then:
|
||||
assertEquals(0L, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSecondsUntilRetryReturnsMinusOneWhenDateCannotBeParsed() {
|
||||
// given:
|
||||
TooManyRequestsException exception = new TooManyRequestsException("", "50 Minutes");
|
||||
|
||||
// when:
|
||||
long result = exception.getSecondsUntilRetry();
|
||||
|
||||
// then:
|
||||
assertEquals(-1L, result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
/**
|
||||
* 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.webservice.language;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* @author Björn Lange - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class CombiningLanguageProviderTest {
|
||||
private static final Optional<String> PRIORITIZED_LANGUAGE = Optional.of("de");
|
||||
private static final Optional<String> FALLBACK_LANGUAGE = Optional.of("en");
|
||||
|
||||
private static final LanguageProvider PRIORITIZED_PROVIDER = new LanguageProvider() {
|
||||
@Override
|
||||
public Optional<String> getLanguage() {
|
||||
return PRIORITIZED_LANGUAGE;
|
||||
}
|
||||
};
|
||||
|
||||
private static final LanguageProvider FALLBACK_PROVIDER = new LanguageProvider() {
|
||||
@Override
|
||||
public Optional<String> getLanguage() {
|
||||
return FALLBACK_LANGUAGE;
|
||||
}
|
||||
};
|
||||
|
||||
private static final LanguageProvider NULL_PROVIDER = new LanguageProvider() {
|
||||
@Override
|
||||
public Optional<String> getLanguage() {
|
||||
return Optional.empty();
|
||||
}
|
||||
};
|
||||
|
||||
@Test
|
||||
public void testPrioritizedLanguageProviderIsUsed() {
|
||||
// given:
|
||||
LanguageProvider provider = new CombiningLanguageProvider(PRIORITIZED_PROVIDER, FALLBACK_PROVIDER);
|
||||
|
||||
// when:
|
||||
Optional<String> language = provider.getLanguage();
|
||||
|
||||
// then:
|
||||
assertEquals(PRIORITIZED_LANGUAGE, language);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFallbackProviderIsUsedWhenPrioritizedProviderIsNull() {
|
||||
// given:
|
||||
LanguageProvider provider = new CombiningLanguageProvider(null, FALLBACK_PROVIDER);
|
||||
|
||||
// when:
|
||||
Optional<String> language = provider.getLanguage();
|
||||
|
||||
// then:
|
||||
assertEquals(FALLBACK_LANGUAGE, language);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFallbackProviderIsUsedWhenPrioritizedProviderProvidesNull() {
|
||||
// given:
|
||||
LanguageProvider provider = new CombiningLanguageProvider(NULL_PROVIDER, FALLBACK_PROVIDER);
|
||||
|
||||
// when:
|
||||
Optional<String> language = provider.getLanguage();
|
||||
|
||||
// then:
|
||||
assertEquals(FALLBACK_LANGUAGE, language);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProvidesNullWhenBothProvidersAreNull() {
|
||||
// given:
|
||||
LanguageProvider provider = new CombiningLanguageProvider(null, null);
|
||||
|
||||
// when:
|
||||
Optional<String> language = provider.getLanguage();
|
||||
|
||||
// then:
|
||||
assertFalse(language.isPresent());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* 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.webservice.language;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openhab.core.i18n.LocaleProvider;
|
||||
|
||||
/**
|
||||
* @author Björn Lange - Initial Contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class OpenHabLanguageProviderTest {
|
||||
@Test
|
||||
public void whenTheLocaleIsSetToEnglishThenTheLanguageCodeIsEn() {
|
||||
// given:
|
||||
LocaleProvider localeProvider = mock(LocaleProvider.class);
|
||||
when(localeProvider.getLocale()).thenReturn(Locale.ENGLISH);
|
||||
|
||||
LanguageProvider languageProvider = new OpenHabLanguageProvider(localeProvider);
|
||||
|
||||
// when:
|
||||
Optional<String> language = languageProvider.getLanguage();
|
||||
|
||||
// then:
|
||||
assertEquals(Optional.of("en"), language);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenTheLocaleIsSetToGermanThenTheLanguageCodeIsDe() {
|
||||
// given:
|
||||
LocaleProvider localeProvider = mock(LocaleProvider.class);
|
||||
when(localeProvider.getLocale()).thenReturn(Locale.GERMAN);
|
||||
|
||||
LanguageProvider languageProvider = new OpenHabLanguageProvider(localeProvider);
|
||||
|
||||
// when:
|
||||
Optional<String> language = languageProvider.getLanguage();
|
||||
|
||||
// then:
|
||||
assertEquals(Optional.of("de"), language);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenTheLocaleIsSetToGermanyThenTheLanguageCodeIsDe() {
|
||||
// given:
|
||||
LocaleProvider localeProvider = mock(LocaleProvider.class);
|
||||
when(localeProvider.getLocale()).thenReturn(Locale.GERMANY);
|
||||
|
||||
LanguageProvider languageProvider = new OpenHabLanguageProvider(localeProvider);
|
||||
|
||||
// when:
|
||||
Optional<String> language = languageProvider.getLanguage();
|
||||
|
||||
// then:
|
||||
assertEquals(Optional.of("de"), language);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
/**
|
||||
* 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.webservice.retry;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.openhab.binding.mielecloud.internal.MieleCloudBindingTestConstants;
|
||||
import org.openhab.binding.mielecloud.internal.auth.OAuthException;
|
||||
import org.openhab.binding.mielecloud.internal.auth.OAuthTokenRefresher;
|
||||
import org.openhab.binding.mielecloud.internal.webservice.exception.AuthorizationFailedException;
|
||||
import org.openhab.binding.mielecloud.internal.webservice.exception.MieleWebserviceException;
|
||||
|
||||
/**
|
||||
* @author Björn Lange - Initial contribution
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@NonNullByDefault
|
||||
public class AuthorizationFailedRetryStrategyTest {
|
||||
private static final String TEST_STRING = "Some Test String";
|
||||
|
||||
@Mock
|
||||
@Nullable
|
||||
private Supplier<@Nullable String> operationWithReturnValue;
|
||||
@Mock
|
||||
@Nullable
|
||||
private Consumer<Exception> onException;
|
||||
@Mock
|
||||
@Nullable
|
||||
private Runnable operation;
|
||||
|
||||
private final OAuthTokenRefresher refresher = mock(OAuthTokenRefresher.class);
|
||||
|
||||
private Supplier<@Nullable String> getOperationWithReturnValue() {
|
||||
assertNotNull(operationWithReturnValue);
|
||||
return Objects.requireNonNull(operationWithReturnValue);
|
||||
}
|
||||
|
||||
private Consumer<Exception> getOnException() {
|
||||
assertNotNull(onException);
|
||||
return Objects.requireNonNull(onException);
|
||||
}
|
||||
|
||||
private Runnable getOperation() {
|
||||
assertNotNull(operation);
|
||||
return Objects.requireNonNull(operation);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPerformRetryableOperationWithReturnValueInvokesOperation() {
|
||||
// given:
|
||||
when(getOperationWithReturnValue().get()).thenReturn(TEST_STRING);
|
||||
|
||||
AuthorizationFailedRetryStrategy retryStrategy = new AuthorizationFailedRetryStrategy(refresher,
|
||||
MieleCloudBindingTestConstants.SERVICE_HANDLE);
|
||||
|
||||
// when:
|
||||
String result = retryStrategy.performRetryableOperation(getOperationWithReturnValue(), getOnException());
|
||||
|
||||
// then:
|
||||
assertEquals(TEST_STRING, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPerformRetryableOperationWithReturnValueInvokesRefreshTokenAndRetriesOperation() {
|
||||
// given:
|
||||
when(getOperationWithReturnValue().get()).thenThrow(AuthorizationFailedException.class).thenReturn(TEST_STRING);
|
||||
|
||||
AuthorizationFailedRetryStrategy retryStrategy = new AuthorizationFailedRetryStrategy(refresher,
|
||||
MieleCloudBindingTestConstants.SERVICE_HANDLE);
|
||||
|
||||
// when:
|
||||
String result = retryStrategy.performRetryableOperation(getOperationWithReturnValue(), getOnException());
|
||||
|
||||
// then:
|
||||
assertEquals(TEST_STRING, result);
|
||||
verify(getOnException()).accept(any());
|
||||
verify(refresher).refreshToken(MieleCloudBindingTestConstants.SERVICE_HANDLE);
|
||||
verifyNoMoreInteractions(getOnException(), refresher);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPerformRetryableOperationWithReturnValueThrowsMieleWebserviceExceptionWhenRetryingTheOperationFails() {
|
||||
// given:
|
||||
when(getOperationWithReturnValue().get()).thenThrow(AuthorizationFailedException.class);
|
||||
|
||||
AuthorizationFailedRetryStrategy retryStrategy = new AuthorizationFailedRetryStrategy(refresher,
|
||||
MieleCloudBindingTestConstants.SERVICE_HANDLE);
|
||||
|
||||
assertThrows(MieleWebserviceException.class, () -> {
|
||||
try {
|
||||
// when:
|
||||
retryStrategy.performRetryableOperation(getOperationWithReturnValue(), getOnException());
|
||||
} catch (Exception e) {
|
||||
// then:
|
||||
verify(getOnException()).accept(any());
|
||||
verify(refresher).refreshToken(MieleCloudBindingTestConstants.SERVICE_HANDLE);
|
||||
verifyNoMoreInteractions(getOnException(), refresher);
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPerformRetryableOperationInvokesOperation() {
|
||||
// given:
|
||||
AuthorizationFailedRetryStrategy retryStrategy = new AuthorizationFailedRetryStrategy(refresher,
|
||||
MieleCloudBindingTestConstants.SERVICE_HANDLE);
|
||||
|
||||
// when:
|
||||
retryStrategy.performRetryableOperation(getOperation(), getOnException());
|
||||
|
||||
// then:
|
||||
verify(getOperation()).run();
|
||||
verifyNoMoreInteractions(getOperation());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPerformRetryableOperationInvokesRefreshTokenAndRetriesOperation() {
|
||||
// given:
|
||||
doThrow(AuthorizationFailedException.class).doNothing().when(getOperation()).run();
|
||||
|
||||
AuthorizationFailedRetryStrategy retryStrategy = new AuthorizationFailedRetryStrategy(refresher,
|
||||
MieleCloudBindingTestConstants.SERVICE_HANDLE);
|
||||
|
||||
// when:
|
||||
retryStrategy.performRetryableOperation(getOperation(), getOnException());
|
||||
|
||||
// then:
|
||||
verify(getOnException()).accept(any());
|
||||
verify(refresher).refreshToken(MieleCloudBindingTestConstants.SERVICE_HANDLE);
|
||||
verify(getOperation(), times(2)).run();
|
||||
verifyNoMoreInteractions(getOnException(), refresher, getOperation());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPerformRetryableOperationThrowsMieleWebserviceExceptionWhenRetryingTheOperationFails() {
|
||||
// given:
|
||||
doThrow(AuthorizationFailedException.class).when(getOperation()).run();
|
||||
|
||||
AuthorizationFailedRetryStrategy retryStrategy = new AuthorizationFailedRetryStrategy(refresher,
|
||||
MieleCloudBindingTestConstants.SERVICE_HANDLE);
|
||||
|
||||
assertThrows(MieleWebserviceException.class, () -> {
|
||||
try {
|
||||
// when:
|
||||
retryStrategy.performRetryableOperation(getOperation(), getOnException());
|
||||
} catch (Exception e) {
|
||||
// then:
|
||||
verify(getOnException()).accept(any());
|
||||
verify(refresher).refreshToken(MieleCloudBindingTestConstants.SERVICE_HANDLE);
|
||||
verify(getOperation(), times(2)).run();
|
||||
verifyNoMoreInteractions(getOnException(), refresher, getOperation());
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPerformRetryableOperationThrowsMieleWebserviceExceptionWhenTokenRefreshingFails() {
|
||||
// given:
|
||||
doThrow(AuthorizationFailedException.class).when(getOperation()).run();
|
||||
doThrow(OAuthException.class).when(refresher).refreshToken(MieleCloudBindingTestConstants.SERVICE_HANDLE);
|
||||
|
||||
AuthorizationFailedRetryStrategy retryStrategy = new AuthorizationFailedRetryStrategy(refresher,
|
||||
MieleCloudBindingTestConstants.SERVICE_HANDLE);
|
||||
|
||||
assertThrows(MieleWebserviceException.class, () -> {
|
||||
try {
|
||||
// when:
|
||||
retryStrategy.performRetryableOperation(getOperation(), getOnException());
|
||||
} catch (Exception e) {
|
||||
// then:
|
||||
verify(getOnException()).accept(any());
|
||||
verify(refresher).refreshToken(MieleCloudBindingTestConstants.SERVICE_HANDLE);
|
||||
verify(getOperation()).run();
|
||||
verifyNoMoreInteractions(getOnException(), refresher, getOperation());
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,220 @@
|
||||
/**
|
||||
* 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.webservice.retry;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.openhab.binding.mielecloud.internal.webservice.exception.MieleWebserviceException;
|
||||
import org.openhab.binding.mielecloud.internal.webservice.exception.MieleWebserviceTransientException;
|
||||
|
||||
/**
|
||||
* @author Björn Lange - Initial contribution
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@NonNullByDefault
|
||||
public class NTimesRetryStrategyTest {
|
||||
private static final int SUCCESSFUL_RETURN_VALUE = 42;
|
||||
|
||||
@Mock
|
||||
@Nullable
|
||||
private Supplier<@Nullable Integer> operation;
|
||||
|
||||
@Mock
|
||||
@Nullable
|
||||
private Consumer<Exception> onTransientException;
|
||||
|
||||
private Supplier<@Nullable Integer> getOperation() {
|
||||
assertNotNull(operation);
|
||||
return Objects.requireNonNull(operation);
|
||||
}
|
||||
|
||||
private Consumer<Exception> getOnTransientException() {
|
||||
assertNotNull(onTransientException);
|
||||
return Objects.requireNonNull(onTransientException);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConstructorThrowsIllegalArgumentExceptionIfNumberOfRetriesIsSmallerThanZero() {
|
||||
// when:
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
new NTimesRetryStrategy(-1);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuccessfulOperationReturnsCorrectValue() {
|
||||
// given:
|
||||
when(getOperation().get()).thenReturn(SUCCESSFUL_RETURN_VALUE);
|
||||
|
||||
NTimesRetryStrategy retryStrategy = new NTimesRetryStrategy(1);
|
||||
|
||||
// when:
|
||||
Integer result = retryStrategy.performRetryableOperation(getOperation(), getOnTransientException());
|
||||
|
||||
// then:
|
||||
assertEquals(Integer.valueOf(SUCCESSFUL_RETURN_VALUE), result);
|
||||
verifyNoMoreInteractions(onTransientException);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailingOperationReturnsCorrectValueOnRetry() {
|
||||
// given:
|
||||
when(getOperation().get()).thenThrow(MieleWebserviceTransientException.class)
|
||||
.thenReturn(SUCCESSFUL_RETURN_VALUE);
|
||||
|
||||
NTimesRetryStrategy retryStrategy = new NTimesRetryStrategy(1);
|
||||
|
||||
// when:
|
||||
Integer result = retryStrategy.performRetryableOperation(getOperation(), getOnTransientException());
|
||||
|
||||
// then:
|
||||
assertEquals(Integer.valueOf(SUCCESSFUL_RETURN_VALUE), result);
|
||||
verify(getOnTransientException()).accept(any());
|
||||
verifyNoMoreInteractions(onTransientException);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailingOperationReturnsCorrectValueOnSecondRetry() {
|
||||
// given:
|
||||
when(getOperation().get()).thenThrow(MieleWebserviceTransientException.class)
|
||||
.thenThrow(MieleWebserviceTransientException.class).thenReturn(SUCCESSFUL_RETURN_VALUE);
|
||||
|
||||
NTimesRetryStrategy retryStrategy = new NTimesRetryStrategy(2);
|
||||
|
||||
// when:
|
||||
Integer result = retryStrategy.performRetryableOperation(getOperation(), getOnTransientException());
|
||||
|
||||
// then:
|
||||
assertEquals(Integer.valueOf(SUCCESSFUL_RETURN_VALUE), result);
|
||||
verify(getOnTransientException(), times(2)).accept(any());
|
||||
verifyNoMoreInteractions(onTransientException);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAlwaysFailingOperationThrowsMieleWebserviceException() {
|
||||
// given:
|
||||
when(getOperation().get()).thenThrow(MieleWebserviceTransientException.class);
|
||||
|
||||
NTimesRetryStrategy retryStrategy = new NTimesRetryStrategy(1);
|
||||
|
||||
// when:
|
||||
try {
|
||||
retryStrategy.performRetryableOperation(getOperation(), getOnTransientException());
|
||||
fail();
|
||||
return;
|
||||
} catch (MieleWebserviceException e) {
|
||||
}
|
||||
|
||||
// then:
|
||||
verify(getOnTransientException()).accept(any());
|
||||
verifyNoMoreInteractions(onTransientException);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNullReturnValueDoesNotCauseMultipleRetries() {
|
||||
// given:
|
||||
when(getOperation().get()).thenReturn(null);
|
||||
|
||||
NTimesRetryStrategy retryStrategy = new NTimesRetryStrategy(1);
|
||||
|
||||
// when:
|
||||
retryStrategy.performRetryableOperation(getOperation(), getOnTransientException());
|
||||
|
||||
// then:
|
||||
verifyNoInteractions(getOnTransientException());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuccessfulOperation() {
|
||||
// given:
|
||||
Runnable operation = mock(Runnable.class);
|
||||
doNothing().when(operation).run();
|
||||
|
||||
NTimesRetryStrategy retryStrategy = new NTimesRetryStrategy(1);
|
||||
|
||||
// when:
|
||||
retryStrategy.performRetryableOperation(operation, getOnTransientException());
|
||||
|
||||
// then:
|
||||
verify(operation).run();
|
||||
verifyNoInteractions(getOnTransientException());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailingOperationCausesRetry() {
|
||||
// given:
|
||||
Runnable operation = mock(Runnable.class);
|
||||
doThrow(MieleWebserviceTransientException.class).doNothing().when(operation).run();
|
||||
|
||||
NTimesRetryStrategy retryStrategy = new NTimesRetryStrategy(1);
|
||||
|
||||
// when:
|
||||
retryStrategy.performRetryableOperation(operation, getOnTransientException());
|
||||
|
||||
// then:
|
||||
verify(getOnTransientException()).accept(any());
|
||||
verify(operation, times(2)).run();
|
||||
verifyNoMoreInteractions(getOnTransientException());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTwoTimesFailingOperationCausesTwoRetries() {
|
||||
// given:
|
||||
Runnable operation = mock(Runnable.class);
|
||||
doThrow(MieleWebserviceTransientException.class).doThrow(MieleWebserviceTransientException.class).doNothing()
|
||||
.when(operation).run();
|
||||
|
||||
NTimesRetryStrategy retryStrategy = new NTimesRetryStrategy(2);
|
||||
|
||||
// when:
|
||||
retryStrategy.performRetryableOperation(operation, getOnTransientException());
|
||||
|
||||
// then:
|
||||
verify(getOnTransientException(), times(2)).accept(any());
|
||||
verify(operation, times(3)).run();
|
||||
verifyNoMoreInteractions(getOnTransientException());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAlwaysFailingRunnableOperationThrowsMieleWebserviceException() {
|
||||
// given:
|
||||
Runnable operation = mock(Runnable.class);
|
||||
doThrow(MieleWebserviceTransientException.class).when(operation).run();
|
||||
|
||||
NTimesRetryStrategy retryStrategy = new NTimesRetryStrategy(1);
|
||||
|
||||
// when:
|
||||
try {
|
||||
retryStrategy.performRetryableOperation(operation, getOnTransientException());
|
||||
fail();
|
||||
return;
|
||||
} catch (MieleWebserviceException e) {
|
||||
}
|
||||
|
||||
// then:
|
||||
verify(getOnTransientException()).accept(any());
|
||||
verifyNoMoreInteractions(getOnTransientException());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
/**
|
||||
* 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.webservice.retry;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.mockito.stubbing.Answer;
|
||||
import org.openhab.binding.mielecloud.internal.util.MockUtil;
|
||||
|
||||
/**
|
||||
* @author Björn Lange - Initial contribution
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@NonNullByDefault
|
||||
public class RetryStrategyCombinerTest {
|
||||
private static final String STRING_CONSTANT = "Some String";
|
||||
|
||||
private final RetryStrategy first = mock(RetryStrategy.class);
|
||||
private final RetryStrategy second = mock(RetryStrategy.class);
|
||||
|
||||
@Mock
|
||||
@Nullable
|
||||
private Supplier<@Nullable String> supplier;
|
||||
@Mock
|
||||
@Nullable
|
||||
private Consumer<Exception> consumer;
|
||||
|
||||
private Supplier<@Nullable String> getSupplier() {
|
||||
assertNotNull(supplier);
|
||||
return Objects.requireNonNull(supplier);
|
||||
}
|
||||
|
||||
private Consumer<Exception> getConsumer() {
|
||||
assertNotNull(consumer);
|
||||
return Objects.requireNonNull(consumer);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void testPerformRetryableOperationInvokesRetryStrategiesInCorrectOrder() {
|
||||
// given:
|
||||
when(first.<@Nullable String> performRetryableOperation(any(Supplier.class), any()))
|
||||
.thenAnswer(new Answer<@Nullable String>() {
|
||||
@Override
|
||||
@Nullable
|
||||
public String answer(@Nullable InvocationOnMock invocation) throws Throwable {
|
||||
Supplier<String> inner = MockUtil.requireNonNull(invocation).getArgument(0);
|
||||
return inner.get();
|
||||
}
|
||||
});
|
||||
when(second.<@Nullable String> performRetryableOperation(any(Supplier.class), any()))
|
||||
.thenAnswer(new Answer<@Nullable String>() {
|
||||
@Override
|
||||
@Nullable
|
||||
public String answer(@Nullable InvocationOnMock invocation) throws Throwable {
|
||||
Supplier<String> inner = MockUtil.requireNonNull(invocation).getArgument(0);
|
||||
return inner.get();
|
||||
}
|
||||
});
|
||||
when(getSupplier().get()).thenReturn(STRING_CONSTANT);
|
||||
|
||||
RetryStrategyCombiner combiner = new RetryStrategyCombiner(first, second);
|
||||
|
||||
// when:
|
||||
String result = combiner.performRetryableOperation(getSupplier(), getConsumer());
|
||||
|
||||
// then:
|
||||
assertEquals(STRING_CONSTANT, result);
|
||||
verify(first).performRetryableOperation(any(Supplier.class), eq(getConsumer()));
|
||||
verify(second).performRetryableOperation(any(Supplier.class), eq(getConsumer()));
|
||||
verify(getSupplier()).get();
|
||||
verifyNoMoreInteractions(first, second, getSupplier());
|
||||
verifyNoInteractions(getConsumer());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
/**
|
||||
* 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.webservice.sse;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* @author Björn Lange - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ExponentialBackoffWithJitterTest {
|
||||
private static final long RETRY_INTERVAL = 2;
|
||||
private static final long ALTERNATIVE_RETRY_INTERVAL = 50;
|
||||
private static final long MINIMUM_WAIT_TIME = 1;
|
||||
private static final long ALTERNATIVE_MINIMUM_WAIT_TIME = 2;
|
||||
private static final long MAXIMUM_WAIT_TIME = 100;
|
||||
private static final long ALTERNATIVE_MAXIMUM_WAIT_TIME = 150;
|
||||
|
||||
@Test
|
||||
public void whenMinimumWaitTimeIsSmallerThanZeroThenAnIllegalArgumentExceptionIsThrown() {
|
||||
// when:
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
new ExponentialBackoffWithJitter(-MINIMUM_WAIT_TIME, MAXIMUM_WAIT_TIME, RETRY_INTERVAL);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenMaximumWaitTimeIsSmallerThanZeroThenAnIllegalArgumentExceptionIsThrown() {
|
||||
// when:
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
new ExponentialBackoffWithJitter(MINIMUM_WAIT_TIME, -MAXIMUM_WAIT_TIME, RETRY_INTERVAL);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenRetryIntervalIsSmallerThanZeroThenAnIllegalArgumentExceptionIsThrown() {
|
||||
// when:
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
new ExponentialBackoffWithJitter(MINIMUM_WAIT_TIME, MAXIMUM_WAIT_TIME, -RETRY_INTERVAL);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenMinimumWaitTimeIsLargerThanMaximumWaitTimeThenAnIllegalArgumentExceptionIsThrown() {
|
||||
// when:
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
new ExponentialBackoffWithJitter(MAXIMUM_WAIT_TIME, MINIMUM_WAIT_TIME, RETRY_INTERVAL);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenRetryIntervalIsLargerThanMaximumWaitTimeThenAnIllegalArgumentExceptionIsThrown() {
|
||||
// when:
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
new ExponentialBackoffWithJitter(MINIMUM_WAIT_TIME, RETRY_INTERVAL, MAXIMUM_WAIT_TIME);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenTheNumberOfFailedAttemptsIsNegativeThenZeroIsAssumedInstead() {
|
||||
// given:
|
||||
Random random = mock(Random.class);
|
||||
when(random.nextLong()).thenReturn(RETRY_INTERVAL);
|
||||
|
||||
ExponentialBackoffWithJitter backoffStrategy = new ExponentialBackoffWithJitter(MINIMUM_WAIT_TIME,
|
||||
MAXIMUM_WAIT_TIME, RETRY_INTERVAL, random);
|
||||
|
||||
// when:
|
||||
long result = backoffStrategy.getSecondsUntilRetry(-10);
|
||||
|
||||
// then:
|
||||
assertEquals(MINIMUM_WAIT_TIME + RETRY_INTERVAL, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenThereIsNoFailedAttemptThenTheMaximalResultIsMinimumWaitTimePlusRetryInterval() {
|
||||
// given:
|
||||
Random random = mock(Random.class);
|
||||
when(random.nextLong()).thenReturn(RETRY_INTERVAL);
|
||||
|
||||
ExponentialBackoffWithJitter backoffStrategy = new ExponentialBackoffWithJitter(MINIMUM_WAIT_TIME,
|
||||
MAXIMUM_WAIT_TIME, RETRY_INTERVAL, random);
|
||||
|
||||
// when:
|
||||
long result = backoffStrategy.getSecondsUntilRetry(0);
|
||||
|
||||
// then:
|
||||
assertEquals(MINIMUM_WAIT_TIME + RETRY_INTERVAL, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenThereIsOneFailedAttemptThenTheMaximalResultIsMinimumWaitTimePlusTwiceTheRetryInterval() {
|
||||
// given:
|
||||
Random random = mock(Random.class);
|
||||
when(random.nextLong()).thenReturn(RETRY_INTERVAL * 2);
|
||||
|
||||
ExponentialBackoffWithJitter backoffStrategy = new ExponentialBackoffWithJitter(MINIMUM_WAIT_TIME,
|
||||
MAXIMUM_WAIT_TIME, RETRY_INTERVAL, random);
|
||||
|
||||
// when:
|
||||
long result = backoffStrategy.getSecondsUntilRetry(1);
|
||||
|
||||
// then:
|
||||
assertEquals(MINIMUM_WAIT_TIME + RETRY_INTERVAL * 2, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenThereAreTwoFailedAttemptsThenTheMaximalResultIsMinimumWaitTimePlusFourTimesTheRetryInterval() {
|
||||
// given:
|
||||
Random random = mock(Random.class);
|
||||
when(random.nextLong()).thenReturn(RETRY_INTERVAL * 4);
|
||||
|
||||
ExponentialBackoffWithJitter backoffStrategy = new ExponentialBackoffWithJitter(MINIMUM_WAIT_TIME,
|
||||
MAXIMUM_WAIT_TIME, RETRY_INTERVAL, random);
|
||||
|
||||
// when:
|
||||
long result = backoffStrategy.getSecondsUntilRetry(2);
|
||||
|
||||
// then:
|
||||
assertEquals(MINIMUM_WAIT_TIME + RETRY_INTERVAL * 4, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenThereAreTwoFailedAttemptsThenTheMinimalResultIsTheMinimumWaitTime() {
|
||||
// given:
|
||||
Random random = mock(Random.class);
|
||||
when(random.nextLong()).thenReturn(0L);
|
||||
|
||||
ExponentialBackoffWithJitter backoffStrategy = new ExponentialBackoffWithJitter(MINIMUM_WAIT_TIME,
|
||||
MAXIMUM_WAIT_TIME, RETRY_INTERVAL, random);
|
||||
|
||||
// when:
|
||||
long result = backoffStrategy.getSecondsUntilRetry(2);
|
||||
|
||||
// then:
|
||||
assertEquals(MINIMUM_WAIT_TIME, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenTheDrawnRandomValueIsNegativeThenItIsProjectedToAPositiveValue() {
|
||||
// given:
|
||||
Random random = mock(Random.class);
|
||||
when(random.nextLong()).thenReturn(-RETRY_INTERVAL * 4 - 1);
|
||||
|
||||
ExponentialBackoffWithJitter backoffStrategy = new ExponentialBackoffWithJitter(MINIMUM_WAIT_TIME,
|
||||
MAXIMUM_WAIT_TIME, RETRY_INTERVAL, random);
|
||||
|
||||
// when:
|
||||
long result = backoffStrategy.getSecondsUntilRetry(2);
|
||||
|
||||
// then:
|
||||
assertEquals(MINIMUM_WAIT_TIME, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenTheResultWouldBeLargerThanTheMaximumThenItIsCappedToTheMaximum() {
|
||||
// given:
|
||||
Random random = mock(Random.class);
|
||||
when(random.nextLong()).thenReturn(MAXIMUM_WAIT_TIME - ALTERNATIVE_MINIMUM_WAIT_TIME);
|
||||
|
||||
ExponentialBackoffWithJitter backoffStrategy = new ExponentialBackoffWithJitter(ALTERNATIVE_MINIMUM_WAIT_TIME,
|
||||
MAXIMUM_WAIT_TIME, ALTERNATIVE_RETRY_INTERVAL, random);
|
||||
|
||||
// when:
|
||||
long result = backoffStrategy.getSecondsUntilRetry(2);
|
||||
|
||||
// then:
|
||||
assertEquals(MAXIMUM_WAIT_TIME, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenTheResultWouldBeLargerThanTheAlternativeMaximumThenItIsCappedToTheAlternativeMaximum() {
|
||||
// given:
|
||||
Random random = mock(Random.class);
|
||||
when(random.nextLong()).thenReturn(ALTERNATIVE_MAXIMUM_WAIT_TIME - ALTERNATIVE_MINIMUM_WAIT_TIME);
|
||||
|
||||
ExponentialBackoffWithJitter backoffStrategy = new ExponentialBackoffWithJitter(ALTERNATIVE_MINIMUM_WAIT_TIME,
|
||||
ALTERNATIVE_MAXIMUM_WAIT_TIME, ALTERNATIVE_RETRY_INTERVAL, random);
|
||||
|
||||
// when:
|
||||
long result = backoffStrategy.getSecondsUntilRetry(2);
|
||||
|
||||
// then:
|
||||
assertEquals(ALTERNATIVE_MAXIMUM_WAIT_TIME, result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,600 @@
|
||||
/**
|
||||
* 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.webservice.sse;
|
||||
|
||||
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.*;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.eclipse.jetty.client.api.Request;
|
||||
import org.eclipse.jetty.client.api.Response;
|
||||
import org.eclipse.jetty.client.api.Response.CompleteListener;
|
||||
import org.eclipse.jetty.client.api.Response.HeadersListener;
|
||||
import org.eclipse.jetty.client.api.Result;
|
||||
import org.eclipse.jetty.http.HttpFields;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentMatchers;
|
||||
import org.openhab.binding.mielecloud.internal.webservice.ConnectionError;
|
||||
import org.openhab.binding.mielecloud.internal.webservice.exception.MieleWebserviceDisconnectSseException;
|
||||
import org.openhab.binding.mielecloud.internal.webservice.retry.AuthorizationFailedRetryStrategy;
|
||||
|
||||
/**
|
||||
* @author Björn Lange - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SseConnectionTest {
|
||||
private final String URL = "https://openhab.org/";
|
||||
|
||||
@Nullable
|
||||
private Request request;
|
||||
|
||||
@Nullable
|
||||
private SseRequestFactory sseRequestFactory;
|
||||
|
||||
@Nullable
|
||||
private ScheduledExecutorService scheduler;
|
||||
|
||||
@Nullable
|
||||
private BackoffStrategy backoffStrategy;
|
||||
|
||||
@Nullable
|
||||
private SseListener sseListener;
|
||||
|
||||
@Nullable
|
||||
private SseConnection sseConnection;
|
||||
|
||||
@Nullable
|
||||
private HeadersListener registeredHeadersListener;
|
||||
|
||||
@Nullable
|
||||
private CompleteListener registeredCompleteListener;
|
||||
|
||||
private SseRequestFactory mockSseRequestFactory(@Nullable Request request) {
|
||||
SseRequestFactory factory = mock(SseRequestFactory.class);
|
||||
when(factory.createSseRequest(URL)).thenReturn(request);
|
||||
return factory;
|
||||
}
|
||||
|
||||
private ScheduledExecutorService mockScheduler() {
|
||||
return mock(ScheduledExecutorService.class);
|
||||
}
|
||||
|
||||
private Request mockRequest() {
|
||||
Request request = mock(Request.class);
|
||||
when(request.onResponseHeaders(any())).thenAnswer(invocation -> {
|
||||
registeredHeadersListener = invocation.getArgument(0);
|
||||
return request;
|
||||
});
|
||||
when(request.onComplete(any())).thenAnswer(invocation -> {
|
||||
registeredCompleteListener = invocation.getArgument(0);
|
||||
return request;
|
||||
});
|
||||
when(request.idleTimeout(anyLong(), any())).thenReturn(request);
|
||||
when(request.timeout(anyLong(), any())).thenReturn(request);
|
||||
return request;
|
||||
}
|
||||
|
||||
private BackoffStrategy mockBackoffStrategy() {
|
||||
BackoffStrategy backoffStrategy = mock(BackoffStrategy.class);
|
||||
when(backoffStrategy.getSecondsUntilRetry(anyInt())).thenReturn(10L);
|
||||
when(backoffStrategy.getMinimumSecondsUntilRetry()).thenReturn(5L);
|
||||
when(backoffStrategy.getMaximumSecondsUntilRetry()).thenReturn(3600L);
|
||||
return backoffStrategy;
|
||||
}
|
||||
|
||||
private void setUpRunningConnection() {
|
||||
request = mockRequest();
|
||||
sseRequestFactory = mockSseRequestFactory(request);
|
||||
scheduler = mockScheduler();
|
||||
backoffStrategy = mockBackoffStrategy();
|
||||
sseConnection = new SseConnection(URL, getMockedSseRequestFactory(), getMockedScheduler(),
|
||||
getMockedBackoffStrategy());
|
||||
|
||||
sseListener = mock(SseListener.class);
|
||||
getSseConnection().addSseListener(getMockedSseListener());
|
||||
getSseConnection().connect();
|
||||
|
||||
getRegisteredHeadersListener().onHeaders(null);
|
||||
}
|
||||
|
||||
private Request getMockedRequest() {
|
||||
Request request = this.request;
|
||||
assertNotNull(request);
|
||||
return Objects.requireNonNull(request);
|
||||
}
|
||||
|
||||
private SseRequestFactory getMockedSseRequestFactory() {
|
||||
SseRequestFactory sseRequestFactory = this.sseRequestFactory;
|
||||
assertNotNull(sseRequestFactory);
|
||||
return Objects.requireNonNull(sseRequestFactory);
|
||||
}
|
||||
|
||||
private ScheduledExecutorService getMockedScheduler() {
|
||||
ScheduledExecutorService scheduler = this.scheduler;
|
||||
assertNotNull(scheduler);
|
||||
return Objects.requireNonNull(scheduler);
|
||||
}
|
||||
|
||||
private BackoffStrategy getMockedBackoffStrategy() {
|
||||
BackoffStrategy backoffStrategy = this.backoffStrategy;
|
||||
assertNotNull(backoffStrategy);
|
||||
return Objects.requireNonNull(backoffStrategy);
|
||||
}
|
||||
|
||||
private SseListener getMockedSseListener() {
|
||||
SseListener sseListener = this.sseListener;
|
||||
assertNotNull(sseListener);
|
||||
return Objects.requireNonNull(sseListener);
|
||||
}
|
||||
|
||||
private SseConnection getSseConnection() {
|
||||
SseConnection sseConnection = this.sseConnection;
|
||||
assertNotNull(sseConnection);
|
||||
return Objects.requireNonNull(sseConnection);
|
||||
}
|
||||
|
||||
private HeadersListener getRegisteredHeadersListener() {
|
||||
HeadersListener headersListener = registeredHeadersListener;
|
||||
assertNotNull(headersListener);
|
||||
return Objects.requireNonNull(headersListener);
|
||||
}
|
||||
|
||||
private CompleteListener getRegisteredCompleteListener() {
|
||||
CompleteListener completeListener = registeredCompleteListener;
|
||||
assertNotNull(completeListener);
|
||||
return Objects.requireNonNull(completeListener);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenSseConnectionIsConnectedThenTheConnectionRequestIsMade() throws Exception {
|
||||
// given:
|
||||
Request request = mockRequest();
|
||||
SseRequestFactory sseRequestFactory = mockSseRequestFactory(request);
|
||||
ScheduledExecutorService scheduler = mockScheduler();
|
||||
SseConnection sseConnection = new SseConnection(URL, sseRequestFactory, scheduler);
|
||||
|
||||
// when:
|
||||
sseConnection.connect();
|
||||
|
||||
// then:
|
||||
verify(request).send(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenSseConnectionIsConnectedButNoRequestIsCreatedThenOnlyTheDesiredConnectionStateChanges()
|
||||
throws Exception {
|
||||
// given:
|
||||
SseRequestFactory sseRequestFactory = mockSseRequestFactory(null);
|
||||
ScheduledExecutorService scheduler = mockScheduler();
|
||||
SseConnection sseConnection = new SseConnection(URL, sseRequestFactory, scheduler);
|
||||
|
||||
// when:
|
||||
sseConnection.connect();
|
||||
|
||||
// then:
|
||||
assertTrue(((Boolean) getPrivate(sseConnection, "active")).booleanValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenHeadersAreReceivedAfterTheSseConnectionWasConnectedThenTheEventStreamParserIsScheduled()
|
||||
throws Exception {
|
||||
// given:
|
||||
Request request = mockRequest();
|
||||
SseRequestFactory sseRequestFactory = mockSseRequestFactory(request);
|
||||
ScheduledExecutorService scheduler = mockScheduler();
|
||||
SseConnection sseConnection = new SseConnection(URL, sseRequestFactory, scheduler);
|
||||
sseConnection.connect();
|
||||
HeadersListener headersListener = registeredHeadersListener;
|
||||
assertNotNull(headersListener);
|
||||
|
||||
// when:
|
||||
headersListener.onHeaders(null);
|
||||
|
||||
// then:
|
||||
verify(scheduler).schedule(ArgumentMatchers.<Runnable> any(), anyLong(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenTheSseStreamIsClosedWithATimeoutThenAReconnectIsScheduledAndTheListenersAreNotified()
|
||||
throws Exception {
|
||||
// given:
|
||||
setUpRunningConnection();
|
||||
|
||||
// when:
|
||||
invokePrivate(getSseConnection(), "onSseStreamClosed", new Class[] { Throwable.class }, new TimeoutException());
|
||||
|
||||
// then:
|
||||
verify(getMockedScheduler(), times(2)).schedule(ArgumentMatchers.<Runnable> any(), anyLong(), any());
|
||||
verify(getMockedSseListener()).onConnectionError(ConnectionError.TIMEOUT, 0);
|
||||
verify(getMockedBackoffStrategy()).getSecondsUntilRetry(anyInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenTheSseStreamIsClosedDueToAJetty401ErrorThenNoReconnectIsScheduledAndATokenRefreshIsRequested()
|
||||
throws Exception {
|
||||
// given:
|
||||
setUpRunningConnection();
|
||||
|
||||
// when:
|
||||
invokePrivate(getSseConnection(), "onSseStreamClosed", new Class[] { Throwable.class }, new RuntimeException(
|
||||
AuthorizationFailedRetryStrategy.JETTY_401_HEADER_BODY_MISMATCH_EXCEPTION_MESSAGE));
|
||||
|
||||
// then:
|
||||
verify(getMockedScheduler()).schedule(ArgumentMatchers.<Runnable> any(), anyLong(), any());
|
||||
verifyNoMoreInteractions(getMockedScheduler());
|
||||
verify(getMockedSseListener()).onConnectionError(ConnectionError.AUTHORIZATION_FAILED, 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenTheSseStreamIsClosedWithADifferentExceptionThanATimeoutThenAReconnectIsScheduledAndTheListenersAreNotified()
|
||||
throws Exception {
|
||||
// given:
|
||||
setUpRunningConnection();
|
||||
|
||||
// when:
|
||||
invokePrivate(getSseConnection(), "onSseStreamClosed", new Class[] { Throwable.class },
|
||||
new IllegalStateException());
|
||||
|
||||
// then:
|
||||
verify(getMockedScheduler(), times(2)).schedule(ArgumentMatchers.<Runnable> any(), anyLong(), any());
|
||||
verify(getMockedSseListener()).onConnectionError(ConnectionError.SSE_STREAM_ENDED, 0);
|
||||
verify(getMockedBackoffStrategy()).getSecondsUntilRetry(anyInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenTheSseRequestCompletesWithoutResultThenAReconnectIsScheduledAndTheListenersAreNotified()
|
||||
throws Exception {
|
||||
// given:
|
||||
setUpRunningConnection();
|
||||
|
||||
// when:
|
||||
getRegisteredCompleteListener().onComplete(null);
|
||||
|
||||
// then:
|
||||
verify(getMockedScheduler(), times(2)).schedule(ArgumentMatchers.<Runnable> any(), anyLong(), any());
|
||||
verify(getMockedSseListener()).onConnectionError(ConnectionError.SSE_STREAM_ENDED, 0);
|
||||
verify(getMockedBackoffStrategy()).getSecondsUntilRetry(anyInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenTheSseRequestCompletesWithoutResponseThenAReconnectIsScheduledAndTheListenersAreNotified()
|
||||
throws Exception {
|
||||
// given:
|
||||
setUpRunningConnection();
|
||||
|
||||
Result result = mock(Result.class);
|
||||
|
||||
// when:
|
||||
getRegisteredCompleteListener().onComplete(result);
|
||||
|
||||
// then:
|
||||
verify(getMockedScheduler(), times(2)).schedule(ArgumentMatchers.<Runnable> any(), anyLong(), any());
|
||||
verify(getMockedSseListener()).onConnectionError(ConnectionError.SSE_STREAM_ENDED, 0);
|
||||
verify(getMockedBackoffStrategy()).getSecondsUntilRetry(anyInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenTheSseRequestCompletesWithASuccessfulResponseThenAReconnectIsScheduledAndTheListenersAreNotified()
|
||||
throws Exception {
|
||||
// given:
|
||||
setUpRunningConnection();
|
||||
|
||||
Response response = mock(Response.class);
|
||||
when(response.getStatus()).thenReturn(200);
|
||||
|
||||
Result result = mock(Result.class);
|
||||
when(result.getResponse()).thenReturn(response);
|
||||
|
||||
// when:
|
||||
getRegisteredCompleteListener().onComplete(result);
|
||||
|
||||
// then:
|
||||
verify(getMockedScheduler(), times(2)).schedule(ArgumentMatchers.<Runnable> any(), anyLong(), any());
|
||||
verify(getMockedSseListener()).onConnectionError(ConnectionError.SSE_STREAM_ENDED, 0);
|
||||
verify(getMockedBackoffStrategy()).getSecondsUntilRetry(anyInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenTheSseRequestCompletesWithAnAuthorizationFailedResponseThenTheListenersAreNotified()
|
||||
throws Exception {
|
||||
// given:
|
||||
setUpRunningConnection();
|
||||
|
||||
Response response = mock(Response.class);
|
||||
when(response.getStatus()).thenReturn(401);
|
||||
|
||||
Result result = mock(Result.class);
|
||||
when(result.getResponse()).thenReturn(response);
|
||||
|
||||
// when:
|
||||
getRegisteredCompleteListener().onComplete(result);
|
||||
|
||||
// then:
|
||||
verify(getMockedScheduler()).schedule(ArgumentMatchers.<Runnable> any(), anyLong(), any());
|
||||
verify(getMockedSseListener()).onConnectionError(ConnectionError.AUTHORIZATION_FAILED, 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenTheSseRequestCompletesWithATooManyRequestsResponseWithoutRetryAfterHeaderThenAReconnectIsScheduledAccordingToTheBackoffStrategyAndTheListenersAreNotified()
|
||||
throws Exception {
|
||||
// given:
|
||||
setUpRunningConnection();
|
||||
|
||||
Response response = mock(Response.class);
|
||||
when(response.getStatus()).thenReturn(429);
|
||||
when(response.getHeaders()).thenReturn(new HttpFields());
|
||||
|
||||
Result result = mock(Result.class);
|
||||
when(result.getResponse()).thenReturn(response);
|
||||
|
||||
// when:
|
||||
getRegisteredCompleteListener().onComplete(result);
|
||||
|
||||
// then:
|
||||
verify(getMockedScheduler(), times(2)).schedule(ArgumentMatchers.<Runnable> any(), anyLong(), any());
|
||||
verify(getMockedScheduler()).schedule(ArgumentMatchers.<Runnable> any(), eq(10L), eq(TimeUnit.SECONDS));
|
||||
verify(getMockedSseListener()).onConnectionError(ConnectionError.TOO_MANY_RERQUESTS, 0);
|
||||
verify(getMockedBackoffStrategy()).getSecondsUntilRetry(anyInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenTheSseRequestCompletesWithATooManyRequestsResponseWithRetryAfterHeaderThenAReconnectIsScheduledAndTheListenersAreNotified()
|
||||
throws Exception {
|
||||
// given:
|
||||
setUpRunningConnection();
|
||||
|
||||
Response response = mock(Response.class);
|
||||
when(response.getStatus()).thenReturn(429);
|
||||
HttpFields httpFields = new HttpFields();
|
||||
httpFields.add("Retry-After", "3600");
|
||||
when(response.getHeaders()).thenReturn(httpFields);
|
||||
|
||||
Result result = mock(Result.class);
|
||||
when(result.getResponse()).thenReturn(response);
|
||||
|
||||
// when:
|
||||
getRegisteredCompleteListener().onComplete(result);
|
||||
|
||||
// then:
|
||||
verify(getMockedScheduler()).schedule(ArgumentMatchers.<Runnable> any(), eq(3600L), eq(TimeUnit.SECONDS));
|
||||
verify(getMockedSseListener()).onConnectionError(ConnectionError.TOO_MANY_RERQUESTS, 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenTheSseRequestCompletesWithATooManyRequestsResponseWithRetryAfterHeaderWithTooLowValueThenAReconnectIsScheduledWithTheMinimumWaitTime()
|
||||
throws Exception {
|
||||
// given:
|
||||
setUpRunningConnection();
|
||||
|
||||
Response response = mock(Response.class);
|
||||
when(response.getStatus()).thenReturn(429);
|
||||
HttpFields httpFields = new HttpFields();
|
||||
httpFields.add("Retry-After", "1");
|
||||
when(response.getHeaders()).thenReturn(httpFields);
|
||||
|
||||
Result result = mock(Result.class);
|
||||
when(result.getResponse()).thenReturn(response);
|
||||
|
||||
// when:
|
||||
getRegisteredCompleteListener().onComplete(result);
|
||||
|
||||
// then:
|
||||
verify(getMockedScheduler()).schedule(ArgumentMatchers.<Runnable> any(), eq(5L), eq(TimeUnit.SECONDS));
|
||||
verify(getMockedSseListener()).onConnectionError(ConnectionError.TOO_MANY_RERQUESTS, 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenTheSseRequestCompletesWithATooManyRequestsResponseWithRetryAfterHeaderWithTooHighValueThenAReconnectIsScheduledWithTheMaximumWaitTime()
|
||||
throws Exception {
|
||||
// given:
|
||||
setUpRunningConnection();
|
||||
|
||||
Response response = mock(Response.class);
|
||||
when(response.getStatus()).thenReturn(429);
|
||||
HttpFields httpFields = new HttpFields();
|
||||
httpFields.add("Retry-After", "3601");
|
||||
when(response.getHeaders()).thenReturn(httpFields);
|
||||
|
||||
Result result = mock(Result.class);
|
||||
when(result.getResponse()).thenReturn(response);
|
||||
|
||||
// when:
|
||||
getRegisteredCompleteListener().onComplete(result);
|
||||
|
||||
// then:
|
||||
verify(getMockedScheduler()).schedule(ArgumentMatchers.<Runnable> any(), eq(3600L), eq(TimeUnit.SECONDS));
|
||||
verify(getMockedSseListener()).onConnectionError(ConnectionError.TOO_MANY_RERQUESTS, 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenTheSseRequestCompletesWithAnInternalServerErrorResponseThenAReconnectIsScheduledAndTheListenersAreNotified()
|
||||
throws Exception {
|
||||
// given:
|
||||
setUpRunningConnection();
|
||||
|
||||
Response response = mock(Response.class);
|
||||
when(response.getStatus()).thenReturn(500);
|
||||
|
||||
Result result = mock(Result.class);
|
||||
when(result.getResponse()).thenReturn(response);
|
||||
|
||||
// when:
|
||||
getRegisteredCompleteListener().onComplete(result);
|
||||
|
||||
// then:
|
||||
verify(getMockedScheduler(), times(2)).schedule(ArgumentMatchers.<Runnable> any(), anyLong(), any());
|
||||
verify(getMockedSseListener()).onConnectionError(ConnectionError.SERVER_ERROR, 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenTheSseRequestCompletesWithAnInternalServerErrorResponseMultipleTimesThenTheConnectionFailedCounterIsIncrementedEachTime()
|
||||
throws Exception {
|
||||
// given:
|
||||
setUpRunningConnection();
|
||||
|
||||
Response response = mock(Response.class);
|
||||
when(response.getStatus()).thenReturn(500);
|
||||
|
||||
Result result = mock(Result.class);
|
||||
when(result.getResponse()).thenReturn(response);
|
||||
|
||||
// when:
|
||||
getRegisteredCompleteListener().onComplete(result);
|
||||
getRegisteredCompleteListener().onComplete(result);
|
||||
|
||||
// then:
|
||||
verify(getMockedSseListener()).onConnectionError(ConnectionError.SERVER_ERROR, 0);
|
||||
verify(getMockedSseListener()).onConnectionError(ConnectionError.SERVER_ERROR, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenTheSseRequestCompletesWithAnUnknownErrorResponseThenAReconnectIsScheduledAndTheListenersAreNotified()
|
||||
throws Exception {
|
||||
// given:
|
||||
setUpRunningConnection();
|
||||
|
||||
Response response = mock(Response.class);
|
||||
when(response.getStatus()).thenReturn(600);
|
||||
|
||||
Result result = mock(Result.class);
|
||||
when(result.getResponse()).thenReturn(response);
|
||||
|
||||
// when:
|
||||
getRegisteredCompleteListener().onComplete(result);
|
||||
|
||||
// then:
|
||||
verify(getMockedScheduler(), times(2)).schedule(ArgumentMatchers.<Runnable> any(), anyLong(), any());
|
||||
verify(getMockedSseListener()).onConnectionError(ConnectionError.OTHER_HTTP_ERROR, 0);
|
||||
verify(getMockedBackoffStrategy()).getSecondsUntilRetry(anyInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenAServerSentEventIsReceivedThenItIsForwardedToTheListenersAndTheFailedConnectionCounterIsReset()
|
||||
throws Exception {
|
||||
// given:
|
||||
Request request = mockRequest();
|
||||
SseRequestFactory sseRequestFactory = mockSseRequestFactory(request);
|
||||
ScheduledExecutorService scheduler = mockScheduler();
|
||||
|
||||
BackoffStrategy backoffStrategy = mock(BackoffStrategy.class);
|
||||
when(backoffStrategy.getSecondsUntilRetry(anyInt())).thenReturn(10L);
|
||||
|
||||
SseConnection sseConnection = new SseConnection(URL, sseRequestFactory, scheduler, backoffStrategy);
|
||||
SseListener sseListener = mock(SseListener.class);
|
||||
sseConnection.addSseListener(sseListener);
|
||||
setPrivate(sseConnection, "failedConnectionAttempts", 10);
|
||||
sseConnection.connect();
|
||||
|
||||
HeadersListener headersListener = registeredHeadersListener;
|
||||
assertNotNull(headersListener);
|
||||
headersListener.onHeaders(null);
|
||||
|
||||
ServerSentEvent serverSentEvent = new ServerSentEvent("ping", "ping");
|
||||
|
||||
// when:
|
||||
invokePrivate(sseConnection, "onServerSentEvent", serverSentEvent);
|
||||
|
||||
// then:
|
||||
verify(sseListener).onServerSentEvent(serverSentEvent);
|
||||
assertEquals(0, (int) getPrivate(sseConnection, "failedConnectionAttempts"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenTheSseStreamIsDisconnectedThenTheRunningRequestIsAborted() throws Exception {
|
||||
// given:
|
||||
setUpRunningConnection();
|
||||
|
||||
// when:
|
||||
getSseConnection().disconnect();
|
||||
|
||||
// then:
|
||||
verify(getMockedRequest()).abort(any());
|
||||
assertNull(getPrivate(getSseConnection(), "sseRequest"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenTheSseStreamIsDisconnectedThenTheConnectionIsClosedAndNoReconnectIsScheduledAndTheListenersAreNotNotified()
|
||||
throws Exception {
|
||||
// given:
|
||||
setUpRunningConnection();
|
||||
|
||||
// when:
|
||||
getSseConnection().disconnect();
|
||||
invokePrivate(getSseConnection(), "onSseStreamClosed", new Class[] { Throwable.class },
|
||||
new MieleWebserviceDisconnectSseException());
|
||||
|
||||
// then:
|
||||
verify(getMockedScheduler()).schedule(ArgumentMatchers.<Runnable> any(), anyLong(), any());
|
||||
verifyNoMoreInteractions(getMockedScheduler());
|
||||
verifyNoInteractions(getMockedSseListener());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenAPendingReconnectAttemptIsPerformedAfterTheSseConnectionWasDisconnectedThenTheConnectionIsNotRestored()
|
||||
throws Exception {
|
||||
// given:
|
||||
setUpRunningConnection();
|
||||
getSseConnection().disconnect();
|
||||
|
||||
// when:
|
||||
invokePrivate(getSseConnection(), "connectInternal");
|
||||
|
||||
// then:
|
||||
verify(getMockedScheduler()).schedule(ArgumentMatchers.<Runnable> any(), anyLong(), any());
|
||||
verifyNoMoreInteractions(getMockedScheduler());
|
||||
verifyNoInteractions(getMockedSseListener());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenTheSseConnectionIsConnectedMultipleTimesWithoutDisconnectingThenOnlyTheFirstConnectResultsInAnConnectionAttempt()
|
||||
throws Exception {
|
||||
// given:
|
||||
Request request = mockRequest();
|
||||
SseRequestFactory sseRequestFactory = mockSseRequestFactory(request);
|
||||
ScheduledExecutorService scheduler = mockScheduler();
|
||||
SseConnection sseConnection = new SseConnection(URL, sseRequestFactory, scheduler);
|
||||
sseConnection.connect();
|
||||
|
||||
// when:
|
||||
sseConnection.connect();
|
||||
|
||||
// then:
|
||||
verify(request, times(1)).onResponseHeaders(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenTheSseConnectionIsDisconnectedMultipleTimesWithoutConnectingAgainThenOnlyTheFirstDisconnectIsPerformed()
|
||||
throws Exception {
|
||||
// given:
|
||||
Request request = mockRequest();
|
||||
SseRequestFactory sseRequestFactory = mockSseRequestFactory(request);
|
||||
ScheduledExecutorService scheduler = mockScheduler();
|
||||
SseConnection sseConnection = new SseConnection(URL, sseRequestFactory, scheduler);
|
||||
sseConnection.connect();
|
||||
sseConnection.disconnect();
|
||||
|
||||
// when:
|
||||
sseConnection.disconnect();
|
||||
|
||||
// then:
|
||||
verify(request, times(1)).abort(any());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
/**
|
||||
* 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.webservice.sse;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.openhab.binding.mielecloud.internal.webservice.exception.MieleWebserviceDisconnectSseException;
|
||||
|
||||
/**
|
||||
* @author Björn Lange - Initial contribution
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@NonNullByDefault
|
||||
public class SseStreamParserTest {
|
||||
@Mock
|
||||
@NonNullByDefault({})
|
||||
private Consumer<ServerSentEvent> serverSentEventCallback;
|
||||
|
||||
@Mock
|
||||
@NonNullByDefault({})
|
||||
private Consumer<@Nullable Throwable> streamClosedCallback;
|
||||
|
||||
private InputStream getInputStreamReadingUtf8Data(String data) {
|
||||
return new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenNoEventIsProvidedThenTheStreamClosedCallbackIsInvoked() {
|
||||
// given:
|
||||
InputStream inputStream = getInputStreamReadingUtf8Data("");
|
||||
|
||||
SseStreamParser parser = new SseStreamParser(inputStream, serverSentEventCallback, streamClosedCallback);
|
||||
|
||||
// when:
|
||||
parser.parseAndDispatchEvents();
|
||||
|
||||
// then:
|
||||
verify(streamClosedCallback).accept(null);
|
||||
verifyNoMoreInteractions(streamClosedCallback);
|
||||
verifyNoInteractions(serverSentEventCallback);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenNoEventAndOnlyWhitespaceIsProvidedThenTheStreamClosedCallbackIsInvoked() {
|
||||
// given:
|
||||
InputStream inputStream = getInputStreamReadingUtf8Data("\r\n");
|
||||
|
||||
SseStreamParser parser = new SseStreamParser(inputStream, serverSentEventCallback, streamClosedCallback);
|
||||
|
||||
// when:
|
||||
parser.parseAndDispatchEvents();
|
||||
|
||||
// then:
|
||||
verify(streamClosedCallback).accept(null);
|
||||
verifyNoMoreInteractions(streamClosedCallback);
|
||||
verifyNoInteractions(serverSentEventCallback);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenAnEventIsProvidedThenItIsPassedToTheCallback() {
|
||||
// given:
|
||||
InputStream inputStream = getInputStreamReadingUtf8Data("event: ping\r\ndata: pong\r\n");
|
||||
|
||||
SseStreamParser parser = new SseStreamParser(inputStream, serverSentEventCallback, streamClosedCallback);
|
||||
|
||||
// when:
|
||||
parser.parseAndDispatchEvents();
|
||||
|
||||
// then:
|
||||
verify(streamClosedCallback).accept(null);
|
||||
verify(serverSentEventCallback).accept(new ServerSentEvent("ping", "pong"));
|
||||
verifyNoMoreInteractions(streamClosedCallback, serverSentEventCallback);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenALineWithInvalidKeyIsProvidedThenItIsIgnored() {
|
||||
// given:
|
||||
InputStream inputStream = getInputStreamReadingUtf8Data("name: ping\r\n");
|
||||
|
||||
SseStreamParser parser = new SseStreamParser(inputStream, serverSentEventCallback, streamClosedCallback);
|
||||
|
||||
// when:
|
||||
parser.parseAndDispatchEvents();
|
||||
|
||||
// then:
|
||||
verify(streamClosedCallback).accept(null);
|
||||
verifyNoMoreInteractions(streamClosedCallback);
|
||||
verifyNoInteractions(serverSentEventCallback);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenDataWithoutEventIsProvidedThenItIsIgnored() {
|
||||
// given:
|
||||
InputStream inputStream = getInputStreamReadingUtf8Data("data: ping\r\n");
|
||||
|
||||
SseStreamParser parser = new SseStreamParser(inputStream, serverSentEventCallback, streamClosedCallback);
|
||||
|
||||
// when:
|
||||
parser.parseAndDispatchEvents();
|
||||
|
||||
// then:
|
||||
verify(streamClosedCallback).accept(null);
|
||||
verifyNoMoreInteractions(streamClosedCallback);
|
||||
verifyNoInteractions(serverSentEventCallback);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenTheEventStreamBreaksThenTheStreamClosedCallbackIsNotifiedWithTheCause() throws IOException {
|
||||
// given:
|
||||
InputStream inputStream = mock(InputStream.class);
|
||||
TimeoutException timeoutException = new TimeoutException();
|
||||
when(inputStream.read(any(), anyInt(), anyInt())).thenThrow(new IOException(timeoutException));
|
||||
|
||||
SseStreamParser parser = new SseStreamParser(inputStream, serverSentEventCallback, streamClosedCallback);
|
||||
|
||||
// when:
|
||||
parser.parseAndDispatchEvents();
|
||||
|
||||
// then:
|
||||
verify(streamClosedCallback).accept(timeoutException);
|
||||
verifyNoMoreInteractions(streamClosedCallback);
|
||||
verifyNoInteractions(serverSentEventCallback);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenTheEventStreamBreaksBecauseOfAnSseDisconnectThenTheStreamCloseCallbackIsNotNotifiedToPreventSseReconnect()
|
||||
throws IOException {
|
||||
// given:
|
||||
InputStream inputStream = mock(InputStream.class);
|
||||
when(inputStream.read(any(), anyInt(), anyInt()))
|
||||
.thenThrow(new IOException(new MieleWebserviceDisconnectSseException()));
|
||||
|
||||
SseStreamParser parser = new SseStreamParser(inputStream, serverSentEventCallback, streamClosedCallback);
|
||||
|
||||
// when:
|
||||
parser.parseAndDispatchEvents();
|
||||
|
||||
// then:
|
||||
verifyNoInteractions(streamClosedCallback, serverSentEventCallback);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenTheEventStreamBreaksAndTheResourceCleanupFailsThenItIsIgnored() throws IOException {
|
||||
// given:
|
||||
InputStream inputStream = mock(InputStream.class);
|
||||
when(inputStream.read(any(), anyInt(), anyInt()))
|
||||
.thenThrow(new IOException(new MieleWebserviceDisconnectSseException()));
|
||||
doThrow(new IOException()).when(inputStream).close();
|
||||
|
||||
SseStreamParser parser = new SseStreamParser(inputStream, serverSentEventCallback, streamClosedCallback);
|
||||
|
||||
// when:
|
||||
parser.parseAndDispatchEvents();
|
||||
|
||||
// then:
|
||||
verifyNoInteractions(streamClosedCallback, serverSentEventCallback);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
{
|
||||
"000124430017": {
|
||||
"ident": {
|
||||
"type": {
|
||||
"key_localized": "Devicetype",
|
||||
"value_raw": 18,
|
||||
"value_localized": "Ventilation Hood"
|
||||
},
|
||||
"deviceName": "My Hood",
|
||||
"deviceIdentLabel": {
|
||||
"fabNumber": "000124430017",
|
||||
"fabIndex": "00",
|
||||
"techType": "DA-6996",
|
||||
"matNumber": "10101010",
|
||||
"swids": [
|
||||
"4164",
|
||||
"20380",
|
||||
"25226"
|
||||
]
|
||||
},
|
||||
"xkmIdentLabel": {
|
||||
"techType": "EK039W",
|
||||
"releaseVersion": "02.31"
|
||||
}
|
||||
},
|
||||
"state": {
|
||||
"status": {
|
||||
"value_raw": 5,
|
||||
"value_localized": "In use",
|
||||
"key_localized": "State"
|
||||
},
|
||||
"programType": {
|
||||
"value_raw": 0,
|
||||
"value_localized": "",
|
||||
"key_localized": "Programme"
|
||||
},
|
||||
"programPhase": {
|
||||
"value_raw": 4609,
|
||||
"value_localized": "",
|
||||
"key_localized": "Phase"
|
||||
},
|
||||
"remainingTime": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"startTime": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"targetTemperature": [
|
||||
{
|
||||
"value_raw": -32768,
|
||||
"value_localized": null,
|
||||
"unit": "Celsius"
|
||||
}
|
||||
],
|
||||
"temperature": [
|
||||
{
|
||||
"value_raw": -32768,
|
||||
"value_localized": null,
|
||||
"unit": "Celsius"
|
||||
},
|
||||
{
|
||||
"value_raw": -32768,
|
||||
"value_localized": null,
|
||||
"unit": "Celsius"
|
||||
},
|
||||
{
|
||||
"value_raw": -32768,
|
||||
"value_localized": null,
|
||||
"unit": "Celsius"
|
||||
}
|
||||
],
|
||||
"signalInfo": false,
|
||||
"signalFailure": false,
|
||||
"signalDoor": false,
|
||||
"remoteEnable": {
|
||||
"fullRemoteControl": false,
|
||||
"smartGrid": false
|
||||
},
|
||||
"light": 1,
|
||||
"elapsedTime": [],
|
||||
"spinningSpeed": {
|
||||
"value_raw" : 1200,
|
||||
"value_localized" : 1200,
|
||||
"unit" : "rpm"
|
||||
},
|
||||
"dryingStep": {
|
||||
"value_raw": null,
|
||||
"value_localized": "",
|
||||
"key_localized": "Drying level"
|
||||
},
|
||||
"ventilationStep": {
|
||||
"value_raw": 2,
|
||||
"value_localized": "2",
|
||||
"key_localized": "Power Level"
|
||||
},
|
||||
"plateStep": [
|
||||
{
|
||||
"value_raw": 0,
|
||||
"value_localized": "0",
|
||||
"key_localized": "Plate Step"
|
||||
},
|
||||
{
|
||||
"value_raw": 1,
|
||||
"value_localized": "1",
|
||||
"key_localized": "Plate Step"
|
||||
},
|
||||
{
|
||||
"value_raw": 2,
|
||||
"value_localized": "1.",
|
||||
"key_localized": "Plate Step"
|
||||
},
|
||||
{
|
||||
"value_raw": 3,
|
||||
"value_localized": "2",
|
||||
"key_localized": "Plate Step"
|
||||
}
|
||||
],
|
||||
"batteryLevel": 20
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"000091465021": {
|
||||
"state": {
|
||||
"targetTemperature": [
|
||||
{
|
||||
"value_raw": 80,
|
||||
"value_localized": 0.8,
|
||||
"unit": "Celsius"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
{
|
||||
"mac-00124B000AE539D6": {
|
||||
"ident": {
|
||||
"type": {
|
||||
"key_localized": "Devicetype",
|
||||
"value_raw": 100,
|
||||
"value_localized": ""
|
||||
},
|
||||
"deviceName": "Some Devicename",
|
||||
"deviceIdentLabel": {
|
||||
"fabNumber": "",
|
||||
"fabIndex": "",
|
||||
"techType": "",
|
||||
"matNumber": "",
|
||||
"swids": []
|
||||
},
|
||||
"xkmIdentLabel": {
|
||||
"techType": "",
|
||||
"releaseVersion": ""
|
||||
}
|
||||
},
|
||||
"state": {
|
||||
"ProgramID": {
|
||||
"value_raw": 2499805184,
|
||||
"value_localized": "",
|
||||
"key_localized": "Program Id"
|
||||
},
|
||||
"status": {
|
||||
"value_raw": 5,
|
||||
"value_localized": "In use",
|
||||
"key_localized": "State"
|
||||
},
|
||||
"programType": {
|
||||
"value_raw": 0,
|
||||
"value_localized": "Operation mode",
|
||||
"key_localized": "Program type"
|
||||
},
|
||||
"programPhase": {
|
||||
"value_raw": 0,
|
||||
"value_localized": "",
|
||||
"key_localized": "Phase"
|
||||
},
|
||||
"remainingTime": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"startTime": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"targetTemperature": [],
|
||||
"signalInfo": false,
|
||||
"signalFailure": false,
|
||||
"signalDoor": false,
|
||||
"remoteEnable": {
|
||||
"fullRemoteControl": true,
|
||||
"smartGrid": false
|
||||
},
|
||||
"light": 0,
|
||||
"elapsedTime": [],
|
||||
"dryingStep": {
|
||||
"value_raw": null,
|
||||
"value_localized": "",
|
||||
"key_localized": "Drying level"
|
||||
},
|
||||
"ventilationStep": {
|
||||
"value_raw": null,
|
||||
"value_localized": "",
|
||||
"key_localized": "Power Level"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"000124430017": {
|
||||
"state": {
|
||||
"spinningSpeed": {
|
||||
"value_raw" : 1600,
|
||||
"value_localized" : 1600,
|
||||
"unit" : "U/min"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
{
|
||||
"000124430017": {
|
||||
"ident": {
|
||||
"type": {
|
||||
"key_localized": "Devicetype",
|
||||
10",
|
||||
"swids": [
|
||||
"4164",
|
||||
"20380",
|
||||
"25226"
|
||||
]
|
||||
},
|
||||
"xkmIdentLabel": {
|
||||
"techType": "EK039W",
|
||||
"releaseVersion": "02.31"
|
||||
}
|
||||
},
|
||||
"state": {
|
||||
"status": {
|
||||
"value_raw": 5,
|
||||
"value_localized": "In use",
|
||||
"key_localized": "State"
|
||||
},
|
||||
"programType": {
|
||||
"value_raw": 0,
|
||||
"value_localized": "",
|
||||
"key_localized": "Programme"
|
||||
},
|
||||
"programPhase": {
|
||||
"value_raw": 4609,
|
||||
"value_localized": "",
|
||||
"key_localized": "Phase"
|
||||
},
|
||||
"remainingTime": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"startTime": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"targetT
|
||||
"unit": "Celsius"
|
||||
},
|
||||
{
|
||||
"value_raw": -32768,
|
||||
"value_localized": null,
|
||||
"unit": "Celsius"
|
||||
}
|
||||
],
|
||||
"signalInfo": false,
|
||||
"signalFailure": false,
|
||||
"signalDoor": false,
|
||||
"remoteEnable": {
|
||||
"fullRemoteControl": false,
|
||||
"smartGrid": false
|
||||
},
|
||||
"light": 1,
|
||||
"elapsedTime": [],
|
||||
"dryingStep": {
|
||||
"value_raw": null,
|
||||
"value_localized": "",
|
||||
"key_localized": "Drying level"
|
||||
},
|
||||
"ventilationStep": {
|
||||
"value_raw": 2,
|
||||
"value_localized": "2",
|
||||
"key_localized": "Power Level"
|
||||
}
|
||||
}
|
||||
},
|
||||
"$$ref": "#/components/examples/devicesExample"
|
||||
}
|
||||
Reference in New Issue
Block a user