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.mqtt.homeassistant.internal;
15 import static org.mockito.ArgumentMatchers.*;
16 import static org.mockito.Mockito.*;
18 import java.io.IOException;
19 import java.net.URISyntaxException;
20 import java.nio.file.Files;
21 import java.nio.file.Path;
22 import java.nio.file.Paths;
23 import java.util.Objects;
25 import java.util.UUID;
26 import java.util.concurrent.CompletableFuture;
27 import java.util.concurrent.ConcurrentHashMap;
28 import java.util.concurrent.ConcurrentMap;
30 import org.eclipse.jdt.annotation.NonNullByDefault;
31 import org.junit.jupiter.api.Assertions;
32 import org.junit.jupiter.api.BeforeEach;
33 import org.junit.jupiter.api.extension.ExtendWith;
34 import org.mockito.Mock;
35 import org.mockito.junit.jupiter.MockitoExtension;
36 import org.mockito.junit.jupiter.MockitoSettings;
37 import org.mockito.quality.Strictness;
38 import org.openhab.binding.mqtt.generic.MqttChannelStateDescriptionProvider;
39 import org.openhab.binding.mqtt.generic.MqttChannelTypeProvider;
40 import org.openhab.binding.mqtt.handler.BrokerHandler;
41 import org.openhab.binding.mqtt.homeassistant.generic.internal.MqttBindingConstants;
42 import org.openhab.core.io.transport.mqtt.MqttBrokerConnection;
43 import org.openhab.core.io.transport.mqtt.MqttMessageSubscriber;
44 import org.openhab.core.test.java.JavaTest;
45 import org.openhab.core.test.storage.VolatileStorageService;
46 import org.openhab.core.thing.Bridge;
47 import org.openhab.core.thing.Thing;
48 import org.openhab.core.thing.ThingStatus;
49 import org.openhab.core.thing.ThingStatusDetail;
50 import org.openhab.core.thing.ThingStatusInfo;
51 import org.openhab.core.thing.ThingTypeUID;
52 import org.openhab.core.thing.ThingUID;
53 import org.openhab.core.thing.binding.builder.BridgeBuilder;
54 import org.openhab.core.thing.binding.builder.ThingBuilder;
55 import org.openhab.core.thing.type.ChannelTypeRegistry;
56 import org.openhab.core.thing.type.ThingType;
57 import org.openhab.core.thing.type.ThingTypeBuilder;
58 import org.openhab.core.thing.type.ThingTypeRegistry;
59 import org.openhab.core.transform.TransformationHelper;
60 import org.openhab.core.transform.TransformationService;
61 import org.osgi.framework.BundleContext;
62 import org.osgi.framework.ServiceReference;
65 * Abstract class for HomeAssistant unit tests.
67 * @author Anton Kharuzhy - Initial contribution
69 @ExtendWith(MockitoExtension.class)
70 @MockitoSettings(strictness = Strictness.LENIENT)
72 public abstract class AbstractHomeAssistantTests extends JavaTest {
73 public static final String BINDING_ID = "mqtt";
75 public static final String BRIDGE_TYPE_ID = "broker";
76 public static final String BRIDGE_TYPE_LABEL = "MQTT Broker";
77 public static final ThingTypeUID BRIDGE_TYPE_UID = new ThingTypeUID(BINDING_ID, BRIDGE_TYPE_ID);
78 public static final String BRIDGE_ID = UUID.randomUUID().toString();
79 public static final ThingUID BRIDGE_UID = new ThingUID(BRIDGE_TYPE_UID, BRIDGE_ID);
81 public static final String HA_TYPE_LABEL = "Home Assistant Thing";
82 public static final ThingTypeUID HA_TYPE_UID = new ThingTypeUID(BINDING_ID, "homeassistant_dynamic_type");
83 public static final String HA_ID = UUID.randomUUID().toString();
84 public static final ThingUID HA_UID = new ThingUID(MqttBindingConstants.HOMEASSISTANT_MQTT_THING, HA_ID);
85 public static final ThingType HA_THING_TYPE = ThingTypeBuilder
86 .instance(MqttBindingConstants.HOMEASSISTANT_MQTT_THING, HA_TYPE_LABEL).build();
88 protected @Mock @NonNullByDefault({}) MqttBrokerConnection bridgeConnection;
89 protected @Mock @NonNullByDefault({}) ThingTypeRegistry thingTypeRegistry;
91 protected @NonNullByDefault({}) MqttChannelTypeProvider channelTypeProvider;
92 protected @NonNullByDefault({}) MqttChannelStateDescriptionProvider stateDescriptionProvider;
93 protected @NonNullByDefault({}) ChannelTypeRegistry channelTypeRegistry;
95 protected final Bridge bridgeThing = BridgeBuilder.create(BRIDGE_TYPE_UID, BRIDGE_UID).build();
96 protected final BrokerHandler bridgeHandler = spy(new BrokerHandler(bridgeThing));
97 protected final Thing haThing = ThingBuilder.create(HA_TYPE_UID, HA_UID).withBridge(BRIDGE_UID).build();
98 protected final ConcurrentMap<String, Set<MqttMessageSubscriber>> subscriptions = new ConcurrentHashMap<>();
100 private @Mock @NonNullByDefault({}) TransformationService transformationService1Mock;
102 private @Mock @NonNullByDefault({}) BundleContext bundleContextMock;
103 private @Mock @NonNullByDefault({}) ServiceReference<TransformationService> serviceRefMock;
105 private @NonNullByDefault({}) TransformationHelper transformationHelper;
108 public void beforeEachAbstractHomeAssistantTests() {
109 transformationHelper = new TransformationHelper(bundleContextMock);
110 transformationHelper.setTransformationService(serviceRefMock);
112 when(thingTypeRegistry.getThingType(BRIDGE_TYPE_UID))
113 .thenReturn(ThingTypeBuilder.instance(BRIDGE_TYPE_UID, BRIDGE_TYPE_LABEL).build());
114 when(thingTypeRegistry.getThingType(MqttBindingConstants.HOMEASSISTANT_MQTT_THING)).thenReturn(HA_THING_TYPE);
116 channelTypeProvider = spy(new MqttChannelTypeProvider(thingTypeRegistry, new VolatileStorageService()));
117 stateDescriptionProvider = spy(new MqttChannelStateDescriptionProvider());
118 channelTypeRegistry = spy(new ChannelTypeRegistry());
122 // Return the mocked connection object if the bridge handler is asked for it
123 when(bridgeHandler.getConnectionAsync()).thenReturn(CompletableFuture.completedFuture(bridgeConnection));
125 bridgeThing.setStatusInfo(new ThingStatusInfo(ThingStatus.ONLINE, ThingStatusDetail.ONLINE.NONE, ""));
126 bridgeThing.setHandler(bridgeHandler);
128 haThing.setStatusInfo(new ThingStatusInfo(ThingStatus.ONLINE, ThingStatusDetail.ONLINE.NONE, ""));
131 protected void setupConnection() {
132 doAnswer(invocation -> {
133 final var topic = (String) invocation.getArgument(0);
134 final var subscriber = (MqttMessageSubscriber) invocation.getArgument(1);
136 subscriptions.putIfAbsent(topic, ConcurrentHashMap.newKeySet());
137 Set<MqttMessageSubscriber> subscribers = subscriptions.get(topic);
138 Objects.requireNonNull(subscribers); // Invariant, thanks to putIfAbsent above. To make compiler happy
139 subscribers.add(subscriber);
140 return CompletableFuture.completedFuture(true);
141 }).when(bridgeConnection).subscribe(any(), any());
143 doAnswer(invocation -> {
144 final var topic = (String) invocation.getArgument(0);
145 final var subscriber = (MqttMessageSubscriber) invocation.getArgument(1);
146 final var topicSubscriptions = subscriptions.get(topic);
148 if (topicSubscriptions != null) {
149 topicSubscriptions.remove(subscriber);
151 return CompletableFuture.completedFuture(true);
152 }).when(bridgeConnection).unsubscribe(any(), any());
154 doAnswer(invocation -> {
155 subscriptions.clear();
156 return CompletableFuture.completedFuture(true);
157 }).when(bridgeConnection).unsubscribeAll();
159 doReturn(CompletableFuture.completedFuture(true)).when(bridgeConnection).publish(any(), any(), anyInt(),
164 * @param relativePath path from src/test/java/org/openhab/binding/mqtt/homeassistant/internal
167 @SuppressWarnings("null")
168 protected Path getResourcePath(String relativePath) {
170 return Paths.get(AbstractHomeAssistantTests.class.getResource(relativePath).toURI());
171 } catch (URISyntaxException e) {
174 throw new IllegalArgumentException();
177 protected String getResourceAsString(String relativePath) {
179 return Files.readString(getResourcePath(relativePath));
180 } catch (IOException e) {
183 throw new IllegalArgumentException();
186 protected byte[] getResourceAsByteArray(String relativePath) {
188 return Files.readAllBytes(getResourcePath(relativePath));
189 } catch (IOException e) {
192 throw new IllegalArgumentException();
195 protected static String configTopicToMqtt(String configTopic) {
196 return HandlerConfiguration.DEFAULT_BASETOPIC + "/" + configTopic + "/config";