[boschshc] Boost unit test coverage (#16500)

Boosts the unit test coverage for the `boschshc` binding in `src/main/java` to 94%.

Signed-off-by: David Pace <dev@davidpace.de>
This commit is contained in:
David Pace 2024-03-31 21:19:25 +02:00 committed by GitHub
parent 1dd8fd10a6
commit 12660f1c26
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 919 additions and 94 deletions

View File

@ -657,7 +657,7 @@ public class BridgeHandler extends BaseBridgeHandler {
*
* @param e error during long polling
*/
private void handleLongPollFailure(Throwable e) {
void handleLongPollFailure(Throwable e) {
logger.warn("Long polling failed, will try to reconnect", e);
@Nullable
BoschHttpClient localHttpClient = this.httpClient;
@ -722,7 +722,7 @@ public class BridgeHandler extends BaseBridgeHandler {
return new BoschSHCException("@text/offline.conf-error.invalid-state-id");
} else {
return new BoschSHCException(String.format(
"Request for info of user-defines state %s failed with status code %d and error code %s",
"Request for info of user-defined state %s failed with status code %d and error code %s",
stateId, errorResponse.statusCode, errorResponse.errorCode));
}
} else {

View File

@ -96,7 +96,7 @@ public class ScenarioHandler {
}
}
private String prettyLogScenarios(final Scenario[] scenarios) {
String prettyLogScenarios(final Scenario[] scenarios) {
final StringBuilder builder = new StringBuilder();
builder.append("[");
for (Scenario scenario : scenarios) {

View File

@ -17,7 +17,12 @@ import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.containsString;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.io.IOException;
import java.nio.file.Files;
@ -35,6 +40,8 @@ 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.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.openhab.binding.boschshc.internal.devices.bridge.BridgeHandler;
@ -89,6 +96,24 @@ class BoschShcCommandExtensionTest {
verify(consoleMock, atLeastOnce()).print(any());
}
@ParameterizedTest
@MethodSource("org.openhab.binding.boschshc.internal.tests.common.CommonTestUtils#getBoschShcAndExecutionAndTimeoutExceptionArguments()")
void executeHandleExceptions(Exception exception)
throws InterruptedException, BoschSHCException, ExecutionException, TimeoutException {
Console console = mock(Console.class);
Bridge bridge = mock(Bridge.class);
BridgeHandler bridgeHandler = mock(BridgeHandler.class);
when(bridgeHandler.getThing()).thenReturn(bridge);
when(bridgeHandler.getPublicInformation()).thenThrow(exception);
when(bridge.getHandler()).thenReturn(bridgeHandler);
List<Thing> things = List.of(bridge);
when(thingRegistry.getAll()).thenReturn(things);
fixture.execute(new String[] { BoschShcCommandExtension.GET_BRIDGEINFO }, console);
verify(console).print(anyString());
}
@Test
void getCompleter() {
assertThat(fixture.getCompleter(), is(fixture));

View File

@ -46,13 +46,13 @@ public abstract class AbstractBatteryPoweredDeviceHandlerTest<T extends Abstract
@BeforeEach
@Override
public void beforeEach() throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
super.beforeEach();
DeviceServiceData deviceServiceData = new DeviceServiceData();
deviceServiceData.path = "/devices/hdm:ZigBee:000d6f0004b93361/services/BatteryLevel";
deviceServiceData.id = "BatteryLevel";
deviceServiceData.deviceId = "hdm:ZigBee:000d6f0004b93361";
lenient().when(bridgeHandler.getServiceData(anyString(), anyString())).thenReturn(deviceServiceData);
when(getBridgeHandler().getServiceData(anyString(), anyString())).thenReturn(deviceServiceData);
super.beforeEach();
}
@Test
@ -137,10 +137,11 @@ public abstract class AbstractBatteryPoweredDeviceHandlerTest<T extends Abstract
"deviceId":"hdm:ZigBee:000d6f0004b93361" }\
""");
getFixture().processUpdate("BatteryLevel", deviceServiceData);
verify(getCallback()).stateUpdated(
// state is updated twice: via short poll in initialize() and via long poll result in this test
verify(getCallback(), times(2)).stateUpdated(
new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_BATTERY_LEVEL),
new DecimalType(100));
verify(getCallback()).stateUpdated(
verify(getCallback(), times(2)).stateUpdated(
new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_LOW_BATTERY), OnOffType.OFF);
}
@ -165,19 +166,24 @@ public abstract class AbstractBatteryPoweredDeviceHandlerTest<T extends Abstract
getFixture().processUpdate("BatteryLevel", deviceServiceData);
verify(getCallback()).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_BATTERY_LEVEL),
UnDefType.UNDEF);
verify(getCallback()).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_LOW_BATTERY), OnOffType.OFF);
// state is updated twice: via short poll in initialize() and via long poll result in this test
verify(getCallback(), times(2)).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_LOW_BATTERY),
OnOffType.OFF);
}
@Test
public void testHandleCommandRefreshBatteryLevelChannel() {
getFixture().handleCommand(getChannelUID(BoschSHCBindingConstants.CHANNEL_BATTERY_LEVEL), RefreshType.REFRESH);
verify(getCallback()).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_BATTERY_LEVEL),
// state is updated twice: via short poll in initialize() and via long poll result in this test
verify(getCallback(), times(2)).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_BATTERY_LEVEL),
new DecimalType(100));
}
@Test
public void testHandleCommandRefreshLowBatteryChannel() {
getFixture().handleCommand(getChannelUID(BoschSHCBindingConstants.CHANNEL_LOW_BATTERY), RefreshType.REFRESH);
verify(getCallback()).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_LOW_BATTERY), OnOffType.OFF);
// state is updated twice: via short poll in initialize() and via long poll result in this test
verify(getCallback(), times(2)).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_LOW_BATTERY),
OnOffType.OFF);
}
}

View File

@ -12,9 +12,23 @@
*/
package org.openhab.binding.boschshc.internal.devices;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.openhab.binding.boschshc.internal.devices.bridge.dto.Device;
import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
/**
* Abstract unit test implementation for device handlers.
@ -42,4 +56,27 @@ public abstract class AbstractBoschSHCDeviceHandlerTest<T extends BoschSHCDevice
}
protected abstract String getDeviceID();
@Test
void initializeInvalidDeviceId() {
getFixture().getThing().getConfiguration().remove("id");
getFixture().initialize();
verify(getCallback()).statusUpdated(eq(getThing()),
argThat(status -> status.getStatus().equals(ThingStatus.OFFLINE)
&& status.getStatusDetail().equals(ThingStatusDetail.CONFIGURATION_ERROR)));
}
@ParameterizedTest
@MethodSource("org.openhab.binding.boschshc.internal.tests.common.CommonTestUtils#getExecutionExceptionAndInterruptedExceptionArguments()")
void initializeHandleExceptionDuringDeviceInfoRestCall(Exception exception)
throws BoschSHCException, InterruptedException, TimeoutException, ExecutionException {
when(getBridgeHandler().getDeviceInfo(getDeviceID())).thenThrow(exception);
getFixture().initialize();
verify(getCallback()).statusUpdated(eq(getThing()),
argThat(status -> status.getStatus().equals(ThingStatus.OFFLINE)
&& status.getStatusDetail().equals(ThingStatusDetail.CONFIGURATION_ERROR)));
}
}

View File

@ -12,6 +12,9 @@
*/
package org.openhab.binding.boschshc.internal.devices;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.sameInstance;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
@ -60,7 +63,7 @@ public abstract class AbstractBoschSHCHandlerTest<T extends BoschSHCHandler> {
private @Mock @NonNullByDefault({}) Bridge bridge;
protected @Mock @NonNullByDefault({}) BridgeHandler bridgeHandler;
private @Mock @NonNullByDefault({}) BridgeHandler bridgeHandler;
private @Mock @NonNullByDefault({}) ThingHandlerCallback callback;
@ -128,8 +131,25 @@ public abstract class AbstractBoschSHCHandlerTest<T extends BoschSHCHandler> {
}
@Test
public void testInitialize() {
void testInitialize() {
ThingStatusInfo expectedStatusInfo = new ThingStatusInfo(ThingStatus.ONLINE, ThingStatusDetail.NONE, null);
verify(callback).statusUpdated(same(thing), eq(expectedStatusInfo));
}
@Test
void testGetBridgeHandler() throws BoschSHCException {
assertThat(fixture.getBridgeHandler(), sameInstance(bridgeHandler));
}
@Test
void testGetBridgeHandlerThrowExceptionIfBridgeIsNull() throws BoschSHCException {
when(callback.getBridge(any())).thenReturn(null);
assertThrows(BoschSHCException.class, () -> fixture.getBridgeHandler());
}
@Test
void testGetBridgeHandlerThrowExceptionIfBridgeHandlerIsNull() throws BoschSHCException {
when(bridge.getHandler()).thenReturn(null);
assertThrows(BoschSHCException.class, () -> fixture.getBridgeHandler());
}
}

View File

@ -13,12 +13,14 @@
package org.openhab.binding.boschshc.internal.devices;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
@ -26,12 +28,16 @@ import java.util.concurrent.TimeoutException;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException;
import org.openhab.binding.boschshc.internal.services.powerswitch.PowerSwitchState;
import org.openhab.binding.boschshc.internal.services.powerswitch.dto.PowerSwitchServiceState;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.types.RefreshType;
import com.google.gson.JsonElement;
@ -53,12 +59,12 @@ public abstract class AbstractPowerSwitchHandlerTest<T extends AbstractPowerSwit
@BeforeEach
@Override
public void beforeEach() throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
super.beforeEach();
PowerSwitchServiceState powerSwitchServiceState = new PowerSwitchServiceState();
powerSwitchServiceState.switchState = PowerSwitchState.ON;
lenient().when(bridgeHandler.getState(anyString(), eq("PowerSwitch"), same(PowerSwitchServiceState.class)))
when(getBridgeHandler().getState(anyString(), eq("PowerSwitch"), same(PowerSwitchServiceState.class)))
.thenReturn(powerSwitchServiceState);
super.beforeEach();
}
@Test
@ -76,22 +82,58 @@ public abstract class AbstractPowerSwitchHandlerTest<T extends AbstractPowerSwit
assertSame(PowerSwitchState.OFF, state.switchState);
}
@ParameterizedTest
@MethodSource("org.openhab.binding.boschshc.internal.tests.common.CommonTestUtils#getExecutionAndTimeoutAndInterruptedExceptionArguments()")
public void testHandleCommandPowerSwitchChannelHandleExceptions(Exception e)
throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
when(getBridgeHandler().putState(any(), any(), any())).thenThrow(e);
getFixture().handleCommand(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_SWITCH), OnOffType.ON);
verify(getCallback()).statusUpdated(same(getThing()),
argThat(status -> status.getStatus().equals(ThingStatus.OFFLINE)
&& status.getStatusDetail().equals(ThingStatusDetail.COMMUNICATION_ERROR)));
}
@Test
public void testUpdateChannelPowerSwitchState() {
JsonElement jsonObject = JsonParser
.parseString("{\n" + " \"@type\": \"powerSwitchState\",\n" + " \"switchState\": \"ON\"\n" + "}");
getFixture().processUpdate("PowerSwitch", jsonObject);
verify(getCallback()).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_SWITCH), OnOffType.ON);
// state is updated twice: via short poll in initialize() and via long poll result in this test
verify(getCallback(), times(2)).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_SWITCH),
OnOffType.ON);
jsonObject = JsonParser
.parseString("{\n" + " \"@type\": \"powerSwitchState\",\n" + " \"switchState\": \"OFF\"\n" + "}");
getFixture().processUpdate("PowerSwitch", jsonObject);
verify(getCallback()).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_SWITCH), OnOffType.OFF);
}
@Test
public void testHandleCommandRefreshPowerSwitchChannel() {
getFixture().handleCommand(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_SWITCH), RefreshType.REFRESH);
verify(getCallback()).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_SWITCH), OnOffType.ON);
// state is updated twice: via short poll in initialize() and via long poll result in this test
verify(getCallback(), times(2)).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_SWITCH),
OnOffType.ON);
}
@ParameterizedTest
@MethodSource("org.openhab.binding.boschshc.internal.tests.common.CommonTestUtils#getBoschShcAndExecutionAndTimeoutAndInterruptedExceptionArguments()")
public void testHandleCommandRefreshPowerSwitchChannelHandleExceptions(Exception e)
throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
when(getBridgeHandler().getState(anyString(), eq("PowerSwitch"), same(PowerSwitchServiceState.class)))
.thenThrow(e);
getFixture().handleCommand(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_SWITCH), RefreshType.REFRESH);
verify(getCallback()).statusUpdated(same(getThing()),
argThat(status -> status.getStatus().equals(ThingStatus.OFFLINE)
&& status.getStatusDetail().equals(ThingStatusDetail.COMMUNICATION_ERROR)));
}
}

View File

@ -17,6 +17,7 @@ import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import java.util.concurrent.ExecutionException;
@ -56,13 +57,13 @@ public abstract class AbstractPowerSwitchHandlerWithPowerMeterTest<T extends Abs
@BeforeEach
public void beforeEach() throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
super.beforeEach();
PowerMeterServiceState powerMeterServiceState = new PowerMeterServiceState();
powerMeterServiceState.powerConsumption = 12.34d;
powerMeterServiceState.energyConsumption = 56.78d;
lenient().when(bridgeHandler.getState(anyString(), eq("PowerMeter"), same(PowerMeterServiceState.class)))
lenient().when(getBridgeHandler().getState(anyString(), eq("PowerMeter"), same(PowerMeterServiceState.class)))
.thenReturn(powerMeterServiceState);
super.beforeEach();
}
@Test
@ -76,13 +77,15 @@ public abstract class AbstractPowerSwitchHandlerWithPowerMeterTest<T extends Abs
""");
getFixture().processUpdate("PowerMeter", jsonObject);
verify(getCallback()).stateUpdated(eq(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_CONSUMPTION)),
powerCaptor.capture());
// state is updated twice: via short poll in initialize() and via long poll result in this test
verify(getCallback(), times(2)).stateUpdated(
eq(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_CONSUMPTION)), powerCaptor.capture());
QuantityType<Power> powerValue = powerCaptor.getValue();
assertEquals(23, powerValue.intValue());
verify(getCallback()).stateUpdated(eq(getChannelUID(BoschSHCBindingConstants.CHANNEL_ENERGY_CONSUMPTION)),
energyCaptor.capture());
// state is updated twice: via short poll in initialize() and via long poll result in this test
verify(getCallback(), times(2)).stateUpdated(
eq(getChannelUID(BoschSHCBindingConstants.CHANNEL_ENERGY_CONSUMPTION)), energyCaptor.capture());
QuantityType<Energy> energyValue = energyCaptor.getValue();
assertEquals(42, energyValue.intValue());
}
@ -91,7 +94,9 @@ public abstract class AbstractPowerSwitchHandlerWithPowerMeterTest<T extends Abs
public void testHandleCommandRefreshPowerConsumptionChannel() {
getFixture().handleCommand(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_CONSUMPTION),
RefreshType.REFRESH);
verify(getCallback()).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_CONSUMPTION),
// state is updated twice: via short poll in initialize() and via refresh command in this test
verify(getCallback(), times(2)).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_CONSUMPTION),
new QuantityType<>(12.34d, Units.WATT));
}
@ -99,7 +104,9 @@ public abstract class AbstractPowerSwitchHandlerWithPowerMeterTest<T extends Abs
public void testHandleCommandRefreshEnergyConsumptionChannel() {
getFixture().handleCommand(getChannelUID(BoschSHCBindingConstants.CHANNEL_ENERGY_CONSUMPTION),
RefreshType.REFRESH);
verify(getCallback()).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_ENERGY_CONSUMPTION),
// state is updated twice: via short poll in initialize() and via refresh command in this test
verify(getCallback(), times(2)).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_ENERGY_CONSUMPTION),
new QuantityType<>(56.78d, Units.WATT_HOUR));
}
}

View File

@ -12,7 +12,10 @@
*/
package org.openhab.binding.boschshc.internal.devices.bridge;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.collection.IsCollectionWithSize.hasSize;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows;
@ -22,9 +25,11 @@ import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.contains;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
@ -50,16 +55,23 @@ 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.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.ArgumentCaptor;
import org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants;
import org.openhab.binding.boschshc.internal.devices.BoschSHCHandler;
import org.openhab.binding.boschshc.internal.devices.bridge.dto.Device;
import org.openhab.binding.boschshc.internal.devices.bridge.dto.DeviceServiceData;
import org.openhab.binding.boschshc.internal.devices.bridge.dto.DeviceTest;
import org.openhab.binding.boschshc.internal.devices.bridge.dto.Faults;
import org.openhab.binding.boschshc.internal.devices.bridge.dto.LongPollResult;
import org.openhab.binding.boschshc.internal.devices.bridge.dto.PublicInformation;
import org.openhab.binding.boschshc.internal.devices.bridge.dto.Room;
import org.openhab.binding.boschshc.internal.devices.bridge.dto.Scenario;
import org.openhab.binding.boschshc.internal.devices.bridge.dto.SubscribeResult;
import org.openhab.binding.boschshc.internal.devices.bridge.dto.UserDefinedState;
import org.openhab.binding.boschshc.internal.devices.bridge.dto.UserDefinedStateTest;
import org.openhab.binding.boschshc.internal.discovery.ThingDiscoveryService;
import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException;
import org.openhab.binding.boschshc.internal.serialization.GsonUtils;
import org.openhab.binding.boschshc.internal.services.binaryswitch.dto.BinarySwitchServiceState;
@ -70,15 +82,21 @@ import org.openhab.binding.boschshc.internal.services.intrusion.dto.IntrusionDet
import org.openhab.binding.boschshc.internal.services.shuttercontact.ShutterContactState;
import org.openhab.binding.boschshc.internal.services.shuttercontact.dto.ShutterContactServiceState;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Channel;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.ThingStatusInfo;
import org.openhab.core.thing.binding.ThingHandlerCallback;
import org.openhab.core.thing.binding.builder.ThingStatusInfoBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import com.google.gson.JsonPrimitive;
/**
* Unit tests for the {@link BridgeHandler}.
@ -92,13 +110,9 @@ class BridgeHandlerTest {
private @NonNullByDefault({}) BridgeHandler fixture;
private @NonNullByDefault({}) BoschHttpClient httpClient;
private @NonNullByDefault({}) ThingHandlerCallback thingHandlerCallback;
/**
* A mocked bridge instance
*/
private @NonNullByDefault({}) Bridge thing;
private @NonNullByDefault({}) Configuration bridgeConfiguration;
@BeforeAll
static void beforeAll() throws IOException {
@ -119,7 +133,7 @@ class BridgeHandlerTest {
thingHandlerCallback = mock(ThingHandlerCallback.class);
fixture.setCallback(thingHandlerCallback);
Configuration bridgeConfiguration = new Configuration();
bridgeConfiguration = new Configuration();
Map<@Nullable String, @Nullable Object> properties = new HashMap<>();
properties.put("ipAddress", "localhost");
properties.put("password", "test");
@ -155,6 +169,19 @@ class BridgeHandlerTest {
verify(mockRequest).send();
}
@Test
void postActionWithoutRequestBody() throws InterruptedException, TimeoutException, ExecutionException {
String endpoint = "/intrusion/actions/disarm";
String url = "https://127.0.0.1:8444/smarthome/intrusion/actions/disarm";
when(httpClient.getBoschSmartHomeUrl(endpoint)).thenReturn(url);
Request mockRequest = mock(Request.class);
when(httpClient.createRequest(anyString(), any(), any())).thenReturn(mockRequest);
fixture.postAction(endpoint);
verify(httpClient).createRequest(eq(url), same(HttpMethod.POST), isNull());
verify(mockRequest).send();
}
@Test
void initialAccessHttpClientOffline() {
fixture.initialAccess(httpClient);
@ -212,9 +239,31 @@ class BridgeHandlerTest {
when(httpClient.createRequest(anyString(), same(HttpMethod.POST),
argThat((JsonRpcRequest r) -> "RE/longPoll".equals(r.method)))).thenReturn(longPollRequest);
ThingDiscoveryService thingDiscoveryListener = mock(ThingDiscoveryService.class);
fixture.registerDiscoveryListener(thingDiscoveryListener);
fixture.initialAccess(httpClient);
verify(thingHandlerCallback).statusUpdated(any(),
eq(ThingStatusInfoBuilder.create(ThingStatus.ONLINE, ThingStatusDetail.NONE).build()));
verify(thingDiscoveryListener).doScan();
}
@Test
void initialAccessNoBridgeAccess() throws InterruptedException, TimeoutException, ExecutionException {
when(httpClient.isOnline()).thenReturn(true);
when(httpClient.isAccessPossible()).thenReturn(true);
Request request = mock(Request.class);
when(httpClient.createRequest(any(), same(HttpMethod.GET))).thenReturn(request);
ContentResponse response = mock(ContentResponse.class);
when(request.send()).thenReturn(response);
when(response.getStatus()).thenReturn(400);
fixture.initialAccess(httpClient);
verify(thingHandlerCallback).statusUpdated(same(thing),
argThat(status -> status.getStatus().equals(ThingStatus.OFFLINE)
&& status.getStatusDetail().equals(ThingStatusDetail.COMMUNICATION_ERROR)));
}
@Test
@ -468,6 +517,44 @@ class BridgeHandlerTest {
assertEquals(stateId, userState.getId());
}
@Test
void getUserStateInfoErrorCases()
throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
when(httpClient.getBoschSmartHomeUrl(anyString())).thenCallRealMethod();
when(httpClient.getBoschShcUrl(anyString())).thenCallRealMethod();
Request request = mock(Request.class);
when(request.header(anyString(), anyString())).thenReturn(request);
ContentResponse response = mock(ContentResponse.class);
when(response.getStatus()).thenReturn(200);
when(request.send()).thenReturn(response);
when(httpClient.createRequest(anyString(), same(HttpMethod.GET))).thenReturn(request);
@SuppressWarnings("unchecked")
ArgumentCaptor<BiFunction<Integer, String, BoschSHCException>> errorResponseHandlerCaptor = ArgumentCaptor
.forClass(BiFunction.class);
String stateId = "abcdef";
when(httpClient.sendRequest(same(request), same(UserDefinedState.class), any(),
errorResponseHandlerCaptor.capture())).thenReturn(UserDefinedStateTest.createTestState(stateId));
fixture.getUserStateInfo(stateId);
BiFunction<Integer, String, BoschSHCException> errorResponseHandler = errorResponseHandlerCaptor.getValue();
Exception e = errorResponseHandler.apply(500,
"{\"@type\":\"JsonRestExceptionResponseEntity\",\"errorCode\": \"testErrorCode\",\"statusCode\": 500}");
assertEquals(
"Request for info of user-defined state abcdef failed with status code 500 and error code testErrorCode",
e.getMessage());
e = errorResponseHandler.apply(404,
"{\"@type\":\"JsonRestExceptionResponseEntity\",\"errorCode\": \"ENTITY_NOT_FOUND\",\"statusCode\": 404}");
assertNotNull(e);
e = errorResponseHandler.apply(500, "");
assertEquals("Request for info of user-defined state abcdef failed with status code 500", e.getMessage());
}
@Test
void getUserStates() throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
when(httpClient.getBoschSmartHomeUrl(anyString())).thenCallRealMethod();
@ -650,4 +737,347 @@ class BridgeHandlerTest {
verify(thingHandler).processChildUpdate("hdm:ZigBee:70ac08fffefead2d#3", "PowerSwitch", expectedState);
}
@Test
void handleLongPollResultScenarioTriggered() {
Channel channel = mock(Channel.class);
when(thing.getChannel(BoschSHCBindingConstants.CHANNEL_SCENARIO_TRIGGERED)).thenReturn(channel);
when(thingHandlerCallback.isChannelLinked(any())).thenReturn(true);
String json = """
{
"result": [{
"@type": "scenarioTriggered",
"name": "My Scenario",
"id": "509bd737-eed0-40b7-8caa-e8686a714399",
"lastTimeTriggered": "1693758693032"
}],
"jsonrpc": "2.0"
}
""";
LongPollResult longPollResult = GsonUtils.DEFAULT_GSON_INSTANCE.fromJson(json, LongPollResult.class);
assertNotNull(longPollResult);
fixture.handleLongPollResult(longPollResult);
verify(thingHandlerCallback).stateUpdated(any(), eq(new StringType("My Scenario")));
}
@Test
void handleLongPollResultUserDefinedState() {
List<Thing> things = new ArrayList<Thing>();
when(thing.getThings()).thenReturn(things);
Thing thing = mock(Thing.class);
things.add(thing);
BoschSHCHandler thingHandler = mock(BoschSHCHandler.class);
when(thing.getHandler()).thenReturn(thingHandler);
when(thingHandler.getBoschID()).thenReturn("3d8023d6-69ca-4e79-89dd-7090295cefbf");
String json = """
{
"result": [{
"deleted": false,
"@type": "userDefinedState",
"name": "Test State",
"id": "3d8023d6-69ca-4e79-89dd-7090295cefbf",
"state": true
}],
"jsonrpc": "2.0"
}
""";
LongPollResult longPollResult = GsonUtils.DEFAULT_GSON_INSTANCE.fromJson(json, LongPollResult.class);
assertNotNull(longPollResult);
fixture.handleLongPollResult(longPollResult);
JsonElement expectedState = new JsonPrimitive(true);
verify(thingHandler).processUpdate("3d8023d6-69ca-4e79-89dd-7090295cefbf", expectedState);
}
@Test
void handleLongPollFailure() {
Throwable e = new RuntimeException("Test exception");
fixture.handleLongPollFailure(e);
ThingStatusInfo expectedStatus = ThingStatusInfoBuilder
.create(ThingStatus.UNKNOWN, ThingStatusDetail.UNKNOWN.NONE).build();
verify(thingHandlerCallback).statusUpdated(thing, expectedStatus);
}
@Test
void getDevices() throws InterruptedException, TimeoutException, ExecutionException {
Request request = mock(Request.class);
when(httpClient.createRequest(any(), eq(HttpMethod.GET))).thenReturn(request);
ContentResponse contentResponse = mock(ContentResponse.class);
when(request.send()).thenReturn(contentResponse);
when(contentResponse.getStatus()).thenReturn(200);
String devicesJson = """
[
{
"@type": "device",
"rootDeviceId": "64-da-a0-3e-81-0c",
"id": "hdm:ZigBee:0c4314fffea15de7",
"deviceServiceIds": [
"CommunicationQuality",
"PowerMeter",
"PowerSwitch",
"PowerSwitchConfiguration",
"PowerSwitchProgram"
],
"manufacturer": "BOSCH",
"roomId": "hz_1",
"deviceModel": "PLUG_COMPACT",
"serial": "0C4314FFFE802BE2",
"profile": "LIGHT",
"iconId": "icon_plug_lamp_table",
"name": "My Lamp Plug",
"status": "AVAILABLE",
"childDeviceIds": [],
"supportedProfiles": [
"LIGHT",
"GENERIC",
"HEATING_RCC"
]
},
{
"@type": "device",
"rootDeviceId": "64-da-a0-3e-81-0c",
"id": "hdm:ZigBee:000d6f0012f13bfa",
"deviceServiceIds": [
"LatestMotion",
"CommunicationQuality",
"WalkTest",
"BatteryLevel",
"MultiLevelSensor",
"DeviceDefect"
],
"manufacturer": "BOSCH",
"roomId": "hz_5",
"deviceModel": "MD",
"serial": "000D6F0012F0da96",
"profile": "GENERIC",
"name": "My Motion Detector",
"status": "AVAILABLE",
"childDeviceIds": [],
"supportedProfiles": []
}
]
""";
when(contentResponse.getContentAsString()).thenReturn(devicesJson);
List<Device> devices = fixture.getDevices();
assertEquals(2, devices.size());
Device plugDevice = devices.get(0);
assertEquals("hdm:ZigBee:0c4314fffea15de7", plugDevice.id);
assertEquals(5, plugDevice.deviceServiceIds.size());
assertEquals(0, plugDevice.childDeviceIds.size());
Device motionDetectorDevice = devices.get(1);
assertEquals("hdm:ZigBee:000d6f0012f13bfa", motionDetectorDevice.id);
assertEquals(6, motionDetectorDevice.deviceServiceIds.size());
assertEquals(0, motionDetectorDevice.childDeviceIds.size());
}
@Test
void getDevicesErrorRestResponse() throws InterruptedException, TimeoutException, ExecutionException {
Request request = mock(Request.class);
when(httpClient.createRequest(any(), eq(HttpMethod.GET))).thenReturn(request);
ContentResponse contentResponse = mock(ContentResponse.class);
when(request.send()).thenReturn(contentResponse);
when(contentResponse.getStatus()).thenReturn(400); // bad request
List<Device> devices = fixture.getDevices();
assertThat(devices, hasSize(0));
}
@ParameterizedTest
@MethodSource("org.openhab.binding.boschshc.internal.tests.common.CommonTestUtils#getExecutionAndTimeoutExceptionArguments()")
void getDevicesHandleExceptions() throws InterruptedException, TimeoutException, ExecutionException {
Request request = mock(Request.class);
when(httpClient.createRequest(any(), eq(HttpMethod.GET))).thenReturn(request);
when(request.send()).thenThrow(new ExecutionException(new RuntimeException("Test Exception")));
List<Device> devices = fixture.getDevices();
assertThat(devices, hasSize(0));
}
@Test
void getRooms() throws InterruptedException, TimeoutException, ExecutionException {
Request request = mock(Request.class);
when(httpClient.createRequest(any(), eq(HttpMethod.GET))).thenReturn(request);
ContentResponse contentResponse = mock(ContentResponse.class);
when(request.send()).thenReturn(contentResponse);
when(contentResponse.getStatus()).thenReturn(200);
String roomsJson = """
[
{
"@type": "room",
"id": "hz_1",
"iconId": "icon_room_living_room",
"name": "Living Room"
},
{
"@type": "room",
"id": "hz_2",
"iconId": "icon_room_dining_room",
"name": "Dining Room"
}
]
""";
when(contentResponse.getContentAsString()).thenReturn(roomsJson);
List<Room> rooms = fixture.getRooms();
assertEquals(2, rooms.size());
Room livingRoom = rooms.get(0);
assertEquals("hz_1", livingRoom.id);
assertEquals("Living Room", livingRoom.name);
Room diningRoom = rooms.get(1);
assertEquals("hz_2", diningRoom.id);
assertEquals("Dining Room", diningRoom.name);
}
@Test
void getRoomsErrorRestResponse() throws InterruptedException, TimeoutException, ExecutionException {
Request request = mock(Request.class);
when(httpClient.createRequest(any(), eq(HttpMethod.GET))).thenReturn(request);
ContentResponse contentResponse = mock(ContentResponse.class);
when(request.send()).thenReturn(contentResponse);
when(contentResponse.getStatus()).thenReturn(400); // bad request
List<Room> rooms = fixture.getRooms();
assertThat(rooms, hasSize(0));
}
@ParameterizedTest
@MethodSource("org.openhab.binding.boschshc.internal.tests.common.CommonTestUtils#getExecutionAndTimeoutExceptionArguments()")
void getRoomsHandleExceptions() throws InterruptedException, TimeoutException, ExecutionException {
Request request = mock(Request.class);
when(httpClient.createRequest(any(), eq(HttpMethod.GET))).thenReturn(request);
when(request.send()).thenThrow(new ExecutionException(new RuntimeException("Test Exception")));
List<Room> rooms = fixture.getRooms();
assertThat(rooms, hasSize(0));
}
@Test
void getServices() {
assertTrue(fixture.getServices().contains(ThingDiscoveryService.class));
}
@Test
void handleCommandIrrelevantChannel() {
ChannelUID channelUID = mock(ChannelUID.class);
when(channelUID.getId()).thenReturn(BoschSHCBindingConstants.CHANNEL_POWER_SWITCH);
fixture.handleCommand(channelUID, OnOffType.ON);
verifyNoInteractions(httpClient);
}
@Test
void handleCommandTriggerScenario()
throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
ChannelUID channelUID = mock(ChannelUID.class);
when(channelUID.getId()).thenReturn(BoschSHCBindingConstants.CHANNEL_TRIGGER_SCENARIO);
// required to prevent NPE
when(httpClient.sendRequest(any(), eq(Scenario[].class), any(), any())).thenReturn(new Scenario[] {});
fixture.handleCommand(channelUID, OnOffType.ON);
verify(httpClient).sendRequest(any(), eq(Scenario[].class), any(), any());
}
@Test
void registerDiscoveryListener() {
ThingDiscoveryService listener = mock(ThingDiscoveryService.class);
assertTrue(fixture.registerDiscoveryListener(listener));
assertFalse(fixture.registerDiscoveryListener(listener));
}
@Test
void unregisterDiscoveryListener() {
assertFalse(fixture.unregisterDiscoveryListener());
fixture.registerDiscoveryListener(mock(ThingDiscoveryService.class));
assertTrue(fixture.unregisterDiscoveryListener());
}
@Test
void initializeNoIpAddress() {
bridgeConfiguration.setProperties(new HashMap<String, Object>());
fixture.initialize();
ThingStatusInfo expectedStatus = ThingStatusInfoBuilder
.create(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR)
.withDescription("@text/offline.conf-error-empty-ip").build();
verify(thingHandlerCallback).statusUpdated(thing, expectedStatus);
}
@Test
void initializeNoPassword() {
HashMap<String, Object> properties = new HashMap<String, Object>();
properties.put("ipAddress", "localhost");
bridgeConfiguration.setProperties(properties);
fixture.initialize();
ThingStatusInfo expectedStatus = ThingStatusInfoBuilder
.create(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR)
.withDescription("@text/offline.conf-error-empty-password").build();
verify(thingHandlerCallback).statusUpdated(thing, expectedStatus);
}
@Test
void checkBridgeAccess() throws InterruptedException, TimeoutException, ExecutionException {
Request request = mock(Request.class);
when(httpClient.createRequest(any(), eq(HttpMethod.GET))).thenReturn(request);
ContentResponse contentResponse = mock(ContentResponse.class);
when(request.send()).thenReturn(contentResponse);
when(contentResponse.getStatus()).thenReturn(200);
assertTrue(fixture.checkBridgeAccess());
}
@Test
void checkBridgeAccessRestResponseError() throws InterruptedException, TimeoutException, ExecutionException {
Request request = mock(Request.class);
when(httpClient.createRequest(any(), eq(HttpMethod.GET))).thenReturn(request);
ContentResponse contentResponse = mock(ContentResponse.class);
when(request.send()).thenReturn(contentResponse);
when(contentResponse.getStatus()).thenReturn(400);
assertFalse(fixture.checkBridgeAccess());
}
@ParameterizedTest
@MethodSource("org.openhab.binding.boschshc.internal.tests.common.CommonTestUtils#getExecutionAndTimeoutExceptionArguments()")
void checkBridgeAccessRestException(Exception e) throws InterruptedException, TimeoutException, ExecutionException {
Request request = mock(Request.class);
when(httpClient.createRequest(any(), eq(HttpMethod.GET))).thenReturn(request);
when(request.send()).thenThrow(e);
assertFalse(fixture.checkBridgeAccess());
}
@Test
void getPublicInformation() throws InterruptedException, BoschSHCException, ExecutionException, TimeoutException {
fixture.getPublicInformation();
verify(httpClient).createRequest(any(), same(HttpMethod.GET));
verify(httpClient).sendRequest(any(), same(PublicInformation.class), any(), isNull());
}
}

View File

@ -12,9 +12,21 @@
*/
package org.openhab.binding.boschshc.internal.devices.bridge;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
@ -43,6 +55,8 @@ 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.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
@ -53,6 +67,7 @@ import org.openhab.binding.boschshc.internal.devices.bridge.dto.SubscribeResult;
import org.openhab.binding.boschshc.internal.devices.bridge.dto.UserDefinedState;
import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException;
import org.openhab.binding.boschshc.internal.exceptions.LongPollingFailedException;
import org.openhab.binding.boschshc.internal.tests.common.CommonTestUtils;
import com.google.gson.JsonObject;
import com.google.gson.JsonSyntaxException;
@ -332,18 +347,21 @@ class LongPollingTest {
assertTrue(longPollResultItem.isState());
}
@Test
void startSubscriptionFailure()
@ParameterizedTest
@MethodSource("org.openhab.binding.boschshc.internal.tests.common.CommonTestUtils#getBoschShcAndExecutionAndTimeoutAndInterruptedExceptionArguments()")
void startSubscriptionFailureHandleExceptions(Exception exception)
throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
when(httpClient.sendRequest(any(), same(SubscribeResult.class), any(), any()))
.thenThrow(new ExecutionException("Subscription failed.", null));
when(httpClient.sendRequest(any(), same(SubscribeResult.class), any(), any())).thenThrow(exception);
LongPollingFailedException e = assertThrows(LongPollingFailedException.class, () -> fixture.start(httpClient));
assertTrue(e.getMessage().contains("Subscription failed."));
assertThat(e.getCause(), instanceOf(exception.getClass()));
assertThat(e.getMessage(), containsString(CommonTestUtils.TEST_EXCEPTION_MESSAGE));
}
@Test
void startLongPollFailure() throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
@ParameterizedTest
@MethodSource("org.openhab.binding.boschshc.internal.tests.common.CommonTestUtils#getExceutionExceptionAndRuntimeExceptionArguments()")
void startLongPollFailure(Exception exception)
throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
when(httpClient.getBoschShcUrl(anyString())).thenCallRealMethod();
Request request = mock(Request.class);
@ -364,7 +382,6 @@ class LongPollingTest {
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);

View File

@ -12,7 +12,13 @@
*/
package org.openhab.binding.boschshc.internal.devices.bridge;
import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.List;
import java.util.UUID;
@ -24,10 +30,12 @@ import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpStatus;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.openhab.binding.boschshc.internal.devices.bridge.dto.Scenario;
import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException;
@ -61,11 +69,19 @@ class ScenarioHandlerTest {
.toArray(Exception[]::new);
}
private @NonNullByDefault({}) ScenarioHandler fixture;
private @NonNullByDefault({}) @Mock BoschHttpClient httpClient;
private @NonNullByDefault({}) @Mock Request request;
@BeforeEach
void beforeEach() {
fixture = new ScenarioHandler();
}
@Test
void triggerScenarioShouldSendPOSTToBoschAPI() throws Exception {
// GIVEN
final var httpClient = mock(BoschHttpClient.class);
final var request = mock(Request.class);
final var contentResponse = mock(ContentResponse.class);
when(httpClient.getBoschSmartHomeUrl(anyString())).thenReturn("http://localhost/smartHome/scenarios")
.thenReturn("http://localhost/smartHome/scenarios/1234/triggers");
@ -74,10 +90,8 @@ class ScenarioHandlerTest {
when(request.send()).thenReturn(contentResponse);
when(contentResponse.getStatus()).thenReturn(HttpStatus.OK_200);
final var handler = new ScenarioHandler();
// WHEN
handler.triggerScenario(httpClient, "Scenario 1");
fixture.triggerScenario(httpClient, "Scenario 1");
// THEN
verify(httpClient).getBoschSmartHomeUrl("scenarios");
@ -85,19 +99,15 @@ class ScenarioHandlerTest {
}
@Test
void triggerScenarioShouldNoSendPOSTToScenarioNameDoesNotExist() throws Exception {
void triggerScenarioShouldNotSendPOSTToScenarioNameDoesNotExist() throws Exception {
// GIVEN
final var httpClient = mock(BoschHttpClient.class);
final var request = mock(Request.class);
when(httpClient.getBoschSmartHomeUrl(anyString())).thenReturn("http://localhost/smartHome/scenarios")
.thenReturn("http://localhost/smartHome/scenarios/1234/triggers");
when(httpClient.createRequest(anyString(), any(HttpMethod.class))).thenReturn(request).thenReturn(request);
when(httpClient.sendRequest(any(Request.class), any(), any(), any())).thenReturn(existingScenarios);
final var handler = new ScenarioHandler();
// WHEN
handler.triggerScenario(httpClient, "not existing Scenario");
fixture.triggerScenario(httpClient, "not existing Scenario");
// THEN
verify(httpClient).getBoschSmartHomeUrl("scenarios");
@ -108,17 +118,13 @@ class ScenarioHandlerTest {
@MethodSource("exceptionData")
void triggerScenarioShouldNotPanicIfBoschAPIThrowsException(final Exception exception) throws Exception {
// GIVEN
final var httpClient = mock(BoschHttpClient.class);
final var request = mock(Request.class);
when(httpClient.getBoschSmartHomeUrl(anyString())).thenReturn("http://localhost/smartHome/scenarios")
.thenReturn("http://localhost/smartHome/scenarios/1234/triggers");
when(httpClient.createRequest(anyString(), any(HttpMethod.class))).thenReturn(request);
when(httpClient.sendRequest(any(Request.class), any(), any(), any())).thenThrow(exception);
final var handler = new ScenarioHandler();
// WHEN
handler.triggerScenario(httpClient, "Scenario 1");
fixture.triggerScenario(httpClient, "Scenario 1");
// THEN
verify(httpClient).getBoschSmartHomeUrl("scenarios");
@ -128,8 +134,6 @@ class ScenarioHandlerTest {
@Test
void triggerScenarioShouldNotPanicIfPOSTIsNotSuccessful() throws Exception {
// GIVEN
final var httpClient = mock(BoschHttpClient.class);
final var request = mock(Request.class);
final var contentResponse = mock(ContentResponse.class);
when(httpClient.getBoschSmartHomeUrl(anyString())).thenReturn("http://localhost/smartHome/scenarios")
.thenReturn("http://localhost/smartHome/scenarios/1234/triggers");
@ -138,10 +142,8 @@ class ScenarioHandlerTest {
when(request.send()).thenReturn(contentResponse);
when(contentResponse.getStatus()).thenReturn(HttpStatus.METHOD_NOT_ALLOWED_405);
final var handler = new ScenarioHandler();
// WHEN
handler.triggerScenario(httpClient, "Scenario 1");
fixture.triggerScenario(httpClient, "Scenario 1");
// THEN
verify(httpClient).getBoschSmartHomeUrl("scenarios");
@ -152,21 +154,27 @@ class ScenarioHandlerTest {
@MethodSource("httpExceptionData")
void triggerScenarioShouldNotPanicIfPOSTThrowsException(final Exception exception) throws Exception {
// GIVEN
final var httpClient = mock(BoschHttpClient.class);
final var request = mock(Request.class);
when(httpClient.getBoschSmartHomeUrl(anyString())).thenReturn("http://localhost/smartHome/scenarios")
.thenReturn("http://localhost/smartHome/scenarios/1234/triggers");
when(httpClient.createRequest(anyString(), any(HttpMethod.class))).thenReturn(request).thenReturn(request);
when(httpClient.sendRequest(any(Request.class), any(), any(), any())).thenReturn(existingScenarios);
when(request.send()).thenThrow(exception);
final var handler = new ScenarioHandler();
// WHEN
handler.triggerScenario(httpClient, "Scenario 1");
fixture.triggerScenario(httpClient, "Scenario 1");
// THEN
verify(httpClient).getBoschSmartHomeUrl("scenarios");
verify(request).send();
}
@Test
void prettyLogScenarios() {
Scenario scenario1 = Scenario.createScenario("id1", "Scenario 1", "1708619045411");
Scenario scenario2 = Scenario.createScenario("id2", "Scenario 2", "1708619065445");
assertEquals(
"[\n" + " Scenario{name='Scenario 1', id='id1', lastTimeTriggered='1708619045411'}\n"
+ " Scenario{name='Scenario 2', id='id2', lastTimeTriggered='1708619065445'}\n" + "]",
fixture.prettyLogScenarios(new Scenario[] { scenario1, scenario2 }));
}
}

View File

@ -0,0 +1,47 @@
/**
* Copyright (c) 2010-2024 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.boschshc.internal.devices.bridge.dto;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
/**
* Unit tests for {@link Scenario}.
*
* @author David Pace - Initial contribution
*
*/
class ScenarioTest {
private Scenario fixture;
@BeforeEach
protected void setUp() throws Exception {
fixture = Scenario.createScenario("abc", "My Scenario", "1708845918493");
}
@Test
void isValid() {
assertTrue(Scenario.isValid(new Scenario[] { fixture }));
assertFalse(Scenario.isValid(new Scenario[] { fixture, new Scenario() }));
}
@Test
void testToString() {
assertEquals("Scenario{name='My Scenario', id='abc', lastTimeTriggered='1708845918493'}", fixture.toString());
}
}

View File

@ -13,14 +13,20 @@
package org.openhab.binding.boschshc.internal.devices.intrusion;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.openhab.binding.boschshc.internal.devices.AbstractBoschSHCHandlerTest;
@ -29,6 +35,8 @@ import org.openhab.binding.boschshc.internal.services.intrusion.actions.arm.dto.
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.ThingTypeUID;
import com.google.gson.JsonElement;
@ -64,6 +72,20 @@ class IntrusionDetectionHandlerTest extends AbstractBoschSHCHandlerTest<Intrusio
assertEquals("0", armRequest.profileId);
}
@ParameterizedTest
@MethodSource("org.openhab.binding.boschshc.internal.tests.common.CommonTestUtils#getExecutionAndTimeoutAndInterruptedExceptionArguments()")
void testHandleCommandArmActionHandleExceptions(Exception e)
throws InterruptedException, TimeoutException, ExecutionException {
when(getBridgeHandler().postAction(any(), any())).thenThrow(e);
getFixture().handleCommand(new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_ARM_ACTION),
new StringType("0"));
verify(getBridgeHandler()).postAction(eq("intrusion/actions/arm"), armActionRequestCaptor.capture());
ArmActionRequest armRequest = armActionRequestCaptor.getValue();
assertEquals("0", armRequest.profileId);
}
@Test
void testHandleCommandDisarmAction() throws InterruptedException, TimeoutException, ExecutionException {
getFixture().handleCommand(new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_DISARM_ACTION),
@ -71,6 +93,20 @@ class IntrusionDetectionHandlerTest extends AbstractBoschSHCHandlerTest<Intrusio
verify(getBridgeHandler()).postAction("intrusion/actions/disarm");
}
@ParameterizedTest
@MethodSource("org.openhab.binding.boschshc.internal.tests.common.CommonTestUtils#getExecutionAndTimeoutAndInterruptedExceptionArguments()")
void testHandleCommandDisarmActionHandleExceptions(Exception e)
throws InterruptedException, TimeoutException, ExecutionException {
when(getBridgeHandler().postAction(any())).thenThrow(e);
getFixture().handleCommand(new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_DISARM_ACTION),
OnOffType.ON);
verify(getCallback()).statusUpdated(same(getThing()),
argThat(status -> status.getStatus().equals(ThingStatus.OFFLINE)
&& status.getStatusDetail().equals(ThingStatusDetail.COMMUNICATION_ERROR)));
}
@Test
void testHandleCommandMuteAction() throws InterruptedException, TimeoutException, ExecutionException {
getFixture().handleCommand(new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_MUTE_ACTION),

View File

@ -15,6 +15,7 @@ package org.openhab.binding.boschshc.internal.devices.shuttercontrol;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import java.util.concurrent.ExecutionException;
@ -140,4 +141,14 @@ class ShutterControl2HandlerTest extends ShutterControlHandlerTest {
ChildProtectionServiceState state = childProtectionServiceStateCaptor.getValue();
assertTrue(state.childLockActive);
}
@Test
void testHandleCommandChildProtectionInvalidCommand()
throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
getFixture().handleCommand(
new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_CHILD_PROTECTION),
DecimalType.ZERO);
verify(getBridgeHandler(), times(0)).putState(eq(getDeviceID()), eq("ChildProtection"),
childProtectionServiceStateCaptor.capture());
}
}

View File

@ -17,22 +17,19 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
import java.util.stream.Stream;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.ArgumentCaptor;
import org.openhab.binding.boschshc.internal.devices.AbstractBoschSHCHandlerTest;
@ -44,9 +41,7 @@ import org.openhab.core.library.types.OnOffType;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.ThingStatusInfo;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.ThingUID;
/**
* Unit tests for UserStateHandlerTest
@ -94,25 +89,41 @@ class UserStateHandlerTest extends AbstractBoschSHCHandlerTest<UserStateHandler>
}
@ParameterizedTest()
@MethodSource("provideExceptions")
void testHandleCommandSetStateUpdatesThingStatusOnException(Exception mockException)
@MethodSource("org.openhab.binding.boschshc.internal.tests.common.CommonTestUtils#getExecutionExceptionAndInterruptedExceptionArguments()")
void testHandleCommandSetStateUpdatesThingStatusOnException(Exception exception)
throws InterruptedException, TimeoutException, ExecutionException {
reset(getCallback());
lenient().when(getBridgeHandler().putState(anyString(), anyString(), any(UserStateServiceState.class)))
.thenThrow(mockException);
when(getBridgeHandler().putState(anyString(), anyString(), any(UserStateServiceState.class)))
.thenThrow(exception);
var channel = new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_USER_DEFINED_STATE);
getFixture().handleCommand(channel, OnOffType.ON);
verify(getCallback()).getBridge(any(ThingUID.class));
ThingStatusInfo expectedStatusInfo = new ThingStatusInfo(ThingStatus.OFFLINE,
ThingStatusDetail.COMMUNICATION_ERROR,
String.format("Error while putting user-defined state for %s", channel.getThingUID().getId()));
verify(getCallback()).statusUpdated(same(getThing()), eq(expectedStatusInfo));
verify(getCallback()).statusUpdated(same(getThing()),
argThat(status -> status.getStatus().equals(ThingStatus.OFFLINE)
&& status.getStatusDetail().equals(ThingStatusDetail.COMMUNICATION_ERROR)));
}
private static Stream<Arguments> provideExceptions() {
return Stream.of(Arguments.of(new TimeoutException("test exception")),
Arguments.of(new InterruptedException("test exception")));
@Test
void initializeWithoutId() {
when(getThing().getConfiguration()).thenReturn(new Configuration());
getFixture().initialize();
verify(getCallback()).statusUpdated(same(getThing()),
argThat(status -> status.getStatus().equals(ThingStatus.OFFLINE)
&& status.getStatusDetail().equals(ThingStatusDetail.CONFIGURATION_ERROR)));
}
@ParameterizedTest
@MethodSource("org.openhab.binding.boschshc.internal.tests.common.CommonTestUtils#getBoschShcAndExecutionAndTimeoutAndInterruptedExceptionArguments()")
void initializeHandleExceptions(Exception e)
throws BoschSHCException, InterruptedException, TimeoutException, ExecutionException {
when(getBridgeHandler().getUserStateInfo(anyString())).thenThrow(e);
getFixture().initialize();
verify(getCallback()).statusUpdated(same(getThing()),
argThat(status -> status.getStatus().equals(ThingStatus.OFFLINE)
&& status.getStatusDetail().equals(ThingStatusDetail.CONFIGURATION_ERROR)));
}
}

View File

@ -22,7 +22,9 @@ import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.net.ConnectException;
@ -48,6 +50,7 @@ import org.mockito.quality.Strictness;
import org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants;
import org.openhab.binding.boschshc.internal.devices.bridge.dto.PublicInformation;
import org.openhab.core.config.discovery.DiscoveryResult;
import org.openhab.core.io.net.http.HttpClientFactory;
import org.openhab.core.thing.ThingUID;
/**
@ -128,6 +131,7 @@ class BridgeDiscoveryParticipantTest {
@Test
void testCreateResult() throws Exception {
DiscoveryResult result = fixture.createResult(shcBridge);
assertNotNull(result);
assertThat(result.getBindingId(), is(BoschSHCBindingConstants.BINDING_ID));
assertThat(result.getThingUID().getId(), is("192-168-0-123"));
@ -138,19 +142,23 @@ class BridgeDiscoveryParticipantTest {
@Test
void testCreateResultOtherDevice() throws Exception {
DiscoveryResult result = fixture.createResult(otherDevice);
assertNull(result);
}
@Test
void testCreateResultNoIPAddress() throws Exception {
when(shcBridge.getHostAddresses()).thenReturn(new String[] { "" });
DiscoveryResult result = fixture.createResult(shcBridge);
assertNull(result);
}
@Test
void testGetThingUID() throws Exception {
ThingUID thingUID = fixture.getThingUID(shcBridge);
assertNotNull(thingUID);
assertThat(thingUID.getBindingId(), is(BoschSHCBindingConstants.BINDING_ID));
assertThat(thingUID.getId(), is("192-168-0-123"));
@ -165,6 +173,7 @@ class BridgeDiscoveryParticipantTest {
void testGetBridgeAddress() throws Exception {
@Nullable
PublicInformation bridgeInformation = fixture.discoverBridge("192.168.0.123");
assertThat(bridgeInformation, not(nullValue()));
assertThat(bridgeInformation.shcIpAddress, is("192.168.0.123"));
}
@ -178,6 +187,7 @@ class BridgeDiscoveryParticipantTest {
void testGetPublicInformationFromPossibleBridgeAddress() throws Exception {
@Nullable
PublicInformation bridgeInformation = fixture.getPublicInformationFromPossibleBridgeAddress("192.168.0.123");
assertThat(bridgeInformation, not(nullValue()));
assertThat(bridgeInformation.shcIpAddress, is("192.168.0.123"));
}
@ -187,6 +197,7 @@ class BridgeDiscoveryParticipantTest {
when(contentResponse.getContentAsString()).thenReturn("{\"nothing\":\"useful\"}");
fixture = new BridgeDiscoveryParticipant(mockHttpClient);
assertThat(fixture.getPublicInformationFromPossibleBridgeAddress("192.168.0.123"), is(nullValue()));
}
@ -195,6 +206,7 @@ class BridgeDiscoveryParticipantTest {
when(contentResponse.getStatus()).thenReturn(HttpStatus.BAD_REQUEST_400);
fixture = new BridgeDiscoveryParticipant(mockHttpClient);
assertThat(fixture.getPublicInformationFromPossibleBridgeAddress("192.168.0.123"), is(nullValue()));
}
@ -207,4 +219,13 @@ class BridgeDiscoveryParticipantTest {
PublicInformation result2 = fixture.getOrComputePublicInformation("192.168.0.123");
assertSame(result, result2);
}
@Test
void testPublicConstructor() {
HttpClientFactory httpClientFactory = mock(HttpClientFactory.class);
fixture = new BridgeDiscoveryParticipant(httpClientFactory);
verify(httpClientFactory).createHttpClient(eq(BoschSHCBindingConstants.BINDING_ID), any());
}
}

View File

@ -25,6 +25,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import org.eclipse.jdt.annotation.NonNullByDefault;
@ -77,10 +78,35 @@ class ThingDiscoveryServiceTest {
when(bridgeHandler.getThing()).thenReturn(mockBridge);
}
@Test
void initialize() {
fixture.initialize();
verify(bridgeHandler).registerDiscoveryListener(fixture);
}
@Test
void testStartScan() throws InterruptedException {
mockBridgeCalls();
Device device = new Device();
device.name = "My Smart Plug";
device.deviceModel = "PSM";
device.id = "hdm:HomeMaticIP:3014F711A00004953859F31B";
device.deviceServiceIds = List.of("PowerMeter", "PowerSwitch", "PowerSwitchProgram", "Routing");
List<Device> devices = new ArrayList<>();
devices.add(device);
when(bridgeHandler.getDevices()).thenReturn(devices);
UserDefinedState userDefinedState = new UserDefinedState();
userDefinedState.setName("My State");
userDefinedState.setId("23d34fa6-382a-444d-8aae-89c706e22158");
userDefinedState.setState(true);
List<UserDefinedState> userDefinedStates = new ArrayList<>();
userDefinedStates.add(userDefinedState);
when(bridgeHandler.getUserStates()).thenReturn(userDefinedStates);
fixture.activate();
fixture.startScan();
@ -268,6 +294,15 @@ class ThingDiscoveryServiceTest {
verify(discoveryListener, times(2)).thingDiscovered(any(), any());
}
@Test
void dispose() {
Bridge thing = mock(Bridge.class);
when(thing.getUID()).thenReturn(new ThingUID(BoschSHCBindingConstants.THING_TYPE_SHC, "shc123456"));
when(bridgeHandler.getThing()).thenReturn(thing);
fixture.dispose();
verify(bridgeHandler).unregisterDiscoveryListener();
}
@Test
void getThingTypeUIDLightControl2ChildDevice() {
Device device = new Device();

View File

@ -71,4 +71,9 @@ class UserStateServiceStateTest {
subject.setState(true);
assertEquals(OnOffType.ON, subject.toOnOffType());
}
@Test
void testToString() {
assertEquals("UserStateServiceState{state=false, type='userdefinedstates'}", subject.toString());
}
}

View File

@ -0,0 +1,67 @@
/**
* Copyright (c) 2010-2024 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.boschshc.internal.tests.common;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException;
/**
* Common utilities used in unit tests.
*
* @author David Pace - Initial contribution
*
*/
@NonNullByDefault
public final class CommonTestUtils {
public static final String TEST_EXCEPTION_MESSAGE = "Test exception";
private CommonTestUtils() {
// Utility Class
}
public static List<Exception> getExecutionExceptionAndInterruptedExceptionArguments() {
return List.of(new ExecutionException(TEST_EXCEPTION_MESSAGE, null),
new InterruptedException(TEST_EXCEPTION_MESSAGE));
}
public static List<Exception> getExceutionExceptionAndRuntimeExceptionArguments() {
return List.of(new ExecutionException(TEST_EXCEPTION_MESSAGE, null),
new RuntimeException(TEST_EXCEPTION_MESSAGE));
}
public static List<Exception> getBoschShcAndExecutionAndTimeoutExceptionArguments() {
return List.of(new BoschSHCException(TEST_EXCEPTION_MESSAGE),
new ExecutionException(TEST_EXCEPTION_MESSAGE, null), new TimeoutException(TEST_EXCEPTION_MESSAGE));
}
public static List<Exception> getBoschShcAndExecutionAndTimeoutAndInterruptedExceptionArguments() {
return List.of(new BoschSHCException(TEST_EXCEPTION_MESSAGE),
new ExecutionException(TEST_EXCEPTION_MESSAGE, null), new TimeoutException(TEST_EXCEPTION_MESSAGE),
new InterruptedException(TEST_EXCEPTION_MESSAGE));
}
public static List<Exception> getExecutionAndTimeoutAndInterruptedExceptionArguments() {
return List.of(new ExecutionException(TEST_EXCEPTION_MESSAGE, null),
new TimeoutException(TEST_EXCEPTION_MESSAGE), new InterruptedException(TEST_EXCEPTION_MESSAGE));
}
public static List<Exception> getExecutionAndTimeoutExceptionArguments() {
return List.of(new ExecutionException(TEST_EXCEPTION_MESSAGE, null),
new TimeoutException(TEST_EXCEPTION_MESSAGE));
}
}