From 8306210a13be8f6eb5f1355a6b075dae419a9ea1 Mon Sep 17 00:00:00 2001 From: David Pace Date: Mon, 20 Feb 2023 08:56:29 +0100 Subject: [PATCH] [boschshc] Add unit tests (#14426) Signed-off-by: David Pace --- .../devices/BoschSHCBindingConstants.java | 2 +- .../devices/bridge/BridgeHandler.java | 13 +- ...stractBatteryPoweredDeviceHandlerTest.java | 70 ++-- .../AbstractBoschSHCDeviceHandlerTest.java | 4 +- ....java => AbstractBoschSHCHandlerTest.java} | 37 +- .../AbstractPowerSwitchHandlerTest.java | 83 +++-- .../AbstractSmokeDetectorHandlerTest.java | 29 +- .../devices/BoschSHCHandlerFactoryTest.java | 68 ++++ .../devices/bridge/BoschHttpClientTest.java | 167 ++++++++- .../bridge/BridgeConfigurationTest.java | 35 ++ .../devices/bridge/BridgeHandlerTest.java | 345 +++++++++++++++++- .../devices/bridge/JsonRpcRequestTest.java | 69 ++++ .../devices/bridge/LongPollingTest.java | 327 +++++++++++++++++ .../bridge/dto/DeviceServiceDataTest.java | 40 ++ .../devices/bridge/dto/DeviceTest.java | 68 ++++ .../bridge/dto/LongPollResultTest.java | 11 +- .../devices/camera/CameraHandlerTest.java | 134 +++++++ .../ClimateControlHandlerTest.java | 98 +++++ .../IntrusionDetectionHandlerTest.java | 139 +++++++ .../MotionDetectorHandlerTest.java | 18 + .../ShutterControlHandlerTest.java | 114 ++++++ .../smartbulb/SmartBulbHandlerTest.java | 26 +- .../thermostat/ThermostatHandlerTest.java | 80 ++++ .../twinguard/TwinguardHandlerTest.java | 53 +++ .../WallThermostatHandlerTest.java | 33 ++ .../WindowContactHandlerTest.java | 23 ++ .../LongPollingFailedExceptionTest.java | 37 ++ .../PairingFailedExceptionTest.java | 53 +++ .../batterylevel/BatteryLevelTest.java | 2 + .../dto/BoschSHCServiceStateTest.java | 9 +- .../dto/JsonRestExceptionResponseTest.java | 48 +++ ...usionDetectionControlStateServiceTest.java | 24 +- ...rusionDetectionSystemStateServiceTest.java | 21 +- 33 files changed, 2130 insertions(+), 150 deletions(-) rename bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/{AbstractSHCHandlerTest.java => AbstractBoschSHCHandlerTest.java} (69%) create mode 100644 bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/BoschSHCHandlerFactoryTest.java create mode 100644 bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/bridge/BridgeConfigurationTest.java create mode 100644 bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/bridge/JsonRpcRequestTest.java create mode 100644 bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/bridge/LongPollingTest.java create mode 100644 bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/bridge/dto/DeviceServiceDataTest.java create mode 100644 bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/bridge/dto/DeviceTest.java create mode 100644 bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/camera/CameraHandlerTest.java create mode 100644 bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/climatecontrol/ClimateControlHandlerTest.java create mode 100644 bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/intrusion/IntrusionDetectionHandlerTest.java create mode 100644 bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/shuttercontrol/ShutterControlHandlerTest.java create mode 100644 bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/exceptions/LongPollingFailedExceptionTest.java create mode 100644 bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/exceptions/PairingFailedExceptionTest.java create mode 100644 bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/services/dto/JsonRestExceptionResponseTest.java diff --git a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/BoschSHCBindingConstants.java b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/BoschSHCBindingConstants.java index 52786a248..f97100d3e 100644 --- a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/BoschSHCBindingConstants.java +++ b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/BoschSHCBindingConstants.java @@ -28,7 +28,7 @@ import org.openhab.core.thing.ThingTypeUID; @NonNullByDefault public class BoschSHCBindingConstants { - private static final String BINDING_ID = "boschshc"; + public static final String BINDING_ID = "boschshc"; // List of all Thing Type UIDs public static final ThingTypeUID THING_TYPE_SHC = new ThingTypeUID(BINDING_ID, "shc"); diff --git a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/bridge/BridgeHandler.java b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/bridge/BridgeHandler.java index d8dab396c..5856316b4 100644 --- a/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/bridge/BridgeHandler.java +++ b/bundles/org.openhab.binding.boschshc/src/main/java/org/openhab/binding/boschshc/internal/devices/bridge/BridgeHandler.java @@ -46,6 +46,7 @@ import org.openhab.core.thing.ThingStatusDetail; import org.openhab.core.thing.binding.BaseBridgeHandler; import org.openhab.core.thing.binding.ThingHandler; import org.openhab.core.types.Command; +import org.osgi.framework.Bundle; import org.osgi.framework.FrameworkUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -95,8 +96,10 @@ public class BridgeHandler extends BaseBridgeHandler { @Override public void initialize() { - logger.debug("Initialize {} Version {}", FrameworkUtil.getBundle(getClass()).getSymbolicName(), - FrameworkUtil.getBundle(getClass()).getVersion()); + Bundle bundle = FrameworkUtil.getBundle(getClass()); + if (bundle != null) { + logger.debug("Initialize {} Version {}", bundle.getSymbolicName(), bundle.getVersion()); + } // Read configuration BridgeConfiguration config = getConfigAs(BridgeConfiguration.class); @@ -190,8 +193,10 @@ public class BridgeHandler extends BaseBridgeHandler { * to check if access if possible * pairs this Bosch SHC Bridge with the SHC if necessary * and starts the first log poll. + *

+ * This method is package-protected to enable unit testing. */ - private void initialAccess(BoschHttpClient httpClient) { + /* package */ void initialAccess(BoschHttpClient httpClient) { logger.debug("Initializing Bosch SHC Bridge: {} - HTTP client is: {}", this, httpClient); try { @@ -482,7 +487,7 @@ public class BridgeHandler extends BaseBridgeHandler { deviceId, errorResponse.statusCode, errorResponse.errorCode)); } } else { - return new BoschSHCException(String.format("Request for info for device %s failed with status code %d", + return new BoschSHCException(String.format("Request for info of device %s failed with status code %d", deviceId, statusCode)); } }); diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/AbstractBatteryPoweredDeviceHandlerTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/AbstractBatteryPoweredDeviceHandlerTest.java index 1a243671c..6e85cefdf 100644 --- a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/AbstractBatteryPoweredDeviceHandlerTest.java +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/AbstractBatteryPoweredDeviceHandlerTest.java @@ -12,13 +12,21 @@ */ package org.openhab.binding.boschshc.internal.devices; -import static org.mockito.Mockito.verify; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.*; + +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.openhab.binding.boschshc.internal.devices.bridge.dto.DeviceServiceData; +import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException; import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.OnOffType; import org.openhab.core.thing.ChannelUID; +import org.openhab.core.types.RefreshType; import org.openhab.core.types.UnDefType; import com.google.gson.JsonElement; @@ -35,8 +43,20 @@ import com.google.gson.JsonParser; public abstract class AbstractBatteryPoweredDeviceHandlerTest extends AbstractBoschSHCDeviceHandlerTest { + @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); + } + @Test - public void testProcessUpdate_BatteryLevel_LowBattery() { + public void testProcessUpdateBatteryLevelLowBattery() { JsonElement deviceServiceData = JsonParser.parseString("{ \n" + " \"@type\":\"DeviceServiceData\",\n" + " \"path\":\"/devices/hdm:ZigBee:000d6f0004b93361/services/BatteryLevel\",\n" + " \"id\":\"BatteryLevel\",\n" + " \"deviceId\":\"hdm:ZigBee:000d6f0004b93361\",\n" @@ -44,15 +64,13 @@ public abstract class AbstractBatteryPoweredDeviceHandlerTest type of the device handler to be tested */ +@NonNullByDefault public abstract class AbstractBoschSHCDeviceHandlerTest - extends AbstractSHCHandlerTest { + extends AbstractBoschSHCHandlerTest { @Override protected Configuration getConfiguration() { diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/AbstractSHCHandlerTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/AbstractBoschSHCHandlerTest.java similarity index 69% rename from bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/AbstractSHCHandlerTest.java rename to bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/AbstractBoschSHCHandlerTest.java index 18fe7ff95..a3ccf28d9 100644 --- a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/AbstractSHCHandlerTest.java +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/AbstractBoschSHCHandlerTest.java @@ -15,14 +15,20 @@ package org.openhab.binding.boschshc.internal.devices; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; +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.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.openhab.binding.boschshc.internal.devices.bridge.BridgeHandler; +import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException; import org.openhab.core.config.core.Configuration; import org.openhab.core.thing.Bridge; +import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.Thing; import org.openhab.core.thing.ThingStatus; import org.openhab.core.thing.ThingStatusDetail; @@ -38,32 +44,33 @@ import org.openhab.core.thing.binding.ThingHandlerCallback; * * @param type of the handler to be tested */ +@NonNullByDefault @ExtendWith(MockitoExtension.class) -public abstract class AbstractSHCHandlerTest { +public abstract class AbstractBoschSHCHandlerTest { private T fixture; - @Mock - private Thing thing; + private @Mock @NonNullByDefault({}) Thing thing; - @Mock - private Bridge bridge; + private @Mock @NonNullByDefault({}) Bridge bridge; - @Mock - private BridgeHandler bridgeHandler; + protected @Mock @NonNullByDefault({}) BridgeHandler bridgeHandler; - @Mock - private ThingHandlerCallback callback; + private @Mock @NonNullByDefault({}) ThingHandlerCallback callback; + + protected AbstractBoschSHCHandlerTest() { + this.fixture = createFixture(); + } @BeforeEach - public void beforeEach() { + void beforeEach() throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { fixture = createFixture(); lenient().when(thing.getUID()).thenReturn(getThingUID()); when(thing.getBridgeUID()).thenReturn(new ThingUID("boschshc", "shc", "myBridgeUID")); when(callback.getBridge(any())).thenReturn(bridge); fixture.setCallback(callback); when(bridge.getHandler()).thenReturn(bridgeHandler); - when(thing.getConfiguration()).thenReturn(getConfiguration()); + lenient().when(thing.getConfiguration()).thenReturn(getConfiguration()); fixture.initialize(); } @@ -80,6 +87,10 @@ public abstract class AbstractSHCHandlerTest { protected abstract ThingTypeUID getThingTypeUID(); + protected ChannelUID getChannelUID(String channelID) { + return new ChannelUID(getThingUID(), channelID); + } + protected Configuration getConfiguration() { return new Configuration(); } @@ -88,11 +99,11 @@ public abstract class AbstractSHCHandlerTest { return thing; } - public BridgeHandler getBridgeHandler() { + protected BridgeHandler getBridgeHandler() { return bridgeHandler; } - public ThingHandlerCallback getCallback() { + protected ThingHandlerCallback getCallback() { return callback; } diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/AbstractPowerSwitchHandlerTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/AbstractPowerSwitchHandlerTest.java index 1dc632bc6..6ec8f4b40 100644 --- a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/AbstractPowerSwitchHandlerTest.java +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/AbstractPowerSwitchHandlerTest.java @@ -13,7 +13,7 @@ package org.openhab.binding.boschshc.internal.devices; import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; import java.util.concurrent.ExecutionException; @@ -22,16 +22,19 @@ import java.util.concurrent.TimeoutException; import javax.measure.quantity.Energy; import javax.measure.quantity.Power; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException; +import org.openhab.binding.boschshc.internal.services.powermeter.dto.PowerMeterServiceState; 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.library.types.QuantityType; -import org.openhab.core.thing.ChannelUID; -import org.openhab.core.thing.ThingUID; +import org.openhab.core.library.unit.Units; +import org.openhab.core.types.RefreshType; import com.google.gson.JsonElement; import com.google.gson.JsonParser; @@ -43,30 +46,42 @@ import com.google.gson.JsonParser; * * @param type of the handler to be tested */ +@NonNullByDefault public abstract class AbstractPowerSwitchHandlerTest extends AbstractBoschSHCDeviceHandlerTest { - @Captor - private ArgumentCaptor serviceStateCaptor; + private @Captor @NonNullByDefault({}) ArgumentCaptor serviceStateCaptor; - @Captor - private ArgumentCaptor> powerCaptor; + private @Captor @NonNullByDefault({}) ArgumentCaptor> powerCaptor; - @Captor - private ArgumentCaptor> energyCaptor; + private @Captor @NonNullByDefault({}) ArgumentCaptor> energyCaptor; + + @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))) + .thenReturn(powerSwitchServiceState); + + PowerMeterServiceState powerMeterServiceState = new PowerMeterServiceState(); + powerMeterServiceState.powerConsumption = 12.34d; + powerMeterServiceState.energyConsumption = 56.78d; + lenient().when(bridgeHandler.getState(anyString(), eq("PowerMeter"), same(PowerMeterServiceState.class))) + .thenReturn(powerMeterServiceState); + } @Test - public void testHandleCommand() + public void testHandleCommandPowerSwitchChannel() throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { - - getFixture().handleCommand(new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_POWER_SWITCH), - OnOffType.ON); + getFixture().handleCommand(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_SWITCH), OnOffType.ON); verify(getBridgeHandler()).putState(eq(getDeviceID()), eq("PowerSwitch"), serviceStateCaptor.capture()); PowerSwitchServiceState state = serviceStateCaptor.getValue(); assertSame(PowerSwitchState.ON, state.switchState); - getFixture().handleCommand(new ChannelUID(new ThingUID(getThingTypeUID(), "abcdef"), - BoschSHCBindingConstants.CHANNEL_POWER_SWITCH), OnOffType.OFF); + getFixture().handleCommand(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_SWITCH), OnOffType.OFF); verify(getBridgeHandler(), times(2)).putState(eq(getDeviceID()), eq("PowerSwitch"), serviceStateCaptor.capture()); state = serviceStateCaptor.getValue(); @@ -74,36 +89,54 @@ public abstract class AbstractPowerSwitchHandlerTest powerValue = powerCaptor.getValue(); assertEquals(23, powerValue.intValue()); - verify(getCallback()).stateUpdated( - eq(new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_ENERGY_CONSUMPTION)), + verify(getCallback()).stateUpdated(eq(getChannelUID(BoschSHCBindingConstants.CHANNEL_ENERGY_CONSUMPTION)), energyCaptor.capture()); QuantityType energyValue = energyCaptor.getValue(); assertEquals(42, energyValue.intValue()); } + + @Test + public void testHandleCommandRefreshPowerSwitchChannel() { + getFixture().handleCommand(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_SWITCH), RefreshType.REFRESH); + verify(getCallback()).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_SWITCH), OnOffType.ON); + } + + @Test + public void testHandleCommandRefreshPowerConsumptionChannel() { + getFixture().handleCommand(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_CONSUMPTION), + RefreshType.REFRESH); + verify(getCallback()).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_CONSUMPTION), + new QuantityType(12.34d, Units.WATT)); + } + + @Test + public void testHandleCommandRefreshEnergyConsumptionChannel() { + getFixture().handleCommand(getChannelUID(BoschSHCBindingConstants.CHANNEL_ENERGY_CONSUMPTION), + RefreshType.REFRESH); + verify(getCallback()).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_ENERGY_CONSUMPTION), + new QuantityType(56.78d, Units.WATT_HOUR)); + } } diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/AbstractSmokeDetectorHandlerTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/AbstractSmokeDetectorHandlerTest.java index a04452bdf..76898ffae 100644 --- a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/AbstractSmokeDetectorHandlerTest.java +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/AbstractSmokeDetectorHandlerTest.java @@ -14,8 +14,7 @@ package org.openhab.binding.boschshc.internal.devices; import static org.junit.jupiter.api.Assertions.assertSame; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.*; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; @@ -28,9 +27,14 @@ import org.openhab.binding.boschshc.internal.devices.smokedetector.SmokeDetector import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException; import org.openhab.binding.boschshc.internal.services.smokedetectorcheck.SmokeDetectorCheckState; import org.openhab.binding.boschshc.internal.services.smokedetectorcheck.dto.SmokeDetectorCheckServiceState; +import org.openhab.core.library.types.OnOffType; import org.openhab.core.library.types.PlayPauseType; 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.ThingStatusInfo; +import org.openhab.core.thing.binding.builder.ThingStatusInfoBuilder; import com.google.gson.JsonElement; import com.google.gson.JsonParser; @@ -51,7 +55,6 @@ public abstract class AbstractSmokeDetectorHandlerTest fields = ReflectionSupport.findFields(BoschHttpClient.class, + f -> f.getName().equalsIgnoreCase("logger"), HierarchyTraversalMode.TOP_DOWN); + Field field = fields.iterator().next(); + field.setAccessible(true); + field.set(mockedHttpClient, mockedLogger); + + Request request = mock(Request.class); + when(mockedHttpClient.createRequest(anyString(), same(HttpMethod.GET))).thenReturn(request); + ContentResponse response = mock(ContentResponse.class); + when(request.send()).thenReturn(response); + when(response.getStatus()).thenReturn(500); + assertFalse(mockedHttpClient.isOnline()); + } + + @Test + void isOnlineMockedResponse() throws InterruptedException, TimeoutException, ExecutionException, + IllegalArgumentException, IllegalAccessException { + BoschHttpClient mockedHttpClient = mock(BoschHttpClient.class); + when(mockedHttpClient.isOnline()).thenCallRealMethod(); + when(mockedHttpClient.getPublicInformationUrl()).thenCallRealMethod(); + + // mock a logger using reflection to avoid NPEs during logger calls + Logger mockedLogger = mock(Logger.class); + List fields = ReflectionSupport.findFields(BoschHttpClient.class, + f -> f.getName().equalsIgnoreCase("logger"), HierarchyTraversalMode.TOP_DOWN); + Field field = fields.iterator().next(); + field.setAccessible(true); + field.set(mockedHttpClient, mockedLogger); + + Request request = mock(Request.class); + when(mockedHttpClient.createRequest(anyString(), same(HttpMethod.GET))).thenReturn(request); + ContentResponse response = mock(ContentResponse.class); + when(request.send()).thenReturn(response); + when(response.getStatus()).thenReturn(200); + when(response.getContentAsString()).thenReturn("response"); + assertTrue(mockedHttpClient.isOnline()); + } + @Test void doPairing() throws InterruptedException { assertFalse(httpClient.doPairing()); @@ -104,16 +164,103 @@ class BoschHttpClientTest { @Test void createRequestWithObject() { - Request request = httpClient.createRequest("https://127.0.0.1", HttpMethod.GET, "someData"); + BinarySwitchServiceState binarySwitchState = new BinarySwitchServiceState(); + binarySwitchState.on = true; + Request request = httpClient.createRequest("https://127.0.0.1", HttpMethod.GET, binarySwitchState); assertNotNull(request); + assertEquals("{\"on\":true,\"stateType\":\"binarySwitchState\",\"@type\":\"binarySwitchState\"}", + StandardCharsets.UTF_8.decode(request.getContent().iterator().next()).toString()); } @Test - void sendRequest() { - Request request = httpClient.createRequest("https://127.0.0.1", HttpMethod.GET); - // Null pointer exception is expected, because localhost will not answer request - assertThrows(NullPointerException.class, () -> { - httpClient.sendRequest(request, SubscribeResult.class, SubscribeResult::isValid, null); - }); + void sendRequest() throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { + Request request = mock(Request.class); + ContentResponse response = mock(ContentResponse.class); + when(request.send()).thenReturn(response); + when(response.getStatus()).thenReturn(200); + when(response.getContentAsString()).thenReturn("{\"jsonrpc\": \"2.0\", \"result\": \"test result\"}"); + + SubscribeResult subscribeResult = httpClient.sendRequest(request, SubscribeResult.class, + SubscribeResult::isValid, null); + assertEquals("2.0", subscribeResult.getJsonrpc()); + assertEquals("test result", subscribeResult.getResult()); + } + + @Test + void sendRequestResponseError() + throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { + Request request = mock(Request.class); + ContentResponse response = mock(ContentResponse.class); + when(request.send()).thenReturn(response); + when(response.getStatus()).thenReturn(500); + ExecutionException e = assertThrows(ExecutionException.class, + () -> httpClient.sendRequest(request, SubscribeResult.class, SubscribeResult::isValid, null)); + assertEquals("Request failed with status code 500", e.getMessage()); + } + + @Test + void sendRequestResponseErrorWithErrorHandler() + throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { + Request request = mock(Request.class); + ContentResponse response = mock(ContentResponse.class); + when(request.send()).thenReturn(response); + when(response.getStatus()).thenReturn(500); + when(response.getContentAsString()).thenReturn( + "{\"@type\": \"JsonRestExceptionResponseEntity\", \"errorCode\": \"500\", \"statusCode\": \"500\"}"); + + BoschSHCException e = assertThrows(BoschSHCException.class, () -> httpClient.sendRequest(request, Device.class, + Device::isValid, (Integer statusCode, String content) -> { + return new BoschSHCException("test exception"); + })); + assertEquals("test exception", e.getMessage()); + } + + @Test + void sendRequestEmptyResponse() + throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { + Request request = mock(Request.class); + ContentResponse response = mock(ContentResponse.class); + when(request.send()).thenReturn(response); + when(response.getStatus()).thenReturn(200); + ExecutionException e = assertThrows(ExecutionException.class, + () -> httpClient.sendRequest(request, SubscribeResult.class, SubscribeResult::isValid, null)); + assertEquals( + "Received no content in response, expected type org.openhab.binding.boschshc.internal.devices.bridge.dto.SubscribeResult", + e.getMessage()); + } + + @Test + void sendRequestInvalidResponse() + throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { + Request request = mock(Request.class); + ContentResponse response = mock(ContentResponse.class); + when(request.send()).thenReturn(response); + when(response.getStatus()).thenReturn(200); + when(response.getContentAsString()).thenReturn( + "{\"@type\": \"JsonRestExceptionResponseEntity\", \"errorCode\": \"500\", \"statusCode\": \"500\"}"); + ExecutionException e = assertThrows(ExecutionException.class, + () -> httpClient.sendRequest(request, SubscribeResult.class, sr -> { + return false; + }, null)); + String actualMessage = e.getMessage(); + assertTrue(actualMessage.contains( + "Received invalid content for type org.openhab.binding.boschshc.internal.devices.bridge.dto.SubscribeResult:")); + } + + @Test + void sendRequestInvalidSyntaxInResponse() + throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { + Request request = mock(Request.class); + ContentResponse response = mock(ContentResponse.class); + when(request.send()).thenReturn(response); + when(response.getStatus()).thenReturn(200); + when(response.getContentAsString()).thenReturn("{\"@type\": \"JsonRestExceptionResponseEntity}"); + ExecutionException e = assertThrows(ExecutionException.class, + () -> httpClient.sendRequest(request, SubscribeResult.class, sr -> { + return false; + }, null)); + assertEquals( + "Received invalid content in response, expected type org.openhab.binding.boschshc.internal.devices.bridge.dto.SubscribeResult: com.google.gson.stream.MalformedJsonException: Unterminated string at line 1 column 44 path $.@type", + e.getMessage()); } } diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/bridge/BridgeConfigurationTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/bridge/BridgeConfigurationTest.java new file mode 100644 index 000000000..4a6a8d38f --- /dev/null +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/bridge/BridgeConfigurationTest.java @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2010-2023 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; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.Test; + +/** + * Unit tests for {@link BridgeConfiguration}. + * + * @author David Pace - Initial contribution + * + */ +@NonNullByDefault +public class BridgeConfigurationTest { + + @Test + void testConstructor() { + BridgeConfiguration fixture = new BridgeConfiguration(); + assertEquals("", fixture.ipAddress); + assertEquals("", fixture.password); + } +} diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/bridge/BridgeHandlerTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/bridge/BridgeHandlerTest.java index 3c61340c1..1f77e1741 100644 --- a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/bridge/BridgeHandlerTest.java +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/bridge/BridgeHandlerTest.java @@ -12,45 +12,101 @@ */ package org.openhab.binding.boschshc.internal.devices.bridge; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -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 static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.Map; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; +import java.util.function.BiFunction; 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.HttpMethod; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +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.SubscribeResult; +import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException; +import org.openhab.binding.boschshc.internal.services.binaryswitch.dto.BinarySwitchServiceState; import org.openhab.binding.boschshc.internal.services.intrusion.actions.arm.dto.ArmActionRequest; +import org.openhab.binding.boschshc.internal.services.intrusion.dto.AlarmState; +import org.openhab.binding.boschshc.internal.services.intrusion.dto.ArmingState; +import org.openhab.binding.boschshc.internal.services.intrusion.dto.IntrusionDetectionSystemState; +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.thing.Bridge; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingStatus; +import org.openhab.core.thing.ThingStatusDetail; +import org.openhab.core.thing.binding.ThingHandlerCallback; +import org.openhab.core.thing.binding.builder.ThingStatusInfoBuilder; /** * Unit tests for the {@link BridgeHandler}. - * + * * @author David Pace - Initial contribution * */ @NonNullByDefault class BridgeHandlerTest { - @Nullable - private BridgeHandler fixture; + private @NonNullByDefault({}) BridgeHandler fixture; - @Nullable - private BoschHttpClient httpClient; + private @NonNullByDefault({}) BoschHttpClient httpClient; + + private @NonNullByDefault({}) ThingHandlerCallback thingHandlerCallback; + + @BeforeAll + static void beforeAll() throws IOException { + Path mavenTargetFolder = Paths.get("target"); + assertTrue(Files.exists(mavenTargetFolder), "Maven target folder does not exist."); + System.setProperty("openhab.userdata", mavenTargetFolder.toFile().getAbsolutePath()); + Path etc = mavenTargetFolder.resolve("etc"); + if (!Files.exists(etc)) { + Files.createDirectory(etc); + } + } @BeforeEach - void beforeEach() { + void beforeEach() throws Exception { Bridge bridge = mock(Bridge.class); fixture = new BridgeHandler(bridge); + + thingHandlerCallback = mock(ThingHandlerCallback.class); + fixture.setCallback(thingHandlerCallback); + + Configuration bridgeConfiguration = new Configuration(); + Map properties = new HashMap<>(); + properties.put("ipAddress", "localhost"); + properties.put("password", "test"); + bridgeConfiguration.setProperties(properties); + + Thing thing = mock(Bridge.class); + when(thing.getConfiguration()).thenReturn(bridgeConfiguration); + // this calls initialize() as well + fixture.thingUpdated(thing); + + // shut down the real HTTP client + if (fixture.httpClient != null) { + fixture.httpClient.stop(); + } + + // use a mocked HTTP client httpClient = mock(BoschHttpClient.class); fixture.httpClient = httpClient; } @@ -69,4 +125,265 @@ class BridgeHandlerTest { verify(httpClient).createRequest(eq(url), same(HttpMethod.POST), same(request)); verify(mockRequest).send(); } + + @Test + void initialAccessHttpClientOffline() { + fixture.initialAccess(httpClient); + } + + @Test + void initialAccessHttpClientOnline() throws InterruptedException { + when(httpClient.isOnline()).thenReturn(true); + fixture.initialAccess(httpClient); + } + + @Test + void initialAccessAccessPossible() + throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { + when(httpClient.isOnline()).thenReturn(true); + when(httpClient.isAccessPossible()).thenReturn(true); + when(httpClient.getBoschSmartHomeUrl(anyString())).thenCallRealMethod(); + when(httpClient.getBoschShcUrl(anyString())).thenCallRealMethod(); + + // mock a request and response to obtain rooms + Request roomsRequest = mock(Request.class); + ContentResponse roomsResponse = mock(ContentResponse.class); + when(roomsResponse.getStatus()).thenReturn(200); + when(roomsResponse.getContentAsString()).thenReturn( + "[{\"@type\":\"room\",\"id\":\"hz_1\",\"iconId\":\"icon_room_bedroom\",\"name\":\"Bedroom\"}]"); + when(roomsRequest.send()).thenReturn(roomsResponse); + when(httpClient.createRequest(contains("/rooms"), same(HttpMethod.GET))).thenReturn(roomsRequest); + + // mock a request and response to obtain devices + Request devicesRequest = mock(Request.class); + ContentResponse devicesResponse = mock(ContentResponse.class); + when(devicesResponse.getStatus()).thenReturn(200); + when(devicesResponse.getContentAsString()).thenReturn("[{\"@type\":\"device\",\r\n" + + " \"rootDeviceId\":\"64-da-a0-02-14-9b\",\r\n" + + " \"id\":\"hdm:HomeMaticIP:3014F711A00004953859F31B\",\r\n" + + " \"deviceServiceIds\":[\"PowerMeter\",\"PowerSwitch\",\"PowerSwitchProgram\",\"Routing\"],\r\n" + + " \"manufacturer\":\"BOSCH\",\r\n" + " \"roomId\":\"hz_3\",\r\n" + " \"deviceModel\":\"PSM\",\r\n" + + " \"serial\":\"3014F711A00004953859F31B\",\r\n" + " \"profile\":\"GENERIC\",\r\n" + + " \"name\":\"Coffee Machine\",\r\n" + " \"status\":\"AVAILABLE\",\r\n" + " \"childDeviceIds\":[]\r\n" + + " }]"); + when(devicesRequest.send()).thenReturn(devicesResponse); + when(httpClient.createRequest(contains("/devices"), same(HttpMethod.GET))).thenReturn(devicesRequest); + + SubscribeResult subscribeResult = new SubscribeResult(); + when(httpClient.sendRequest(any(), same(SubscribeResult.class), any(), any())).thenReturn(subscribeResult); + + Request longPollRequest = mock(Request.class); + when(httpClient.createRequest(anyString(), same(HttpMethod.POST), + argThat((JsonRpcRequest r) -> r.method.equals("RE/longPoll")))).thenReturn(longPollRequest); + + fixture.initialAccess(httpClient); + verify(thingHandlerCallback).statusUpdated(any(), + eq(ThingStatusInfoBuilder.create(ThingStatus.ONLINE, ThingStatusDetail.NONE).build())); + } + + @Test + void getState() 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(response.getContentAsString()).thenReturn("{\r\n" + " \"@type\": \"systemState\",\r\n" + + " \"systemAvailability\": {\r\n" + " \"@type\": \"systemAvailabilityState\",\r\n" + + " \"available\": true,\r\n" + " \"deleted\": false\r\n" + " },\r\n" + + " \"armingState\": {\r\n" + " \"@type\": \"armingState\",\r\n" + + " \"state\": \"SYSTEM_DISARMED\",\r\n" + " \"deleted\": false\r\n" + " },\r\n" + + " \"alarmState\": {\r\n" + " \"@type\": \"alarmState\",\r\n" + + " \"value\": \"ALARM_OFF\",\r\n" + " \"incidents\": [],\r\n" + + " \"deleted\": false\r\n" + " },\r\n" + " \"activeConfigurationProfile\": {\r\n" + + " \"@type\": \"activeConfigurationProfile\",\r\n" + " \"deleted\": false\r\n" + + " },\r\n" + " \"securityGapState\": {\r\n" + " \"@type\": \"securityGapState\",\r\n" + + " \"securityGaps\": [],\r\n" + " \"deleted\": false\r\n" + " },\r\n" + + " \"deleted\": false\r\n" + " }"); + when(request.send()).thenReturn(response); + when(httpClient.createRequest(anyString(), same(HttpMethod.GET))).thenReturn(request); + + IntrusionDetectionSystemState state = fixture.getState("intrusion/states/system", + IntrusionDetectionSystemState.class); + assertNotNull(state); + assertTrue(state.systemAvailability.available); + assertSame(AlarmState.ALARM_OFF, state.alarmState.value); + assertSame(ArmingState.SYSTEM_DISARMED, state.armingState.state); + } + + @Test + void getDeviceState() throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { + when(httpClient.getBoschSmartHomeUrl(anyString())).thenCallRealMethod(); + when(httpClient.getBoschShcUrl(anyString())).thenCallRealMethod(); + when(httpClient.getServiceStateUrl(anyString(), 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(response.getContentAsString()) + .thenReturn("{\n" + " \"@type\": \"shutterContactState\",\n" + " \"value\": \"OPEN\"\n" + " }"); + when(request.send()).thenReturn(response); + when(httpClient.createRequest(anyString(), same(HttpMethod.GET))).thenReturn(request); + + ShutterContactServiceState state = fixture.getState("hdm:HomeMaticIP:3014D711A000009D545DEB39D", + "ShutterContact", ShutterContactServiceState.class); + assertNotNull(state); + assertSame(ShutterContactState.OPEN, state.value); + } + + @Test + void getDeviceInfo() 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); + when(httpClient.sendRequest(same(request), same(Device.class), any(), any())) + .thenReturn(DeviceTest.createTestDevice()); + + String deviceId = "hdm:HomeMaticIP:3014F711A00004953859F31B"; + Device device = fixture.getDeviceInfo(deviceId); + assertEquals(deviceId, device.id); + } + + @Test + void getDeviceInfoErrorCases() + 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> errorResponseHandlerCaptor = ArgumentCaptor + .forClass(BiFunction.class); + + when(httpClient.sendRequest(same(request), same(Device.class), any(), errorResponseHandlerCaptor.capture())) + .thenReturn(DeviceTest.createTestDevice()); + + String deviceId = "hdm:HomeMaticIP:3014F711A00004953859F31B"; + fixture.getDeviceInfo(deviceId); + + BiFunction errorResponseHandler = errorResponseHandlerCaptor.getValue(); + Exception e = errorResponseHandler.apply(500, + "{\"@type\":\"JsonRestExceptionResponseEntity\",\"errorCode\": \"testErrorCode\",\"statusCode\": 500}"); + assertEquals( + "Request for info of device hdm:HomeMaticIP:3014F711A00004953859F31B 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 device hdm:HomeMaticIP:3014F711A00004953859F31B failed with status code 500", + e.getMessage()); + } + + @Test + void getServiceData() throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { + when(httpClient.getBoschSmartHomeUrl(anyString())).thenCallRealMethod(); + when(httpClient.getBoschShcUrl(anyString())).thenCallRealMethod(); + when(httpClient.getServiceUrl(anyString(), 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(response.getContentAsString()).thenReturn("{ \n" + " \"@type\":\"DeviceServiceData\",\n" + + " \"path\":\"/devices/hdm:ZigBee:000d6f0004b93361/services/BatteryLevel\",\n" + + " \"id\":\"BatteryLevel\",\n" + " \"deviceId\":\"hdm:ZigBee:000d6f0004b93361\",\n" + + " \"faults\":{ \n" + " \"entries\":[\n" + " {\n" + + " \"type\":\"LOW_BATTERY\",\n" + " \"category\":\"WARNING\"\n" + " }\n" + + " ]\n" + " }\n" + "}"); + when(request.send()).thenReturn(response); + when(httpClient.createRequest(anyString(), same(HttpMethod.GET))).thenReturn(request); + + DeviceServiceData serviceData = fixture.getServiceData("hdm:ZigBee:000d6f0004b93361", "BatteryLevel"); + assertNotNull(serviceData); + Faults faults = serviceData.faults; + assertNotNull(faults); + assertEquals("LOW_BATTERY", faults.entries.get(0).type); + } + + @Test + void getServiceDataError() throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { + when(httpClient.getBoschSmartHomeUrl(anyString())).thenCallRealMethod(); + when(httpClient.getBoschShcUrl(anyString())).thenCallRealMethod(); + when(httpClient.getServiceUrl(anyString(), anyString())).thenCallRealMethod(); + + Request request = mock(Request.class); + when(request.header(anyString(), anyString())).thenReturn(request); + ContentResponse response = mock(ContentResponse.class); + when(response.getStatus()).thenReturn(500); + when(response.getContentAsString()).thenReturn( + "{\"@type\":\"JsonRestExceptionResponseEntity\",\"errorCode\": \"testErrorCode\",\"statusCode\": 500}"); + when(request.send()).thenReturn(response); + when(httpClient.createRequest(anyString(), same(HttpMethod.GET))).thenReturn(request); + when(httpClient.sendRequest(same(request), same(Device.class), any(), any())) + .thenReturn(DeviceTest.createTestDevice()); + + BoschSHCException e = assertThrows(BoschSHCException.class, + () -> fixture.getServiceData("hdm:ZigBee:000d6f0004b93361", "BatteryLevel")); + assertEquals( + "State request with URL https://null:8444/smarthome/devices/hdm:ZigBee:000d6f0004b93361/services/BatteryLevel failed with status code 500 and error code testErrorCode", + e.getMessage()); + } + + @Test + void getServiceDataErrorNoRestExceptionResponse() + throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { + when(httpClient.getBoschSmartHomeUrl(anyString())).thenCallRealMethod(); + when(httpClient.getBoschShcUrl(anyString())).thenCallRealMethod(); + when(httpClient.getServiceUrl(anyString(), anyString())).thenCallRealMethod(); + + Request request = mock(Request.class); + when(request.header(anyString(), anyString())).thenReturn(request); + ContentResponse response = mock(ContentResponse.class); + when(response.getStatus()).thenReturn(500); + when(response.getContentAsString()).thenReturn(""); + when(request.send()).thenReturn(response); + when(httpClient.createRequest(anyString(), same(HttpMethod.GET))).thenReturn(request); + + BoschSHCException e = assertThrows(BoschSHCException.class, + () -> fixture.getServiceData("hdm:ZigBee:000d6f0004b93361", "BatteryLevel")); + assertEquals( + "State request with URL https://null:8444/smarthome/devices/hdm:ZigBee:000d6f0004b93361/services/BatteryLevel failed with status code 500", + e.getMessage()); + } + + @Test + void putState() throws InterruptedException, TimeoutException, ExecutionException { + when(httpClient.getBoschSmartHomeUrl(anyString())).thenCallRealMethod(); + when(httpClient.getBoschShcUrl(anyString())).thenCallRealMethod(); + when(httpClient.getServiceStateUrl(anyString(), anyString())).thenCallRealMethod(); + + Request request = mock(Request.class); + when(request.header(anyString(), anyString())).thenReturn(request); + ContentResponse response = mock(ContentResponse.class); + + when(httpClient.createRequest(anyString(), same(HttpMethod.PUT), any(BinarySwitchServiceState.class))) + .thenReturn(request); + when(request.send()).thenReturn(response); + + BinarySwitchServiceState binarySwitchState = new BinarySwitchServiceState(); + binarySwitchState.on = true; + fixture.putState("hdm:ZigBee:f0d1b80000f2a3e9", "BinarySwitch", binarySwitchState); + } + + @AfterEach + void afterEach() throws Exception { + fixture.dispose(); + } } diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/bridge/JsonRpcRequestTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/bridge/JsonRpcRequestTest.java new file mode 100644 index 000000000..7d3b43b39 --- /dev/null +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/bridge/JsonRpcRequestTest.java @@ -0,0 +1,69 @@ +/** + * Copyright (c) 2010-2023 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; + +import static org.junit.jupiter.api.Assertions.*; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + * Unit tests for {@link JsonRpcRequest}. + * + * @author David Pace - Initial contribution + * + */ +@NonNullByDefault +public class JsonRpcRequestTest { + + private @NonNullByDefault({}) JsonRpcRequest fixture; + + @BeforeEach + protected void setUp() throws Exception { + fixture = new JsonRpcRequest("2.0", "RE/longPoll", new String[] { "subscriptionId", "20" }); + } + + @Test + public void testConstructor() { + assertEquals("2.0", fixture.getJsonrpc()); + assertEquals("RE/longPoll", fixture.getMethod()); + assertArrayEquals(new String[] { "subscriptionId", "20" }, fixture.getParams()); + } + + @Test + public void testNoArgConstructor() { + fixture = new JsonRpcRequest(); + assertEquals("", fixture.getJsonrpc()); + assertEquals("", fixture.getMethod()); + assertArrayEquals(new String[0], fixture.getParams()); + } + + @Test + public void testSetJsonrpc() { + fixture.setJsonrpc("test"); + assertEquals("test", fixture.getJsonrpc()); + } + + @Test + public void testSetMethod() { + fixture.setMethod("RE/subscribe"); + assertEquals("RE/subscribe", fixture.getMethod()); + } + + @Test + public void testSetParams() { + fixture.setParams(new String[] { "com/bosch/sh/remote/*", null }); + assertArrayEquals(new String[] { "com/bosch/sh/remote/*", null }, fixture.getParams()); + } +} diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/bridge/LongPollingTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/bridge/LongPollingTest.java new file mode 100644 index 000000000..524d23fc1 --- /dev/null +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/bridge/LongPollingTest.java @@ -0,0 +1,327 @@ +/** + * Copyright (c) 2010-2023 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; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.AbstractExecutorService; +import java.util.concurrent.Callable; +import java.util.concurrent.Delayed; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.function.Consumer; + +import org.eclipse.jdt.annotation.NonNull; +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.Result; +import org.eclipse.jetty.client.util.BufferingResponseListener; +import org.eclipse.jetty.http.HttpMethod; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.openhab.binding.boschshc.internal.devices.bridge.dto.DeviceServiceData; +import org.openhab.binding.boschshc.internal.devices.bridge.dto.LongPollResult; +import org.openhab.binding.boschshc.internal.devices.bridge.dto.SubscribeResult; +import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException; +import org.openhab.binding.boschshc.internal.exceptions.LongPollingFailedException; + +import com.google.gson.JsonObject; + +/** + * Unit tests for {@link LongPolling}. + * + * @author David Pace - Initial contribution + * + */ +@NonNullByDefault +@ExtendWith(MockitoExtension.class) +public class LongPollingTest { + + /** + * A dummy implementation of {@link ScheduledFuture}. + *

+ * This is required because we can not return null in the executor service test implementation (see + * below). + * + * @author David Pace - Initial contribution + * + * @param The result type returned by this Future + */ + private static class NullScheduledFuture implements ScheduledFuture { + + @Override + public long getDelay(@Nullable TimeUnit unit) { + return 0; + } + + @Override + public int compareTo(@Nullable Delayed o) { + return 0; + } + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + return false; + } + + @Override + public boolean isCancelled() { + return false; + } + + @Override + public boolean isDone() { + return false; + } + + @Override + public T get() throws InterruptedException, ExecutionException { + return null; + } + + @Override + public T get(long timeout, @Nullable TimeUnit unit) + throws InterruptedException, ExecutionException, TimeoutException { + return null; + } + } + + /** + * Executor service implementation that runs all runnables in the same thread in order to enable deterministic + * testing. + * + * @author David Pace - Initial contribution + * + */ + private static class SameThreadExecutorService extends AbstractExecutorService implements ScheduledExecutorService { + + private volatile boolean terminated; + + @Override + public void shutdown() { + terminated = true; + } + + @NonNullByDefault({}) + @Override + public List shutdownNow() { + return Collections.emptyList(); + } + + @Override + public boolean isShutdown() { + return terminated; + } + + @Override + public boolean isTerminated() { + return terminated; + } + + @Override + public boolean awaitTermination(long timeout, @Nullable TimeUnit unit) throws InterruptedException { + shutdown(); + return terminated; + } + + @Override + public void execute(@Nullable Runnable command) { + if (command != null) { + // execute in the same thread in unit tests + command.run(); + } + } + + @Override + public ScheduledFuture schedule(@Nullable Runnable command, long delay, @Nullable TimeUnit unit) { + // not used in this tests + return new NullScheduledFuture(); + } + + @Override + public ScheduledFuture schedule(@Nullable Callable callable, long delay, @Nullable TimeUnit unit) { + return new NullScheduledFuture(); + } + + @Override + public ScheduledFuture scheduleAtFixedRate(@Nullable Runnable command, long initialDelay, long period, + @Nullable TimeUnit unit) { + if (command != null) { + command.run(); + } + return new NullScheduledFuture(); + } + + @Override + public ScheduledFuture scheduleWithFixedDelay(@Nullable Runnable command, long initialDelay, long delay, + @Nullable TimeUnit unit) { + if (command != null) { + command.run(); + } + return new NullScheduledFuture(); + } + } + + private @NonNullByDefault({}) LongPolling fixture; + + private @NonNullByDefault({}) BoschHttpClient httpClient; + + private @Mock @NonNullByDefault({}) Consumer<@NonNull LongPollResult> longPollHandler; + + private @Mock @NonNullByDefault({}) Consumer<@NonNull Throwable> failureHandler; + + @BeforeEach + void beforeEach() { + fixture = new LongPolling(new SameThreadExecutorService(), longPollHandler, failureHandler); + httpClient = mock(BoschHttpClient.class); + } + + @Test + void start() throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { + // when(httpClient.getBoschSmartHomeUrl(anyString())).thenCallRealMethod(); + when(httpClient.getBoschShcUrl(anyString())).thenCallRealMethod(); + + Request subscribeRequest = mock(Request.class); + when(httpClient.createRequest(anyString(), same(HttpMethod.POST), + argThat((JsonRpcRequest r) -> r.method.equals("RE/subscribe")))).thenReturn(subscribeRequest); + SubscribeResult subscribeResult = new SubscribeResult(); + when(httpClient.sendRequest(any(), same(SubscribeResult.class), any(), any())).thenReturn(subscribeResult); + + Request longPollRequest = mock(Request.class); + when(httpClient.createRequest(anyString(), same(HttpMethod.POST), + argThat((JsonRpcRequest r) -> r.method.equals("RE/longPoll")))).thenReturn(longPollRequest); + + fixture.start(httpClient); + + ArgumentCaptor completeListener = ArgumentCaptor.forClass(CompleteListener.class); + verify(longPollRequest).send(completeListener.capture()); + + BufferingResponseListener bufferingResponseListener = (BufferingResponseListener) completeListener.getValue(); + + String longPollResultJSON = "{\"result\":[{\"path\":\"/devices/hdm:HomeMaticIP:3014F711A0001916D859A8A9/services/PowerSwitch\",\"@type\":\"DeviceServiceData\",\"id\":\"PowerSwitch\",\"state\":{\"@type\":\"powerSwitchState\",\"switchState\":\"ON\"},\"deviceId\":\"hdm:HomeMaticIP:3014F711A0001916D859A8A9\"}],\"jsonrpc\":\"2.0\"}\n"; + Response response = mock(Response.class); + bufferingResponseListener.onContent(response, + ByteBuffer.wrap(longPollResultJSON.getBytes(StandardCharsets.UTF_8))); + + Result result = mock(Result.class); + bufferingResponseListener.onComplete(result); + + ArgumentCaptor longPollResultCaptor = ArgumentCaptor.forClass(LongPollResult.class); + verify(longPollHandler).accept(longPollResultCaptor.capture()); + LongPollResult longPollResult = longPollResultCaptor.getValue(); + assertEquals(1, longPollResult.result.size()); + DeviceServiceData longPollResultItem = longPollResult.result.get(0); + assertEquals("hdm:HomeMaticIP:3014F711A0001916D859A8A9", longPollResultItem.deviceId); + assertEquals("/devices/hdm:HomeMaticIP:3014F711A0001916D859A8A9/services/PowerSwitch", longPollResultItem.path); + assertEquals("PowerSwitch", longPollResultItem.id); + JsonObject stateObject = (JsonObject) longPollResultItem.state; + assertNotNull(stateObject); + assertEquals("ON", stateObject.get("switchState").getAsString()); + } + + @Test + void startSubscriptionFailure() + throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { + when(httpClient.sendRequest(any(), same(SubscribeResult.class), any(), any())) + .thenThrow(new ExecutionException("Subscription failed.", null)); + + LongPollingFailedException e = assertThrows(LongPollingFailedException.class, () -> fixture.start(httpClient)); + assertTrue(e.getMessage().contains("Subscription failed.")); + } + + @Test + void startLongPollFailure() throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { + when(httpClient.getBoschShcUrl(anyString())).thenCallRealMethod(); + + Request request = mock(Request.class); + when(httpClient.createRequest(anyString(), same(HttpMethod.POST), any(JsonRpcRequest.class))) + .thenReturn(request); + SubscribeResult subscribeResult = new SubscribeResult(); + when(httpClient.sendRequest(any(), same(SubscribeResult.class), any(), any())).thenReturn(subscribeResult); + + Request longPollRequest = mock(Request.class); + when(httpClient.createRequest(anyString(), same(HttpMethod.POST), + argThat((JsonRpcRequest r) -> r.method.equals("RE/longPoll")))).thenReturn(longPollRequest); + + fixture.start(httpClient); + + ArgumentCaptor completeListener = ArgumentCaptor.forClass(CompleteListener.class); + verify(longPollRequest).send(completeListener.capture()); + + 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); + + ArgumentCaptor throwableCaptor = ArgumentCaptor.forClass(Throwable.class); + verify(failureHandler).accept(throwableCaptor.capture()); + Throwable t = throwableCaptor.getValue(); + assertEquals("Unexpected exception during long polling request", t.getMessage()); + assertSame(exception, t.getCause()); + } + + @Test + void startSubscriptionInvalid() + throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { + when(httpClient.getBoschShcUrl(anyString())).thenCallRealMethod(); + + Request subscribeRequest = mock(Request.class); + when(httpClient.createRequest(anyString(), same(HttpMethod.POST), + argThat((JsonRpcRequest r) -> r.method.equals("RE/subscribe")))).thenReturn(subscribeRequest); + SubscribeResult subscribeResult = new SubscribeResult(); + when(httpClient.sendRequest(any(), same(SubscribeResult.class), any(), any())).thenReturn(subscribeResult); + + Request longPollRequest = mock(Request.class); + when(httpClient.createRequest(anyString(), same(HttpMethod.POST), + argThat((JsonRpcRequest r) -> r.method.equals("RE/longPoll")))).thenReturn(longPollRequest); + + fixture.start(httpClient); + + ArgumentCaptor completeListener = ArgumentCaptor.forClass(CompleteListener.class); + verify(longPollRequest).send(completeListener.capture()); + + BufferingResponseListener bufferingResponseListener = (BufferingResponseListener) completeListener.getValue(); + + String longPollResultJSON = "{\"jsonrpc\":\"2.0\",\"error\": {\"code\":-32001,\"message\":\"No subscription with id: e8fei62b0-0\"}}\n"; + Response response = mock(Response.class); + bufferingResponseListener.onContent(response, + ByteBuffer.wrap(longPollResultJSON.getBytes(StandardCharsets.UTF_8))); + + Result result = mock(Result.class); + bufferingResponseListener.onComplete(result); + } + + @AfterEach + void afterEach() { + fixture.stop(); + } +} diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/bridge/dto/DeviceServiceDataTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/bridge/dto/DeviceServiceDataTest.java new file mode 100644 index 000000000..cfe653674 --- /dev/null +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/bridge/dto/DeviceServiceDataTest.java @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2010-2023 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 org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + * Unit tests for {@link DeviceServiceData}. + * + * @author David Pace - Initial contribution + * + */ +public class DeviceServiceDataTest { + + private DeviceServiceData fixture; + + @BeforeEach + void beforeEach() { + fixture = new DeviceServiceData(); + fixture.deviceId = "64-da-a0-02-14-9b"; + } + + @Test + public void testToString() { + assertEquals("64-da-a0-02-14-9b state: DeviceServiceData", fixture.toString()); + } +} diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/bridge/dto/DeviceTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/bridge/dto/DeviceTest.java new file mode 100644 index 000000000..9ee3ac9ed --- /dev/null +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/bridge/dto/DeviceTest.java @@ -0,0 +1,68 @@ +/** + * Copyright (c) 2010-2023 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.*; + +import java.util.Collections; +import java.util.List; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + * Unit tests for {@link Device}. + * + * @author David Pace - Initial contribution + * + */ +@NonNullByDefault +public class DeviceTest { + + public static Device createTestDevice() { + Device device = new Device(); + device.type = "device"; + device.rootDeviceId = "64-da-a0-02-14-9b"; + device.id = "hdm:HomeMaticIP:3014F711A00004953859F31B"; + device.deviceServiceIds = Collections + .unmodifiableList(List.of("PowerMeter", "PowerSwitch", "PowerSwitchProgram", "Routing")); + device.manufacturer = "BOSCH"; + device.roomId = "hz_3"; + device.deviceModel = "PSM"; + device.serial = "3014F711A00004953859F31B"; + device.profile = "GENERIC"; + device.name = "Coffee Machine"; + device.status = "AVAILABLE"; + return device; + } + + private @NonNullByDefault({}) Device fixture; + + @BeforeEach + void beforeEach() { + fixture = createTestDevice(); + } + + @Test + void testIsValid() { + assertTrue(Device.isValid(fixture)); + } + + @Test + public void testToString() { + assertEquals( + "Type device; RootDeviceId: 64-da-a0-02-14-9b; Id: hdm:HomeMaticIP:3014F711A00004953859F31B; Device Service Ids: PowerMeter, PowerSwitch, PowerSwitchProgram, Routing; Manufacturer: BOSCH; Room Id: hz_3; Device Model: PSM; Serial: 3014F711A00004953859F31B; Profile: GENERIC; Name: Coffee Machine; Status: AVAILABLE; Child Device Ids: null ", + fixture.toString()); + } +} diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/bridge/dto/LongPollResultTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/bridge/dto/LongPollResultTest.java index 0cb373d36..fb8a3e3c2 100644 --- a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/bridge/dto/LongPollResultTest.java +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/bridge/dto/LongPollResultTest.java @@ -12,8 +12,7 @@ */ package org.openhab.binding.boschshc.internal.devices.bridge.dto; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.*; import org.eclipse.jdt.annotation.NonNullByDefault; import org.junit.jupiter.api.Test; @@ -30,13 +29,11 @@ public class LongPollResultTest { private final Gson gson = new Gson(); @Test - public void noResultsForErrorResult() { + void noResultsForErrorResult() { LongPollResult longPollResult = gson.fromJson( "{\"jsonrpc\":\"2.0\", \"error\": { \"code\":-32001, \"message\":\"No subscription with id: e8fei62b0-0\" } }", LongPollResult.class); - assertNotEquals(null, longPollResult); - if (longPollResult != null) { - assertEquals(null, longPollResult.result); - } + assertNotNull(longPollResult); + assertEquals(null, longPollResult.result); } } diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/camera/CameraHandlerTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/camera/CameraHandlerTest.java new file mode 100644 index 000000000..b2b791909 --- /dev/null +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/camera/CameraHandlerTest.java @@ -0,0 +1,134 @@ +/** + * Copyright (c) 2010-2023 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.camera; + +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.*; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.openhab.binding.boschshc.internal.devices.AbstractBoschSHCDeviceHandlerTest; +import org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants; +import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException; +import org.openhab.binding.boschshc.internal.services.cameranotification.CameraNotificationState; +import org.openhab.binding.boschshc.internal.services.cameranotification.dto.CameraNotificationServiceState; +import org.openhab.binding.boschshc.internal.services.privacymode.PrivacyModeState; +import org.openhab.binding.boschshc.internal.services.privacymode.dto.PrivacyModeServiceState; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.ThingTypeUID; + +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; + +/** + * Unit tests for {@link CameraHandler}. + * + * @author David Pace - Initial contribution + * + */ +@NonNullByDefault +public class CameraHandlerTest extends AbstractBoschSHCDeviceHandlerTest { + + private @Captor @NonNullByDefault({}) ArgumentCaptor privacyModeServiceStateCaptor; + + private @Captor @NonNullByDefault({}) ArgumentCaptor cameraNotificationServiceStateCaptor; + + @Override + protected CameraHandler createFixture() { + return new CameraHandler(getThing()); + } + + @Override + protected ThingTypeUID getThingTypeUID() { + return BoschSHCBindingConstants.THING_TYPE_CAMERA_360; + } + + @Override + protected String getDeviceID() { + return "8e28ce2d-e7bf-3e3d-8e3a-a78de61b493e"; + } + + @Test + public void testHandleCommandPrivacyMode() + throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { + getFixture().handleCommand(new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_PRIVACY_MODE), + OnOffType.ON); + verify(getBridgeHandler()).putState(eq(getDeviceID()), eq("PrivacyMode"), + privacyModeServiceStateCaptor.capture()); + PrivacyModeServiceState state = privacyModeServiceStateCaptor.getValue(); + assertSame(PrivacyModeState.ENABLED, state.value); + + getFixture().handleCommand(new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_PRIVACY_MODE), + OnOffType.OFF); + verify(getBridgeHandler(), times(2)).putState(eq(getDeviceID()), eq("PrivacyMode"), + privacyModeServiceStateCaptor.capture()); + state = privacyModeServiceStateCaptor.getValue(); + assertSame(PrivacyModeState.DISABLED, state.value); + } + + @Test + public void testHandleCommandCameraNotification() + throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { + getFixture().handleCommand( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_CAMERA_NOTIFICATION), + OnOffType.ON); + verify(getBridgeHandler()).putState(eq(getDeviceID()), eq("CameraNotification"), + cameraNotificationServiceStateCaptor.capture()); + CameraNotificationServiceState state = cameraNotificationServiceStateCaptor.getValue(); + assertSame(CameraNotificationState.ENABLED, state.value); + + getFixture().handleCommand( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_CAMERA_NOTIFICATION), + OnOffType.OFF); + verify(getBridgeHandler(), times(2)).putState(eq(getDeviceID()), eq("CameraNotification"), + cameraNotificationServiceStateCaptor.capture()); + state = cameraNotificationServiceStateCaptor.getValue(); + assertSame(CameraNotificationState.DISABLED, state.value); + } + + @Test + public void testUpdateChannelsPrivacyModeState() { + JsonElement jsonObject = JsonParser.parseString("{\"@type\":\"privacyModeState\",\"value\":\"ENABLED\"}"); + getFixture().processUpdate("PrivacyMode", jsonObject); + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_PRIVACY_MODE), OnOffType.ON); + + jsonObject = JsonParser.parseString("{\"@type\":\"privacyModeState\",\"value\":\"DISABLED\"}"); + getFixture().processUpdate("PrivacyMode", jsonObject); + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_PRIVACY_MODE), OnOffType.OFF); + } + + @Test + public void testUpdateChannelsCameraNotificationState() { + JsonElement jsonObject = JsonParser + .parseString("{\"@type\":\"cameraNotificationState\",\"value\":\"ENABLED\"}"); + getFixture().processUpdate("CameraNotification", jsonObject); + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_CAMERA_NOTIFICATION), + OnOffType.ON); + + jsonObject = JsonParser.parseString("{\"@type\":\"cameraNotificationState\",\"value\":\"DISABLED\"}"); + getFixture().processUpdate("CameraNotification", jsonObject); + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_CAMERA_NOTIFICATION), + OnOffType.OFF); + } +} diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/climatecontrol/ClimateControlHandlerTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/climatecontrol/ClimateControlHandlerTest.java new file mode 100644 index 000000000..86dc3120b --- /dev/null +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/climatecontrol/ClimateControlHandlerTest.java @@ -0,0 +1,98 @@ +/** + * Copyright (c) 2010-2023 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.climatecontrol; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; + +import javax.measure.quantity.Temperature; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.openhab.binding.boschshc.internal.devices.AbstractBoschSHCDeviceHandlerTest; +import org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants; +import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException; +import org.openhab.binding.boschshc.internal.services.roomclimatecontrol.dto.RoomClimateControlServiceState; +import org.openhab.core.library.types.QuantityType; +import org.openhab.core.library.unit.SIUnits; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.ThingTypeUID; + +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; + +/** + * Unit tests for {@link ClimateControlHandler}. + * + * @author David Pace - Initial contribution + * + */ +@NonNullByDefault +public class ClimateControlHandlerTest extends AbstractBoschSHCDeviceHandlerTest { + + private @Captor @NonNullByDefault({}) ArgumentCaptor roomClimateControlServiceStateCaptor; + + @Override + protected String getDeviceID() { + return "hdm:ZigBee:abcd6fc012ad25b1"; + } + + @Override + protected ClimateControlHandler createFixture() { + return new ClimateControlHandler(getThing()); + } + + @Override + protected ThingTypeUID getThingTypeUID() { + return BoschSHCBindingConstants.THING_TYPE_CLIMATE_CONTROL; + } + + @Test + public void testHandleCommandRoomClimateControlService() + throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { + QuantityType temperature = new QuantityType<>(21.5, SIUnits.CELSIUS); + getFixture().handleCommand( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_SETPOINT_TEMPERATURE), + temperature); + verify(getBridgeHandler()).putState(eq(getDeviceID()), eq("RoomClimateControl"), + roomClimateControlServiceStateCaptor.capture()); + RoomClimateControlServiceState state = roomClimateControlServiceStateCaptor.getValue(); + assertEquals(temperature, state.getSetpointTemperatureState()); + } + + @Test + public void testUpdateChannelsTemperatureLevelService() { + JsonElement jsonObject = JsonParser.parseString( + "{\n" + " \"@type\": \"temperatureLevelState\",\n" + " \"temperature\": 21.5\n" + " }"); + getFixture().processUpdate("TemperatureLevel", jsonObject); + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_TEMPERATURE), + new QuantityType(21.5, SIUnits.CELSIUS)); + } + + @Test + public void testUpdateChannelsRoomClimateControlService() { + JsonElement jsonObject = JsonParser.parseString( + "{\n" + " \"@type\": \"climateControlState\",\n" + " \"setpointTemperature\": 21.5\n" + " }"); + getFixture().processUpdate("RoomClimateControl", jsonObject); + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_SETPOINT_TEMPERATURE), + new QuantityType(21.5, SIUnits.CELSIUS)); + } +} diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/intrusion/IntrusionDetectionHandlerTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/intrusion/IntrusionDetectionHandlerTest.java new file mode 100644 index 000000000..caf80c432 --- /dev/null +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/intrusion/IntrusionDetectionHandlerTest.java @@ -0,0 +1,139 @@ +/** + * Copyright (c) 2010-2023 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.intrusion; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.openhab.binding.boschshc.internal.devices.AbstractBoschSHCHandlerTest; +import org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants; +import org.openhab.binding.boschshc.internal.services.intrusion.actions.arm.dto.ArmActionRequest; +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.ThingTypeUID; + +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; + +/** + * Unit test for {@link IntrusionDetectionHandler}. + * + * @author David Pace - Initial contribution + * + */ +@NonNullByDefault +public class IntrusionDetectionHandlerTest extends AbstractBoschSHCHandlerTest { + + private @Captor @NonNullByDefault({}) ArgumentCaptor armActionRequestCaptor; + + @Override + protected IntrusionDetectionHandler createFixture() { + return new IntrusionDetectionHandler(getThing()); + } + + @Override + protected ThingTypeUID getThingTypeUID() { + return BoschSHCBindingConstants.THING_TYPE_INTRUSION_DETECTION_SYSTEM; + } + + @Test + public void testHandleCommandArmAction() throws InterruptedException, TimeoutException, ExecutionException { + 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 + public void testHandleCommandDisarmAction() throws InterruptedException, TimeoutException, ExecutionException { + getFixture().handleCommand(new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_DISARM_ACTION), + OnOffType.ON); + verify(getBridgeHandler()).postAction("intrusion/actions/disarm"); + } + + @Test + public void testHandleCommandMuteAction() throws InterruptedException, TimeoutException, ExecutionException { + getFixture().handleCommand(new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_MUTE_ACTION), + OnOffType.ON); + verify(getBridgeHandler()).postAction("intrusion/actions/mute"); + } + + @Test + public void testUpdateChannelsIntrusionDetectionSystemState() { + JsonElement jsonObject = JsonParser.parseString("{\n" + " \"@type\": \"systemState\",\n" + + " \"systemAvailability\": {\n" + " \"@type\": \"systemAvailabilityState\",\n" + + " \"available\": true,\n" + " \"deleted\": false\n" + " },\n" + + " \"armingState\": {\n" + " \"@type\": \"armingState\",\n" + + " \"state\": \"SYSTEM_DISARMED\",\n" + " \"deleted\": false\n" + " },\n" + + " \"alarmState\": {\n" + " \"@type\": \"alarmState\",\n" + + " \"value\": \"ALARM_OFF\",\n" + " \"incidents\": [],\n" + + " \"deleted\": false\n" + " },\n" + " \"activeConfigurationProfile\": {\n" + + " \"@type\": \"activeConfigurationProfile\",\n" + " \"deleted\": false\n" + + " },\n" + " \"securityGapState\": {\n" + " \"@type\": \"securityGapState\",\n" + + " \"securityGaps\": [],\n" + " \"deleted\": false\n" + " },\n" + + " \"deleted\": false\n" + " }\n"); + getFixture().processUpdate(BoschSHCBindingConstants.SERVICE_INTRUSION_DETECTION, jsonObject); + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_SYSTEM_AVAILABILITY), + OnOffType.ON); + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_ARMING_STATE), + new StringType("SYSTEM_DISARMED")); + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_ALARM_STATE), + new StringType("ALARM_OFF")); + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_ACTIVE_CONFIGURATION_PROFILE), + new StringType(null)); + } + + @Test + public void testUpdateChannelsIntrusionDetectionControlState() { + JsonElement jsonObject = JsonParser.parseString("{\n" + " \"@type\": \"intrusionDetectionControlState\",\n" + + " \"activeProfile\": \"0\",\n" + " \"alarmActivationDelayTime\": 30,\n" + " \"actuators\": [\n" + + " {\n" + " \"readonly\": false,\n" + " \"active\": true,\n" + + " \"id\": \"intrusion:video\"\n" + " },\n" + " {\n" + " \"readonly\": false,\n" + + " \"active\": false,\n" + " \"id\": \"intrusion:siren\"\n" + " }\n" + " ],\n" + + " \"remainingTimeUntilArmed\": 29559,\n" + " \"armActivationDelayTime\": 30,\n" + + " \"triggers\": [\n" + " {\n" + " \"readonly\": false,\n" + " \"active\": true,\n" + + " \"id\": \"hdm:ZigBee:000d6f0012f02378\"\n" + " }\n" + " ],\n" + + " \"value\": \"SYSTEM_ARMING\"\n" + " }"); + getFixture().processUpdate("IntrusionDetectionControl", jsonObject); + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_ARMING_STATE), + new StringType("SYSTEM_ARMING")); + } + + @Test + public void testUpdateChannelsSurveillanceAlarmState() { + JsonElement jsonObject = JsonParser.parseString("{\n" + " \"@type\": \"surveillanceAlarmState\",\n" + + " \"incidents\": [\n" + " {\n" + " \"triggerName\": \"Motion Detector\",\n" + + " \"locationId\": \"hz_5\",\n" + " \"location\": \"Living Room\",\n" + + " \"id\": \"hdm:ZigBee:000d6f0012f02342\",\n" + " \"time\": 1652615755336,\n" + + " \"type\": \"INTRUSION\"\n" + " }\n" + " ],\n" + " \"value\": \"ALARM_ON\"\n" + " }"); + getFixture().processUpdate("SurveillanceAlarm", jsonObject); + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_ALARM_STATE), + new StringType("ALARM_ON")); + } +} diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/motiondetector/MotionDetectorHandlerTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/motiondetector/MotionDetectorHandlerTest.java index f36f78805..418218426 100644 --- a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/motiondetector/MotionDetectorHandlerTest.java +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/motiondetector/MotionDetectorHandlerTest.java @@ -12,11 +12,19 @@ */ package org.openhab.binding.boschshc.internal.devices.motiondetector; +import static org.mockito.Mockito.verify; + import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.Test; import org.openhab.binding.boschshc.internal.devices.AbstractBatteryPoweredDeviceHandlerTest; import org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants; +import org.openhab.core.library.types.DateTimeType; +import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.ThingTypeUID; +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; + /** * Unit Tests for {@link MotionDetectorHandler}. * @@ -40,4 +48,14 @@ public class MotionDetectorHandlerTest extends AbstractBatteryPoweredDeviceHandl protected ThingTypeUID getThingTypeUID() { return BoschSHCBindingConstants.THING_TYPE_MOTION_DETECTOR; } + + @Test + public void testUpdateChannelsLatestMotionService() { + JsonElement jsonObject = JsonParser.parseString("{\n" + " \"@type\": \"latestMotionState\",\n" + + " \"latestMotionDetected\": \"2020-04-03T19:02:19.054Z\"\n" + " }"); + getFixture().processUpdate("LatestMotion", jsonObject); + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_LATEST_MOTION), + new DateTimeType("2020-04-03T19:02:19.054Z")); + } } diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/shuttercontrol/ShutterControlHandlerTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/shuttercontrol/ShutterControlHandlerTest.java new file mode 100644 index 000000000..e3a9a5a07 --- /dev/null +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/shuttercontrol/ShutterControlHandlerTest.java @@ -0,0 +1,114 @@ +/** + * Copyright (c) 2010-2023 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.shuttercontrol; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.*; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.openhab.binding.boschshc.internal.devices.AbstractBoschSHCDeviceHandlerTest; +import org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants; +import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException; +import org.openhab.binding.boschshc.internal.services.shuttercontrol.OperationState; +import org.openhab.binding.boschshc.internal.services.shuttercontrol.dto.ShutterControlServiceState; +import org.openhab.core.library.types.PercentType; +import org.openhab.core.library.types.StopMoveType; +import org.openhab.core.library.types.UpDownType; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.ThingTypeUID; + +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; + +/** + * Unit tests for {@link ShutterControlHandler}. + * + * @author David Pace - Initial contribution + * + */ +@NonNullByDefault +public class ShutterControlHandlerTest extends AbstractBoschSHCDeviceHandlerTest { + + private @Captor @NonNullByDefault({}) ArgumentCaptor shutterControlServiceStateCaptor; + + @Override + protected String getDeviceID() { + return "hdm:ZigBee:abcd6fc012ad25b1"; + } + + @Override + protected ShutterControlHandler createFixture() { + return new ShutterControlHandler(getThing()); + } + + @Override + protected ThingTypeUID getThingTypeUID() { + return BoschSHCBindingConstants.THING_TYPE_SHUTTER_CONTROL; + } + + @Test + public void testHandleCommandUpDownType() + throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { + getFixture().handleCommand(new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_LEVEL), + UpDownType.UP); + verify(getBridgeHandler()).putState(eq(getDeviceID()), eq("ShutterControl"), + shutterControlServiceStateCaptor.capture()); + ShutterControlServiceState state = shutterControlServiceStateCaptor.getValue(); + assertEquals(1d, state.level); + + getFixture().handleCommand(new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_LEVEL), + UpDownType.DOWN); + verify(getBridgeHandler(), times(2)).putState(eq(getDeviceID()), eq("ShutterControl"), + shutterControlServiceStateCaptor.capture()); + state = shutterControlServiceStateCaptor.getValue(); + assertEquals(0d, state.level); + } + + @Test + public void testHandleCommandStopMoveType() + throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { + getFixture().handleCommand(new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_LEVEL), + StopMoveType.STOP); + verify(getBridgeHandler()).putState(eq(getDeviceID()), eq("ShutterControl"), + shutterControlServiceStateCaptor.capture()); + ShutterControlServiceState state = shutterControlServiceStateCaptor.getValue(); + assertEquals(OperationState.STOPPED, state.operationState); + } + + @Test + public void testHandleCommandPercentType() + throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { + getFixture().handleCommand(new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_LEVEL), + new PercentType(42)); + verify(getBridgeHandler()).putState(eq(getDeviceID()), eq("ShutterControl"), + shutterControlServiceStateCaptor.capture()); + ShutterControlServiceState state = shutterControlServiceStateCaptor.getValue(); + assertEquals(0.58d, state.level); + } + + @Test + public void testUpdateChannelsShutterControlService() { + JsonElement jsonObject = JsonParser + .parseString("{\n" + " \"@type\": \"shutterControlState\",\n" + " \"level\": 0.58\n" + " }"); + getFixture().processUpdate("ShutterControl", jsonObject); + verify(getCallback()).stateUpdated(new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_LEVEL), + new PercentType(42)); + } +} diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/smartbulb/SmartBulbHandlerTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/smartbulb/SmartBulbHandlerTest.java index 8662d5da0..9f3fd5058 100644 --- a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/smartbulb/SmartBulbHandlerTest.java +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/smartbulb/SmartBulbHandlerTest.java @@ -19,6 +19,7 @@ import static org.mockito.Mockito.*; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.mockito.Captor; @@ -44,16 +45,14 @@ import com.google.gson.JsonParser; * @author David Pace - Initial contribution * */ +@NonNullByDefault public class SmartBulbHandlerTest extends AbstractBoschSHCDeviceHandlerTest { - @Captor - private ArgumentCaptor binarySwitchServiceStateCaptor; + private @Captor @NonNullByDefault({}) ArgumentCaptor binarySwitchServiceStateCaptor; - @Captor - private ArgumentCaptor multiLevelSwitchServiceStateCaptor; + private @Captor @NonNullByDefault({}) ArgumentCaptor multiLevelSwitchServiceStateCaptor; - @Captor - private ArgumentCaptor hsbColorActuatorServiceStateCaptor; + private @Captor @NonNullByDefault({}) ArgumentCaptor hsbColorActuatorServiceStateCaptor; @Override protected SmartBulbHandler createFixture() { @@ -71,9 +70,8 @@ public class SmartBulbHandlerTest extends AbstractBoschSHCDeviceHandlerTest { + private @Captor @NonNullByDefault({}) ArgumentCaptor childLockServiceStateCaptor; + @Override protected ThermostatHandler createFixture() { return new ThermostatHandler(getThing()); @@ -40,4 +69,55 @@ public class ThermostatHandlerTest extends AbstractBatteryPoweredDeviceHandlerTe protected ThingTypeUID getThingTypeUID() { return BoschSHCBindingConstants.THING_TYPE_THERMOSTAT; } + + @Test + public void testHandleCommand() + throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { + getFixture().handleCommand(new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_CHILD_LOCK), + OnOffType.ON); + verify(getBridgeHandler()).putState(eq(getDeviceID()), eq("Thermostat"), childLockServiceStateCaptor.capture()); + ChildLockServiceState state = childLockServiceStateCaptor.getValue(); + assertSame(ChildLockState.ON, state.childLock); + } + + @Test + public void testHandleCommandUnknownCommand() { + getFixture().handleCommand(new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_CHILD_LOCK), + new DecimalType(42)); + ThingStatusInfo expectedThingStatusInfo = ThingStatusInfoBuilder + .create(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR) + .withDescription( + "Error when service Thermostat should handle command org.openhab.core.library.types.DecimalType: Thermostat: Can not handle command org.openhab.core.library.types.DecimalType") + .build(); + verify(getCallback()).statusUpdated(getThing(), expectedThingStatusInfo); + } + + @Test + public void testUpdateChannelsTemperatureLevelService() { + JsonElement jsonObject = JsonParser.parseString( + "{\n" + " \"@type\": \"temperatureLevelState\",\n" + " \"temperature\": 21.5\n" + " }"); + getFixture().processUpdate("TemperatureLevel", jsonObject); + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_TEMPERATURE), + new QuantityType(21.5, SIUnits.CELSIUS)); + } + + @Test + public void testUpdateChannelsValveTappetService() { + JsonElement jsonObject = JsonParser + .parseString("{\n" + " \"@type\": \"valveTappetState\",\n" + " \"position\": 42\n" + " }"); + getFixture().processUpdate("ValveTappet", jsonObject); + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_VALVE_TAPPET_POSITION), + new DecimalType(42)); + } + + @Test + public void testUpdateChannelsChildLockService() { + JsonElement jsonObject = JsonParser + .parseString("{\n" + " \"@type\": \"childLockState\",\n" + " \"childLock\": \"ON\"\n" + " }"); + getFixture().processUpdate("Thermostat", jsonObject); + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_CHILD_LOCK), OnOffType.ON); + } } diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/twinguard/TwinguardHandlerTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/twinguard/TwinguardHandlerTest.java index 58dd99efc..2ef534dc6 100644 --- a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/twinguard/TwinguardHandlerTest.java +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/twinguard/TwinguardHandlerTest.java @@ -12,11 +12,25 @@ */ package org.openhab.binding.boschshc.internal.devices.twinguard; +import static org.mockito.Mockito.verify; + +import javax.measure.quantity.Dimensionless; +import javax.measure.quantity.Temperature; + import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.Test; import org.openhab.binding.boschshc.internal.devices.AbstractSmokeDetectorHandlerTest; import org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants; +import org.openhab.core.library.types.QuantityType; +import org.openhab.core.library.types.StringType; +import org.openhab.core.library.unit.SIUnits; +import org.openhab.core.library.unit.Units; +import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.ThingTypeUID; +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; + /** * Unit Tests for {@link TwinguardHandler}. * @@ -40,4 +54,43 @@ public class TwinguardHandlerTest extends AbstractSmokeDetectorHandlerTest(23.77, SIUnits.CELSIUS)); + + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_TEMPERATURE_RATING), + new StringType("GOOD")); + + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_HUMIDITY), + new QuantityType(32.69, Units.PERCENT)); + + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_HUMIDITY_RATING), + new StringType("MEDIUM")); + + verify(getCallback()).stateUpdated(new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_PURITY), + new QuantityType(620, Units.PARTS_PER_MILLION)); + + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_PURITY_RATING), + new StringType("GOOD")); + + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_AIR_DESCRIPTION), + new StringType("LITTLE_DRY")); + + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_COMBINED_RATING), + new StringType("MEDIUM")); + } } diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/wallthermostat/WallThermostatHandlerTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/wallthermostat/WallThermostatHandlerTest.java index 96b1ec31a..9f755f696 100644 --- a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/wallthermostat/WallThermostatHandlerTest.java +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/wallthermostat/WallThermostatHandlerTest.java @@ -12,11 +12,24 @@ */ package org.openhab.binding.boschshc.internal.devices.wallthermostat; +import static org.mockito.Mockito.verify; + +import javax.measure.quantity.Dimensionless; +import javax.measure.quantity.Temperature; + import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.Test; import org.openhab.binding.boschshc.internal.devices.AbstractBatteryPoweredDeviceHandlerTest; import org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants; +import org.openhab.core.library.types.QuantityType; +import org.openhab.core.library.unit.SIUnits; +import org.openhab.core.library.unit.Units; +import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.ThingTypeUID; +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; + /** * Unit Tests for {@link WallThermostatHandler}. * @@ -40,4 +53,24 @@ public class WallThermostatHandlerTest extends AbstractBatteryPoweredDeviceHandl protected ThingTypeUID getThingTypeUID() { return BoschSHCBindingConstants.THING_TYPE_WALL_THERMOSTAT; } + + @Test + public void testUpdateChannelsTemperatureLevelService() { + JsonElement jsonObject = JsonParser.parseString( + "{\n" + " \"@type\": \"temperatureLevelState\",\n" + " \"temperature\": 21.5\n" + " }"); + getFixture().processUpdate("TemperatureLevel", jsonObject); + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_TEMPERATURE), + new QuantityType(21.5, SIUnits.CELSIUS)); + } + + @Test + public void testUpdateChannelsHumidityLevelService() { + JsonElement jsonObject = JsonParser + .parseString("{\n" + " \"@type\": \"humidityLevelState\",\n" + " \"humidity\": 42.5\n" + " }"); + getFixture().processUpdate("HumidityLevel", jsonObject); + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_HUMIDITY), + new QuantityType(42.5, Units.PERCENT)); + } } diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/windowcontact/WindowContactHandlerTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/windowcontact/WindowContactHandlerTest.java index 510ec0d3e..9c1b3920a 100644 --- a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/windowcontact/WindowContactHandlerTest.java +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/devices/windowcontact/WindowContactHandlerTest.java @@ -12,11 +12,19 @@ */ package org.openhab.binding.boschshc.internal.devices.windowcontact; +import static org.mockito.Mockito.verify; + import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.Test; import org.openhab.binding.boschshc.internal.devices.AbstractBatteryPoweredDeviceHandlerTest; import org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants; +import org.openhab.core.library.types.OpenClosedType; +import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.ThingTypeUID; +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; + /** * Unit Tests for {@link WindowContactHandler}. * @@ -40,4 +48,19 @@ public class WindowContactHandlerTest extends AbstractBatteryPoweredDeviceHandle protected ThingTypeUID getThingTypeUID() { return BoschSHCBindingConstants.THING_TYPE_WINDOW_CONTACT; } + + @Test + public void testUpdateChannelsShutterContactService() { + JsonElement jsonObject = JsonParser + .parseString("{\n" + " \"@type\": \"shutterContactState\",\n" + " \"value\": \"OPEN\"\n" + " }"); + getFixture().processUpdate("ShutterContact", jsonObject); + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_CONTACT), OpenClosedType.OPEN); + + jsonObject = JsonParser + .parseString("{\n" + " \"@type\": \"shutterContactState\",\n" + " \"value\": \"CLOSED\"\n" + " }"); + getFixture().processUpdate("ShutterContact", jsonObject); + verify(getCallback()).stateUpdated( + new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_CONTACT), OpenClosedType.CLOSED); + } } diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/exceptions/LongPollingFailedExceptionTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/exceptions/LongPollingFailedExceptionTest.java new file mode 100644 index 000000000..7eb58b799 --- /dev/null +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/exceptions/LongPollingFailedExceptionTest.java @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2010-2023 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.exceptions; + +import static org.junit.jupiter.api.Assertions.*; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.Test; + +/** + * Unit tests for {@link LongPollingFailedException}. + * + * @author David Pace - Initial contribution + * + */ +@NonNullByDefault +public class LongPollingFailedExceptionTest { + + @Test + public void testConstructor() { + RuntimeException testException = new RuntimeException("test exception"); + LongPollingFailedException longPollingFailedException = new LongPollingFailedException("message", + testException); + assertEquals("message", longPollingFailedException.getMessage()); + assertSame(testException, longPollingFailedException.getCause()); + } +} diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/exceptions/PairingFailedExceptionTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/exceptions/PairingFailedExceptionTest.java new file mode 100644 index 000000000..b9bdfae9e --- /dev/null +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/exceptions/PairingFailedExceptionTest.java @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2010-2023 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.exceptions; + +import static org.junit.jupiter.api.Assertions.*; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.Test; + +/** + * Unit tests for {@link PairingFailedException}. + * + * @author David Pace - Initial contribution + * + */ +@NonNullByDefault +public class PairingFailedExceptionTest { + + @Test + public void testConstructor() { + PairingFailedException fixture = new PairingFailedException(); + assertNotNull(fixture); + assertNull(fixture.getMessage()); + assertNull(fixture.getCause()); + } + + @Test + public void testConstructorWithMessage() { + PairingFailedException fixture = new PairingFailedException("message"); + assertNotNull(fixture); + assertEquals("message", fixture.getMessage()); + assertNull(fixture.getCause()); + } + + @Test + public void testConstructorWithMessageAndCause() { + RuntimeException testException = new RuntimeException("test exception"); + PairingFailedException fixture = new PairingFailedException("message", testException); + assertNotNull(fixture); + assertEquals("message", fixture.getMessage()); + assertSame(testException, fixture.getCause()); + } +} diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/services/batterylevel/BatteryLevelTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/services/batterylevel/BatteryLevelTest.java index d8ac8049d..04b3c328c 100644 --- a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/services/batterylevel/BatteryLevelTest.java +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/services/batterylevel/BatteryLevelTest.java @@ -16,6 +16,7 @@ import static org.junit.jupiter.api.Assertions.*; import java.util.ArrayList; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.junit.jupiter.api.Test; import org.openhab.binding.boschshc.internal.devices.bridge.dto.DeviceServiceData; import org.openhab.binding.boschshc.internal.devices.bridge.dto.Fault; @@ -30,6 +31,7 @@ import org.openhab.core.types.UnDefType; * @author David Pace - Initial contribution * */ +@NonNullByDefault class BatteryLevelTest { @Test diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/services/dto/BoschSHCServiceStateTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/services/dto/BoschSHCServiceStateTest.java index 05a3ae157..dd7804135 100644 --- a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/services/dto/BoschSHCServiceStateTest.java +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/services/dto/BoschSHCServiceStateTest.java @@ -12,8 +12,7 @@ */ package org.openhab.binding.boschshc.internal.services.dto; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.*; import org.eclipse.jdt.annotation.NonNullByDefault; import org.junit.jupiter.api.Test; @@ -53,14 +52,14 @@ public class BoschSHCServiceStateTest { private final Gson gson = new Gson(); @Test - public void fromJson_nullStateForDifferentType() { + public void fromJsonNullStateForDifferentType() { var state = BoschSHCServiceState.fromJson(gson.fromJson("{\"@type\":\"differentState\"}", JsonObject.class), TestState.class); assertEquals(null, state); } @Test - public void fromJson_stateObjectForValidJson() { + public void fromJsonStateObjectForValidJson() { var state = BoschSHCServiceState.fromJson(gson.fromJson("{\"@type\":\"testState\"}", JsonObject.class), TestState.class); assertNotEquals(null, state); @@ -70,7 +69,7 @@ public class BoschSHCServiceStateTest { * This checks for a bug we had where the expected type stayed the same for different state classes */ @Test - public void fromJson_stateObjectForValidJsonAfterOtherState() { + public void fromJsonStateObjectForValidJsonAfterOtherState() { BoschSHCServiceState.fromJson(gson.fromJson("{\"@type\":\"testState\"}", JsonObject.class), TestState.class); var state2 = BoschSHCServiceState.fromJson(gson.fromJson("{\"@type\":\"testState2\"}", JsonObject.class), TestState2.class); diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/services/dto/JsonRestExceptionResponseTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/services/dto/JsonRestExceptionResponseTest.java new file mode 100644 index 000000000..86e485acb --- /dev/null +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/services/dto/JsonRestExceptionResponseTest.java @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2010-2023 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.services.dto; + +import static org.junit.jupiter.api.Assertions.*; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + * Unit tests for {@link JsonRestExceptionResponse}. + * + * @author David Pace - Initial contribution + * + */ +@NonNullByDefault +public class JsonRestExceptionResponseTest { + + private @NonNullByDefault({}) JsonRestExceptionResponse fixture; + + @BeforeEach + public void setUp() throws Exception { + fixture = new JsonRestExceptionResponse(); + } + + @Test + public void testIsValid() { + assertFalse(JsonRestExceptionResponse.isValid(null)); + assertTrue(JsonRestExceptionResponse.isValid(fixture)); + fixture.errorCode = null; + assertFalse(JsonRestExceptionResponse.isValid(fixture)); + fixture.statusCode = null; + assertFalse(JsonRestExceptionResponse.isValid(fixture)); + fixture.errorCode = ""; + assertFalse(JsonRestExceptionResponse.isValid(fixture)); + } +} diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/services/intrusion/IntrusionDetectionControlStateServiceTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/services/intrusion/IntrusionDetectionControlStateServiceTest.java index 390d3ecca..3f4fe0c83 100644 --- a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/services/intrusion/IntrusionDetectionControlStateServiceTest.java +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/services/intrusion/IntrusionDetectionControlStateServiceTest.java @@ -12,17 +12,15 @@ */ package org.openhab.binding.boschshc.internal.services.intrusion; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; import java.util.function.Consumer; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -38,23 +36,21 @@ import com.google.gson.JsonParser; /** * Unit tests for {@link IntrusionDetectionControlStateService}. - * + * * @author David Pace - Initial contribution * */ +@NonNullByDefault @ExtendWith(MockitoExtension.class) class IntrusionDetectionControlStateServiceTest { - private IntrusionDetectionControlStateService fixture; + private @NonNullByDefault({}) IntrusionDetectionControlStateService fixture; - @Mock - private BridgeHandler bridgeHandler; + private @Mock @NonNullByDefault({}) BridgeHandler bridgeHandler; - @Mock - private Consumer consumer; + private @Mock @NonNullByDefault({}) Consumer consumer; - @Mock - private IntrusionDetectionControlState testState; + private @Mock @NonNullByDefault({}) IntrusionDetectionControlState testState; @BeforeEach void beforeEach() throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException { diff --git a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/services/intrusion/IntrusionDetectionSystemStateServiceTest.java b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/services/intrusion/IntrusionDetectionSystemStateServiceTest.java index 5ac919c85..5372efa5c 100644 --- a/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/services/intrusion/IntrusionDetectionSystemStateServiceTest.java +++ b/bundles/org.openhab.binding.boschshc/src/test/java/org/openhab/binding/boschshc/internal/services/intrusion/IntrusionDetectionSystemStateServiceTest.java @@ -13,15 +13,14 @@ package org.openhab.binding.boschshc.internal.services.intrusion; import static org.junit.jupiter.api.Assertions.assertSame; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; import java.util.function.Consumer; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -34,23 +33,21 @@ import org.openhab.binding.boschshc.internal.services.intrusion.dto.IntrusionDet /** * Unit tests for {@link IntrusionDetectionSystemStateService}. - * + * * @author David Pace - Initial contribution * */ +@NonNullByDefault @ExtendWith(MockitoExtension.class) class IntrusionDetectionSystemStateServiceTest { - private IntrusionDetectionSystemStateService fixture; + private @NonNullByDefault({}) IntrusionDetectionSystemStateService fixture; - @Mock - private BridgeHandler bridgeHandler; + private @Mock @NonNullByDefault({}) BridgeHandler bridgeHandler; - @Mock - private Consumer consumer; + private @Mock @NonNullByDefault({}) Consumer consumer; - @Mock - private IntrusionDetectionSystemState testState; + private @Mock @NonNullByDefault({}) IntrusionDetectionSystemState testState; @BeforeEach void beforeEach() {