]> git.basschouten.com Git - openhab-addons.git/blob
fa02cee14c4d59c6f0e5cc4e31b18d5338d2020a
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2022 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.wundergroundupdatereceiver.internal;
14
15 import static org.hamcrest.MatcherAssert.assertThat;
16 import static org.hamcrest.core.Is.is;
17 import static org.hamcrest.core.IsIterableContaining.hasItems;
18 import static org.mockito.Mockito.any;
19 import static org.mockito.Mockito.eq;
20 import static org.mockito.Mockito.mock;
21 import static org.mockito.Mockito.never;
22 import static org.mockito.Mockito.times;
23 import static org.mockito.Mockito.verify;
24 import static org.mockito.Mockito.when;
25 import static org.mockito.MockitoAnnotations.openMocks;
26 import static org.openhab.binding.wundergroundupdatereceiver.internal.WundergroundUpdateReceiverBindingConstants.DATEUTC_DATETIME_CHANNELTYPEUID;
27 import static org.openhab.binding.wundergroundupdatereceiver.internal.WundergroundUpdateReceiverBindingConstants.HUMIDITY_GROUP;
28 import static org.openhab.binding.wundergroundupdatereceiver.internal.WundergroundUpdateReceiverBindingConstants.LAST_QUERY_STATE_CHANNELTYPEUID;
29 import static org.openhab.binding.wundergroundupdatereceiver.internal.WundergroundUpdateReceiverBindingConstants.LAST_QUERY_TRIGGER_CHANNELTYPEUID;
30 import static org.openhab.binding.wundergroundupdatereceiver.internal.WundergroundUpdateReceiverBindingConstants.LAST_RECEIVED_DATETIME_CHANNELTYPEUID;
31 import static org.openhab.binding.wundergroundupdatereceiver.internal.WundergroundUpdateReceiverBindingConstants.METADATA_GROUP;
32 import static org.openhab.binding.wundergroundupdatereceiver.internal.WundergroundUpdateReceiverBindingConstants.POLLUTION_GROUP;
33 import static org.openhab.binding.wundergroundupdatereceiver.internal.WundergroundUpdateReceiverBindingConstants.PRESSURE_GROUP;
34 import static org.openhab.binding.wundergroundupdatereceiver.internal.WundergroundUpdateReceiverBindingConstants.RAIN_GROUP;
35 import static org.openhab.binding.wundergroundupdatereceiver.internal.WundergroundUpdateReceiverBindingConstants.SUNLIGHT_GROUP;
36 import static org.openhab.binding.wundergroundupdatereceiver.internal.WundergroundUpdateReceiverBindingConstants.TEMPERATURE_GROUP;
37 import static org.openhab.binding.wundergroundupdatereceiver.internal.WundergroundUpdateReceiverBindingConstants.WIND_GROUP;
38
39 import java.io.IOException;
40 import java.util.List;
41 import java.util.Map;
42
43 import javax.servlet.ServletException;
44 import javax.servlet.http.HttpServletResponse;
45
46 import org.eclipse.jdt.annotation.NonNullByDefault;
47 import org.eclipse.jetty.http.HttpFields;
48 import org.eclipse.jetty.http.HttpURI;
49 import org.eclipse.jetty.http.HttpVersion;
50 import org.eclipse.jetty.http.MetaData;
51 import org.eclipse.jetty.server.HttpChannel;
52 import org.eclipse.jetty.server.Request;
53 import org.junit.jupiter.api.BeforeEach;
54 import org.junit.jupiter.api.Test;
55 import org.mockito.Answers;
56 import org.mockito.ArgumentCaptor;
57 import org.mockito.Mock;
58 import org.openhab.core.config.core.Configuration;
59 import org.openhab.core.library.types.DateTimeType;
60 import org.openhab.core.library.types.DecimalType;
61 import org.openhab.core.library.types.OnOffType;
62 import org.openhab.core.library.types.QuantityType;
63 import org.openhab.core.library.types.StringType;
64 import org.openhab.core.library.unit.ImperialUnits;
65 import org.openhab.core.library.unit.Units;
66 import org.openhab.core.thing.Channel;
67 import org.openhab.core.thing.ChannelUID;
68 import org.openhab.core.thing.Thing;
69 import org.openhab.core.thing.ThingStatus;
70 import org.openhab.core.thing.ThingUID;
71 import org.openhab.core.thing.binding.ThingHandlerCallback;
72 import org.openhab.core.thing.binding.builder.ChannelBuilder;
73 import org.openhab.core.thing.binding.builder.ThingBuilder;
74 import org.openhab.core.thing.type.ChannelKind;
75 import org.openhab.core.thing.type.ChannelTypeBuilder;
76 import org.openhab.core.thing.type.ChannelTypeRegistry;
77 import org.osgi.service.http.HttpService;
78 import org.osgi.service.http.NamespaceException;
79
80 /**
81  * @author Daniel Demus - Initial contribution
82  */
83 @NonNullByDefault({})
84 class WundergroundUpdateReceiverServletTest {
85
86     private static final String STATION_ID_1 = "abcd1234";
87     private static final String STATION_ID_2 = "1234abcd";
88     private static final String REQ_STATION_ID = "dfggger";
89     private static final ThingUID TEST_THING_UID = new ThingUID(
90             WundergroundUpdateReceiverBindingConstants.THING_TYPE_UPDATE_RECEIVER, "test-receiver");
91
92     private @Mock HttpService httpService;
93     private @Mock ChannelTypeRegistry channelTypeRegistry;
94     private @Mock WundergroundUpdateReceiverDiscoveryService discoveryService;
95
96     @BeforeEach
97     public void setUp() {
98         openMocks(this);
99     }
100
101     @Test
102     void theServletIsActiveAfterTheFirstHandlerIsAdded() throws ServletException, NamespaceException {
103         // Given
104         WundergroundUpdateReceiverServlet sut = new WundergroundUpdateReceiverServlet(httpService, discoveryService);
105         WundergroundUpdateReceiverHandler handler = mock(WundergroundUpdateReceiverHandler.class);
106         when(handler.getStationId()).thenReturn(STATION_ID_1);
107
108         // When
109         sut.addHandler(handler);
110
111         // Then
112         verify(httpService).registerServlet(eq(WundergroundUpdateReceiverServlet.SERVLET_URL), eq(sut), any(), any());
113         assertThat(sut.isActive(), is(true));
114     }
115
116     @Test
117     void theServletIsInactiveAfterTheLastHandlerIsRemovedAndBackgroundDiscoveryIsDisabled()
118             throws ServletException, NamespaceException {
119         // Given
120         WundergroundUpdateReceiverServlet sut = new WundergroundUpdateReceiverServlet(httpService, discoveryService);
121         WundergroundUpdateReceiverHandler handler = mock(WundergroundUpdateReceiverHandler.class);
122         when(handler.getStationId()).thenReturn(STATION_ID_1);
123         when(discoveryService.isBackgroundDiscoveryEnabled()).thenReturn(false);
124
125         // When
126         sut.addHandler(handler);
127
128         // Then
129         verify(httpService).registerServlet(eq(WundergroundUpdateReceiverServlet.SERVLET_URL), eq(sut), any(), any());
130         assertThat(sut.isActive(), is(true));
131
132         // When
133         sut.removeHandler(handler.getStationId());
134
135         // Then
136         verify(httpService).unregister(WundergroundUpdateReceiverServlet.SERVLET_URL);
137         assertThat(sut.isActive(), is(false));
138     }
139
140     @Test
141     void theServletIsActiveAfterTheLastHandlerIsRemovedButBackgroundDiscoveryIsEnabled()
142             throws ServletException, NamespaceException {
143         // Given
144         WundergroundUpdateReceiverServlet sut = new WundergroundUpdateReceiverServlet(httpService, discoveryService);
145         WundergroundUpdateReceiverHandler handler = mock(WundergroundUpdateReceiverHandler.class);
146         when(handler.getStationId()).thenReturn(STATION_ID_1);
147         when(discoveryService.isBackgroundDiscoveryEnabled()).thenReturn(true);
148
149         // When
150         sut.addHandler(handler);
151
152         // Then
153         verify(httpService).registerServlet(eq(WundergroundUpdateReceiverServlet.SERVLET_URL), eq(sut), any(), any());
154         assertThat(sut.isActive(), is(true));
155
156         // When
157         sut.removeHandler(handler.getStationId());
158
159         // Then
160         verify(httpService, never()).unregister(WundergroundUpdateReceiverServlet.SERVLET_URL);
161         assertThat(sut.isActive(), is(true));
162     }
163
164     @Test
165     void onDisposeAllHandlersAreRemovedAndServletIsInactive() throws ServletException, NamespaceException {
166         // Given
167         WundergroundUpdateReceiverServlet sut = new WundergroundUpdateReceiverServlet(httpService, discoveryService);
168         WundergroundUpdateReceiverHandler handler1 = mock(WundergroundUpdateReceiverHandler.class);
169         when(handler1.getStationId()).thenReturn(STATION_ID_1);
170         WundergroundUpdateReceiverHandler handler2 = mock(WundergroundUpdateReceiverHandler.class);
171         when(handler2.getStationId()).thenReturn(STATION_ID_2);
172
173         // When
174         sut.addHandler(handler1);
175         sut.addHandler(handler2);
176
177         // Then
178         verify(httpService).registerServlet(eq(WundergroundUpdateReceiverServlet.SERVLET_URL), eq(sut), any(), any());
179         assertThat(sut.isActive(), is(true));
180
181         // When
182         sut.dispose();
183
184         // Then
185         verify(httpService, times(2)).unregister(WundergroundUpdateReceiverServlet.SERVLET_URL);
186         assertThat(sut.isActive(), is(false));
187     }
188
189     @Test
190     void OnDisposeAllHandlersAreRemovedAndServletIsInactiveEvenThoughBackgroundDiscoveryIsEnabled()
191             throws ServletException, NamespaceException {
192         // Given
193         WundergroundUpdateReceiverServlet sut = new WundergroundUpdateReceiverServlet(httpService, discoveryService);
194         WundergroundUpdateReceiverHandler handler1 = mock(WundergroundUpdateReceiverHandler.class);
195         when(handler1.getStationId()).thenReturn(STATION_ID_1);
196         WundergroundUpdateReceiverHandler handler2 = mock(WundergroundUpdateReceiverHandler.class);
197         when(handler2.getStationId()).thenReturn(STATION_ID_2);
198         when(discoveryService.isBackgroundDiscoveryEnabled()).thenReturn(true);
199
200         // When
201         sut.addHandler(handler1);
202         sut.addHandler(handler2);
203
204         // Then
205         verify(httpService).registerServlet(eq(WundergroundUpdateReceiverServlet.SERVLET_URL), eq(sut), any(), any());
206         assertThat(sut.isActive(), is(true));
207
208         // When
209         sut.dispose();
210
211         // Then
212         verify(httpService).unregister(WundergroundUpdateReceiverServlet.SERVLET_URL);
213         assertThat(sut.isActive(), is(false));
214     }
215
216     @Test
217     void changedStationIdPropagatesToHandlerKey() throws ServletException, NamespaceException {
218         // Given
219         Thing thing = mock(Thing.class);
220         when(thing.getUID()).thenReturn(TEST_THING_UID);
221         when(thing.getConfiguration()).thenReturn(new Configuration(
222                 Map.of(WundergroundUpdateReceiverBindingConstants.REPRESENTATION_PROPERTY, STATION_ID_1)));
223         when(thing.getStatus()).thenReturn(ThingStatus.ONLINE);
224         when(this.channelTypeRegistry.getChannelType(LAST_RECEIVED_DATETIME_CHANNELTYPEUID))
225                 .thenReturn(ChannelTypeBuilder.state(LAST_RECEIVED_DATETIME_CHANNELTYPEUID, "Label", "String").build());
226         when(this.channelTypeRegistry.getChannelType(DATEUTC_DATETIME_CHANNELTYPEUID))
227                 .thenReturn(ChannelTypeBuilder.state(DATEUTC_DATETIME_CHANNELTYPEUID, "Label", "DateTime").build());
228         when(this.channelTypeRegistry.getChannelType(LAST_QUERY_STATE_CHANNELTYPEUID))
229                 .thenReturn(ChannelTypeBuilder.state(LAST_QUERY_STATE_CHANNELTYPEUID, "Label", "String").build());
230         when(this.channelTypeRegistry.getChannelType(LAST_QUERY_TRIGGER_CHANNELTYPEUID))
231                 .thenReturn(ChannelTypeBuilder.trigger(LAST_QUERY_TRIGGER_CHANNELTYPEUID, "Label").build());
232         WundergroundUpdateReceiverServlet sut = new WundergroundUpdateReceiverServlet(httpService, discoveryService);
233         WundergroundUpdateReceiverHandler handler = new WundergroundUpdateReceiverHandler(thing, sut, discoveryService,
234                 new WundergroundUpdateReceiverUnknownChannelTypeProvider(), channelTypeRegistry);
235         ThingHandlerCallback mockCallback = mock(ThingHandlerCallback.class);
236         handler.setCallback(mockCallback);
237
238         // When
239         handler.initialize();
240
241         // Then
242         verify(httpService).registerServlet(eq(WundergroundUpdateReceiverServlet.SERVLET_URL), eq(sut), any(), any());
243         assertThat(sut.isActive(), is(true));
244         assertThat(sut.getStationIds(), hasItems(STATION_ID_1));
245
246         // When
247         handler.handleConfigurationUpdate(
248                 Map.of(WundergroundUpdateReceiverBindingConstants.REPRESENTATION_PROPERTY, STATION_ID_2));
249
250         // Then
251         assertThat(sut.isActive(), is(true));
252         ArgumentCaptor<Thing> thingArg = ArgumentCaptor.forClass(Thing.class);
253         verify(mockCallback).configurationUpdated(thingArg.capture());
254         assertThat(thingArg.getValue().getConfiguration().getProperties()
255                 .get(WundergroundUpdateReceiverBindingConstants.REPRESENTATION_PROPERTY), is(STATION_ID_2));
256     }
257
258     @Test
259     void aGetRequestIsCorrectlyParsed() throws IOException {
260         // Given
261         ThingUID testThingUID = new ThingUID(WundergroundUpdateReceiverBindingConstants.THING_TYPE_UPDATE_RECEIVER,
262                 "test-receiver");
263         final String queryString = "ID=dfggger&PASSWORD=XXXXXX&tempf=26.1&humidity=74&dewptf=18.9&windchillf=26.1&winddir=14&windspeedmph=1.34&windgustmph=2.46&rainin=0.00&dailyrainin=0.00&weeklyrainin=0.00&monthlyrainin=0.08&yearlyrainin=3.06&solarradiation=42.24&UV=1&indoortempf=69.3&indoorhumidity=32&baromin=30.39&AqNOX=21&lowbatt=1&dateutc=2021-02-07%2014:04:03&softwaretype=WH2600%20V2.2.8&action=updateraw&realtime=1&rtfreq=5";
264         WundergroundUpdateReceiverServlet sut = new WundergroundUpdateReceiverServlet(httpService, discoveryService);
265         List<Channel> channels = List.of(
266                 ChannelBuilder
267                         .create(new ChannelUID(testThingUID, METADATA_GROUP,
268                                 WundergroundUpdateReceiverBindingConstants.DATEUTC), "String")
269                         .withKind(ChannelKind.STATE).build(),
270                 ChannelBuilder
271                         .create(new ChannelUID(testThingUID, METADATA_GROUP,
272                                 WundergroundUpdateReceiverBindingConstants.REALTIME_FREQUENCY), "Number")
273                         .withKind(ChannelKind.STATE).build(),
274                 ChannelBuilder
275                         .create(new ChannelUID(testThingUID, METADATA_GROUP,
276                                 WundergroundUpdateReceiverBindingConstants.LOW_BATTERY), "Switch")
277                         .withKind(ChannelKind.STATE).build(),
278                 ChannelBuilder
279                         .create(new ChannelUID(testThingUID, WIND_GROUP,
280                                 WundergroundUpdateReceiverBindingConstants.WIND_DIRECTION), "Number:Angle")
281                         .withKind(ChannelKind.STATE).build(),
282                 ChannelBuilder
283                         .create(new ChannelUID(testThingUID, WIND_GROUP,
284                                 WundergroundUpdateReceiverBindingConstants.WIND_SPEED), "Number:Speed")
285                         .withKind(ChannelKind.STATE).build(),
286                 ChannelBuilder
287                         .create(new ChannelUID(testThingUID, WIND_GROUP,
288                                 WundergroundUpdateReceiverBindingConstants.GUST_SPEED), "Number:Speed")
289                         .withKind(ChannelKind.STATE).build(),
290                 ChannelBuilder
291                         .create(new ChannelUID(testThingUID, TEMPERATURE_GROUP,
292                                 WundergroundUpdateReceiverBindingConstants.TEMPERATURE), "Number:Temperature")
293                         .withKind(ChannelKind.STATE).build(),
294                 ChannelBuilder
295                         .create(new ChannelUID(testThingUID, RAIN_GROUP,
296                                 WundergroundUpdateReceiverBindingConstants.RAIN_IN), "Number:Length")
297                         .withKind(ChannelKind.STATE).build(),
298                 ChannelBuilder
299                         .create(new ChannelUID(testThingUID, SUNLIGHT_GROUP,
300                                 WundergroundUpdateReceiverBindingConstants.SOLAR_RADIATION), "Number:Intensity")
301                         .withKind(ChannelKind.STATE).build(),
302                 ChannelBuilder
303                         .create(new ChannelUID(testThingUID, SUNLIGHT_GROUP,
304                                 WundergroundUpdateReceiverBindingConstants.UV), "Number")
305                         .withKind(ChannelKind.STATE).build(),
306                 ChannelBuilder
307                         .create(new ChannelUID(testThingUID, PRESSURE_GROUP,
308                                 WundergroundUpdateReceiverBindingConstants.BAROM_IN), "Number:Pressure")
309                         .withKind(ChannelKind.STATE).build(),
310                 ChannelBuilder
311                         .create(new ChannelUID(testThingUID, HUMIDITY_GROUP,
312                                 WundergroundUpdateReceiverBindingConstants.DEWPOINT), "Number:Temperature")
313                         .withKind(ChannelKind.STATE).build(),
314                 ChannelBuilder
315                         .create(new ChannelUID(testThingUID, HUMIDITY_GROUP,
316                                 WundergroundUpdateReceiverBindingConstants.HUMIDITY), "Number:Dimensionless")
317                         .withKind(ChannelKind.STATE).build(),
318                 ChannelBuilder
319                         .create(new ChannelUID(testThingUID, POLLUTION_GROUP,
320                                 WundergroundUpdateReceiverBindingConstants.AQ_NOX), "Number:Dimensionless")
321                         .withKind(ChannelKind.STATE).build(),
322                 ChannelBuilder
323                         .create(new ChannelUID(testThingUID, METADATA_GROUP,
324                                 WundergroundUpdateReceiverBindingConstants.LAST_QUERY_TRIGGER), "StringType")
325                         .withKind(ChannelKind.TRIGGER).build());
326
327         Configuration config = new Configuration(Map.of("stationId", REQ_STATION_ID));
328         Thing testThing = ThingBuilder
329                 .create(WundergroundUpdateReceiverBindingConstants.THING_TYPE_UPDATE_RECEIVER, testThingUID)
330                 .withChannels(channels).withConfiguration(config).build();
331         ThingHandlerCallback callback = mock(ThingHandlerCallback.class);
332         WundergroundUpdateReceiverHandler handler = new WundergroundUpdateReceiverHandler(testThing, sut,
333                 discoveryService, new WundergroundUpdateReceiverUnknownChannelTypeProvider(), channelTypeRegistry);
334         handler.setCallback(callback);
335         handler.initialize();
336
337         HttpChannel httpChannel = mock(HttpChannel.class);
338         MetaData.Request request = new MetaData.Request("GET",
339                 new HttpURI("http://localhost" + WundergroundUpdateReceiverServlet.SERVLET_URL + "?" + queryString),
340                 HttpVersion.HTTP_1_1, new HttpFields());
341         Request req = new Request(httpChannel, null);
342         req.setMetaData(request);
343
344         // When
345         sut.doGet(req, mock(HttpServletResponse.class, Answers.RETURNS_MOCKS));
346
347         // Then
348         verify(callback).stateUpdated(
349                 new ChannelUID(TEST_THING_UID, METADATA_GROUP, WundergroundUpdateReceiverBindingConstants.DATEUTC),
350                 StringType.valueOf("2021-02-07 14:04:03"));
351         verify(callback).stateUpdated(
352                 new ChannelUID(TEST_THING_UID, METADATA_GROUP, WundergroundUpdateReceiverBindingConstants.LOW_BATTERY),
353                 OnOffType.ON);
354         verify(callback).stateUpdated(new ChannelUID(TEST_THING_UID, METADATA_GROUP,
355                 WundergroundUpdateReceiverBindingConstants.REALTIME_FREQUENCY), new DecimalType(5));
356         verify(callback).stateUpdated(
357                 new ChannelUID(TEST_THING_UID, WIND_GROUP, WundergroundUpdateReceiverBindingConstants.WIND_DIRECTION),
358                 new QuantityType<>(14, Units.DEGREE_ANGLE));
359         verify(callback).stateUpdated(
360                 new ChannelUID(TEST_THING_UID, WIND_GROUP, WundergroundUpdateReceiverBindingConstants.WIND_SPEED),
361                 new QuantityType<>(1.34, ImperialUnits.MILES_PER_HOUR));
362         verify(callback).stateUpdated(
363                 new ChannelUID(TEST_THING_UID, WIND_GROUP, WundergroundUpdateReceiverBindingConstants.GUST_SPEED),
364                 new QuantityType<>(2.46, ImperialUnits.MILES_PER_HOUR));
365         verify(callback).stateUpdated(
366                 new ChannelUID(TEST_THING_UID, TEMPERATURE_GROUP,
367                         WundergroundUpdateReceiverBindingConstants.TEMPERATURE),
368                 new QuantityType<>(26.1, ImperialUnits.FAHRENHEIT));
369         verify(callback).stateUpdated(
370                 new ChannelUID(TEST_THING_UID, RAIN_GROUP, WundergroundUpdateReceiverBindingConstants.RAIN_IN),
371                 new QuantityType<>(0, ImperialUnits.INCH));
372         verify(callback).stateUpdated(
373                 new ChannelUID(TEST_THING_UID, SUNLIGHT_GROUP,
374                         WundergroundUpdateReceiverBindingConstants.SOLAR_RADIATION),
375                 new QuantityType<>(42.24, Units.IRRADIANCE));
376         verify(callback).stateUpdated(
377                 new ChannelUID(TEST_THING_UID, SUNLIGHT_GROUP, WundergroundUpdateReceiverBindingConstants.UV),
378                 new DecimalType(1));
379         verify(callback).stateUpdated(
380                 new ChannelUID(TEST_THING_UID, PRESSURE_GROUP, WundergroundUpdateReceiverBindingConstants.BAROM_IN),
381                 new QuantityType<>(30.39, ImperialUnits.INCH_OF_MERCURY));
382         verify(callback).stateUpdated(
383                 new ChannelUID(TEST_THING_UID, HUMIDITY_GROUP, WundergroundUpdateReceiverBindingConstants.DEWPOINT),
384                 new QuantityType<>(18.9, ImperialUnits.FAHRENHEIT));
385         verify(callback).stateUpdated(
386                 new ChannelUID(TEST_THING_UID, HUMIDITY_GROUP, WundergroundUpdateReceiverBindingConstants.HUMIDITY),
387                 new QuantityType<>(74, Units.PERCENT));
388         verify(callback).stateUpdated(
389                 new ChannelUID(TEST_THING_UID, POLLUTION_GROUP, WundergroundUpdateReceiverBindingConstants.AQ_NOX),
390                 new QuantityType<>(21, Units.PARTS_PER_BILLION));
391         verify(callback, never()).stateUpdated(new ChannelUID(TEST_THING_UID, METADATA_GROUP,
392                 WundergroundUpdateReceiverBindingConstants.SOFTWARE_TYPE), StringType.valueOf("WH2600 V2.2.8"));
393         verify(callback).stateUpdated(new ChannelUID(TEST_THING_UID, METADATA_GROUP,
394                 WundergroundUpdateReceiverBindingConstants.LAST_QUERY_STATE), StringType.valueOf(queryString));
395         verify(callback).stateUpdated(eq(new ChannelUID(TEST_THING_UID, METADATA_GROUP,
396                 WundergroundUpdateReceiverBindingConstants.LAST_RECEIVED)), any(DateTimeType.class));
397         verify(callback).channelTriggered(testThing, new ChannelUID(TEST_THING_UID, METADATA_GROUP,
398                 WundergroundUpdateReceiverBindingConstants.LAST_QUERY_TRIGGER), queryString);
399     }
400
401     @Test
402     void aGetRequestWithIndexedParametresAreCorrectlyParsed() throws IOException {
403         // Given
404         ThingUID testThingUID = new ThingUID(WundergroundUpdateReceiverBindingConstants.THING_TYPE_UPDATE_RECEIVER,
405                 "test-receiver");
406         final String queryString = "ID=dfggger&PASSWORD=XXXXXX&temp1f=26.1&humidity=74&temp2f=25.1&lowbatt=1&soilmoisture1=78&soilmoisture2=73&dateutc=2021-02-07%2014:04:03&softwaretype=WH2600%20V2.2.8&action=updateraw&realtime=1&rtfreq=5";
407         WundergroundUpdateReceiverServlet sut = new WundergroundUpdateReceiverServlet(httpService, discoveryService);
408         List<Channel> channels = List.of(
409                 ChannelBuilder
410                         .create(new ChannelUID(testThingUID, METADATA_GROUP,
411                                 WundergroundUpdateReceiverBindingConstants.DATEUTC), "String")
412                         .withKind(ChannelKind.STATE).build(),
413                 ChannelBuilder
414                         .create(new ChannelUID(testThingUID, METADATA_GROUP,
415                                 WundergroundUpdateReceiverBindingConstants.REALTIME_FREQUENCY), "Number")
416                         .withKind(ChannelKind.STATE).build(),
417                 ChannelBuilder
418                         .create(new ChannelUID(testThingUID, METADATA_GROUP,
419                                 WundergroundUpdateReceiverBindingConstants.LOW_BATTERY), "Switch")
420                         .withKind(ChannelKind.STATE).build(),
421                 ChannelBuilder.create(new ChannelUID(testThingUID, TEMPERATURE_GROUP, "temp1f"), "Number:Temperature")
422                         .withKind(ChannelKind.STATE).build(),
423                 ChannelBuilder.create(new ChannelUID(testThingUID, TEMPERATURE_GROUP, "temp2f"), "Number:Temperature")
424                         .withKind(ChannelKind.STATE).build(),
425                 ChannelBuilder
426                         .create(new ChannelUID(testThingUID, HUMIDITY_GROUP, "soilmoisture1"), "Number:Dimensionless")
427                         .withKind(ChannelKind.STATE).build(),
428                 ChannelBuilder
429                         .create(new ChannelUID(testThingUID, HUMIDITY_GROUP, "soilmoisture2"), "Number:Dimensionless")
430                         .withKind(ChannelKind.STATE).build(),
431                 ChannelBuilder
432                         .create(new ChannelUID(testThingUID, HUMIDITY_GROUP,
433                                 WundergroundUpdateReceiverBindingConstants.HUMIDITY), "Number:Dimensionless")
434                         .withKind(ChannelKind.STATE).build(),
435                 ChannelBuilder
436                         .create(new ChannelUID(testThingUID, METADATA_GROUP,
437                                 WundergroundUpdateReceiverBindingConstants.LAST_QUERY_TRIGGER), "StringType")
438                         .withKind(ChannelKind.TRIGGER).build());
439
440         Configuration config = new Configuration(Map.of("stationId", REQ_STATION_ID));
441         Thing testThing = ThingBuilder
442                 .create(WundergroundUpdateReceiverBindingConstants.THING_TYPE_UPDATE_RECEIVER, testThingUID)
443                 .withChannels(channels).withConfiguration(config).build();
444         ThingHandlerCallback callback = mock(ThingHandlerCallback.class);
445         WundergroundUpdateReceiverHandler handler = new WundergroundUpdateReceiverHandler(testThing, sut,
446                 discoveryService, new WundergroundUpdateReceiverUnknownChannelTypeProvider(), channelTypeRegistry);
447         handler.setCallback(callback);
448         handler.initialize();
449
450         HttpChannel httpChannel = mock(HttpChannel.class);
451         MetaData.Request request = new MetaData.Request("GET",
452                 new HttpURI("http://localhost" + WundergroundUpdateReceiverServlet.SERVLET_URL + "?" + queryString),
453                 HttpVersion.HTTP_1_1, new HttpFields());
454         Request req = new Request(httpChannel, null);
455         req.setMetaData(request);
456
457         // When
458         sut.doGet(req, mock(HttpServletResponse.class, Answers.RETURNS_MOCKS));
459
460         // Then
461         verify(callback).stateUpdated(
462                 new ChannelUID(TEST_THING_UID, METADATA_GROUP, WundergroundUpdateReceiverBindingConstants.DATEUTC),
463                 StringType.valueOf("2021-02-07 14:04:03"));
464         verify(callback).stateUpdated(
465                 new ChannelUID(TEST_THING_UID, METADATA_GROUP, WundergroundUpdateReceiverBindingConstants.LOW_BATTERY),
466                 OnOffType.ON);
467         verify(callback).stateUpdated(new ChannelUID(TEST_THING_UID, METADATA_GROUP,
468                 WundergroundUpdateReceiverBindingConstants.REALTIME_FREQUENCY), new DecimalType(5));
469         verify(callback).stateUpdated(new ChannelUID(TEST_THING_UID, TEMPERATURE_GROUP, "temp1f"),
470                 new QuantityType<>(26.1, ImperialUnits.FAHRENHEIT));
471         verify(callback).stateUpdated(new ChannelUID(TEST_THING_UID, TEMPERATURE_GROUP, "temp2f"),
472                 new QuantityType<>(25.1, ImperialUnits.FAHRENHEIT));
473         verify(callback).stateUpdated(
474                 new ChannelUID(TEST_THING_UID, HUMIDITY_GROUP, WundergroundUpdateReceiverBindingConstants.HUMIDITY),
475                 new QuantityType<>(74, Units.PERCENT));
476         verify(callback).stateUpdated(new ChannelUID(TEST_THING_UID, HUMIDITY_GROUP, "soilmoisture1"),
477                 new QuantityType<>(78, Units.PERCENT));
478         verify(callback).stateUpdated(new ChannelUID(TEST_THING_UID, HUMIDITY_GROUP, "soilmoisture2"),
479                 new QuantityType<>(73, Units.PERCENT));
480         verify(callback, never()).stateUpdated(new ChannelUID(TEST_THING_UID, METADATA_GROUP,
481                 WundergroundUpdateReceiverBindingConstants.SOFTWARE_TYPE), StringType.valueOf("WH2600 V2.2.8"));
482         verify(callback).stateUpdated(new ChannelUID(TEST_THING_UID, METADATA_GROUP,
483                 WundergroundUpdateReceiverBindingConstants.LAST_QUERY_STATE), StringType.valueOf(queryString));
484         verify(callback).stateUpdated(eq(new ChannelUID(TEST_THING_UID, METADATA_GROUP,
485                 WundergroundUpdateReceiverBindingConstants.LAST_RECEIVED)), any(DateTimeType.class));
486         verify(callback).channelTriggered(testThing, new ChannelUID(TEST_THING_UID, METADATA_GROUP,
487                 WundergroundUpdateReceiverBindingConstants.LAST_QUERY_TRIGGER), queryString);
488     }
489 }