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