Boosts the unit test coverage for the `boschshc` binding in `src/main/java` to 94%.
Signed-off-by: David Pace <dev@davidpace.de>
*
* @param e error during long polling
*/
- private void handleLongPollFailure(Throwable e) {
+ void handleLongPollFailure(Throwable e) {
logger.warn("Long polling failed, will try to reconnect", e);
@Nullable
BoschHttpClient localHttpClient = this.httpClient;
return new BoschSHCException("@text/offline.conf-error.invalid-state-id");
} else {
return new BoschSHCException(String.format(
- "Request for info of user-defines state %s failed with status code %d and error code %s",
+ "Request for info of user-defined state %s failed with status code %d and error code %s",
stateId, errorResponse.statusCode, errorResponse.errorCode));
}
} else {
}
}
- private String prettyLogScenarios(final Scenario[] scenarios) {
+ String prettyLogScenarios(final Scenario[] scenarios) {
final StringBuilder builder = new StringBuilder();
builder.append("[");
for (Scenario scenario : scenarios) {
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.containsString;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.*;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import java.io.IOException;
import java.nio.file.Files;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.openhab.binding.boschshc.internal.devices.bridge.BridgeHandler;
verify(consoleMock, atLeastOnce()).print(any());
}
+ @ParameterizedTest
+ @MethodSource("org.openhab.binding.boschshc.internal.tests.common.CommonTestUtils#getBoschShcAndExecutionAndTimeoutExceptionArguments()")
+ void executeHandleExceptions(Exception exception)
+ throws InterruptedException, BoschSHCException, ExecutionException, TimeoutException {
+ Console console = mock(Console.class);
+ Bridge bridge = mock(Bridge.class);
+ BridgeHandler bridgeHandler = mock(BridgeHandler.class);
+ when(bridgeHandler.getThing()).thenReturn(bridge);
+ when(bridgeHandler.getPublicInformation()).thenThrow(exception);
+ when(bridge.getHandler()).thenReturn(bridgeHandler);
+ List<Thing> things = List.of(bridge);
+ when(thingRegistry.getAll()).thenReturn(things);
+
+ fixture.execute(new String[] { BoschShcCommandExtension.GET_BRIDGEINFO }, console);
+
+ verify(console).print(anyString());
+ }
+
@Test
void getCompleter() {
assertThat(fixture.getCompleter(), is(fixture));
@BeforeEach
@Override
public void beforeEach() throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
- super.beforeEach();
-
DeviceServiceData deviceServiceData = new DeviceServiceData();
deviceServiceData.path = "/devices/hdm:ZigBee:000d6f0004b93361/services/BatteryLevel";
deviceServiceData.id = "BatteryLevel";
deviceServiceData.deviceId = "hdm:ZigBee:000d6f0004b93361";
- lenient().when(bridgeHandler.getServiceData(anyString(), anyString())).thenReturn(deviceServiceData);
+ when(getBridgeHandler().getServiceData(anyString(), anyString())).thenReturn(deviceServiceData);
+
+ super.beforeEach();
}
@Test
"deviceId":"hdm:ZigBee:000d6f0004b93361" }\
""");
getFixture().processUpdate("BatteryLevel", deviceServiceData);
- verify(getCallback()).stateUpdated(
+ // state is updated twice: via short poll in initialize() and via long poll result in this test
+ verify(getCallback(), times(2)).stateUpdated(
new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_BATTERY_LEVEL),
new DecimalType(100));
- verify(getCallback()).stateUpdated(
+ verify(getCallback(), times(2)).stateUpdated(
new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_LOW_BATTERY), OnOffType.OFF);
}
getFixture().processUpdate("BatteryLevel", deviceServiceData);
verify(getCallback()).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_BATTERY_LEVEL),
UnDefType.UNDEF);
- verify(getCallback()).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_LOW_BATTERY), OnOffType.OFF);
+ // state is updated twice: via short poll in initialize() and via long poll result in this test
+ verify(getCallback(), times(2)).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_LOW_BATTERY),
+ OnOffType.OFF);
}
@Test
public void testHandleCommandRefreshBatteryLevelChannel() {
getFixture().handleCommand(getChannelUID(BoschSHCBindingConstants.CHANNEL_BATTERY_LEVEL), RefreshType.REFRESH);
- verify(getCallback()).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_BATTERY_LEVEL),
+ // state is updated twice: via short poll in initialize() and via long poll result in this test
+ verify(getCallback(), times(2)).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_BATTERY_LEVEL),
new DecimalType(100));
}
@Test
public void testHandleCommandRefreshLowBatteryChannel() {
getFixture().handleCommand(getChannelUID(BoschSHCBindingConstants.CHANNEL_LOW_BATTERY), RefreshType.REFRESH);
- verify(getCallback()).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_LOW_BATTERY), OnOffType.OFF);
+ // state is updated twice: via short poll in initialize() and via long poll result in this test
+ verify(getCallback(), times(2)).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_LOW_BATTERY),
+ OnOffType.OFF);
}
}
*/
package org.openhab.binding.boschshc.internal.devices;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
+
import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
import org.openhab.binding.boschshc.internal.devices.bridge.dto.Device;
+import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException;
import org.openhab.core.config.core.Configuration;
+import org.openhab.core.thing.ThingStatus;
+import org.openhab.core.thing.ThingStatusDetail;
/**
* Abstract unit test implementation for device handlers.
}
protected abstract String getDeviceID();
+
+ @Test
+ void initializeInvalidDeviceId() {
+ getFixture().getThing().getConfiguration().remove("id");
+ getFixture().initialize();
+
+ verify(getCallback()).statusUpdated(eq(getThing()),
+ argThat(status -> status.getStatus().equals(ThingStatus.OFFLINE)
+ && status.getStatusDetail().equals(ThingStatusDetail.CONFIGURATION_ERROR)));
+ }
+
+ @ParameterizedTest
+ @MethodSource("org.openhab.binding.boschshc.internal.tests.common.CommonTestUtils#getExecutionExceptionAndInterruptedExceptionArguments()")
+ void initializeHandleExceptionDuringDeviceInfoRestCall(Exception exception)
+ throws BoschSHCException, InterruptedException, TimeoutException, ExecutionException {
+ when(getBridgeHandler().getDeviceInfo(getDeviceID())).thenThrow(exception);
+
+ getFixture().initialize();
+
+ verify(getCallback()).statusUpdated(eq(getThing()),
+ argThat(status -> status.getStatus().equals(ThingStatus.OFFLINE)
+ && status.getStatusDetail().equals(ThingStatusDetail.CONFIGURATION_ERROR)));
+ }
}
*/
package org.openhab.binding.boschshc.internal.devices;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.sameInstance;
+import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
private @Mock @NonNullByDefault({}) Bridge bridge;
- protected @Mock @NonNullByDefault({}) BridgeHandler bridgeHandler;
+ private @Mock @NonNullByDefault({}) BridgeHandler bridgeHandler;
private @Mock @NonNullByDefault({}) ThingHandlerCallback callback;
}
@Test
- public void testInitialize() {
+ void testInitialize() {
ThingStatusInfo expectedStatusInfo = new ThingStatusInfo(ThingStatus.ONLINE, ThingStatusDetail.NONE, null);
verify(callback).statusUpdated(same(thing), eq(expectedStatusInfo));
}
+
+ @Test
+ void testGetBridgeHandler() throws BoschSHCException {
+ assertThat(fixture.getBridgeHandler(), sameInstance(bridgeHandler));
+ }
+
+ @Test
+ void testGetBridgeHandlerThrowExceptionIfBridgeIsNull() throws BoschSHCException {
+ when(callback.getBridge(any())).thenReturn(null);
+ assertThrows(BoschSHCException.class, () -> fixture.getBridgeHandler());
+ }
+
+ @Test
+ void testGetBridgeHandlerThrowExceptionIfBridgeHandlerIsNull() throws BoschSHCException {
+ when(bridge.getHandler()).thenReturn(null);
+ assertThrows(BoschSHCException.class, () -> fixture.getBridgeHandler());
+ }
}
package org.openhab.binding.boschshc.internal.devices;
import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.same;
-import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException;
import org.openhab.binding.boschshc.internal.services.powerswitch.PowerSwitchState;
import org.openhab.binding.boschshc.internal.services.powerswitch.dto.PowerSwitchServiceState;
import org.openhab.core.library.types.OnOffType;
+import org.openhab.core.thing.ThingStatus;
+import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.types.RefreshType;
import com.google.gson.JsonElement;
@BeforeEach
@Override
public void beforeEach() throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
- super.beforeEach();
-
PowerSwitchServiceState powerSwitchServiceState = new PowerSwitchServiceState();
powerSwitchServiceState.switchState = PowerSwitchState.ON;
- lenient().when(bridgeHandler.getState(anyString(), eq("PowerSwitch"), same(PowerSwitchServiceState.class)))
+ when(getBridgeHandler().getState(anyString(), eq("PowerSwitch"), same(PowerSwitchServiceState.class)))
.thenReturn(powerSwitchServiceState);
+
+ super.beforeEach();
}
@Test
assertSame(PowerSwitchState.OFF, state.switchState);
}
+ @ParameterizedTest
+ @MethodSource("org.openhab.binding.boschshc.internal.tests.common.CommonTestUtils#getExecutionAndTimeoutAndInterruptedExceptionArguments()")
+ public void testHandleCommandPowerSwitchChannelHandleExceptions(Exception e)
+ throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
+ when(getBridgeHandler().putState(any(), any(), any())).thenThrow(e);
+
+ getFixture().handleCommand(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_SWITCH), OnOffType.ON);
+
+ verify(getCallback()).statusUpdated(same(getThing()),
+ argThat(status -> status.getStatus().equals(ThingStatus.OFFLINE)
+ && status.getStatusDetail().equals(ThingStatusDetail.COMMUNICATION_ERROR)));
+ }
+
@Test
public void testUpdateChannelPowerSwitchState() {
JsonElement jsonObject = JsonParser
.parseString("{\n" + " \"@type\": \"powerSwitchState\",\n" + " \"switchState\": \"ON\"\n" + "}");
+
getFixture().processUpdate("PowerSwitch", jsonObject);
- verify(getCallback()).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_SWITCH), OnOffType.ON);
+
+ // state is updated twice: via short poll in initialize() and via long poll result in this test
+ verify(getCallback(), times(2)).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_SWITCH),
+ OnOffType.ON);
jsonObject = JsonParser
.parseString("{\n" + " \"@type\": \"powerSwitchState\",\n" + " \"switchState\": \"OFF\"\n" + "}");
+
getFixture().processUpdate("PowerSwitch", jsonObject);
+
verify(getCallback()).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_SWITCH), OnOffType.OFF);
}
@Test
public void testHandleCommandRefreshPowerSwitchChannel() {
getFixture().handleCommand(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_SWITCH), RefreshType.REFRESH);
- verify(getCallback()).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_SWITCH), OnOffType.ON);
+
+ // state is updated twice: via short poll in initialize() and via long poll result in this test
+ verify(getCallback(), times(2)).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_SWITCH),
+ OnOffType.ON);
+ }
+
+ @ParameterizedTest
+ @MethodSource("org.openhab.binding.boschshc.internal.tests.common.CommonTestUtils#getBoschShcAndExecutionAndTimeoutAndInterruptedExceptionArguments()")
+ public void testHandleCommandRefreshPowerSwitchChannelHandleExceptions(Exception e)
+ throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
+ when(getBridgeHandler().getState(anyString(), eq("PowerSwitch"), same(PowerSwitchServiceState.class)))
+ .thenThrow(e);
+
+ getFixture().handleCommand(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_SWITCH), RefreshType.REFRESH);
+
+ verify(getCallback()).statusUpdated(same(getThing()),
+ argThat(status -> status.getStatus().equals(ThingStatus.OFFLINE)
+ && status.getStatusDetail().equals(ThingStatusDetail.COMMUNICATION_ERROR)));
}
}
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.lenient;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import java.util.concurrent.ExecutionException;
@BeforeEach
public void beforeEach() throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
- super.beforeEach();
-
PowerMeterServiceState powerMeterServiceState = new PowerMeterServiceState();
powerMeterServiceState.powerConsumption = 12.34d;
powerMeterServiceState.energyConsumption = 56.78d;
- lenient().when(bridgeHandler.getState(anyString(), eq("PowerMeter"), same(PowerMeterServiceState.class)))
+ lenient().when(getBridgeHandler().getState(anyString(), eq("PowerMeter"), same(PowerMeterServiceState.class)))
.thenReturn(powerMeterServiceState);
+
+ super.beforeEach();
}
@Test
""");
getFixture().processUpdate("PowerMeter", jsonObject);
- verify(getCallback()).stateUpdated(eq(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_CONSUMPTION)),
- powerCaptor.capture());
+ // state is updated twice: via short poll in initialize() and via long poll result in this test
+ verify(getCallback(), times(2)).stateUpdated(
+ eq(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_CONSUMPTION)), powerCaptor.capture());
QuantityType<Power> powerValue = powerCaptor.getValue();
assertEquals(23, powerValue.intValue());
- verify(getCallback()).stateUpdated(eq(getChannelUID(BoschSHCBindingConstants.CHANNEL_ENERGY_CONSUMPTION)),
- energyCaptor.capture());
+ // state is updated twice: via short poll in initialize() and via long poll result in this test
+ verify(getCallback(), times(2)).stateUpdated(
+ eq(getChannelUID(BoschSHCBindingConstants.CHANNEL_ENERGY_CONSUMPTION)), energyCaptor.capture());
QuantityType<Energy> energyValue = energyCaptor.getValue();
assertEquals(42, energyValue.intValue());
}
public void testHandleCommandRefreshPowerConsumptionChannel() {
getFixture().handleCommand(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_CONSUMPTION),
RefreshType.REFRESH);
- verify(getCallback()).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_CONSUMPTION),
+
+ // state is updated twice: via short poll in initialize() and via refresh command in this test
+ verify(getCallback(), times(2)).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_CONSUMPTION),
new QuantityType<>(12.34d, Units.WATT));
}
public void testHandleCommandRefreshEnergyConsumptionChannel() {
getFixture().handleCommand(getChannelUID(BoschSHCBindingConstants.CHANNEL_ENERGY_CONSUMPTION),
RefreshType.REFRESH);
- verify(getCallback()).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_ENERGY_CONSUMPTION),
+
+ // state is updated twice: via short poll in initialize() and via refresh command in this test
+ verify(getCallback(), times(2)).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_ENERGY_CONSUMPTION),
new QuantityType<>(56.78d, Units.WATT_HOUR));
}
}
*/
package org.openhab.binding.boschshc.internal.devices.bridge;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.collection.IsCollectionWithSize.hasSize;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.contains;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.ArgumentCaptor;
+import org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants;
import org.openhab.binding.boschshc.internal.devices.BoschSHCHandler;
import org.openhab.binding.boschshc.internal.devices.bridge.dto.Device;
import org.openhab.binding.boschshc.internal.devices.bridge.dto.DeviceServiceData;
import org.openhab.binding.boschshc.internal.devices.bridge.dto.DeviceTest;
import org.openhab.binding.boschshc.internal.devices.bridge.dto.Faults;
import org.openhab.binding.boschshc.internal.devices.bridge.dto.LongPollResult;
+import org.openhab.binding.boschshc.internal.devices.bridge.dto.PublicInformation;
+import org.openhab.binding.boschshc.internal.devices.bridge.dto.Room;
+import org.openhab.binding.boschshc.internal.devices.bridge.dto.Scenario;
import org.openhab.binding.boschshc.internal.devices.bridge.dto.SubscribeResult;
import org.openhab.binding.boschshc.internal.devices.bridge.dto.UserDefinedState;
import org.openhab.binding.boschshc.internal.devices.bridge.dto.UserDefinedStateTest;
+import org.openhab.binding.boschshc.internal.discovery.ThingDiscoveryService;
import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException;
import org.openhab.binding.boschshc.internal.serialization.GsonUtils;
import org.openhab.binding.boschshc.internal.services.binaryswitch.dto.BinarySwitchServiceState;
import org.openhab.binding.boschshc.internal.services.shuttercontact.ShutterContactState;
import org.openhab.binding.boschshc.internal.services.shuttercontact.dto.ShutterContactServiceState;
import org.openhab.core.config.core.Configuration;
+import org.openhab.core.library.types.OnOffType;
+import org.openhab.core.library.types.StringType;
import org.openhab.core.thing.Bridge;
+import org.openhab.core.thing.Channel;
+import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
+import org.openhab.core.thing.ThingStatusInfo;
import org.openhab.core.thing.binding.ThingHandlerCallback;
import org.openhab.core.thing.binding.builder.ThingStatusInfoBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
+import com.google.gson.JsonPrimitive;
/**
* Unit tests for the {@link BridgeHandler}.
private @NonNullByDefault({}) BridgeHandler fixture;
private @NonNullByDefault({}) BoschHttpClient httpClient;
-
private @NonNullByDefault({}) ThingHandlerCallback thingHandlerCallback;
-
- /**
- * A mocked bridge instance
- */
private @NonNullByDefault({}) Bridge thing;
+ private @NonNullByDefault({}) Configuration bridgeConfiguration;
@BeforeAll
static void beforeAll() throws IOException {
thingHandlerCallback = mock(ThingHandlerCallback.class);
fixture.setCallback(thingHandlerCallback);
- Configuration bridgeConfiguration = new Configuration();
+ bridgeConfiguration = new Configuration();
Map<@Nullable String, @Nullable Object> properties = new HashMap<>();
properties.put("ipAddress", "localhost");
properties.put("password", "test");
verify(mockRequest).send();
}
+ @Test
+ void postActionWithoutRequestBody() throws InterruptedException, TimeoutException, ExecutionException {
+ String endpoint = "/intrusion/actions/disarm";
+ String url = "https://127.0.0.1:8444/smarthome/intrusion/actions/disarm";
+ when(httpClient.getBoschSmartHomeUrl(endpoint)).thenReturn(url);
+ Request mockRequest = mock(Request.class);
+ when(httpClient.createRequest(anyString(), any(), any())).thenReturn(mockRequest);
+
+ fixture.postAction(endpoint);
+ verify(httpClient).createRequest(eq(url), same(HttpMethod.POST), isNull());
+ verify(mockRequest).send();
+ }
+
@Test
void initialAccessHttpClientOffline() {
fixture.initialAccess(httpClient);
when(httpClient.createRequest(anyString(), same(HttpMethod.POST),
argThat((JsonRpcRequest r) -> "RE/longPoll".equals(r.method)))).thenReturn(longPollRequest);
+ ThingDiscoveryService thingDiscoveryListener = mock(ThingDiscoveryService.class);
+ fixture.registerDiscoveryListener(thingDiscoveryListener);
+
fixture.initialAccess(httpClient);
+
verify(thingHandlerCallback).statusUpdated(any(),
eq(ThingStatusInfoBuilder.create(ThingStatus.ONLINE, ThingStatusDetail.NONE).build()));
+ verify(thingDiscoveryListener).doScan();
+ }
+
+ @Test
+ void initialAccessNoBridgeAccess() throws InterruptedException, TimeoutException, ExecutionException {
+ when(httpClient.isOnline()).thenReturn(true);
+ when(httpClient.isAccessPossible()).thenReturn(true);
+ Request request = mock(Request.class);
+ when(httpClient.createRequest(any(), same(HttpMethod.GET))).thenReturn(request);
+ ContentResponse response = mock(ContentResponse.class);
+ when(request.send()).thenReturn(response);
+ when(response.getStatus()).thenReturn(400);
+
+ fixture.initialAccess(httpClient);
+
+ verify(thingHandlerCallback).statusUpdated(same(thing),
+ argThat(status -> status.getStatus().equals(ThingStatus.OFFLINE)
+ && status.getStatusDetail().equals(ThingStatusDetail.COMMUNICATION_ERROR)));
}
@Test
assertEquals(stateId, userState.getId());
}
+ @Test
+ void getUserStateInfoErrorCases()
+ throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
+ when(httpClient.getBoschSmartHomeUrl(anyString())).thenCallRealMethod();
+ when(httpClient.getBoschShcUrl(anyString())).thenCallRealMethod();
+
+ Request request = mock(Request.class);
+ when(request.header(anyString(), anyString())).thenReturn(request);
+ ContentResponse response = mock(ContentResponse.class);
+ when(response.getStatus()).thenReturn(200);
+ when(request.send()).thenReturn(response);
+ when(httpClient.createRequest(anyString(), same(HttpMethod.GET))).thenReturn(request);
+
+ @SuppressWarnings("unchecked")
+ ArgumentCaptor<BiFunction<Integer, String, BoschSHCException>> errorResponseHandlerCaptor = ArgumentCaptor
+ .forClass(BiFunction.class);
+
+ String stateId = "abcdef";
+ when(httpClient.sendRequest(same(request), same(UserDefinedState.class), any(),
+ errorResponseHandlerCaptor.capture())).thenReturn(UserDefinedStateTest.createTestState(stateId));
+
+ fixture.getUserStateInfo(stateId);
+
+ BiFunction<Integer, String, BoschSHCException> errorResponseHandler = errorResponseHandlerCaptor.getValue();
+ Exception e = errorResponseHandler.apply(500,
+ "{\"@type\":\"JsonRestExceptionResponseEntity\",\"errorCode\": \"testErrorCode\",\"statusCode\": 500}");
+ assertEquals(
+ "Request for info of user-defined state abcdef failed with status code 500 and error code testErrorCode",
+ e.getMessage());
+
+ e = errorResponseHandler.apply(404,
+ "{\"@type\":\"JsonRestExceptionResponseEntity\",\"errorCode\": \"ENTITY_NOT_FOUND\",\"statusCode\": 404}");
+ assertNotNull(e);
+
+ e = errorResponseHandler.apply(500, "");
+ assertEquals("Request for info of user-defined state abcdef failed with status code 500", e.getMessage());
+ }
+
@Test
void getUserStates() throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
when(httpClient.getBoschSmartHomeUrl(anyString())).thenCallRealMethod();
verify(thingHandler).processChildUpdate("hdm:ZigBee:70ac08fffefead2d#3", "PowerSwitch", expectedState);
}
+
+ @Test
+ void handleLongPollResultScenarioTriggered() {
+ Channel channel = mock(Channel.class);
+ when(thing.getChannel(BoschSHCBindingConstants.CHANNEL_SCENARIO_TRIGGERED)).thenReturn(channel);
+ when(thingHandlerCallback.isChannelLinked(any())).thenReturn(true);
+
+ String json = """
+ {
+ "result": [{
+ "@type": "scenarioTriggered",
+ "name": "My Scenario",
+ "id": "509bd737-eed0-40b7-8caa-e8686a714399",
+ "lastTimeTriggered": "1693758693032"
+ }],
+ "jsonrpc": "2.0"
+ }
+ """;
+ LongPollResult longPollResult = GsonUtils.DEFAULT_GSON_INSTANCE.fromJson(json, LongPollResult.class);
+ assertNotNull(longPollResult);
+
+ fixture.handleLongPollResult(longPollResult);
+
+ verify(thingHandlerCallback).stateUpdated(any(), eq(new StringType("My Scenario")));
+ }
+
+ @Test
+ void handleLongPollResultUserDefinedState() {
+ List<Thing> things = new ArrayList<Thing>();
+ when(thing.getThings()).thenReturn(things);
+
+ Thing thing = mock(Thing.class);
+ things.add(thing);
+
+ BoschSHCHandler thingHandler = mock(BoschSHCHandler.class);
+ when(thing.getHandler()).thenReturn(thingHandler);
+
+ when(thingHandler.getBoschID()).thenReturn("3d8023d6-69ca-4e79-89dd-7090295cefbf");
+
+ String json = """
+ {
+ "result": [{
+ "deleted": false,
+ "@type": "userDefinedState",
+ "name": "Test State",
+ "id": "3d8023d6-69ca-4e79-89dd-7090295cefbf",
+ "state": true
+ }],
+ "jsonrpc": "2.0"
+ }
+ """;
+ LongPollResult longPollResult = GsonUtils.DEFAULT_GSON_INSTANCE.fromJson(json, LongPollResult.class);
+ assertNotNull(longPollResult);
+
+ fixture.handleLongPollResult(longPollResult);
+
+ JsonElement expectedState = new JsonPrimitive(true);
+
+ verify(thingHandler).processUpdate("3d8023d6-69ca-4e79-89dd-7090295cefbf", expectedState);
+ }
+
+ @Test
+ void handleLongPollFailure() {
+ Throwable e = new RuntimeException("Test exception");
+ fixture.handleLongPollFailure(e);
+
+ ThingStatusInfo expectedStatus = ThingStatusInfoBuilder
+ .create(ThingStatus.UNKNOWN, ThingStatusDetail.UNKNOWN.NONE).build();
+ verify(thingHandlerCallback).statusUpdated(thing, expectedStatus);
+ }
+
+ @Test
+ void getDevices() throws InterruptedException, TimeoutException, ExecutionException {
+ Request request = mock(Request.class);
+ when(httpClient.createRequest(any(), eq(HttpMethod.GET))).thenReturn(request);
+ ContentResponse contentResponse = mock(ContentResponse.class);
+ when(request.send()).thenReturn(contentResponse);
+ when(contentResponse.getStatus()).thenReturn(200);
+ String devicesJson = """
+ [
+ {
+ "@type": "device",
+ "rootDeviceId": "64-da-a0-3e-81-0c",
+ "id": "hdm:ZigBee:0c4314fffea15de7",
+ "deviceServiceIds": [
+ "CommunicationQuality",
+ "PowerMeter",
+ "PowerSwitch",
+ "PowerSwitchConfiguration",
+ "PowerSwitchProgram"
+ ],
+ "manufacturer": "BOSCH",
+ "roomId": "hz_1",
+ "deviceModel": "PLUG_COMPACT",
+ "serial": "0C4314FFFE802BE2",
+ "profile": "LIGHT",
+ "iconId": "icon_plug_lamp_table",
+ "name": "My Lamp Plug",
+ "status": "AVAILABLE",
+ "childDeviceIds": [],
+ "supportedProfiles": [
+ "LIGHT",
+ "GENERIC",
+ "HEATING_RCC"
+ ]
+ },
+ {
+ "@type": "device",
+ "rootDeviceId": "64-da-a0-3e-81-0c",
+ "id": "hdm:ZigBee:000d6f0012f13bfa",
+ "deviceServiceIds": [
+ "LatestMotion",
+ "CommunicationQuality",
+ "WalkTest",
+ "BatteryLevel",
+ "MultiLevelSensor",
+ "DeviceDefect"
+ ],
+ "manufacturer": "BOSCH",
+ "roomId": "hz_5",
+ "deviceModel": "MD",
+ "serial": "000D6F0012F0da96",
+ "profile": "GENERIC",
+ "name": "My Motion Detector",
+ "status": "AVAILABLE",
+ "childDeviceIds": [],
+ "supportedProfiles": []
+ }
+ ]
+ """;
+ when(contentResponse.getContentAsString()).thenReturn(devicesJson);
+
+ List<Device> devices = fixture.getDevices();
+
+ assertEquals(2, devices.size());
+
+ Device plugDevice = devices.get(0);
+ assertEquals("hdm:ZigBee:0c4314fffea15de7", plugDevice.id);
+ assertEquals(5, plugDevice.deviceServiceIds.size());
+ assertEquals(0, plugDevice.childDeviceIds.size());
+
+ Device motionDetectorDevice = devices.get(1);
+ assertEquals("hdm:ZigBee:000d6f0012f13bfa", motionDetectorDevice.id);
+ assertEquals(6, motionDetectorDevice.deviceServiceIds.size());
+ assertEquals(0, motionDetectorDevice.childDeviceIds.size());
+ }
+
+ @Test
+ void getDevicesErrorRestResponse() throws InterruptedException, TimeoutException, ExecutionException {
+ Request request = mock(Request.class);
+ when(httpClient.createRequest(any(), eq(HttpMethod.GET))).thenReturn(request);
+ ContentResponse contentResponse = mock(ContentResponse.class);
+ when(request.send()).thenReturn(contentResponse);
+ when(contentResponse.getStatus()).thenReturn(400); // bad request
+
+ List<Device> devices = fixture.getDevices();
+
+ assertThat(devices, hasSize(0));
+ }
+
+ @ParameterizedTest
+ @MethodSource("org.openhab.binding.boschshc.internal.tests.common.CommonTestUtils#getExecutionAndTimeoutExceptionArguments()")
+ void getDevicesHandleExceptions() throws InterruptedException, TimeoutException, ExecutionException {
+ Request request = mock(Request.class);
+ when(httpClient.createRequest(any(), eq(HttpMethod.GET))).thenReturn(request);
+ when(request.send()).thenThrow(new ExecutionException(new RuntimeException("Test Exception")));
+
+ List<Device> devices = fixture.getDevices();
+
+ assertThat(devices, hasSize(0));
+ }
+
+ @Test
+ void getRooms() throws InterruptedException, TimeoutException, ExecutionException {
+ Request request = mock(Request.class);
+ when(httpClient.createRequest(any(), eq(HttpMethod.GET))).thenReturn(request);
+ ContentResponse contentResponse = mock(ContentResponse.class);
+ when(request.send()).thenReturn(contentResponse);
+ when(contentResponse.getStatus()).thenReturn(200);
+ String roomsJson = """
+ [
+ {
+ "@type": "room",
+ "id": "hz_1",
+ "iconId": "icon_room_living_room",
+ "name": "Living Room"
+ },
+ {
+ "@type": "room",
+ "id": "hz_2",
+ "iconId": "icon_room_dining_room",
+ "name": "Dining Room"
+ }
+ ]
+ """;
+ when(contentResponse.getContentAsString()).thenReturn(roomsJson);
+
+ List<Room> rooms = fixture.getRooms();
+
+ assertEquals(2, rooms.size());
+
+ Room livingRoom = rooms.get(0);
+ assertEquals("hz_1", livingRoom.id);
+ assertEquals("Living Room", livingRoom.name);
+
+ Room diningRoom = rooms.get(1);
+ assertEquals("hz_2", diningRoom.id);
+ assertEquals("Dining Room", diningRoom.name);
+ }
+
+ @Test
+ void getRoomsErrorRestResponse() throws InterruptedException, TimeoutException, ExecutionException {
+ Request request = mock(Request.class);
+ when(httpClient.createRequest(any(), eq(HttpMethod.GET))).thenReturn(request);
+ ContentResponse contentResponse = mock(ContentResponse.class);
+ when(request.send()).thenReturn(contentResponse);
+ when(contentResponse.getStatus()).thenReturn(400); // bad request
+
+ List<Room> rooms = fixture.getRooms();
+
+ assertThat(rooms, hasSize(0));
+ }
+
+ @ParameterizedTest
+ @MethodSource("org.openhab.binding.boschshc.internal.tests.common.CommonTestUtils#getExecutionAndTimeoutExceptionArguments()")
+ void getRoomsHandleExceptions() throws InterruptedException, TimeoutException, ExecutionException {
+ Request request = mock(Request.class);
+ when(httpClient.createRequest(any(), eq(HttpMethod.GET))).thenReturn(request);
+ when(request.send()).thenThrow(new ExecutionException(new RuntimeException("Test Exception")));
+
+ List<Room> rooms = fixture.getRooms();
+
+ assertThat(rooms, hasSize(0));
+ }
+
+ @Test
+ void getServices() {
+ assertTrue(fixture.getServices().contains(ThingDiscoveryService.class));
+ }
+
+ @Test
+ void handleCommandIrrelevantChannel() {
+ ChannelUID channelUID = mock(ChannelUID.class);
+ when(channelUID.getId()).thenReturn(BoschSHCBindingConstants.CHANNEL_POWER_SWITCH);
+
+ fixture.handleCommand(channelUID, OnOffType.ON);
+
+ verifyNoInteractions(httpClient);
+ }
+
+ @Test
+ void handleCommandTriggerScenario()
+ throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
+ ChannelUID channelUID = mock(ChannelUID.class);
+ when(channelUID.getId()).thenReturn(BoschSHCBindingConstants.CHANNEL_TRIGGER_SCENARIO);
+
+ // required to prevent NPE
+ when(httpClient.sendRequest(any(), eq(Scenario[].class), any(), any())).thenReturn(new Scenario[] {});
+
+ fixture.handleCommand(channelUID, OnOffType.ON);
+
+ verify(httpClient).sendRequest(any(), eq(Scenario[].class), any(), any());
+ }
+
+ @Test
+ void registerDiscoveryListener() {
+ ThingDiscoveryService listener = mock(ThingDiscoveryService.class);
+ assertTrue(fixture.registerDiscoveryListener(listener));
+ assertFalse(fixture.registerDiscoveryListener(listener));
+ }
+
+ @Test
+ void unregisterDiscoveryListener() {
+ assertFalse(fixture.unregisterDiscoveryListener());
+ fixture.registerDiscoveryListener(mock(ThingDiscoveryService.class));
+ assertTrue(fixture.unregisterDiscoveryListener());
+ }
+
+ @Test
+ void initializeNoIpAddress() {
+ bridgeConfiguration.setProperties(new HashMap<String, Object>());
+
+ fixture.initialize();
+
+ ThingStatusInfo expectedStatus = ThingStatusInfoBuilder
+ .create(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR)
+ .withDescription("@text/offline.conf-error-empty-ip").build();
+ verify(thingHandlerCallback).statusUpdated(thing, expectedStatus);
+ }
+
+ @Test
+ void initializeNoPassword() {
+ HashMap<String, Object> properties = new HashMap<String, Object>();
+ properties.put("ipAddress", "localhost");
+ bridgeConfiguration.setProperties(properties);
+
+ fixture.initialize();
+
+ ThingStatusInfo expectedStatus = ThingStatusInfoBuilder
+ .create(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR)
+ .withDescription("@text/offline.conf-error-empty-password").build();
+ verify(thingHandlerCallback).statusUpdated(thing, expectedStatus);
+ }
+
+ @Test
+ void checkBridgeAccess() throws InterruptedException, TimeoutException, ExecutionException {
+ Request request = mock(Request.class);
+ when(httpClient.createRequest(any(), eq(HttpMethod.GET))).thenReturn(request);
+ ContentResponse contentResponse = mock(ContentResponse.class);
+ when(request.send()).thenReturn(contentResponse);
+ when(contentResponse.getStatus()).thenReturn(200);
+
+ assertTrue(fixture.checkBridgeAccess());
+ }
+
+ @Test
+ void checkBridgeAccessRestResponseError() throws InterruptedException, TimeoutException, ExecutionException {
+ Request request = mock(Request.class);
+ when(httpClient.createRequest(any(), eq(HttpMethod.GET))).thenReturn(request);
+ ContentResponse contentResponse = mock(ContentResponse.class);
+ when(request.send()).thenReturn(contentResponse);
+ when(contentResponse.getStatus()).thenReturn(400);
+
+ assertFalse(fixture.checkBridgeAccess());
+ }
+
+ @ParameterizedTest
+ @MethodSource("org.openhab.binding.boschshc.internal.tests.common.CommonTestUtils#getExecutionAndTimeoutExceptionArguments()")
+ void checkBridgeAccessRestException(Exception e) throws InterruptedException, TimeoutException, ExecutionException {
+ Request request = mock(Request.class);
+ when(httpClient.createRequest(any(), eq(HttpMethod.GET))).thenReturn(request);
+ when(request.send()).thenThrow(e);
+
+ assertFalse(fixture.checkBridgeAccess());
+ }
+
+ @Test
+ void getPublicInformation() throws InterruptedException, BoschSHCException, ExecutionException, TimeoutException {
+ fixture.getPublicInformation();
+
+ verify(httpClient).createRequest(any(), same(HttpMethod.GET));
+ verify(httpClient).sendRequest(any(), same(PublicInformation.class), any(), isNull());
+ }
}
*/
package org.openhab.binding.boschshc.internal.devices.bridge;
-import static org.junit.jupiter.api.Assertions.*;
-import static org.mockito.ArgumentMatchers.*;
-import static org.mockito.Mockito.*;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.same;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.openhab.binding.boschshc.internal.devices.bridge.dto.UserDefinedState;
import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException;
import org.openhab.binding.boschshc.internal.exceptions.LongPollingFailedException;
+import org.openhab.binding.boschshc.internal.tests.common.CommonTestUtils;
import com.google.gson.JsonObject;
import com.google.gson.JsonSyntaxException;
assertTrue(longPollResultItem.isState());
}
- @Test
- void startSubscriptionFailure()
+ @ParameterizedTest
+ @MethodSource("org.openhab.binding.boschshc.internal.tests.common.CommonTestUtils#getBoschShcAndExecutionAndTimeoutAndInterruptedExceptionArguments()")
+ void startSubscriptionFailureHandleExceptions(Exception exception)
throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
- when(httpClient.sendRequest(any(), same(SubscribeResult.class), any(), any()))
- .thenThrow(new ExecutionException("Subscription failed.", null));
+ when(httpClient.sendRequest(any(), same(SubscribeResult.class), any(), any())).thenThrow(exception);
LongPollingFailedException e = assertThrows(LongPollingFailedException.class, () -> fixture.start(httpClient));
- assertTrue(e.getMessage().contains("Subscription failed."));
+ assertThat(e.getCause(), instanceOf(exception.getClass()));
+ assertThat(e.getMessage(), containsString(CommonTestUtils.TEST_EXCEPTION_MESSAGE));
}
- @Test
- void startLongPollFailure() throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
+ @ParameterizedTest
+ @MethodSource("org.openhab.binding.boschshc.internal.tests.common.CommonTestUtils#getExceutionExceptionAndRuntimeExceptionArguments()")
+ void startLongPollFailure(Exception exception)
+ throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
when(httpClient.getBoschShcUrl(anyString())).thenCallRealMethod();
Request request = mock(Request.class);
BufferingResponseListener bufferingResponseListener = (BufferingResponseListener) completeListener.getValue();
Result result = mock(Result.class);
- ExecutionException exception = new ExecutionException("test exception", null);
when(result.getFailure()).thenReturn(exception);
bufferingResponseListener.onComplete(result);
*/
package org.openhab.binding.boschshc.internal.devices.bridge;
-import static org.mockito.Mockito.*;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import java.util.List;
import java.util.UUID;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpStatus;
+import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
+import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.openhab.binding.boschshc.internal.devices.bridge.dto.Scenario;
import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException;
.toArray(Exception[]::new);
}
+ private @NonNullByDefault({}) ScenarioHandler fixture;
+
+ private @NonNullByDefault({}) @Mock BoschHttpClient httpClient;
+ private @NonNullByDefault({}) @Mock Request request;
+
+ @BeforeEach
+ void beforeEach() {
+ fixture = new ScenarioHandler();
+ }
+
@Test
void triggerScenarioShouldSendPOSTToBoschAPI() throws Exception {
// GIVEN
- final var httpClient = mock(BoschHttpClient.class);
- final var request = mock(Request.class);
final var contentResponse = mock(ContentResponse.class);
when(httpClient.getBoschSmartHomeUrl(anyString())).thenReturn("http://localhost/smartHome/scenarios")
.thenReturn("http://localhost/smartHome/scenarios/1234/triggers");
when(request.send()).thenReturn(contentResponse);
when(contentResponse.getStatus()).thenReturn(HttpStatus.OK_200);
- final var handler = new ScenarioHandler();
-
// WHEN
- handler.triggerScenario(httpClient, "Scenario 1");
+ fixture.triggerScenario(httpClient, "Scenario 1");
// THEN
verify(httpClient).getBoschSmartHomeUrl("scenarios");
}
@Test
- void triggerScenarioShouldNoSendPOSTToScenarioNameDoesNotExist() throws Exception {
+ void triggerScenarioShouldNotSendPOSTToScenarioNameDoesNotExist() throws Exception {
// GIVEN
- final var httpClient = mock(BoschHttpClient.class);
- final var request = mock(Request.class);
when(httpClient.getBoschSmartHomeUrl(anyString())).thenReturn("http://localhost/smartHome/scenarios")
.thenReturn("http://localhost/smartHome/scenarios/1234/triggers");
when(httpClient.createRequest(anyString(), any(HttpMethod.class))).thenReturn(request).thenReturn(request);
when(httpClient.sendRequest(any(Request.class), any(), any(), any())).thenReturn(existingScenarios);
- final var handler = new ScenarioHandler();
-
// WHEN
- handler.triggerScenario(httpClient, "not existing Scenario");
+ fixture.triggerScenario(httpClient, "not existing Scenario");
// THEN
verify(httpClient).getBoschSmartHomeUrl("scenarios");
@MethodSource("exceptionData")
void triggerScenarioShouldNotPanicIfBoschAPIThrowsException(final Exception exception) throws Exception {
// GIVEN
- final var httpClient = mock(BoschHttpClient.class);
- final var request = mock(Request.class);
when(httpClient.getBoschSmartHomeUrl(anyString())).thenReturn("http://localhost/smartHome/scenarios")
.thenReturn("http://localhost/smartHome/scenarios/1234/triggers");
when(httpClient.createRequest(anyString(), any(HttpMethod.class))).thenReturn(request);
when(httpClient.sendRequest(any(Request.class), any(), any(), any())).thenThrow(exception);
- final var handler = new ScenarioHandler();
-
// WHEN
- handler.triggerScenario(httpClient, "Scenario 1");
+ fixture.triggerScenario(httpClient, "Scenario 1");
// THEN
verify(httpClient).getBoschSmartHomeUrl("scenarios");
@Test
void triggerScenarioShouldNotPanicIfPOSTIsNotSuccessful() throws Exception {
// GIVEN
- final var httpClient = mock(BoschHttpClient.class);
- final var request = mock(Request.class);
final var contentResponse = mock(ContentResponse.class);
when(httpClient.getBoschSmartHomeUrl(anyString())).thenReturn("http://localhost/smartHome/scenarios")
.thenReturn("http://localhost/smartHome/scenarios/1234/triggers");
when(request.send()).thenReturn(contentResponse);
when(contentResponse.getStatus()).thenReturn(HttpStatus.METHOD_NOT_ALLOWED_405);
- final var handler = new ScenarioHandler();
-
// WHEN
- handler.triggerScenario(httpClient, "Scenario 1");
+ fixture.triggerScenario(httpClient, "Scenario 1");
// THEN
verify(httpClient).getBoschSmartHomeUrl("scenarios");
@MethodSource("httpExceptionData")
void triggerScenarioShouldNotPanicIfPOSTThrowsException(final Exception exception) throws Exception {
// GIVEN
- final var httpClient = mock(BoschHttpClient.class);
- final var request = mock(Request.class);
when(httpClient.getBoschSmartHomeUrl(anyString())).thenReturn("http://localhost/smartHome/scenarios")
.thenReturn("http://localhost/smartHome/scenarios/1234/triggers");
when(httpClient.createRequest(anyString(), any(HttpMethod.class))).thenReturn(request).thenReturn(request);
when(httpClient.sendRequest(any(Request.class), any(), any(), any())).thenReturn(existingScenarios);
when(request.send()).thenThrow(exception);
- final var handler = new ScenarioHandler();
-
// WHEN
- handler.triggerScenario(httpClient, "Scenario 1");
+ fixture.triggerScenario(httpClient, "Scenario 1");
// THEN
verify(httpClient).getBoschSmartHomeUrl("scenarios");
verify(request).send();
}
+
+ @Test
+ void prettyLogScenarios() {
+ Scenario scenario1 = Scenario.createScenario("id1", "Scenario 1", "1708619045411");
+ Scenario scenario2 = Scenario.createScenario("id2", "Scenario 2", "1708619065445");
+ assertEquals(
+ "[\n" + " Scenario{name='Scenario 1', id='id1', lastTimeTriggered='1708619045411'}\n"
+ + " Scenario{name='Scenario 2', id='id2', lastTimeTriggered='1708619065445'}\n" + "]",
+ fixture.prettyLogScenarios(new Scenario[] { scenario1, scenario2 }));
+ }
}
--- /dev/null
+/**
+ * Copyright (c) 2010-2024 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.boschshc.internal.devices.bridge.dto;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Unit tests for {@link Scenario}.
+ *
+ * @author David Pace - Initial contribution
+ *
+ */
+class ScenarioTest {
+
+ private Scenario fixture;
+
+ @BeforeEach
+ protected void setUp() throws Exception {
+ fixture = Scenario.createScenario("abc", "My Scenario", "1708845918493");
+ }
+
+ @Test
+ void isValid() {
+ assertTrue(Scenario.isValid(new Scenario[] { fixture }));
+ assertFalse(Scenario.isValid(new Scenario[] { fixture, new Scenario() }));
+ }
+
+ @Test
+ void testToString() {
+ assertEquals("Scenario{name='My Scenario', id='abc', lastTimeTriggered='1708845918493'}", fixture.toString());
+ }
+}
package org.openhab.binding.boschshc.internal.devices.intrusion;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.openhab.binding.boschshc.internal.devices.AbstractBoschSHCHandlerTest;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.thing.ChannelUID;
+import org.openhab.core.thing.ThingStatus;
+import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.ThingTypeUID;
import com.google.gson.JsonElement;
assertEquals("0", armRequest.profileId);
}
+ @ParameterizedTest
+ @MethodSource("org.openhab.binding.boschshc.internal.tests.common.CommonTestUtils#getExecutionAndTimeoutAndInterruptedExceptionArguments()")
+ void testHandleCommandArmActionHandleExceptions(Exception e)
+ throws InterruptedException, TimeoutException, ExecutionException {
+ when(getBridgeHandler().postAction(any(), any())).thenThrow(e);
+
+ getFixture().handleCommand(new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_ARM_ACTION),
+ new StringType("0"));
+
+ verify(getBridgeHandler()).postAction(eq("intrusion/actions/arm"), armActionRequestCaptor.capture());
+ ArmActionRequest armRequest = armActionRequestCaptor.getValue();
+ assertEquals("0", armRequest.profileId);
+ }
+
@Test
void testHandleCommandDisarmAction() throws InterruptedException, TimeoutException, ExecutionException {
getFixture().handleCommand(new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_DISARM_ACTION),
verify(getBridgeHandler()).postAction("intrusion/actions/disarm");
}
+ @ParameterizedTest
+ @MethodSource("org.openhab.binding.boschshc.internal.tests.common.CommonTestUtils#getExecutionAndTimeoutAndInterruptedExceptionArguments()")
+ void testHandleCommandDisarmActionHandleExceptions(Exception e)
+ throws InterruptedException, TimeoutException, ExecutionException {
+ when(getBridgeHandler().postAction(any())).thenThrow(e);
+
+ getFixture().handleCommand(new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_DISARM_ACTION),
+ OnOffType.ON);
+
+ verify(getCallback()).statusUpdated(same(getThing()),
+ argThat(status -> status.getStatus().equals(ThingStatus.OFFLINE)
+ && status.getStatusDetail().equals(ThingStatusDetail.COMMUNICATION_ERROR)));
+ }
+
@Test
void testHandleCommandMuteAction() throws InterruptedException, TimeoutException, ExecutionException {
getFixture().handleCommand(new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_MUTE_ACTION),
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import java.util.concurrent.ExecutionException;
ChildProtectionServiceState state = childProtectionServiceStateCaptor.getValue();
assertTrue(state.childLockActive);
}
+
+ @Test
+ void testHandleCommandChildProtectionInvalidCommand()
+ throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
+ getFixture().handleCommand(
+ new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_CHILD_PROTECTION),
+ DecimalType.ZERO);
+ verify(getBridgeHandler(), times(0)).putState(eq(getDeviceID()), eq("ChildProtection"),
+ childProtectionServiceStateCaptor.capture());
+ }
}
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.same;
-import static org.mockito.Mockito.lenient;
-import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
-import java.util.stream.Stream;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
-import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.ArgumentCaptor;
import org.openhab.binding.boschshc.internal.devices.AbstractBoschSHCHandlerTest;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
-import org.openhab.core.thing.ThingStatusInfo;
import org.openhab.core.thing.ThingTypeUID;
-import org.openhab.core.thing.ThingUID;
/**
* Unit tests for UserStateHandlerTest
}
@ParameterizedTest()
- @MethodSource("provideExceptions")
- void testHandleCommandSetStateUpdatesThingStatusOnException(Exception mockException)
+ @MethodSource("org.openhab.binding.boschshc.internal.tests.common.CommonTestUtils#getExecutionExceptionAndInterruptedExceptionArguments()")
+ void testHandleCommandSetStateUpdatesThingStatusOnException(Exception exception)
throws InterruptedException, TimeoutException, ExecutionException {
- reset(getCallback());
- lenient().when(getBridgeHandler().putState(anyString(), anyString(), any(UserStateServiceState.class)))
- .thenThrow(mockException);
+ when(getBridgeHandler().putState(anyString(), anyString(), any(UserStateServiceState.class)))
+ .thenThrow(exception);
var channel = new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_USER_DEFINED_STATE);
+
getFixture().handleCommand(channel, OnOffType.ON);
- verify(getCallback()).getBridge(any(ThingUID.class));
+ verify(getCallback()).statusUpdated(same(getThing()),
+ argThat(status -> status.getStatus().equals(ThingStatus.OFFLINE)
+ && status.getStatusDetail().equals(ThingStatusDetail.COMMUNICATION_ERROR)));
+ }
+
+ @Test
+ void initializeWithoutId() {
+ when(getThing().getConfiguration()).thenReturn(new Configuration());
- ThingStatusInfo expectedStatusInfo = new ThingStatusInfo(ThingStatus.OFFLINE,
- ThingStatusDetail.COMMUNICATION_ERROR,
- String.format("Error while putting user-defined state for %s", channel.getThingUID().getId()));
- verify(getCallback()).statusUpdated(same(getThing()), eq(expectedStatusInfo));
+ getFixture().initialize();
+
+ verify(getCallback()).statusUpdated(same(getThing()),
+ argThat(status -> status.getStatus().equals(ThingStatus.OFFLINE)
+ && status.getStatusDetail().equals(ThingStatusDetail.CONFIGURATION_ERROR)));
}
- private static Stream<Arguments> provideExceptions() {
- return Stream.of(Arguments.of(new TimeoutException("test exception")),
- Arguments.of(new InterruptedException("test exception")));
+ @ParameterizedTest
+ @MethodSource("org.openhab.binding.boschshc.internal.tests.common.CommonTestUtils#getBoschShcAndExecutionAndTimeoutAndInterruptedExceptionArguments()")
+ void initializeHandleExceptions(Exception e)
+ throws BoschSHCException, InterruptedException, TimeoutException, ExecutionException {
+ when(getBridgeHandler().getUserStateInfo(anyString())).thenThrow(e);
+
+ getFixture().initialize();
+
+ verify(getCallback()).statusUpdated(same(getThing()),
+ argThat(status -> status.getStatus().equals(ThingStatus.OFFLINE)
+ && status.getStatusDetail().equals(ThingStatusDetail.CONFIGURATION_ERROR)));
}
}
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.net.ConnectException;
import org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants;
import org.openhab.binding.boschshc.internal.devices.bridge.dto.PublicInformation;
import org.openhab.core.config.discovery.DiscoveryResult;
+import org.openhab.core.io.net.http.HttpClientFactory;
import org.openhab.core.thing.ThingUID;
/**
@Test
void testCreateResult() throws Exception {
DiscoveryResult result = fixture.createResult(shcBridge);
+
assertNotNull(result);
assertThat(result.getBindingId(), is(BoschSHCBindingConstants.BINDING_ID));
assertThat(result.getThingUID().getId(), is("192-168-0-123"));
@Test
void testCreateResultOtherDevice() throws Exception {
DiscoveryResult result = fixture.createResult(otherDevice);
+
assertNull(result);
}
@Test
void testCreateResultNoIPAddress() throws Exception {
when(shcBridge.getHostAddresses()).thenReturn(new String[] { "" });
+
DiscoveryResult result = fixture.createResult(shcBridge);
+
assertNull(result);
}
@Test
void testGetThingUID() throws Exception {
ThingUID thingUID = fixture.getThingUID(shcBridge);
+
assertNotNull(thingUID);
assertThat(thingUID.getBindingId(), is(BoschSHCBindingConstants.BINDING_ID));
assertThat(thingUID.getId(), is("192-168-0-123"));
void testGetBridgeAddress() throws Exception {
@Nullable
PublicInformation bridgeInformation = fixture.discoverBridge("192.168.0.123");
+
assertThat(bridgeInformation, not(nullValue()));
assertThat(bridgeInformation.shcIpAddress, is("192.168.0.123"));
}
void testGetPublicInformationFromPossibleBridgeAddress() throws Exception {
@Nullable
PublicInformation bridgeInformation = fixture.getPublicInformationFromPossibleBridgeAddress("192.168.0.123");
+
assertThat(bridgeInformation, not(nullValue()));
assertThat(bridgeInformation.shcIpAddress, is("192.168.0.123"));
}
when(contentResponse.getContentAsString()).thenReturn("{\"nothing\":\"useful\"}");
fixture = new BridgeDiscoveryParticipant(mockHttpClient);
+
assertThat(fixture.getPublicInformationFromPossibleBridgeAddress("192.168.0.123"), is(nullValue()));
}
when(contentResponse.getStatus()).thenReturn(HttpStatus.BAD_REQUEST_400);
fixture = new BridgeDiscoveryParticipant(mockHttpClient);
+
assertThat(fixture.getPublicInformationFromPossibleBridgeAddress("192.168.0.123"), is(nullValue()));
}
PublicInformation result2 = fixture.getOrComputePublicInformation("192.168.0.123");
assertSame(result, result2);
}
+
+ @Test
+ void testPublicConstructor() {
+ HttpClientFactory httpClientFactory = mock(HttpClientFactory.class);
+
+ fixture = new BridgeDiscoveryParticipant(httpClientFactory);
+
+ verify(httpClientFactory).createHttpClient(eq(BoschSHCBindingConstants.BINDING_ID), any());
+ }
}
import static org.mockito.Mockito.when;
import java.util.ArrayList;
+import java.util.List;
import java.util.UUID;
import org.eclipse.jdt.annotation.NonNullByDefault;
when(bridgeHandler.getThing()).thenReturn(mockBridge);
}
+ @Test
+ void initialize() {
+ fixture.initialize();
+ verify(bridgeHandler).registerDiscoveryListener(fixture);
+ }
+
@Test
void testStartScan() throws InterruptedException {
mockBridgeCalls();
+ Device device = new Device();
+ device.name = "My Smart Plug";
+ device.deviceModel = "PSM";
+ device.id = "hdm:HomeMaticIP:3014F711A00004953859F31B";
+ device.deviceServiceIds = List.of("PowerMeter", "PowerSwitch", "PowerSwitchProgram", "Routing");
+
+ List<Device> devices = new ArrayList<>();
+ devices.add(device);
+ when(bridgeHandler.getDevices()).thenReturn(devices);
+
+ UserDefinedState userDefinedState = new UserDefinedState();
+ userDefinedState.setName("My State");
+ userDefinedState.setId("23d34fa6-382a-444d-8aae-89c706e22158");
+ userDefinedState.setState(true);
+
+ List<UserDefinedState> userDefinedStates = new ArrayList<>();
+ userDefinedStates.add(userDefinedState);
+ when(bridgeHandler.getUserStates()).thenReturn(userDefinedStates);
+
fixture.activate();
fixture.startScan();
verify(discoveryListener, times(2)).thingDiscovered(any(), any());
}
+ @Test
+ void dispose() {
+ Bridge thing = mock(Bridge.class);
+ when(thing.getUID()).thenReturn(new ThingUID(BoschSHCBindingConstants.THING_TYPE_SHC, "shc123456"));
+ when(bridgeHandler.getThing()).thenReturn(thing);
+ fixture.dispose();
+ verify(bridgeHandler).unregisterDiscoveryListener();
+ }
+
@Test
void getThingTypeUIDLightControl2ChildDevice() {
Device device = new Device();
subject.setState(true);
assertEquals(OnOffType.ON, subject.toOnOffType());
}
+
+ @Test
+ void testToString() {
+ assertEquals("UserStateServiceState{state=false, type='userdefinedstates'}", subject.toString());
+ }
}
--- /dev/null
+/**
+ * Copyright (c) 2010-2024 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.boschshc.internal.tests.common;
+
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException;
+
+/**
+ * Common utilities used in unit tests.
+ *
+ * @author David Pace - Initial contribution
+ *
+ */
+@NonNullByDefault
+public final class CommonTestUtils {
+
+ public static final String TEST_EXCEPTION_MESSAGE = "Test exception";
+
+ private CommonTestUtils() {
+ // Utility Class
+ }
+
+ public static List<Exception> getExecutionExceptionAndInterruptedExceptionArguments() {
+ return List.of(new ExecutionException(TEST_EXCEPTION_MESSAGE, null),
+ new InterruptedException(TEST_EXCEPTION_MESSAGE));
+ }
+
+ public static List<Exception> getExceutionExceptionAndRuntimeExceptionArguments() {
+ return List.of(new ExecutionException(TEST_EXCEPTION_MESSAGE, null),
+ new RuntimeException(TEST_EXCEPTION_MESSAGE));
+ }
+
+ public static List<Exception> getBoschShcAndExecutionAndTimeoutExceptionArguments() {
+ return List.of(new BoschSHCException(TEST_EXCEPTION_MESSAGE),
+ new ExecutionException(TEST_EXCEPTION_MESSAGE, null), new TimeoutException(TEST_EXCEPTION_MESSAGE));
+ }
+
+ public static List<Exception> getBoschShcAndExecutionAndTimeoutAndInterruptedExceptionArguments() {
+ return List.of(new BoschSHCException(TEST_EXCEPTION_MESSAGE),
+ new ExecutionException(TEST_EXCEPTION_MESSAGE, null), new TimeoutException(TEST_EXCEPTION_MESSAGE),
+ new InterruptedException(TEST_EXCEPTION_MESSAGE));
+ }
+
+ public static List<Exception> getExecutionAndTimeoutAndInterruptedExceptionArguments() {
+ return List.of(new ExecutionException(TEST_EXCEPTION_MESSAGE, null),
+ new TimeoutException(TEST_EXCEPTION_MESSAGE), new InterruptedException(TEST_EXCEPTION_MESSAGE));
+ }
+
+ public static List<Exception> getExecutionAndTimeoutExceptionArguments() {
+ return List.of(new ExecutionException(TEST_EXCEPTION_MESSAGE, null),
+ new TimeoutException(TEST_EXCEPTION_MESSAGE));
+ }
+}