]> git.basschouten.com Git - openhab-addons.git/blob
f301554eefe962c2067c1a59a90d2d472bf68609
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 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.persistence.dynamodb.internal;
14
15 import static org.junit.jupiter.api.Assertions.*;
16
17 import java.io.IOException;
18 import java.math.BigDecimal;
19 import java.time.Instant;
20 import java.time.ZoneId;
21 import java.time.ZonedDateTime;
22 import java.util.Objects;
23 import java.util.TimeZone;
24
25 import org.eclipse.jdt.annotation.NonNullByDefault;
26 import org.junit.jupiter.params.ParameterizedTest;
27 import org.junit.jupiter.params.provider.CsvSource;
28 import org.openhab.core.items.GenericItem;
29 import org.openhab.core.library.items.CallItem;
30 import org.openhab.core.library.items.ColorItem;
31 import org.openhab.core.library.items.ContactItem;
32 import org.openhab.core.library.items.DateTimeItem;
33 import org.openhab.core.library.items.DimmerItem;
34 import org.openhab.core.library.items.LocationItem;
35 import org.openhab.core.library.items.NumberItem;
36 import org.openhab.core.library.items.RollershutterItem;
37 import org.openhab.core.library.items.StringItem;
38 import org.openhab.core.library.items.SwitchItem;
39 import org.openhab.core.library.types.DateTimeType;
40 import org.openhab.core.library.types.DecimalType;
41 import org.openhab.core.library.types.HSBType;
42 import org.openhab.core.library.types.OnOffType;
43 import org.openhab.core.library.types.OpenClosedType;
44 import org.openhab.core.library.types.PercentType;
45 import org.openhab.core.library.types.PointType;
46 import org.openhab.core.library.types.StringListType;
47 import org.openhab.core.library.types.StringType;
48 import org.openhab.core.library.types.UpDownType;
49 import org.openhab.core.persistence.HistoricItem;
50 import org.openhab.core.types.State;
51
52 /**
53  * Test for AbstractDynamoDBItem.fromState and AbstractDynamoDBItem.asHistoricItem for all kind of states
54  *
55  * @author Sami Salonen - Initial contribution
56  */
57 @NonNullByDefault
58 public class AbstractDynamoDBItemSerializationTest {
59
60     private final ZonedDateTime date = ZonedDateTime.ofInstant(Instant.ofEpochSecond(400), ZoneId.systemDefault());
61
62     /**
63      * Generic function testing serialization of GenericItem state to internal format in DB. In other words, conversion
64      * of
65      * GenericItem with state to DynamoDBItem
66      *
67      * @param legacy whether we have legacy
68      * @param GenericItem item
69      * @param stateOverride state
70      * @param expectedState internal format in DB representing the GenericItem state
71      * @return dynamo db item
72      * @throws IOException
73      */
74     public DynamoDBItem<?> testSerializationToDTO(boolean legacy, GenericItem item, State stateOverride,
75             Object expectedState) throws IOException {
76         item.setState(stateOverride);
77         DynamoDBItem<?> dbItem = legacy ? AbstractDynamoDBItem.fromStateLegacy(item, date)
78                 : AbstractDynamoDBItem.fromStateNew(item, date, null);
79
80         assertEquals("foo", dbItem.getName());
81         assertEquals(date, dbItem.getTime());
82         Object actualState = dbItem.getState();
83         assertNotNull(actualState);
84         Objects.requireNonNull(actualState);
85         if (expectedState instanceof BigDecimal) {
86             BigDecimal expectedRounded = DynamoDBBigDecimalItem.loseDigits(((BigDecimal) expectedState));
87             assertEquals(0, expectedRounded.compareTo((BigDecimal) actualState),
88                     String.format("Expected state %s (%s but with some digits lost) did not match actual state %s",
89                             expectedRounded, expectedState, actualState));
90         } else {
91             assertEquals(expectedState, actualState);
92         }
93         return dbItem;
94     }
95
96     /**
97      * Test state deserialization, that is DynamoDBItem conversion to HistoricItem
98      *
99      * @param dbItem dynamo db item
100      * @param GenericItem parameter for DynamoDBItem.asHistoricItem
101      * @param expectedState Expected state of the historic item. DecimalTypes are compared with reduced accuracy
102      * @return
103      * @throws IOException
104      */
105     public HistoricItem testAsHistoricGeneric(DynamoDBItem<?> dbItem, GenericItem item, Object expectedState)
106             throws IOException {
107         HistoricItem historicItem = dbItem.asHistoricItem(item);
108         assertNotNull(historicItem);
109         assert historicItem != null; // getting rid off null pointer access warning
110         assertEquals("foo", historicItem.getName());
111         assertEquals(date, historicItem.getTimestamp());
112         assertEquals(expectedState.getClass(), historicItem.getState().getClass());
113         if (expectedState.getClass() == DecimalType.class) {
114             // serialization loses accuracy, take this into consideration
115             BigDecimal expectedRounded = DynamoDBBigDecimalItem
116                     .loseDigits(((DecimalType) expectedState).toBigDecimal());
117             BigDecimal actual = ((DecimalType) historicItem.getState()).toBigDecimal();
118             assertEquals(0, expectedRounded.compareTo(actual),
119                     String.format("Expected state %s (%s but with some digits lost) did not match actual state %s",
120                             expectedRounded, expectedState, actual));
121         } else {
122             assertEquals(expectedState, historicItem.getState());
123         }
124         return historicItem;
125     }
126
127     @ParameterizedTest
128     @CsvSource({ "true", "false" })
129     public void testCallTypeWithCallItemLegacy(boolean legacy) throws IOException {
130         GenericItem item = new CallItem("foo");
131         final DynamoDBItem<?> dbitem = testSerializationToDTO(legacy, item, new StringListType("origNum", "destNum"),
132                 "origNum,destNum");
133         testAsHistoricGeneric(dbitem, item, new StringListType("origNum", "destNum"));
134     }
135
136     @ParameterizedTest
137     @CsvSource({ "true", "false" })
138     public void testOpenClosedTypeWithContactItem(boolean legacy) throws IOException {
139         GenericItem item = new ContactItem("foo");
140         final DynamoDBItem<?> dbitemOpen = testSerializationToDTO(legacy, item, OpenClosedType.CLOSED, BigDecimal.ZERO);
141         testAsHistoricGeneric(dbitemOpen, item, OpenClosedType.CLOSED);
142
143         final DynamoDBItem<?> dbitemClosed = testSerializationToDTO(legacy, item, OpenClosedType.OPEN, BigDecimal.ONE);
144         testAsHistoricGeneric(dbitemClosed, item, OpenClosedType.OPEN);
145     }
146
147     @ParameterizedTest
148     @CsvSource({ "true", "false" })
149     public void testDateTimeTypeWithDateTimeItem(boolean legacy) throws IOException {
150         GenericItem item = new DateTimeItem("foo");
151         ZonedDateTime zdt = ZonedDateTime.parse("2016-05-01T13:46:00.050Z");
152         DynamoDBItem<?> dbitem = testSerializationToDTO(legacy, item, new DateTimeType(zdt.toString()),
153                 "2016-05-01T13:46:00.050Z");
154         testAsHistoricGeneric(dbitem, item, new DateTimeType(zdt.withZoneSameInstant(ZoneId.systemDefault())));
155     }
156
157     @ParameterizedTest
158     @CsvSource({ "true", "false" })
159     public void testDateTimeTypeWithStringItem(boolean legacy) throws IOException {
160         GenericItem item = new StringItem("foo");
161         DynamoDBItem<?> dbitem = testSerializationToDTO(legacy, item,
162                 new DateTimeType(ZonedDateTime.parse("2016-05-01T13:46:00.050Z")), "2016-05-01T13:46:00.050Z");
163         testAsHistoricGeneric(dbitem, item, new StringType("2016-05-01T13:46:00.050Z"));
164     }
165
166     @ParameterizedTest
167     @CsvSource({ "true", "false" })
168     public void testDateTimeTypeLocalWithDateTimeItem(boolean legacy) throws IOException {
169         GenericItem item = new DateTimeItem("foo");
170         ZonedDateTime expectedZdt = Instant.ofEpochMilli(1468773487050L).atZone(ZoneId.systemDefault());
171         DynamoDBItem<?> dbitem = testSerializationToDTO(legacy, item, new DateTimeType("2016-07-17T19:38:07.050+0300"),
172                 "2016-07-17T16:38:07.050Z");
173         testAsHistoricGeneric(dbitem, item, new DateTimeType(expectedZdt));
174     }
175
176     @ParameterizedTest
177     @CsvSource({ "true", "false" })
178     public void testDateTimeTypeLocalWithStringItem(boolean legacy) throws IOException {
179         GenericItem item = new StringItem("foo");
180         Instant instant = Instant.ofEpochMilli(1468773487050L); // GMT: Sun, 17 Jul 2016 16:38:07.050 GMT
181         ZonedDateTime zdt = instant.atZone(TimeZone.getTimeZone("GMT+03:00").toZoneId());
182         DynamoDBItem<?> dbitem = testSerializationToDTO(legacy, item, new DateTimeType(zdt),
183                 "2016-07-17T16:38:07.050Z");
184         testAsHistoricGeneric(dbitem, item, new StringType("2016-07-17T16:38:07.050Z"));
185     }
186
187     @ParameterizedTest
188     @CsvSource({ "true", "false" })
189     public void testPointTypeWithLocationItem(boolean legacy) throws IOException {
190         GenericItem item = new LocationItem("foo");
191         final PointType point = new PointType(new DecimalType(60.3), new DecimalType(30.2), new DecimalType(510.90));
192         String expected = point.getLatitude().toBigDecimal().toString() + ","
193                 + point.getLongitude().toBigDecimal().toString() + "," + point.getAltitude().toBigDecimal().toString();
194         DynamoDBItem<?> dbitem = testSerializationToDTO(legacy, item, point, expected);
195         testAsHistoricGeneric(dbitem, item, point);
196     }
197
198     @ParameterizedTest
199     @CsvSource({ "true", "false" })
200     public void testDecimalTypeWithNumberItem(boolean legacy) throws IOException {
201         GenericItem item = new NumberItem("foo");
202         DynamoDBItem<?> dbitem = testSerializationToDTO(legacy, item, new DecimalType("3.2"), new BigDecimal("3.2"));
203         testAsHistoricGeneric(dbitem, item, new DecimalType("3.2"));
204     }
205
206     @ParameterizedTest
207     @CsvSource({ "true", "false" })
208     public void testPercentTypeWithColorItem(boolean legacy) throws IOException {
209         GenericItem item = new ColorItem("foo");
210         DynamoDBItem<?> dbitem = testSerializationToDTO(legacy, item, new PercentType(new BigDecimal("3.2")),
211                 "0,0,3.2");
212         testAsHistoricGeneric(dbitem, item, new HSBType(DecimalType.ZERO, PercentType.ZERO, new PercentType("3.2")));
213     }
214
215     @ParameterizedTest
216     @CsvSource({ "true", "false" })
217     public void testPercentTypeWithDimmerItem(boolean legacy) throws IOException {
218         GenericItem item = new DimmerItem("foo");
219         DynamoDBItem<?> dbitem = testSerializationToDTO(legacy, item, new PercentType(new BigDecimal("3.2")),
220                 new BigDecimal("3.2"));
221         testAsHistoricGeneric(dbitem, item, new PercentType(new BigDecimal("3.2")));
222     }
223
224     @ParameterizedTest
225     @CsvSource({ "true", "false" })
226     public void testPercentTypeWithRollerShutterItem(boolean legacy) throws IOException {
227         GenericItem item = new RollershutterItem("foo");
228         DynamoDBItem<?> dbitem = testSerializationToDTO(legacy, item, new PercentType(81), new BigDecimal("81"));
229         testAsHistoricGeneric(dbitem, item, new PercentType(81));
230     }
231
232     @ParameterizedTest
233     @CsvSource({ "true", "false" })
234     public void testUpDownTypeWithRollershutterItem(boolean legacy) throws IOException {
235         GenericItem item = new RollershutterItem("foo");
236         // note: comes back as PercentType instead of the original UpDownType
237         {
238             // down == 1.0 = 100%
239             State expectedDeserializedState = PercentType.HUNDRED;
240             DynamoDBItem<?> dbItemDown = testSerializationToDTO(legacy, item, UpDownType.DOWN, new BigDecimal(100));
241             testAsHistoricGeneric(dbItemDown, item, expectedDeserializedState);
242             assertEquals(UpDownType.DOWN, expectedDeserializedState.as(UpDownType.class));
243         }
244
245         {
246             // up == 0
247             State expectedDeserializedState = PercentType.ZERO;
248             DynamoDBItem<?> dbItemUp = testSerializationToDTO(legacy, item, UpDownType.UP, BigDecimal.ZERO);
249             testAsHistoricGeneric(dbItemUp, item, expectedDeserializedState);
250             assertEquals(UpDownType.UP, expectedDeserializedState.as(UpDownType.class));
251         }
252     }
253
254     @ParameterizedTest
255     @CsvSource({ "true", "false" })
256     public void testStringTypeWithStringItem(boolean legacy) throws IOException {
257         GenericItem item = new StringItem("foo");
258         DynamoDBItem<?> dbitem = testSerializationToDTO(legacy, item, new StringType("foo bar"), "foo bar");
259         testAsHistoricGeneric(dbitem, item, new StringType("foo bar"));
260     }
261
262     @ParameterizedTest
263     @CsvSource({ "true", "false" })
264     public void testOnOffTypeWithColorItem(boolean legacy) throws IOException {
265         GenericItem item = new ColorItem("foo");
266         DynamoDBItem<?> dbitemOff = testSerializationToDTO(legacy, item, OnOffType.OFF, "0,0,0");
267         testAsHistoricGeneric(dbitemOff, item, HSBType.BLACK);
268
269         DynamoDBItem<?> dbitemOn = testSerializationToDTO(legacy, item, OnOffType.ON, "0,0,100");
270         testAsHistoricGeneric(dbitemOn, item, HSBType.WHITE);
271     }
272
273     @ParameterizedTest
274     @CsvSource({ "true", "false" })
275     public void testOnOffTypeWithDimmerItem(boolean legacy) throws IOException {
276         GenericItem item = new DimmerItem("foo");
277         {
278             State expectedDeserializedState = PercentType.ZERO;
279             DynamoDBItem<?> dbitemOff = testSerializationToDTO(legacy, item, OnOffType.OFF, BigDecimal.ZERO);
280             testAsHistoricGeneric(dbitemOff, item, expectedDeserializedState);
281             assertEquals(OnOffType.OFF, expectedDeserializedState.as(OnOffType.class));
282         }
283
284         {
285             State expectedDeserializedState = PercentType.HUNDRED;
286             DynamoDBItem<?> dbitemOn = testSerializationToDTO(legacy, item, OnOffType.ON, new BigDecimal(100));
287             testAsHistoricGeneric(dbitemOn, item, expectedDeserializedState);
288             assertEquals(OnOffType.ON, expectedDeserializedState.as(OnOffType.class));
289         }
290     }
291
292     @ParameterizedTest
293     @CsvSource({ "true", "false" })
294     public void testOnOffTypeWithSwitchItem(boolean legacy) throws IOException {
295         GenericItem item = new SwitchItem("foo");
296         DynamoDBItem<?> dbitemOff = testSerializationToDTO(legacy, item, OnOffType.OFF, BigDecimal.ZERO);
297         testAsHistoricGeneric(dbitemOff, item, OnOffType.OFF);
298
299         DynamoDBItem<?> dbitemOn = testSerializationToDTO(legacy, item, OnOffType.ON, BigDecimal.ONE);
300         testAsHistoricGeneric(dbitemOn, item, OnOffType.ON);
301     }
302
303     @ParameterizedTest
304     @CsvSource({ "true", "false" })
305     public void testHSBTypeWithColorItem(boolean legacy) throws IOException {
306         GenericItem item = new ColorItem("foo");
307         HSBType hsb = new HSBType(new DecimalType(1.5), new PercentType(new BigDecimal(2.5)),
308                 new PercentType(new BigDecimal(3.5)));
309         DynamoDBItem<?> dbitem = testSerializationToDTO(legacy, item, hsb, "1.5,2.5,3.5");
310         testAsHistoricGeneric(dbitem, item, hsb);
311     }
312 }