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.wundergroundupdatereceiver.internal;
15 import static org.hamcrest.MatcherAssert.assertThat;
16 import static org.hamcrest.Matchers.*;
17 import static org.hamcrest.core.Is.is;
18 import static org.mockito.Mockito.*;
19 import static org.mockito.MockitoAnnotations.openMocks;
20 import static org.openhab.binding.wundergroundupdatereceiver.internal.WundergroundUpdateReceiverBindingConstants.*;
22 import java.io.IOException;
23 import java.util.Arrays;
24 import java.util.List;
26 import java.util.stream.Collectors;
28 import javax.servlet.ServletException;
29 import javax.servlet.http.HttpServletResponse;
31 import org.eclipse.jdt.annotation.NonNullByDefault;
32 import org.eclipse.jetty.http.HttpFields;
33 import org.eclipse.jetty.http.HttpURI;
34 import org.eclipse.jetty.http.HttpVersion;
35 import org.eclipse.jetty.http.MetaData;
36 import org.eclipse.jetty.server.HttpChannel;
37 import org.eclipse.jetty.server.Request;
38 import org.hamcrest.Matcher;
39 import org.junit.jupiter.api.BeforeEach;
40 import org.junit.jupiter.api.Test;
41 import org.mockito.Answers;
42 import org.openhab.core.config.core.Configuration;
43 import org.openhab.core.thing.Channel;
44 import org.openhab.core.thing.ChannelUID;
45 import org.openhab.core.thing.DefaultSystemChannelTypeProvider;
46 import org.openhab.core.thing.ManagedThingProvider;
47 import org.openhab.core.thing.Thing;
48 import org.openhab.core.thing.ThingUID;
49 import org.openhab.core.thing.binding.ThingHandlerCallback;
50 import org.openhab.core.thing.binding.builder.ThingBuilder;
51 import org.openhab.core.thing.internal.type.StateChannelTypeBuilderImpl;
52 import org.openhab.core.thing.internal.type.TriggerChannelTypeBuilderImpl;
53 import org.openhab.core.thing.type.ChannelKind;
54 import org.openhab.core.thing.type.ChannelTypeProvider;
55 import org.openhab.core.thing.type.ChannelTypeRegistry;
56 import org.openhab.core.thing.type.ChannelTypeUID;
57 import org.osgi.service.http.HttpService;
58 import org.osgi.service.http.NamespaceException;
61 * @author Daniel Demus - Initial contribution
64 class WundergroundUpdateReceiverDiscoveryServiceTest {
66 private static final String STATION_ID_1 = "abcd1234";
67 private static final String REQ_STATION_ID = "dfggger";
68 private static final ThingUID TEST_THING_UID = new ThingUID(THING_TYPE_UPDATE_RECEIVER, "test-receiver");
76 void programmaticChannelsAreAddedCorrectlyOnce() {
78 final String queryString = "ID=dfggger&" + "PASSWORD=XXXXXX&" + "humidity=74&" + "AqPM2.5=30&"
79 + "windspdmph_avg2m=10&" + "dateutc=2021-02-07%2014:04:03&" + "softwaretype=WH2600%20V2.2.8&"
80 + "action=updateraw&" + "realtime=1&" + "rtfreq=5";
81 MetaData.Request request = new MetaData.Request("GET",
82 new HttpURI("http://localhost" + WundergroundUpdateReceiverServlet.SERVLET_URL + "?" + queryString),
83 HttpVersion.HTTP_1_1, new HttpFields());
84 HttpChannel httpChannel = mock(HttpChannel.class);
85 Request req = new Request(httpChannel, null);
86 req.setMetaData(request);
88 TestChannelTypeRegistry channelTypeRegistry = new TestChannelTypeRegistry();
89 WundergroundUpdateReceiverDiscoveryService discoveryService = new WundergroundUpdateReceiverDiscoveryService(
91 HttpService httpService = mock(HttpService.class);
92 WundergroundUpdateReceiverServlet sut = new WundergroundUpdateReceiverServlet(httpService, discoveryService);
93 discoveryService.addUnhandledStationId(REQ_STATION_ID, sut.normalizeParameterMap(req.getParameterMap()));
94 Thing thing = ThingBuilder.create(SUPPORTED_THING_TYPES_UIDS.stream().findFirst().get(), TEST_THING_UID)
95 .withConfiguration(new Configuration(Map.of(REPRESENTATION_PROPERTY, REQ_STATION_ID)))
96 .withLabel("test thing").withLocation("location").build();
97 ManagedThingProvider managedThingProvider = mock(ManagedThingProvider.class);
98 when(managedThingProvider.get(any())).thenReturn(thing);
99 WundergroundUpdateReceiverHandler handler = new WundergroundUpdateReceiverHandler(thing, sut, discoveryService,
100 new WundergroundUpdateReceiverUnknownChannelTypeProvider(), channelTypeRegistry, managedThingProvider);
101 handler.setCallback(mock(ThingHandlerCallback.class));
104 handler.initialize();
105 var actual = handler.getThing().getChannels();
108 assertThat(actual.size(), is(9));
110 assertChannel(actual, METADATA_GROUP, LAST_RECEIVED, LAST_RECEIVED_DATETIME_CHANNELTYPEUID, ChannelKind.STATE,
112 assertChannel(actual, METADATA_GROUP, LAST_QUERY_TRIGGER, LAST_QUERY_TRIGGER_CHANNELTYPEUID,
113 ChannelKind.TRIGGER, nullValue());
114 assertChannel(actual, METADATA_GROUP, LAST_QUERY_STATE, LAST_QUERY_STATE_CHANNELTYPEUID, ChannelKind.STATE,
116 assertChannel(actual, METADATA_GROUP, DATEUTC, DATEUTC_CHANNELTYPEUID, ChannelKind.STATE, is("String"));
117 assertChannel(actual, METADATA_GROUP, REALTIME_FREQUENCY, REALTIME_FREQUENCY_CHANNELTYPEUID, ChannelKind.STATE,
119 assertChannel(actual, METADATA_GROUP, SOFTWARE_TYPE, SOFTWARETYPE_CHANNELTYPEUID, ChannelKind.STATE,
121 assertChannel(actual, HUMIDITY_GROUP, HUMIDITY, HUMIDITY_CHANNELTYPEUID, ChannelKind.STATE,
122 is("Number:Dimensionless"));
123 assertChannel(actual, WIND_GROUP, WIND_SPEED_AVG_2MIN, WIND_SPEED_AVG_2MIN_CHANNELTYPEUID, ChannelKind.STATE,
125 assertChannel(actual, POLLUTION_GROUP, AQ_PM2_5, PM2_5_MASS_CHANNELTYPEUID, ChannelKind.STATE,
126 is("Number:Density"));
130 void aRequestWithAnUnregisteredStationidIsAddedToTheQueueOnce()
131 throws ServletException, NamespaceException, IOException {
133 final String queryString = "ID=dfggger&" + "PASSWORD=XXXXXX&" + "tempf=26.1&" + "humidity=74&" + "dewptf=18.9&"
134 + "windchillf=26.1&" + "winddir=14&" + "windspeedmph=1.34&" + "windgustmph=2.46&" + "rainin=0.00&"
135 + "dailyrainin=0.00&" + "weeklyrainin=0.00&" + "monthlyrainin=0.08&" + "yearlyrainin=3.06&"
136 + "solarradiation=42.24&" + "UV=1&indoortempf=69.3&" + "indoorhumidity=32&" + "baromin=30.39&"
137 + "AqNOX=21&" + "lowbatt=1&" + "dateutc=2021-02-07%2014:04:03&" + "softwaretype=WH2600%20V2.2.8&"
138 + "action=updateraw&" + "realtime=1&" + "rtfreq=5";
139 WundergroundUpdateReceiverDiscoveryService discoveryService = mock(
140 WundergroundUpdateReceiverDiscoveryService.class);
141 HttpService httpService = mock(HttpService.class);
142 WundergroundUpdateReceiverServlet sut = new WundergroundUpdateReceiverServlet(httpService, discoveryService);
143 WundergroundUpdateReceiverHandler handler = mock(WundergroundUpdateReceiverHandler.class);
144 when(handler.getStationId()).thenReturn(STATION_ID_1);
145 sut.addHandler(handler);
146 when(discoveryService.isBackgroundDiscoveryEnabled()).thenReturn(false);
149 verify(httpService).registerServlet(eq(WundergroundUpdateReceiverServlet.SERVLET_URL), eq(sut), any(), any());
150 assertThat(sut.isActive(), is(true));
152 HttpChannel httpChannel = mock(HttpChannel.class);
153 MetaData.Request request = new MetaData.Request("GET",
154 new HttpURI("http://localhost" + WundergroundUpdateReceiverServlet.SERVLET_URL + "?" + queryString),
155 HttpVersion.HTTP_1_1, new HttpFields());
156 Request req = new Request(httpChannel, null);
157 req.setMetaData(request);
160 sut.doGet(req, mock(HttpServletResponse.class, Answers.RETURNS_MOCKS));
163 verify(handler, never()).updateChannelStates(any());
164 verify(discoveryService).addUnhandledStationId(eq("dfggger"), any());
165 assertThat(sut.isActive(), is(true));
169 void multipleIndexedParametersOfTheSameChanneltypeAreCorrectlyDiscovered() throws IOException {
171 final String queryString = "ID=dfggger&" + "PASSWORD=XXXXXX&" + "temp1f=26.1&" + "humidity=74&" + "temp2f=25.1&"
172 + "lowbatt=1&" + "soilmoisture1=78&" + "soilmoisture2=73&" + "dateutc=2021-02-07%2014:04:03&"
173 + "softwaretype=WH2600%20V2.2.8&" + "action=updateraw&" + "realtime=1&" + "rtfreq=5";
174 MetaData.Request request = new MetaData.Request("GET",
175 new HttpURI("http://localhost" + WundergroundUpdateReceiverServlet.SERVLET_URL + "?" + queryString),
176 HttpVersion.HTTP_1_1, new HttpFields());
177 HttpChannel httpChannel = mock(HttpChannel.class);
178 Request req = new Request(httpChannel, null);
179 req.setMetaData(request);
181 TestChannelTypeRegistry channelTypeRegistry = new TestChannelTypeRegistry();
182 WundergroundUpdateReceiverDiscoveryService discoveryService = new WundergroundUpdateReceiverDiscoveryService(
184 HttpService httpService = mock(HttpService.class);
185 WundergroundUpdateReceiverServlet sut = new WundergroundUpdateReceiverServlet(httpService, discoveryService);
186 discoveryService.addUnhandledStationId(REQ_STATION_ID, sut.normalizeParameterMap(req.getParameterMap()));
187 Thing thing = ThingBuilder.create(SUPPORTED_THING_TYPES_UIDS.stream().findFirst().get(), TEST_THING_UID)
188 .withConfiguration(new Configuration(Map.of(REPRESENTATION_PROPERTY, REQ_STATION_ID)))
189 .withLabel("test thing").withLocation("location").build();
190 ManagedThingProvider managedThingProvider = mock(ManagedThingProvider.class);
191 when(managedThingProvider.get(TEST_THING_UID)).thenReturn(thing);
192 WundergroundUpdateReceiverHandler handler = new WundergroundUpdateReceiverHandler(thing, sut, discoveryService,
193 new WundergroundUpdateReceiverUnknownChannelTypeProvider(), channelTypeRegistry, managedThingProvider);
194 handler.setCallback(mock(ThingHandlerCallback.class));
195 handler.initialize();
196 sut.addHandler(handler);
202 assertThat(sut.isActive(), is(true));
205 sut.doGet(req, mock(HttpServletResponse.class, Answers.RETURNS_MOCKS));
208 assertThat(sut.getHandlers().size(), is(1));
209 assertThat(sut.getHandlers().containsKey(REQ_STATION_ID), is(true));
210 assertThat(handler.getThing().getChannels().stream()
211 .filter(channel -> channel.getChannelTypeUID() == TEMPERATURE_CHANNELTYPEUID).count(), is(2L));
215 void unregisteredChannelsAreAddedOnTheFlyWhenDiscovered() throws IOException {
217 final String firstDeviceQueryString = "ID=dfggger&" + "PASSWORD=XXXXXX&" + "tempf=26.1&" + "humidity=74&"
218 + "dateutc=2021-02-07%2014:04:03&" + "softwaretype=WH2600%20V2.2.8&" + "action=updateraw&"
219 + "realtime=1&" + "rtfreq=5";
220 MetaData.Request request1 = new MetaData.Request("GET", new HttpURI(
221 "http://localhost" + WundergroundUpdateReceiverServlet.SERVLET_URL + "?" + firstDeviceQueryString),
222 HttpVersion.HTTP_1_1, new HttpFields());
223 HttpChannel httpChannel = mock(HttpChannel.class);
224 Request req1 = new Request(httpChannel, null);
225 req1.setMetaData(request1);
227 TestChannelTypeRegistry channelTypeRegistry = new TestChannelTypeRegistry();
228 WundergroundUpdateReceiverDiscoveryService discoveryService = new WundergroundUpdateReceiverDiscoveryService(
230 HttpService httpService = mock(HttpService.class);
231 WundergroundUpdateReceiverServlet sut = new WundergroundUpdateReceiverServlet(httpService, discoveryService);
232 discoveryService.addUnhandledStationId(REQ_STATION_ID, sut.normalizeParameterMap(req1.getParameterMap()));
233 Thing thing = ThingBuilder.create(SUPPORTED_THING_TYPES_UIDS.stream().findFirst().get(), TEST_THING_UID)
234 .withConfiguration(new Configuration(Map.of(REPRESENTATION_PROPERTY, REQ_STATION_ID)))
235 .withLabel("test thing").withLocation("location").build();
236 ManagedThingProvider managedThingProvider = mock(ManagedThingProvider.class);
237 when(managedThingProvider.get(any())).thenReturn(thing);
238 WundergroundUpdateReceiverHandler handler = new WundergroundUpdateReceiverHandler(thing, sut, discoveryService,
239 new WundergroundUpdateReceiverUnknownChannelTypeProvider(), channelTypeRegistry, managedThingProvider);
240 handler.setCallback(mock(ThingHandlerCallback.class));
243 handler.initialize();
244 sut.addHandler(handler);
247 ChannelTypeUID[] expectedBefore = new ChannelTypeUID[] { TEMPERATURE_CHANNELTYPEUID, HUMIDITY_CHANNELTYPEUID,
248 DATEUTC_CHANNELTYPEUID, SOFTWARETYPE_CHANNELTYPEUID, REALTIME_FREQUENCY_CHANNELTYPEUID,
249 LAST_QUERY_STATE_CHANNELTYPEUID, LAST_RECEIVED_DATETIME_CHANNELTYPEUID,
250 LAST_QUERY_TRIGGER_CHANNELTYPEUID };
251 List<ChannelTypeUID> before = handler.getThing().getChannels().stream().map(Channel::getChannelTypeUID)
252 .collect(Collectors.toList());
253 assertThat(before, hasItems(expectedBefore));
256 final String secondDeviceQueryString = "ID=dfggger&" + "PASSWORD=XXXXXX&" + "lowbatt=1&" + "soilmoisture1=78&"
257 + "soilmoisture2=73&" + "solarradiation=42.24&" + "dateutc=2021-02-07%2014:04:03&"
258 + "softwaretype=WH2600%20V2.2.8&" + "action=updateraw&" + "realtime=1&" + "rtfreq=5";
259 MetaData.Request request = new MetaData.Request("GET", new HttpURI(
260 "http://localhost" + WundergroundUpdateReceiverServlet.SERVLET_URL + "?" + secondDeviceQueryString),
261 HttpVersion.HTTP_1_1, new HttpFields());
262 Request req2 = new Request(httpChannel, null);
263 req2.setMetaData(request);
267 assertThat(sut.isActive(), is(true));
270 sut.doGet(req2, mock(HttpServletResponse.class, Answers.RETURNS_MOCKS));
273 ChannelTypeUID[] expectedActual = Arrays.copyOf(expectedBefore, expectedBefore.length + 3);
274 System.arraycopy(new ChannelTypeUID[] { LOW_BATTERY_CHANNELTYPEUID, SOIL_MOISTURE_CHANNELTYPEUID,
275 SOLARRADIATION_CHANNELTYPEUID }, 0, expectedActual, expectedBefore.length, 3);
276 List<ChannelTypeUID> actual = handler.getThing().getChannels().stream().map(Channel::getChannelTypeUID)
277 .collect(Collectors.toList());
278 assertThat(actual, hasItems(expectedActual));
282 void unregisteredChannelsAreNotAddedOnUnmanagedThings() throws IOException {
284 final String firstDeviceQueryString = "ID=dfggger&" + "PASSWORD=XXXXXX&" + "tempf=26.1&" + "humidity=74&"
285 + "dateutc=2021-02-07%2014:04:03&" + "softwaretype=WH2600%20V2.2.8&" + "action=updateraw&"
286 + "realtime=1&" + "rtfreq=5";
287 MetaData.Request request1 = new MetaData.Request("GET", new HttpURI(
288 "http://localhost" + WundergroundUpdateReceiverServlet.SERVLET_URL + "?" + firstDeviceQueryString),
289 HttpVersion.HTTP_1_1, new HttpFields());
290 HttpChannel httpChannel = mock(HttpChannel.class);
291 Request req1 = new Request(httpChannel, null);
292 req1.setMetaData(request1);
294 TestChannelTypeRegistry channelTypeRegistry = new TestChannelTypeRegistry();
295 WundergroundUpdateReceiverDiscoveryService discoveryService = new WundergroundUpdateReceiverDiscoveryService(
297 HttpService httpService = mock(HttpService.class);
298 WundergroundUpdateReceiverServlet sut = new WundergroundUpdateReceiverServlet(httpService, discoveryService);
299 discoveryService.addUnhandledStationId(REQ_STATION_ID, sut.normalizeParameterMap(req1.getParameterMap()));
300 Thing thing = ThingBuilder.create(SUPPORTED_THING_TYPES_UIDS.stream().findFirst().get(), TEST_THING_UID)
301 .withConfiguration(new Configuration(Map.of(REPRESENTATION_PROPERTY, REQ_STATION_ID)))
302 .withLabel("test thing").withLocation("location").build();
303 ManagedThingProvider managedThingProvider = mock(ManagedThingProvider.class);
304 when(managedThingProvider.get(any())).thenReturn(null);
305 WundergroundUpdateReceiverHandler handler = new WundergroundUpdateReceiverHandler(thing, sut, discoveryService,
306 new WundergroundUpdateReceiverUnknownChannelTypeProvider(), channelTypeRegistry, managedThingProvider);
307 handler.setCallback(mock(ThingHandlerCallback.class));
310 handler.initialize();
311 sut.addHandler(handler);
314 ChannelTypeUID[] expectedBefore = new ChannelTypeUID[] { TEMPERATURE_CHANNELTYPEUID, HUMIDITY_CHANNELTYPEUID,
315 DATEUTC_CHANNELTYPEUID, SOFTWARETYPE_CHANNELTYPEUID, REALTIME_FREQUENCY_CHANNELTYPEUID,
316 LAST_QUERY_STATE_CHANNELTYPEUID, LAST_RECEIVED_DATETIME_CHANNELTYPEUID,
317 LAST_QUERY_TRIGGER_CHANNELTYPEUID };
318 List<ChannelTypeUID> before = handler.getThing().getChannels().stream().map(Channel::getChannelTypeUID)
319 .collect(Collectors.toList());
320 assertThat(before, hasItems(expectedBefore));
323 final String secondDeviceQueryString = "ID=dfggger&" + "PASSWORD=XXXXXX&" + "lowbatt=1&" + "soilmoisture1=78&"
324 + "soilmoisture2=73&" + "solarradiation=42.24&" + "dateutc=2021-02-07%2014:04:03&"
325 + "softwaretype=WH2600%20V2.2.8&" + "action=updateraw&" + "realtime=1&" + "rtfreq=5";
326 MetaData.Request request = new MetaData.Request("GET", new HttpURI(
327 "http://localhost" + WundergroundUpdateReceiverServlet.SERVLET_URL + "?" + secondDeviceQueryString),
328 HttpVersion.HTTP_1_1, new HttpFields());
329 Request req2 = new Request(httpChannel, null);
330 req2.setMetaData(request);
334 assertThat(sut.isActive(), is(true));
337 sut.doGet(req2, mock(HttpServletResponse.class, Answers.RETURNS_MOCKS));
340 List<ChannelTypeUID> actual = handler.getThing().getChannels().stream().map(Channel::getChannelTypeUID)
341 .collect(Collectors.toList());
342 assertThat(actual, equalTo(before));
346 void lastQueryTriggerIsMigratedSuccessfully() throws IOException {
348 final String firstDeviceQueryString = "ID=dfggger&" + "PASSWORD=XXXXXX&" + "tempf=26.1&" + "humidity=74&"
349 + "dateutc=2021-02-07%2014:04:03&" + "softwaretype=WH2600%20V2.2.8&" + "action=updateraw&"
350 + "realtime=1&" + "rtfreq=5";
351 MetaData.Request request1 = new MetaData.Request("GET", new HttpURI(
352 "http://localhost" + WundergroundUpdateReceiverServlet.SERVLET_URL + "?" + firstDeviceQueryString),
353 HttpVersion.HTTP_1_1, new HttpFields());
354 HttpChannel httpChannel = mock(HttpChannel.class);
355 Request req1 = new Request(httpChannel, null);
356 req1.setMetaData(request1);
358 UpdatingChannelTypeRegistry channelTypeRegistry = new UpdatingChannelTypeRegistry();
359 WundergroundUpdateReceiverDiscoveryService discoveryService = new WundergroundUpdateReceiverDiscoveryService(
361 HttpService httpService = mock(HttpService.class);
362 WundergroundUpdateReceiverServlet sut = new WundergroundUpdateReceiverServlet(httpService, discoveryService);
363 discoveryService.addUnhandledStationId(REQ_STATION_ID, sut.normalizeParameterMap(req1.getParameterMap()));
364 Thing thing = ThingBuilder.create(SUPPORTED_THING_TYPES_UIDS.stream().findFirst().get(), TEST_THING_UID)
365 .withConfiguration(new Configuration(Map.of(REPRESENTATION_PROPERTY, REQ_STATION_ID)))
366 .withLabel("test thing").withLocation("location").build();
367 ManagedThingProvider managedThingProvider = mock(ManagedThingProvider.class);
368 when(managedThingProvider.get(any())).thenReturn(null);
369 WundergroundUpdateReceiverHandler handler = new WundergroundUpdateReceiverHandler(thing, sut, discoveryService,
370 new WundergroundUpdateReceiverUnknownChannelTypeProvider(), channelTypeRegistry, managedThingProvider);
371 handler.setCallback(mock(ThingHandlerCallback.class));
374 handler.initialize();
375 sut.addHandler(handler);
378 ChannelTypeUID[] expectedBefore = new ChannelTypeUID[] { TEMPERATURE_CHANNELTYPEUID, HUMIDITY_CHANNELTYPEUID,
379 DATEUTC_CHANNELTYPEUID, SOFTWARETYPE_CHANNELTYPEUID, REALTIME_FREQUENCY_CHANNELTYPEUID,
380 LAST_QUERY_STATE_CHANNELTYPEUID, LAST_RECEIVED_DATETIME_CHANNELTYPEUID,
381 LAST_QUERY_TRIGGER_CHANNELTYPEUID };
382 List<ChannelTypeUID> before = handler.getThing().getChannels().stream().map(Channel::getChannelTypeUID)
383 .collect(Collectors.toList());
384 assertThat(before, hasItems(expectedBefore));
387 var actual = handler.getThing().getChannels();
390 assertThat(actual.size(), is(8));
391 assertChannel(actual, METADATA_GROUP, LAST_QUERY_TRIGGER, LAST_QUERY_TRIGGER_CHANNELTYPEUID, ChannelKind.STATE,
396 handler.initialize();
398 final String secondDeviceQueryString = "ID=dfggger&" + "PASSWORD=XXXXXX&" + "lowbatt=1&" + "soilmoisture1=78&"
399 + "soilmoisture2=73&" + "solarradiation=42.24&" + "dateutc=2021-02-07%2014:04:03&"
400 + "softwaretype=WH2600%20V2.2.8&" + "action=updateraw&" + "realtime=1&" + "rtfreq=5";
401 MetaData.Request request = new MetaData.Request("GET", new HttpURI(
402 "http://localhost" + WundergroundUpdateReceiverServlet.SERVLET_URL + "?" + secondDeviceQueryString),
403 HttpVersion.HTTP_1_1, new HttpFields());
404 Request req2 = new Request(httpChannel, null);
405 req2.setMetaData(request);
409 assertThat(sut.isActive(), is(true));
412 sut.doGet(req2, mock(HttpServletResponse.class, Answers.RETURNS_MOCKS));
413 actual = handler.getThing().getChannels();
416 assertThat(actual.size(), is(8));
417 assertChannel(actual, METADATA_GROUP, LAST_QUERY_TRIGGER, LAST_QUERY_TRIGGER_CHANNELTYPEUID,
418 ChannelKind.TRIGGER, nullValue());
421 private void assertChannel(List<Channel> channels, String expectedGroup, String expectedName,
422 ChannelTypeUID expectedUid, ChannelKind expectedKind, Matcher<Object> expectedItemType) {
423 ChannelUID channelUID = new ChannelUID(TEST_THING_UID, expectedGroup, expectedName);
424 Channel actual = channels.stream().filter(c -> channelUID.equals(c.getUID())).findFirst().orElse(null);
425 assertThat(actual, is(notNullValue()));
426 assertThat(actual.getLabel() + " UID", actual.getUID(), is(channelUID));
427 assertThat(actual.getLabel() + " ChannelTypeUID", actual.getChannelTypeUID(), is(expectedUid));
428 assertThat(actual.getLabel() + " Kind", actual.getKind(), is(expectedKind));
429 assertThat(actual.getLabel() + " AcceptedItemType", actual.getAcceptedItemType(), expectedItemType);
432 abstract class AbstractTestChannelTypeRegistry extends ChannelTypeRegistry {
434 protected final ChannelTypeProvider provider;
436 AbstractTestChannelTypeRegistry(ChannelTypeProvider mock) {
438 this.provider = mock;
439 when(provider.getChannelType(eq(SOFTWARETYPE_CHANNELTYPEUID), any())).thenReturn(
440 new StateChannelTypeBuilderImpl(SOFTWARETYPE_CHANNELTYPEUID, "Software type", "String").build());
441 when(provider.getChannelType(eq(TEMPERATURE_CHANNELTYPEUID), any()))
442 .thenReturn(DefaultSystemChannelTypeProvider.SYSTEM_OUTDOOR_TEMPERATURE);
443 when(provider.getChannelType(eq(SOIL_MOISTURE_CHANNELTYPEUID), any()))
444 .thenReturn(new StateChannelTypeBuilderImpl(SOIL_MOISTURE_CHANNELTYPEUID, "Soilmoisture",
445 "Number:Dimensionless").build());
446 when(provider.getChannelType(eq(SOLARRADIATION_CHANNELTYPEUID), any()))
447 .thenReturn(new StateChannelTypeBuilderImpl(SOLARRADIATION_CHANNELTYPEUID, "Solar Radiation",
448 "Number:Intensity").build());
449 when(provider.getChannelType(eq(HUMIDITY_CHANNELTYPEUID), any())).thenReturn(
450 new StateChannelTypeBuilderImpl(HUMIDITY_CHANNELTYPEUID, "Humidity", "Number:Dimensionless")
452 when(provider.getChannelType(eq(WIND_SPEED_AVG_2MIN_CHANNELTYPEUID), any()))
453 .thenReturn(new StateChannelTypeBuilderImpl(WIND_SPEED_AVG_2MIN_CHANNELTYPEUID,
454 "Wind Speed 2min Average", "Number:Speed").build());
455 when(provider.getChannelType(eq(PM2_5_MASS_CHANNELTYPEUID), any())).thenReturn(
456 new StateChannelTypeBuilderImpl(PM2_5_MASS_CHANNELTYPEUID, "PM2.5 Mass", "Number:Density").build());
457 when(provider.getChannelType(eq(DATEUTC_CHANNELTYPEUID), any())).thenReturn(
458 new StateChannelTypeBuilderImpl(DATEUTC_CHANNELTYPEUID, "Last Updated", "String").build());
459 when(provider.getChannelType(eq(LOW_BATTERY_CHANNELTYPEUID), any()))
460 .thenReturn(DefaultSystemChannelTypeProvider.SYSTEM_CHANNEL_LOW_BATTERY);
461 when(provider.getChannelType(eq(REALTIME_FREQUENCY_CHANNELTYPEUID), any())).thenReturn(
462 new StateChannelTypeBuilderImpl(REALTIME_FREQUENCY_CHANNELTYPEUID, "Realtime frequency", "Number")
464 when(provider.getChannelType(eq(LAST_QUERY_STATE_CHANNELTYPEUID), any())).thenReturn(
465 new StateChannelTypeBuilderImpl(LAST_QUERY_STATE_CHANNELTYPEUID, "The last query", "String")
467 when(provider.getChannelType(eq(LAST_RECEIVED_DATETIME_CHANNELTYPEUID), any())).thenReturn(
468 new StateChannelTypeBuilderImpl(LAST_RECEIVED_DATETIME_CHANNELTYPEUID, "Last Received", "DateTime")
470 this.addChannelTypeProvider(provider);
471 this.addChannelTypeProvider(new WundergroundUpdateReceiverUnknownChannelTypeProvider());
475 class TestChannelTypeRegistry extends AbstractTestChannelTypeRegistry {
477 TestChannelTypeRegistry() {
478 super(mock(ChannelTypeProvider.class));
479 when(provider.getChannelType(eq(LAST_QUERY_TRIGGER_CHANNELTYPEUID), any())).thenReturn(
480 new TriggerChannelTypeBuilderImpl(LAST_QUERY_TRIGGER_CHANNELTYPEUID, "The last query").build());
484 class UpdatingChannelTypeRegistry extends AbstractTestChannelTypeRegistry {
486 UpdatingChannelTypeRegistry() {
487 super(mock(ChannelTypeProvider.class));
488 when(provider.getChannelType(eq(LAST_QUERY_TRIGGER_CHANNELTYPEUID), any()))
489 .thenReturn(new StateChannelTypeBuilderImpl(LAST_QUERY_TRIGGER_CHANNELTYPEUID, "The last query",
491 .thenReturn(new StateChannelTypeBuilderImpl(LAST_QUERY_TRIGGER_CHANNELTYPEUID, "The last query",
493 .thenReturn(new TriggerChannelTypeBuilderImpl(LAST_QUERY_TRIGGER_CHANNELTYPEUID, "The last query")