2 * Copyright (c) 2010-2023 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.groupepsa.internal.things;
15 import static org.mockito.ArgumentMatchers.*;
16 import static org.mockito.Mockito.*;
18 import java.io.BufferedReader;
19 import java.io.InputStream;
20 import java.io.InputStreamReader;
21 import java.time.ZoneId;
22 import java.util.stream.Collectors;
24 import org.eclipse.jdt.annotation.NonNullByDefault;
25 import org.eclipse.jetty.client.HttpClient;
26 import org.eclipse.jetty.client.HttpContentResponse;
27 import org.eclipse.jetty.client.HttpResponse;
28 import org.junit.jupiter.api.AfterEach;
29 import org.junit.jupiter.api.BeforeEach;
30 import org.junit.jupiter.api.Test;
31 import org.mockito.Mock;
32 import org.mockito.MockitoAnnotations;
33 import org.openhab.binding.groupepsa.internal.bridge.GroupePSABridgeHandler;
34 import org.openhab.binding.groupepsa.internal.rest.api.GroupePSAConnectApi;
35 import org.openhab.binding.groupepsa.internal.rest.exceptions.GroupePSACommunicationException;
36 import org.openhab.core.auth.client.oauth2.OAuthFactory;
37 import org.openhab.core.config.core.Configuration;
38 import org.openhab.core.i18n.TimeZoneProvider;
39 import org.openhab.core.library.types.DateTimeType;
40 import org.openhab.core.library.types.StringType;
41 import org.openhab.core.thing.Bridge;
42 import org.openhab.core.thing.ChannelUID;
43 import org.openhab.core.thing.Thing;
44 import org.openhab.core.thing.ThingStatus;
45 import org.openhab.core.thing.ThingStatusDetail;
46 import org.openhab.core.thing.ThingUID;
47 import org.openhab.core.thing.binding.ThingHandlerCallback;
48 import org.openhab.core.types.State;
51 * The {@link GroupePSAHandlerTest} is responsible for testing the binding
53 * @author Arjan Mels - Initial contribution
56 public class GroupePSAHandlerTest {
57 private @NonNullByDefault({}) AutoCloseable closeable;
59 private @NonNullByDefault({}) GroupePSAConnectApi api;
60 private @NonNullByDefault({}) GroupePSABridgeHandler bridgeHandler;
61 private @NonNullByDefault({}) GroupePSAHandler thingHandler;
63 private @NonNullByDefault({}) @Mock ThingHandlerCallback thingCallback;
64 private @NonNullByDefault({}) @Mock ThingHandlerCallback bridgeCallback;
65 private @NonNullByDefault({}) @Mock Thing thing;
66 private @NonNullByDefault({}) @Mock Bridge bridge;
68 private @NonNullByDefault({}) @Mock OAuthFactory oAuthFactory;
69 private @NonNullByDefault({}) @Mock HttpClient httpClient;
70 private @NonNullByDefault({}) @Mock TimeZoneProvider timeZoneProvider;
72 static String getResourceFileAsString(String fileName) throws GroupePSACommunicationException {
73 try (InputStream is = GroupePSAConnectApi.class.getResourceAsStream(fileName)) {
74 try (InputStreamReader isr = new InputStreamReader(is); BufferedReader reader = new BufferedReader(isr)) {
75 return reader.lines().collect(Collectors.joining(System.lineSeparator()));
77 } catch (Exception e) {
78 throw new GroupePSACommunicationException(e);
82 static HttpContentResponse createHttpResponse(String file) throws GroupePSACommunicationException {
83 return new HttpContentResponse(new HttpResponse(null, null).status(200),
84 getResourceFileAsString("/" + file).getBytes(), "json", "UTF-8");
88 public void setUp() throws GroupePSACommunicationException {
89 closeable = MockitoAnnotations.openMocks(this);
91 // Create real objects
92 bridgeHandler = spy(new GroupePSABridgeHandler(bridge, oAuthFactory, httpClient));
93 thingHandler = spy(new GroupePSAHandler(thing, timeZoneProvider));
94 api = spy(new GroupePSAConnectApi(httpClient, bridgeHandler, "clientId", "realm"));
97 doReturn(createHttpResponse("dummy_user.json")).when(api).executeRequest(contains("user"), anyString());
98 doReturn(createHttpResponse("dummy_vehiclestatus3.json")).when(api).executeRequest(contains("status"),
101 // Setup bridge handler mock
102 bridgeHandler.setCallback(bridgeCallback);
103 doReturn(api).when(bridgeHandler).getAPI();
106 Configuration bridgeConfig = new Configuration();
107 bridgeConfig.put("vendor", "OPEL");
108 bridgeConfig.put("userName", "user");
109 bridgeConfig.put("password", "pwd");
110 bridgeConfig.put("clientId", "clientIdValue");
111 bridgeConfig.put("clientSecret", "clientSecretValue");
112 doReturn(bridgeConfig).when(bridge).getConfiguration();
113 doReturn(ThingStatus.ONLINE).when(bridge).getStatus();
114 doReturn(bridgeHandler).when(bridge).getHandler();
115 doReturn(new ThingUID("a:b:c")).when(bridge).getUID();
118 Configuration thingConfig = new Configuration();
119 thingConfig.put("id", "mock_id");
120 doReturn(thingConfig).when(thing).getConfiguration();
121 doReturn(new ThingUID("a:b:c")).when(thing).getUID();
123 // Setup thing handler mock
124 thingHandler.setCallback(thingCallback);
125 doReturn(bridge).when(thingHandler).getBridge();
126 doNothing().when(thingHandler).buildDoorChannels(any());
127 doReturn(ZoneId.systemDefault()).when(timeZoneProvider).getTimeZone();
131 public void tearDown() throws Exception {
132 // Free any resources, like open database connections, files etc.
133 thingHandler.dispose();
134 bridgeHandler.dispose();
139 public void intializeAndCheckChannels() throws InterruptedException {
140 // Initialize the bridge
141 bridgeHandler.initialize();
143 // check that the bridge is online
144 verify(bridgeCallback, timeout(10000)).statusUpdated(eq(bridge),
145 argThat(arg -> arg.getStatus().equals(ThingStatus.ONLINE)));
147 // Initialize the thing
148 thingHandler.initialize();
150 // check that the thing is offline without detail (last update time is not
151 // within 15 minutes)
152 verify(thingCallback, timeout(10000)).statusUpdated(eq(thing),
153 argThat(arg -> arg.getStatus().equals(ThingStatus.OFFLINE)
154 && arg.getStatusDetail().equals(ThingStatusDetail.NONE)));
156 // check that the channels are updated
157 verify(thingCallback, atLeast(30)).stateUpdated(any(ChannelUID.class), any(State.class));
158 verify(thingCallback).stateUpdated(eq(new ChannelUID("a:b:c:electric#chargingStatus")),
159 eq(new StringType("Disconnected")));
160 verify(thingCallback).stateUpdated(eq(new ChannelUID("a:b:c:various#lastUpdated")), any(DateTimeType.class));