2 * Copyright (c) 2010-2024 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.persistence.mongodb.internal;
15 import static org.junit.jupiter.api.Assertions.assertEquals;
16 import static org.junit.jupiter.api.Assertions.assertNotNull;
18 import java.util.ArrayList;
19 import java.util.List;
21 import java.util.function.BiFunction;
23 import org.apache.commons.lang3.tuple.Pair;
24 import org.bson.Document;
25 import org.bson.json.JsonWriterSettings;
26 import org.bson.types.Binary;
27 import org.eclipse.jdt.annotation.NonNullByDefault;
28 import org.openhab.core.library.types.DateTimeType;
29 import org.openhab.core.library.types.DecimalType;
30 import org.openhab.core.library.types.HSBType;
31 import org.openhab.core.library.types.IncreaseDecreaseType;
32 import org.openhab.core.library.types.NextPreviousType;
33 import org.openhab.core.library.types.OnOffType;
34 import org.openhab.core.library.types.OpenClosedType;
35 import org.openhab.core.library.types.PercentType;
36 import org.openhab.core.library.types.PlayPauseType;
37 import org.openhab.core.library.types.PointType;
38 import org.openhab.core.library.types.QuantityType;
39 import org.openhab.core.library.types.RawType;
40 import org.openhab.core.library.types.RewindFastforwardType;
41 import org.openhab.core.library.types.StopMoveType;
42 import org.openhab.core.library.types.StringListType;
43 import org.openhab.core.library.types.StringType;
44 import org.openhab.core.library.types.UpDownType;
45 import org.openhab.core.persistence.HistoricItem;
47 import ch.qos.logback.classic.Level;
48 import ch.qos.logback.classic.spi.ILoggingEvent;
51 * This is a helper class for verifying various aspects of the MongoDB persistence service.
52 * It provides methods for verifying log messages, MongoDB documents, and query results.
53 * Each verification method checks if the actual value matches the expected value and throws an
54 * AssertionError if they do not match.
56 * @author René Ulbricht - Initial contribution
59 public class VerificationHelper {
62 * Verifies a log message.
64 * @param logEvent The log event to verify.
65 * @param expectedMessage The expected message of the log event.
66 * @param expectedLevel The expected level of the log event.
68 public static void verifyLogMessage(ILoggingEvent logEvent, String expectedMessage, Level expectedLevel) {
69 assertEquals(expectedMessage, logEvent.getFormattedMessage());
70 assertEquals(expectedLevel, logEvent.getLevel());
74 * Verifies a document.
76 * @param document The document to verify.
77 * @param expectedItem The expected item of the document.
78 * @param expectedValue The expected value of the document.
80 public static void verifyDocument(Document document, String expectedItem, Object expectedValue) {
81 verifyDocumentWithAlias(document, expectedItem, expectedItem, expectedValue);
85 * Verifies a document with an alias.
87 * @param document The document to verify.
88 * @param expectedAlias The expected alias of the document.
89 * @param expectedRealName The expected real name of the document.
90 * @param expectedValue The expected value of the document. Can be a String or a Double.
92 public static void verifyDocumentWithAlias(Document document, String expectedAlias, String expectedRealName,
93 Object expectedValue) {
94 assertEquals(expectedAlias, document.get(MongoDBFields.FIELD_ITEM));
95 assertEquals(expectedRealName, document.get(MongoDBFields.FIELD_REALNAME));
97 // Use the map to handle the expected value
98 BiFunction<Object, Document, Pair<Object, Object>> handler = HandleTypes.get(expectedValue.getClass());
99 if (handler == null) {
100 throw new IllegalArgumentException("Unsupported type: " + expectedValue.getClass());
102 Pair<Object, Object> values = handler.apply(expectedValue, document);
104 JsonWriterSettings jsonWriterSettings = JsonWriterSettings.builder().indent(true).build();
105 assertEquals(values.getLeft(), values.getRight(),
106 "Document: (" + expectedValue.getClass().getSimpleName() + ") " + document.toJson(jsonWriterSettings));
108 assertNotNull(document.get("_id"));
109 assertNotNull(document.get("timestamp"));
113 * Verifies the result of a query.
115 * @param result The result of the query.
116 * @param startState The state of the first item in the result.
117 * @param increment The increment for the expected state.
119 public static void verifyQueryResult(Iterable<HistoricItem> result, int startState, int increment, int totalSize) {
120 List<HistoricItem> resultList = new ArrayList<>();
121 result.forEach(resultList::add);
123 assertEquals(totalSize, resultList.size());
125 int expectedState = startState;
126 for (HistoricItem item : resultList) {
127 assertEquals(expectedState, ((DecimalType) item.getState()).intValue());
128 expectedState += increment;
132 public static void verifyQueryResult(Iterable<HistoricItem> result, Object expectedState) {
133 List<HistoricItem> resultList = new ArrayList<>();
134 result.forEach(resultList::add);
136 assertEquals(1, resultList.size());
138 assertEquals(expectedState, resultList.get(0).getState());
141 // Define a map from types to functions that handle those types
142 private static final Map<Class<?>, BiFunction<Object, Document, Pair<Object, Object>>> HandleTypes = Map.ofEntries(
143 Map.entry(Double.class, VerificationHelper::handleGeneric),
144 Map.entry(String.class, VerificationHelper::handleGeneric),
145 Map.entry(HSBType.class, VerificationHelper::handleToString),
146 Map.entry(DecimalType.class, VerificationHelper::handleDecimalType),
147 Map.entry(DateTimeType.class, VerificationHelper::handleDateTimeType),
148 Map.entry(IncreaseDecreaseType.class, VerificationHelper::handleToString),
149 Map.entry(RewindFastforwardType.class, VerificationHelper::handleToString),
150 Map.entry(NextPreviousType.class, VerificationHelper::handleToString),
151 Map.entry(OnOffType.class, VerificationHelper::handleToString),
152 Map.entry(OpenClosedType.class, VerificationHelper::handleToString),
153 Map.entry(PercentType.class, VerificationHelper::handlePercentType),
154 Map.entry(PlayPauseType.class, VerificationHelper::handleToString),
155 Map.entry(PointType.class, VerificationHelper::handleToString),
156 Map.entry(StopMoveType.class, VerificationHelper::handleToString),
157 Map.entry(StringListType.class, VerificationHelper::handleToString),
158 Map.entry(StringType.class, VerificationHelper::handleGeneric),
159 Map.entry(UpDownType.class, VerificationHelper::handleToString),
160 Map.entry(QuantityType.class, VerificationHelper::handleQuantityType),
161 Map.entry(RawType.class, VerificationHelper::handleRawType));
163 private static Pair<Object, Object> handleGeneric(Object ev, Document doc) {
164 Object value = doc.get(MongoDBFields.FIELD_VALUE);
165 return Pair.of(ev, value != null ? value : new Object());
168 private static Pair<Object, Object> handleToString(Object ev, Document doc) {
169 Object value = doc.get(MongoDBFields.FIELD_VALUE);
170 return Pair.of(ev.toString(), value != null ? value : new Object());
173 private static Pair<Object, Object> handleDecimalType(Object ev, Document doc) {
174 Double value = doc.getDouble(MongoDBFields.FIELD_VALUE);
175 return Pair.of(((DecimalType) ev).doubleValue(), value != null ? value : new Object());
178 private static Pair<Object, Object> handleDateTimeType(Object ev, Document doc) {
179 String value = doc.getString(MongoDBFields.FIELD_VALUE);
180 return Pair.of(((DateTimeType) ev).getZonedDateTime().toString(), value != null ? value : new Object());
183 private static Pair<Object, Object> handlePercentType(Object ev, Document doc) {
184 Integer value = doc.getInteger(MongoDBFields.FIELD_VALUE);
185 return Pair.of(((PercentType) ev).intValue(), value != null ? value : new Object());
188 private static Pair<Object, Object> handleQuantityType(Object ev, Document doc) {
189 Double value = doc.getDouble(MongoDBFields.FIELD_VALUE);
190 String unit = doc.getString(MongoDBFields.FIELD_UNIT);
191 if (value != null && unit != null) {
192 QuantityType<?> quantityType = (QuantityType<?>) ev;
193 return Pair.of(quantityType.doubleValue() + "--" + quantityType.getUnit(), value + "--" + unit);
195 return Pair.of(new Object(), new Object());
198 private static Pair<Object, Object> handleRawType(Object ev, Document doc) {
199 RawType rawType = (RawType) ev;
200 Document expectedDoc = new Document();
201 expectedDoc.put(MongoDBFields.FIELD_VALUE_TYPE, rawType.getMimeType());
202 expectedDoc.put(MongoDBFields.FIELD_VALUE_DATA, new Binary(rawType.getBytes()));
203 Object value = doc.get(MongoDBFields.FIELD_VALUE);
204 return Pair.of(expectedDoc, value != null ? value : new Object());