]> git.basschouten.com Git - openhab-addons.git/blob
5c1ce6c705e8cba0ce19767e0ba7f2dcf3fb621c
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2024 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.awattar.internal.handler;
14
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;
26
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;
33 import java.util.Map;
34 import java.util.Objects;
35 import java.util.SortedSet;
36 import java.util.TreeSet;
37 import java.util.stream.Stream;
38
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;
69
70 import com.google.gson.Gson;
71
72 /**
73  * The {@link AwattarBridgeHandlerTest} contains tests for the {@link AwattarBridgeHandler}
74  *
75  * @author Jan N. Klug - Initial contribution
76  */
77 @ExtendWith(MockitoExtension.class)
78 @MockitoSettings(strictness = Strictness.LENIENT)
79 @NonNullByDefault
80 public class AwattarBridgeHandlerTest extends JavaTest {
81     public static final ThingUID BRIDGE_UID = new ThingUID(AwattarBindingConstants.THING_TYPE_BRIDGE, "testBridge");
82
83     // bridge mocks
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;
89
90     // best price handler mocks
91     private @Mock @NonNullByDefault({}) Thing bestpriceMock;
92     private @Mock @NonNullByDefault({}) ThingHandlerCallback bestPriceCallbackMock;
93
94     private @NonNullByDefault({}) AwattarBridgeHandler bridgeHandler;
95
96     @BeforeEach
97     public void setUp() throws IOException, IllegalArgumentException, IllegalAccessException, AwattarApiException {
98
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();
103
104             String json = new String(inputStream.readAllBytes());
105
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);
111         }
112
113         when(timeZoneProviderMock.getTimeZone()).thenReturn(ZoneId.of("GMT+2"));
114
115         when(bridgeMock.getUID()).thenReturn(BRIDGE_UID);
116         bridgeHandler = new AwattarBridgeHandler(bridgeMock, httpClientMock, timeZoneProviderMock);
117         bridgeHandler.setCallback(bridgeCallbackMock);
118
119         // mock the private field awattarApi
120         List<Field> fields = ReflectionSupport.findFields(AwattarBridgeHandler.class,
121                 field -> field.getName().equals("awattarApi"), HierarchyTraversalMode.BOTTOM_UP);
122
123         for (Field field : fields) {
124             field.setAccessible(true);
125             field.set(bridgeHandler, awattarApiMock);
126         }
127
128         bridgeHandler.refreshIfNeeded();
129         when(bridgeMock.getHandler()).thenReturn(bridgeHandler);
130
131         // other mocks
132         when(bestpriceMock.getBridgeUID()).thenReturn(BRIDGE_UID);
133         when(bestPriceCallbackMock.getBridge(any())).thenReturn(bridgeMock);
134         when(bestPriceCallbackMock.isChannelLinked(any())).thenReturn(true);
135     }
136
137     @Test
138     void testGetPriceForSuccess() {
139         AwattarPrice price = bridgeHandler.getPriceFor(1718503200000L);
140
141         assertThat(price, is(notNullValue()));
142         Objects.requireNonNull(price);
143         assertThat(price.netPrice(), is(closeTo(2.19, 0.001)));
144     }
145
146     @Test
147     void testGetPriceForFail() {
148         AwattarPrice price = bridgeHandler.getPriceFor(1518503200000L);
149
150         assertThat(price, is(nullValue()));
151     }
152
153     @Test
154     void testContainsPrizeFor() {
155         assertThat(bridgeHandler.containsPriceFor(1618503200000L), is(false));
156         assertThat(bridgeHandler.containsPriceFor(1718503200000L), is(true));
157         assertThat(bridgeHandler.containsPriceFor(1818503200000L), is(false));
158     }
159
160     public static Stream<Arguments> testBestpriceHandler() {
161         return Stream.of( //
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")));
174     }
175
176     @ParameterizedTest
177     @MethodSource
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));
182
183         AwattarBestPriceHandler handler = new AwattarBestPriceHandler(bestpriceMock, timeZoneProviderMock) {
184             @Override
185             protected TimeRange getRange(int start, int duration, ZoneId zoneId) {
186                 return new TimeRange(1718402400000L, 1718488800000L);
187             }
188         };
189
190         handler.setCallback(bestPriceCallbackMock);
191
192         ChannelUID channelUID = new ChannelUID(bestPriceUid, channelId);
193         handler.refreshChannel(channelUID);
194         verify(bestPriceCallbackMock).stateUpdated(channelUID, expectedState);
195     }
196 }