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.*;
17 import static org.mockito.ArgumentMatchers.*;
18 import static org.mockito.Mockito.*;
19 import static org.openhab.binding.awattar.internal.AwattarBindingConstants.*;
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.nio.charset.StandardCharsets;
24 import java.time.ZoneId;
26 import java.util.Objects;
27 import java.util.SortedSet;
28 import java.util.concurrent.ExecutionException;
29 import java.util.concurrent.TimeUnit;
30 import java.util.concurrent.TimeoutException;
31 import java.util.stream.Stream;
33 import org.eclipse.jdt.annotation.NonNullByDefault;
34 import org.eclipse.jetty.client.HttpClient;
35 import org.eclipse.jetty.client.api.ContentResponse;
36 import org.eclipse.jetty.client.api.Request;
37 import org.eclipse.jetty.http.HttpMethod;
38 import org.eclipse.jetty.http.HttpStatus;
39 import org.junit.jupiter.api.BeforeEach;
40 import org.junit.jupiter.api.Test;
41 import org.junit.jupiter.api.extension.ExtendWith;
42 import org.junit.jupiter.params.ParameterizedTest;
43 import org.junit.jupiter.params.provider.Arguments;
44 import org.junit.jupiter.params.provider.MethodSource;
45 import org.mockito.Mock;
46 import org.mockito.junit.jupiter.MockitoExtension;
47 import org.mockito.junit.jupiter.MockitoSettings;
48 import org.mockito.quality.Strictness;
49 import org.openhab.binding.awattar.internal.AwattarBindingConstants;
50 import org.openhab.binding.awattar.internal.AwattarPrice;
51 import org.openhab.core.config.core.Configuration;
52 import org.openhab.core.i18n.TimeZoneProvider;
53 import org.openhab.core.library.types.DateTimeType;
54 import org.openhab.core.library.types.StringType;
55 import org.openhab.core.test.java.JavaTest;
56 import org.openhab.core.thing.Bridge;
57 import org.openhab.core.thing.ChannelUID;
58 import org.openhab.core.thing.Thing;
59 import org.openhab.core.thing.ThingUID;
60 import org.openhab.core.thing.binding.ThingHandlerCallback;
61 import org.openhab.core.types.State;
64 * The {@link AwattarBridgeHandlerTest} contains tests for the {@link AwattarBridgeHandler}
66 * @author Jan N. Klug - Initial contribution
68 @ExtendWith(MockitoExtension.class)
69 @MockitoSettings(strictness = Strictness.LENIENT)
71 public class AwattarBridgeHandlerTest extends JavaTest {
72 public static final ThingUID BRIDGE_UID = new ThingUID(AwattarBindingConstants.THING_TYPE_BRIDGE, "testBridge");
75 private @Mock @NonNullByDefault({}) Bridge bridgeMock;
76 private @Mock @NonNullByDefault({}) ThingHandlerCallback bridgeCallbackMock;
77 private @Mock @NonNullByDefault({}) HttpClient httpClientMock;
78 private @Mock @NonNullByDefault({}) TimeZoneProvider timeZoneProviderMock;
79 private @Mock @NonNullByDefault({}) Request requestMock;
80 private @Mock @NonNullByDefault({}) ContentResponse contentResponseMock;
82 // best price handler mocks
83 private @Mock @NonNullByDefault({}) Thing bestpriceMock;
84 private @Mock @NonNullByDefault({}) ThingHandlerCallback bestPriceCallbackMock;
86 private @NonNullByDefault({}) AwattarBridgeHandler bridgeHandler;
89 public void setUp() throws IOException, ExecutionException, InterruptedException, TimeoutException {
90 try (InputStream inputStream = AwattarBridgeHandlerTest.class.getResourceAsStream("api_response.json")) {
91 if (inputStream == null) {
92 throw new IOException("inputstream is null");
94 byte[] bytes = inputStream.readAllBytes();
96 throw new IOException("Resulting byte-array empty");
98 when(contentResponseMock.getContentAsString()).thenReturn(new String(bytes, StandardCharsets.UTF_8));
100 when(contentResponseMock.getStatus()).thenReturn(HttpStatus.OK_200);
101 when(httpClientMock.newRequest(anyString())).thenReturn(requestMock);
102 when(requestMock.method(HttpMethod.GET)).thenReturn(requestMock);
103 when(requestMock.timeout(10, TimeUnit.SECONDS)).thenReturn(requestMock);
104 when(requestMock.send()).thenReturn(contentResponseMock);
106 when(timeZoneProviderMock.getTimeZone()).thenReturn(ZoneId.of("GMT+2"));
108 when(bridgeMock.getUID()).thenReturn(BRIDGE_UID);
109 bridgeHandler = new AwattarBridgeHandler(bridgeMock, httpClientMock, timeZoneProviderMock);
110 bridgeHandler.setCallback(bridgeCallbackMock);
111 bridgeHandler.refreshIfNeeded();
112 when(bridgeMock.getHandler()).thenReturn(bridgeHandler);
115 when(bestpriceMock.getBridgeUID()).thenReturn(BRIDGE_UID);
117 when(bestPriceCallbackMock.getBridge(any())).thenReturn(bridgeMock);
118 when(bestPriceCallbackMock.isChannelLinked(any())).thenReturn(true);
122 public void testPricesRetrieval() {
123 SortedSet<AwattarPrice> prices = bridgeHandler.getPrices();
125 assertThat(prices, hasSize(72));
127 Objects.requireNonNull(prices);
129 // check if first and last element are correct
130 assertThat(prices.first().timerange().start(), is(1718316000000L));
131 assertThat(prices.last().timerange().end(), is(1718575200000L));
135 public void testGetPriceForSuccess() {
136 AwattarPrice price = bridgeHandler.getPriceFor(1718503200000L);
138 assertThat(price, is(notNullValue()));
139 Objects.requireNonNull(price);
140 assertThat(price.netPrice(), is(closeTo(0.219, 0.001)));
144 public void testGetPriceForFail() {
145 AwattarPrice price = bridgeHandler.getPriceFor(1518503200000L);
147 assertThat(price, is(nullValue()));
151 public void testContainsPrizeFor() {
152 assertThat(bridgeHandler.containsPriceFor(1618503200000L), is(false));
153 assertThat(bridgeHandler.containsPriceFor(1718503200000L), is(true));
154 assertThat(bridgeHandler.containsPriceFor(1818503200000L), is(false));
157 public static Stream<Arguments> testBestpriceHandler() {
159 Arguments.of(1, true, CHANNEL_START, new DateTimeType("2024-06-15T14:00:00.000+0200")),
160 Arguments.of(1, true, CHANNEL_END, new DateTimeType("2024-06-15T15:00:00.000+0200")),
161 Arguments.of(1, true, CHANNEL_HOURS, new StringType("14")),
162 Arguments.of(1, false, CHANNEL_START, new DateTimeType("2024-06-15T14:00:00.000+0200")),
163 Arguments.of(1, false, CHANNEL_END, new DateTimeType("2024-06-15T15:00:00.000+0200")),
164 Arguments.of(1, false, CHANNEL_HOURS, new StringType("14")),
165 Arguments.of(2, true, CHANNEL_START, new DateTimeType("2024-06-15T13:00:00.000+0200")),
166 Arguments.of(2, true, CHANNEL_END, new DateTimeType("2024-06-15T15:00:00.000+0200")),
167 Arguments.of(2, true, CHANNEL_HOURS, new StringType("13,14")),
168 Arguments.of(2, false, CHANNEL_START, new DateTimeType("2024-06-15T13:00:00.000+0200")),
169 Arguments.of(2, false, CHANNEL_END, new DateTimeType("2024-06-15T15:00:00.000+0200")),
170 Arguments.of(2, false, CHANNEL_HOURS, new StringType("13,14")));
175 public void testBestpriceHandler(int length, boolean consecutive, String channelId, State expectedState) {
176 ThingUID bestPriceUid = new ThingUID(AwattarBindingConstants.THING_TYPE_BESTPRICE, "foo");
177 Map<String, Object> config = Map.of("length", length, "consecutive", consecutive);
178 when(bestpriceMock.getConfiguration()).thenReturn(new Configuration(config));
180 AwattarBestPriceHandler handler = new AwattarBestPriceHandler(bestpriceMock, timeZoneProviderMock) {
182 protected TimeRange getRange(int start, int duration, ZoneId zoneId) {
183 return new TimeRange(1718402400000L, 1718488800000L);
187 handler.setCallback(bestPriceCallbackMock);
189 ChannelUID channelUID = new ChannelUID(bestPriceUid, channelId);
190 handler.refreshChannel(channelUID);
191 verify(bestPriceCallbackMock).stateUpdated(channelUID, expectedState);