2 * Copyright (c) 2010-2024 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.awattar.internal.handler;
15 import static org.hamcrest.MatcherAssert.assertThat;
16 import static org.hamcrest.Matchers.closeTo;
17 import static org.hamcrest.Matchers.is;
18 import static org.hamcrest.Matchers.notNullValue;
19 import static org.hamcrest.Matchers.nullValue;
20 import static org.mockito.ArgumentMatchers.any;
21 import static org.mockito.Mockito.verify;
22 import static org.mockito.Mockito.when;
23 import static org.openhab.binding.awattar.internal.AwattarBindingConstants.CHANNEL_END;
24 import static org.openhab.binding.awattar.internal.AwattarBindingConstants.CHANNEL_HOURS;
25 import static org.openhab.binding.awattar.internal.AwattarBindingConstants.CHANNEL_START;
27 import java.io.IOException;
28 import java.io.InputStream;
29 import java.lang.reflect.Field;
30 import java.time.ZoneId;
31 import java.util.Comparator;
32 import java.util.List;
34 import java.util.Objects;
35 import java.util.SortedSet;
36 import java.util.TreeSet;
37 import java.util.stream.Stream;
39 import org.eclipse.jdt.annotation.NonNullByDefault;
40 import org.eclipse.jetty.client.HttpClient;
41 import org.junit.jupiter.api.BeforeEach;
42 import org.junit.jupiter.api.Test;
43 import org.junit.jupiter.api.extension.ExtendWith;
44 import org.junit.jupiter.params.ParameterizedTest;
45 import org.junit.jupiter.params.provider.Arguments;
46 import org.junit.jupiter.params.provider.MethodSource;
47 import org.junit.platform.commons.support.HierarchyTraversalMode;
48 import org.junit.platform.commons.support.ReflectionSupport;
49 import org.mockito.Mock;
50 import org.mockito.junit.jupiter.MockitoExtension;
51 import org.mockito.junit.jupiter.MockitoSettings;
52 import org.mockito.quality.Strictness;
53 import org.openhab.binding.awattar.internal.AwattarBindingConstants;
54 import org.openhab.binding.awattar.internal.AwattarPrice;
55 import org.openhab.binding.awattar.internal.api.AwattarApi;
56 import org.openhab.binding.awattar.internal.api.AwattarApi.AwattarApiException;
57 import org.openhab.binding.awattar.internal.dto.AwattarApiData;
58 import org.openhab.core.config.core.Configuration;
59 import org.openhab.core.i18n.TimeZoneProvider;
60 import org.openhab.core.library.types.DateTimeType;
61 import org.openhab.core.library.types.StringType;
62 import org.openhab.core.test.java.JavaTest;
63 import org.openhab.core.thing.Bridge;
64 import org.openhab.core.thing.ChannelUID;
65 import org.openhab.core.thing.Thing;
66 import org.openhab.core.thing.ThingUID;
67 import org.openhab.core.thing.binding.ThingHandlerCallback;
68 import org.openhab.core.types.State;
70 import com.google.gson.Gson;
73 * The {@link AwattarBridgeHandlerTest} contains tests for the {@link AwattarBridgeHandler}
75 * @author Jan N. Klug - Initial contribution
77 @ExtendWith(MockitoExtension.class)
78 @MockitoSettings(strictness = Strictness.LENIENT)
80 public class AwattarBridgeHandlerTest extends JavaTest {
81 public static final ThingUID BRIDGE_UID = new ThingUID(AwattarBindingConstants.THING_TYPE_BRIDGE, "testBridge");
84 private @Mock @NonNullByDefault({}) Bridge bridgeMock;
85 private @Mock @NonNullByDefault({}) ThingHandlerCallback bridgeCallbackMock;
86 private @Mock @NonNullByDefault({}) HttpClient httpClientMock;
87 private @Mock @NonNullByDefault({}) TimeZoneProvider timeZoneProviderMock;
88 private @Mock @NonNullByDefault({}) AwattarApi awattarApiMock;
90 // best price handler mocks
91 private @Mock @NonNullByDefault({}) Thing bestpriceMock;
92 private @Mock @NonNullByDefault({}) ThingHandlerCallback bestPriceCallbackMock;
94 private @NonNullByDefault({}) AwattarBridgeHandler bridgeHandler;
97 public void setUp() throws IOException, IllegalArgumentException, IllegalAccessException, AwattarApiException {
99 // mock the API response
100 try (InputStream inputStream = AwattarBridgeHandlerTest.class.getResourceAsStream("api_response.json")) {
101 SortedSet<AwattarPrice> result = new TreeSet<>(Comparator.comparing(AwattarPrice::timerange));
102 Gson gson = new Gson();
104 String json = new String(inputStream.readAllBytes());
106 // read json file into sorted set of AwattarPrices
107 AwattarApiData apiData = gson.fromJson(json, AwattarApiData.class);
108 apiData.data.forEach(datum -> result.add(new AwattarPrice(datum.marketprice, datum.marketprice,
109 datum.marketprice, datum.marketprice, new TimeRange(datum.startTimestamp, datum.endTimestamp))));
110 when(awattarApiMock.getData()).thenReturn(result);
113 when(timeZoneProviderMock.getTimeZone()).thenReturn(ZoneId.of("GMT+2"));
115 when(bridgeMock.getUID()).thenReturn(BRIDGE_UID);
116 bridgeHandler = new AwattarBridgeHandler(bridgeMock, httpClientMock, timeZoneProviderMock);
117 bridgeHandler.setCallback(bridgeCallbackMock);
119 // mock the private field awattarApi
120 List<Field> fields = ReflectionSupport.findFields(AwattarBridgeHandler.class,
121 field -> field.getName().equals("awattarApi"), HierarchyTraversalMode.BOTTOM_UP);
123 for (Field field : fields) {
124 field.setAccessible(true);
125 field.set(bridgeHandler, awattarApiMock);
128 bridgeHandler.refreshIfNeeded();
129 when(bridgeMock.getHandler()).thenReturn(bridgeHandler);
132 when(bestpriceMock.getBridgeUID()).thenReturn(BRIDGE_UID);
133 when(bestPriceCallbackMock.getBridge(any())).thenReturn(bridgeMock);
134 when(bestPriceCallbackMock.isChannelLinked(any())).thenReturn(true);
138 void testGetPriceForSuccess() {
139 AwattarPrice price = bridgeHandler.getPriceFor(1718503200000L);
141 assertThat(price, is(notNullValue()));
142 Objects.requireNonNull(price);
143 assertThat(price.netPrice(), is(closeTo(2.19, 0.001)));
147 void testGetPriceForFail() {
148 AwattarPrice price = bridgeHandler.getPriceFor(1518503200000L);
150 assertThat(price, is(nullValue()));
154 void testContainsPrizeFor() {
155 assertThat(bridgeHandler.containsPriceFor(1618503200000L), is(false));
156 assertThat(bridgeHandler.containsPriceFor(1718503200000L), is(true));
157 assertThat(bridgeHandler.containsPriceFor(1818503200000L), is(false));
160 public static Stream<Arguments> testBestpriceHandler() {
162 Arguments.of(1, true, CHANNEL_START, new DateTimeType("2024-06-15T14:00:00.000+0200")),
163 Arguments.of(1, true, CHANNEL_END, new DateTimeType("2024-06-15T15:00:00.000+0200")),
164 Arguments.of(1, true, CHANNEL_HOURS, new StringType("14")),
165 Arguments.of(1, false, CHANNEL_START, new DateTimeType("2024-06-15T14:00:00.000+0200")),
166 Arguments.of(1, false, CHANNEL_END, new DateTimeType("2024-06-15T15:00:00.000+0200")),
167 Arguments.of(1, false, CHANNEL_HOURS, new StringType("14")),
168 Arguments.of(2, true, CHANNEL_START, new DateTimeType("2024-06-15T13:00:00.000+0200")),
169 Arguments.of(2, true, CHANNEL_END, new DateTimeType("2024-06-15T15:00:00.000+0200")),
170 Arguments.of(2, true, CHANNEL_HOURS, new StringType("13,14")),
171 Arguments.of(2, false, CHANNEL_START, new DateTimeType("2024-06-15T13:00:00.000+0200")),
172 Arguments.of(2, false, CHANNEL_END, new DateTimeType("2024-06-15T15:00:00.000+0200")),
173 Arguments.of(2, false, CHANNEL_HOURS, new StringType("13,14")));
178 void testBestpriceHandler(int length, boolean consecutive, String channelId, State expectedState) {
179 ThingUID bestPriceUid = new ThingUID(AwattarBindingConstants.THING_TYPE_BESTPRICE, "foo");
180 Map<String, Object> config = Map.of("length", length, "consecutive", consecutive);
181 when(bestpriceMock.getConfiguration()).thenReturn(new Configuration(config));
183 AwattarBestPriceHandler handler = new AwattarBestPriceHandler(bestpriceMock, timeZoneProviderMock) {
185 protected TimeRange getRange(int start, int duration, ZoneId zoneId) {
186 return new TimeRange(1718402400000L, 1718488800000L);
190 handler.setCallback(bestPriceCallbackMock);
192 ChannelUID channelUID = new ChannelUID(bestPriceUid, channelId);
193 handler.refreshChannel(channelUID);
194 verify(bestPriceCallbackMock).stateUpdated(channelUID, expectedState);