]> git.basschouten.com Git - openhab-addons.git/blob
dff84eacd1e737c249a39a49713bd4f71671fdc9
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2024 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.mongodb.internal;
14
15 import static org.junit.jupiter.api.Assertions.assertEquals;
16 import static org.junit.jupiter.api.Assertions.assertTrue;
17
18 import java.text.DateFormat;
19 import java.time.LocalDate;
20 import java.time.ZoneId;
21 import java.time.ZonedDateTime;
22 import java.time.temporal.ChronoUnit;
23 import java.util.ArrayList;
24 import java.util.Date;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.Set;
28 import java.util.stream.Collectors;
29
30 import org.bson.Document;
31 import org.bson.types.ObjectId;
32 import org.junit.jupiter.api.Disabled;
33 import org.junit.jupiter.api.Test;
34 import org.junit.jupiter.params.ParameterizedTest;
35 import org.junit.jupiter.params.provider.MethodSource;
36 import org.mockito.Mockito;
37 import org.openhab.core.items.GenericItem;
38 import org.openhab.core.items.ItemNotFoundException;
39 import org.openhab.core.library.items.ColorItem;
40 import org.openhab.core.library.items.DateTimeItem;
41 import org.openhab.core.library.items.ImageItem;
42 import org.openhab.core.library.items.NumberItem;
43 import org.openhab.core.library.items.StringItem;
44 import org.openhab.core.library.types.DateTimeType;
45 import org.openhab.core.library.types.DecimalType;
46 import org.openhab.core.library.types.HSBType;
47 import org.openhab.core.library.types.QuantityType;
48 import org.openhab.core.library.types.RawType;
49 import org.openhab.core.persistence.FilterCriteria;
50 import org.openhab.core.persistence.HistoricItem;
51 import org.osgi.framework.BundleContext;
52
53 import com.mongodb.client.MongoCollection;
54 import com.mongodb.client.MongoDatabase;
55
56 import ch.qos.logback.classic.Level;
57 import ch.qos.logback.classic.spi.ILoggingEvent;
58 import ch.qos.logback.core.read.ListAppender;
59 import de.bwaldvogel.mongo.backend.memory.MemoryBackend;
60
61 /**
62  * This is the implementation of the test for MongoDB {@link PersistenceService}.
63  *
64  * @author RenĂ© Ulbricht - Initial contribution
65  */
66 @Disabled("Fails on CPUs without AVX support, see: https://github.com/openhab/openhab-addons/issues/17046")
67 public class MongoDBPersistenceServiceTest {
68
69     /**
70      * Tests the activate method of MongoDBPersistenceService.
71      *
72      * This test checks if the activate method correctly logs the MongoDB URL, database, and collection.
73      * It uses different database backends provided by the provideDatabaseBackends method.
74      *
75      * @param dbContainer The container running the MongoDB instance.
76      */
77     @ParameterizedTest
78     @MethodSource("org.openhab.persistence.mongodb.internal.DataCreationHelper#provideDatabaseBackends")
79     public void testActivate(DatabaseTestContainer dbContainer) {
80         try {
81             // Preparation
82             SetupResult setupResult = DataCreationHelper.setupMongoDB("testCollection", dbContainer);
83
84             // Set up logger
85             ListAppender<ILoggingEvent> listAppender = DataCreationHelper.setupLogger(MongoDBPersistenceService.class,
86                     Level.DEBUG);
87
88             // Execution
89             setupResult.service.activate(setupResult.bundleContext, setupResult.config);
90
91             // Verification
92             List<ILoggingEvent> logsList = listAppender.list;
93             VerificationHelper.verifyLogMessage(logsList.get(0), "MongoDB URL " + dbContainer.getConnectionString(),
94                     Level.DEBUG);
95             VerificationHelper.verifyLogMessage(logsList.get(1), "MongoDB database " + setupResult.dbname, Level.DEBUG);
96             VerificationHelper.verifyLogMessage(logsList.get(2), "MongoDB collection testCollection", Level.DEBUG);
97         } finally {
98             dbContainer.stop();
99         }
100     }
101
102     /**
103      * Tests the deactivate method of MongoDBPersistenceService.
104      *
105      * This test checks if the deactivate method correctly logs a message when the MongoDB persistence bundle is
106      * stopping.
107      * It uses different database backends provided by the provideDatabaseBackends method.
108      *
109      * @param dbContainer The container running the MongoDB instance.
110      */
111     @ParameterizedTest
112     @MethodSource("org.openhab.persistence.mongodb.internal.DataCreationHelper#provideDatabaseBackends")
113     public void testDeactivate(DatabaseTestContainer dbContainer) {
114         try {
115             // Preparation
116             SetupResult setupResult = DataCreationHelper.setupMongoDB("testCollection", dbContainer);
117
118             setupResult.service.activate(setupResult.bundleContext, setupResult.config);
119
120             // Set up logger
121             ListAppender<ILoggingEvent> listAppender = DataCreationHelper.setupLogger(MongoDBPersistenceService.class,
122                     Level.DEBUG);
123
124             // Execution
125             setupResult.service.deactivate(1);
126
127             // Verification
128             List<ILoggingEvent> logsList = listAppender.list;
129             VerificationHelper.verifyLogMessage(logsList.get(0),
130                     "MongoDB persistence bundle stopping. Disconnecting from database.", Level.DEBUG);
131         } finally {
132             dbContainer.stop();
133         }
134     }
135
136     /**
137      * Tests the getId method of MongoDBPersistenceService.
138      *
139      * This test checks if the getId method correctly returns the ID of the MongoDBPersistenceService, which should be
140      * "mongodb".
141      */
142     @Test
143     public void testGetId() {
144         // Preparation
145         DatabaseTestContainer dbContainer = new DatabaseTestContainer(new MemoryBackend());
146         try {
147             SetupResult setupResult = DataCreationHelper.setupMongoDB(null, dbContainer);
148             MongoDBPersistenceService service = setupResult.service;
149
150             // Execution
151             String id = service.getId();
152
153             // Verification
154             assertEquals("mongodb", id);
155         } finally {
156             dbContainer.stop();
157         }
158     }
159
160     /**
161      * Tests the getLabel method of MongoDBPersistenceService.
162      *
163      * This test checks if the getLabel method correctly returns the label of the MongoDBPersistenceService, which
164      * should be "MongoDB".
165      */
166     @Test
167     public void testGetLabel() {
168         // Preparation
169         DatabaseTestContainer dbContainer = new DatabaseTestContainer(new MemoryBackend());
170         try {
171             SetupResult setupResult = DataCreationHelper.setupMongoDB(null, dbContainer);
172             MongoDBPersistenceService service = setupResult.service;
173
174             // Execution
175             String label = service.getLabel(null);
176
177             // Verification
178             assertEquals("MongoDB", label);
179         } finally {
180             dbContainer.stop();
181         }
182     }
183
184     /**
185      * Tests the store method of MongoDBPersistenceService with a NumberItem.
186      *
187      * This test checks if the store method correctly stores a NumberItem in the MongoDB database.
188      * It uses different database backends provided by the provideDatabaseBackends method.
189      *
190      * @param dbContainer The container running the MongoDB instance.
191      */
192     @ParameterizedTest
193     @MethodSource("org.openhab.persistence.mongodb.internal.DataCreationHelper#provideDatabaseBackends")
194     public void testStoreNumber(DatabaseTestContainer dbContainer) {
195         try {
196             // Preparation
197             SetupResult setupResult = DataCreationHelper.setupMongoDB("testCollection", dbContainer);
198             MongoDBPersistenceService service = setupResult.service;
199             MongoDatabase database = setupResult.database;
200
201             service.activate(setupResult.bundleContext, setupResult.config);
202
203             NumberItem item = DataCreationHelper.createNumberItem("TestItem", 10.1);
204
205             // Execution
206             service.store(item, null);
207
208             // Verification
209             MongoCollection<Document> collection = database.getCollection("testCollection");
210             List<Document> documents = (ArrayList<Document>) collection.find().into(new ArrayList<>());
211
212             assertEquals(1, documents.size()); // Assert that there is only one document
213
214             Document insertedDocument = documents.get(0); // Get the first (and only) document
215
216             VerificationHelper.verifyDocument(insertedDocument, "TestItem", 10.1);
217         } finally {
218             dbContainer.stop();
219         }
220     }
221
222     /**
223      * Tests the store method of MongoDBPersistenceService with a StringItem.
224      *
225      * This test checks if the store method correctly stores a StringItem in the MongoDB database.
226      * It uses different database backends provided by the provideDatabaseBackends method.
227      *
228      * @param dbContainer The container running the MongoDB instance.
229      */
230     @ParameterizedTest
231     @MethodSource("org.openhab.persistence.mongodb.internal.DataCreationHelper#provideDatabaseBackends")
232     public void testStoreString(DatabaseTestContainer dbContainer) {
233         try {
234             // Preparation
235             SetupResult setupResult = DataCreationHelper.setupMongoDB("testCollection", dbContainer);
236             MongoDBPersistenceService service = setupResult.service;
237             MongoDatabase database = setupResult.database;
238
239             service.activate(setupResult.bundleContext, setupResult.config);
240
241             StringItem item = DataCreationHelper.createStringItem("TestItem", "TestValue");
242
243             // Execution
244             service.store(item, null);
245
246             // Verification
247             MongoCollection<Document> collection = database.getCollection("testCollection");
248             List<Document> documents = (ArrayList<Document>) collection.find().into(new ArrayList<>());
249
250             assertEquals(1, documents.size()); // Assert that there is only one document
251
252             Document insertedDocument = documents.get(0); // Get the first (and only) document
253
254             VerificationHelper.verifyDocument(insertedDocument, "TestItem", "TestValue");
255         } finally {
256             dbContainer.stop();
257         }
258     }
259
260     /**
261      * Tests the store method of MongoDBPersistenceService with multiple items in a single collection.
262      *
263      * This test checks if the store method correctly stores multiple items in the same MongoDB collection.
264      * It uses different database backends provided by the provideDatabaseBackends method.
265      *
266      * @param dbContainer The container running the MongoDB instance.
267      */
268     @ParameterizedTest
269     @MethodSource("org.openhab.persistence.mongodb.internal.DataCreationHelper#provideDatabaseBackends")
270     public void testStoreSingleCollection(DatabaseTestContainer dbContainer) {
271         try {
272             // Preparation
273             SetupResult setupResult = DataCreationHelper.setupMongoDB("testCollection", dbContainer);
274             MongoDBPersistenceService service = setupResult.service;
275             MongoDatabase database = setupResult.database;
276
277             service.activate(setupResult.bundleContext, setupResult.config);
278
279             StringItem strItem1 = DataCreationHelper.createStringItem("TestItem", "TestValue");
280             StringItem strItem2 = DataCreationHelper.createStringItem("SecondTestItem", "SecondTestValue");
281
282             // Execution
283             service.store(strItem1, null);
284             service.store(strItem2, null);
285
286             // Verification
287             MongoCollection<Document> collection = database.getCollection("testCollection");
288             List<Document> documents = (ArrayList<Document>) collection.find().into(new ArrayList<>());
289
290             assertEquals(2, documents.size()); // Assert that there are two documents
291
292             Document insertedDocument1 = documents.get(0); // Get the first document
293             VerificationHelper.verifyDocument(insertedDocument1, "TestItem", "TestValue");
294
295             Document insertedDocument2 = documents.get(1); // Get the second document
296             VerificationHelper.verifyDocument(insertedDocument2, "SecondTestItem", "SecondTestValue");
297         } finally {
298             dbContainer.stop();
299         }
300     }
301
302     /**
303      * Tests the store method of MongoDBPersistenceService with multiple items.
304      *
305      * This test checks if the store method correctly stores multiple items in the same MongoDB collection.
306      * It uses different database backends provided by the provideDatabaseBackends method.
307      *
308      * @param dbContainer The container running the MongoDB instance.
309      */
310     @ParameterizedTest
311     @MethodSource("org.openhab.persistence.mongodb.internal.DataCreationHelper#provideDatabaseBackends")
312     public void testStoreMultipleItemsSingleCollection(DatabaseTestContainer dbContainer) {
313         try {
314             // Preparation
315             SetupResult setupResult = DataCreationHelper.setupMongoDB("testCollection", dbContainer);
316             MongoDBPersistenceService service = setupResult.service;
317             MongoDatabase database = setupResult.database;
318
319             service.activate(setupResult.bundleContext, setupResult.config);
320
321             StringItem strItem1 = DataCreationHelper.createStringItem("TestItem1", "TestValue1");
322             StringItem strItem2 = DataCreationHelper.createStringItem("TestItem2", "TestValue2");
323
324             // Execution
325             service.store(strItem1, null);
326             service.store(strItem2, null);
327
328             // Verification
329             MongoCollection<Document> collection = database.getCollection("testCollection");
330             List<Document> documents = (ArrayList<Document>) collection.find().into(new ArrayList<>());
331
332             assertEquals(2, documents.size()); // Assert that there are two documents
333
334             Document insertedDocument1 = documents.get(0); // Get the first document
335             VerificationHelper.verifyDocument(insertedDocument1, "TestItem1", "TestValue1");
336
337             Document insertedDocument2 = documents.get(1); // Get the second document
338             VerificationHelper.verifyDocument(insertedDocument2, "TestItem2", "TestValue2");
339         } finally {
340             dbContainer.stop();
341         }
342     }
343
344     /**
345      * Tests the store method of MongoDBPersistenceService with a StringItem and an alias.
346      *
347      * This test checks if the store method correctly stores a StringItem with an alias in the MongoDB database.
348      * It uses different database backends provided by the provideDatabaseBackends method.
349      *
350      * @param dbContainer The container running the MongoDB instance.
351      */
352     @ParameterizedTest
353     @MethodSource("org.openhab.persistence.mongodb.internal.DataCreationHelper#provideDatabaseBackends")
354     public void testStoreStringWithAlias(DatabaseTestContainer dbContainer) {
355         try {
356             // Preparation
357             SetupResult setupResult = DataCreationHelper.setupMongoDB("testCollection", dbContainer);
358             MongoDBPersistenceService service = setupResult.service;
359             MongoDatabase database = setupResult.database;
360
361             service.activate(setupResult.bundleContext, setupResult.config);
362
363             StringItem item = DataCreationHelper.createStringItem("TestItem", "TestValue");
364
365             // Execution
366             service.store(item, "AliasName");
367
368             // Verification
369             MongoCollection<Document> collection = database.getCollection("testCollection");
370             List<Document> documents = (ArrayList<Document>) collection.find().into(new ArrayList<>());
371
372             assertEquals(1, documents.size()); // Assert that there is only one document
373
374             Document insertedDocument = documents.get(0); // Get the first (and only) document
375
376             VerificationHelper.verifyDocumentWithAlias(insertedDocument, "AliasName", "TestItem", "TestValue");
377         } finally {
378             dbContainer.stop();
379         }
380     }
381
382     /**
383      * Tests the query method of MongoDBPersistenceService with NumberItems in a single collection.
384      *
385      * This test checks if the query method correctly retrieves NumberItems from a single MongoDB collection.
386      * It uses different database backends provided by the provideDatabaseBackends method.
387      *
388      * @param dbContainer The container running the MongoDB instance.
389      */
390     @ParameterizedTest
391     @MethodSource("org.openhab.persistence.mongodb.internal.DataCreationHelper#provideDatabaseBackends")
392     public void testQueryNumberItemsInOneCollection(DatabaseTestContainer dbContainer) {
393         try {
394             // Preparation
395             SetupResult setupResult = DataCreationHelper.setupMongoDB("testCollection", dbContainer);
396             MongoDBPersistenceService service = setupResult.service;
397
398             // Add items to the ItemRegistry
399             NumberItem itemReg1 = DataCreationHelper.createNumberItem("TestItem", 0);
400             NumberItem itemReg2 = DataCreationHelper.createNumberItem("TestItem2", 0);
401             try {
402                 Mockito.when(setupResult.itemRegistry.getItem("TestItem")).thenReturn(itemReg1);
403                 Mockito.when(setupResult.itemRegistry.getItem("TestItem2")).thenReturn(itemReg2);
404             } catch (ItemNotFoundException e) {
405             }
406
407             service.activate(setupResult.bundleContext, setupResult.config);
408
409             // Store some items
410             for (int i = 0; i < 10; i++) {
411                 NumberItem item1 = DataCreationHelper.createNumberItem("TestItem", i);
412                 NumberItem item2 = DataCreationHelper.createNumberItem("TestItem2", i * 2);
413                 service.store(item1, null);
414                 service.store(item2, null);
415             }
416
417             // Execution
418             FilterCriteria filter1 = DataCreationHelper.createFilterCriteria("TestItem");
419             Iterable<HistoricItem> result1 = service.query(filter1);
420
421             FilterCriteria filter2 = DataCreationHelper.createFilterCriteria("TestItem2");
422             Iterable<HistoricItem> result2 = service.query(filter2);
423
424             // Verification
425             VerificationHelper.verifyQueryResult(result1, 0, 1, 10);
426             VerificationHelper.verifyQueryResult(result2, 0, 2, 10);
427         } finally {
428             dbContainer.stop();
429         }
430     }
431
432     /**
433      * Tests the query method of MongoDBPersistenceService with NumberItems in multiple collections.
434      *
435      * This test checks if the query method correctly retrieves NumberItems from multiple MongoDB collections.
436      * It uses different database backends provided by the provideDatabaseBackends method.
437      *
438      * @param dbContainer The container running the MongoDB instance.
439      */
440     @ParameterizedTest
441     @MethodSource("org.openhab.persistence.mongodb.internal.DataCreationHelper#provideDatabaseBackends")
442     public void testQueryNumberItemsInMultipleCollections(DatabaseTestContainer dbContainer) {
443         try {
444             // Preparation
445             SetupResult setupResult = DataCreationHelper.setupMongoDB(null, dbContainer);
446             MongoDBPersistenceService service = setupResult.service;
447             BundleContext bundleContext = setupResult.bundleContext;
448             Map<String, Object> config = setupResult.config;
449
450             try {
451                 Mockito.when(setupResult.itemRegistry.getItem("TestItem"))
452                         .thenReturn(DataCreationHelper.createNumberItem("TestItem", 0));
453                 Mockito.when(setupResult.itemRegistry.getItem("TestItem2"))
454                         .thenReturn(DataCreationHelper.createNumberItem("TestItem2", 0));
455             } catch (ItemNotFoundException e) {
456             }
457
458             service.activate(bundleContext, config);
459
460             // Store some items
461             for (int i = 0; i < 10; i++) {
462                 NumberItem item1 = DataCreationHelper.createNumberItem("TestItem", i);
463                 NumberItem item2 = DataCreationHelper.createNumberItem("TestItem2", i * 2);
464                 service.store(item1, null);
465                 service.store(item2, null);
466             }
467
468             // Execution
469             FilterCriteria filter1 = DataCreationHelper.createFilterCriteria("TestItem");
470             Iterable<HistoricItem> result1 = service.query(filter1);
471
472             FilterCriteria filter2 = DataCreationHelper.createFilterCriteria("TestItem2");
473             Iterable<HistoricItem> result2 = service.query(filter2);
474
475             // Verification
476             VerificationHelper.verifyQueryResult(result1, 0, 1, 10);
477             VerificationHelper.verifyQueryResult(result2, 0, 2, 10);
478         } finally {
479             dbContainer.stop();
480         }
481     }
482
483     /**
484      * Tests the query method of MongoDBPersistenceService with NumberItems in a single collection and a time range.
485      *
486      * This test checks if the query method correctly retrieves NumberItems from a single MongoDB collection within a
487      * specified time range.
488      * It uses different database backends provided by the provideDatabaseBackends method.
489      *
490      * @param dbContainer The container running the MongoDB instance.
491      */
492     @ParameterizedTest
493     @MethodSource("org.openhab.persistence.mongodb.internal.DataCreationHelper#provideDatabaseBackends")
494     public void testQueryNumberItemsInOneCollectionTimeRange(DatabaseTestContainer dbContainer) {
495         try {
496             // Preparation
497             SetupResult setupResult = DataCreationHelper.setupMongoDB("testCollection", dbContainer);
498             MongoDBPersistenceService service = setupResult.service;
499             MongoDatabase database = setupResult.database;
500
501             try {
502                 Mockito.when(setupResult.itemRegistry.getItem("TestItem"))
503                         .thenReturn(DataCreationHelper.createNumberItem("TestItem", 0));
504                 Mockito.when(setupResult.itemRegistry.getItem("TestItem2"))
505                         .thenReturn(DataCreationHelper.createNumberItem("TestItem2", 0));
506             } catch (ItemNotFoundException e) {
507             }
508
509             service.activate(setupResult.bundleContext, setupResult.config);
510
511             // Get the collection
512             MongoCollection<Document> collection = database.getCollection("testCollection");
513
514             // Store items directly to the database with defined timestamps
515             for (int i = 0; i < 10; i++) {
516                 Document obj = DataCreationHelper.createDocument("TestItem", i, LocalDate.now().minusDays(i));
517                 collection.insertOne(obj);
518
519                 Document obj2 = DataCreationHelper.createDocument("TestItem2", i * 2, LocalDate.now().minusDays(i));
520                 collection.insertOne(obj2);
521             }
522
523             // Execution
524             FilterCriteria filter1 = DataCreationHelper.createFilterCriteria("TestItem",
525                     ZonedDateTime.now().minusDays(5), null);
526             Iterable<HistoricItem> result1 = service.query(filter1);
527
528             // Verification
529             VerificationHelper.verifyQueryResult(result1, 4, -1, 5);
530         } finally {
531             dbContainer.stop();
532         }
533     }
534
535     /**
536      * Tests the query method of MongoDBPersistenceService with NumberItems in a single collection and a state equals
537      * filter.
538      *
539      * This test checks if the query method correctly retrieves NumberItems from a single MongoDB collection that match
540      * a specified state.
541      * It uses different database backends provided by the provideDatabaseBackends method.
542      *
543      * @param dbContainer The container running the MongoDB instance.
544      */
545     @ParameterizedTest
546     @MethodSource("org.openhab.persistence.mongodb.internal.DataCreationHelper#provideDatabaseBackends")
547     public void testQueryNumberItemsInOneCollectionStateEquals(DatabaseTestContainer dbContainer) {
548         try {
549             // Preparation
550             SetupResult setupResult = DataCreationHelper.setupMongoDB("testCollection", dbContainer);
551             MongoDBPersistenceService service = setupResult.service;
552             MongoDatabase database = setupResult.database;
553
554             try {
555                 Mockito.when(setupResult.itemRegistry.getItem("TestItem"))
556                         .thenReturn(DataCreationHelper.createNumberItem("TestItem", 0));
557             } catch (ItemNotFoundException e) {
558             }
559
560             service.activate(setupResult.bundleContext, setupResult.config);
561
562             // Get the collection
563             MongoCollection<Document> collection = database.getCollection("testCollection");
564
565             // Store items directly to the database with defined timestamps
566             for (int i = 0; i < 10; i++) {
567                 Document obj = DataCreationHelper.createDocument("TestItem", i, LocalDate.now().minusDays(i));
568                 collection.insertOne(obj);
569             }
570
571             Document obj = DataCreationHelper.createDocument("TestItem", 4.0, LocalDate.now());
572             collection.insertOne(obj);
573
574             // Execution
575             FilterCriteria filter1 = DataCreationHelper.createFilterCriteria("TestItem", null, null);
576             filter1.setState(new DecimalType(4.0));
577             filter1.setOperator(FilterCriteria.Operator.EQ);
578
579             Iterable<HistoricItem> result1 = service.query(filter1);
580
581             // Verification
582             VerificationHelper.verifyQueryResult(result1, 4, 0, 2);
583         } finally {
584             dbContainer.stop();
585         }
586     }
587
588     /**
589      * Tests the store method of the MongoDBPersistenceService with all types of openHAB items.
590      * Each item is stored in the collection in the MongoDB database.
591      *
592      * @param item The item to store in the database.
593      */
594     @ParameterizedTest
595     @MethodSource("org.openhab.persistence.mongodb.internal.DataCreationHelper#provideOpenhabItemTypes")
596     public void testStoreAllOpenhabItemTypesSingleCollection(GenericItem item) {
597         // Preparation
598         DatabaseTestContainer dbContainer = new DatabaseTestContainer(new MemoryBackend());
599         try {
600             SetupResult setupResult = DataCreationHelper.setupMongoDB("testCollection", dbContainer);
601             MongoDBPersistenceService service = setupResult.service;
602             MongoDatabase database = setupResult.database;
603
604             service.activate(setupResult.bundleContext, setupResult.config);
605
606             // Execution
607             service.store(item, null);
608
609             // Verification
610             MongoCollection<Document> collection = database.getCollection("testCollection");
611             List<Document> documents = (ArrayList<Document>) collection.find().into(new ArrayList<>());
612
613             assertEquals(1, documents.size()); // Assert that there is only one document
614
615             Document insertedDocument = documents.get(0); // Get the first (and only) document
616
617             VerificationHelper.verifyDocument(insertedDocument, item.getName(), item.getState());
618         } finally {
619             dbContainer.stop();
620         }
621     }
622
623     /**
624      * Tests the store and query method for various image sizes of the MongoDBPersistenceService
625      * Each item is queried with the type from one collection in the MongoDB database.
626      *
627      * @param item The item to store in the database.
628      */
629     @ParameterizedTest
630     @MethodSource("org.openhab.persistence.mongodb.internal.DataCreationHelper#provideOpenhabImageItemsInDifferentSizes")
631     public void testStoreAndQueryyLargerImages(ImageItem item) {
632         // Preparation
633         DatabaseTestContainer dbContainer = new DatabaseTestContainer(new MemoryBackend());
634         try {
635             SetupResult setupResult = DataCreationHelper.setupMongoDB("testCollection", dbContainer);
636             MongoDBPersistenceService service = setupResult.service;
637
638             service.activate(setupResult.bundleContext, setupResult.config);
639             try {
640                 Mockito.when(setupResult.itemRegistry.getItem(item.getName())).thenReturn(item);
641             } catch (ItemNotFoundException e) {
642             }
643             try {
644                 service.store(item, null);
645             } catch (org.bson.BsonMaximumSizeExceededException e) {
646                 if (item.getName().equals("ImageItem20MB")) {
647                     // this is expected
648                     return;
649                 } else {
650                     throw e;
651                 }
652             }
653
654             // Execution
655             FilterCriteria filter = DataCreationHelper.createFilterCriteria(item.getName());
656             Iterable<HistoricItem> result = service.query(filter);
657             // Verification
658
659             VerificationHelper.verifyQueryResult(result, item.getState());
660         } finally {
661             dbContainer.stop();
662         }
663     }
664
665     /**
666      * Tests the old way of storing data and query method of the MongoDBPersistenceService with all types of openHAB
667      * items.
668      * Each item is queried with the type from one collection in the MongoDB database.
669      *
670      * @param item The item to store in the database.
671      */
672     @ParameterizedTest
673     @MethodSource("org.openhab.persistence.mongodb.internal.DataCreationHelper#provideOpenhabItemTypes")
674     public void testOldDataQueryAllOpenhabItemTypesSingleCollection(GenericItem item) {
675         // Preparation
676         DatabaseTestContainer dbContainer = new DatabaseTestContainer(new MemoryBackend());
677         try {
678             SetupResult setupResult = DataCreationHelper.setupMongoDB("testCollection", dbContainer);
679             MongoDBPersistenceService service = setupResult.service;
680             MongoDatabase database = setupResult.database;
681
682             service.activate(setupResult.bundleContext, setupResult.config);
683             try {
684                 Mockito.when(setupResult.itemRegistry.getItem(item.getName())).thenReturn(item);
685             } catch (ItemNotFoundException e) {
686             }
687             MongoCollection<Document> collection = database.getCollection("testCollection");
688             DataCreationHelper.storeOldData(collection, item.getName(), item.getState());
689             // after storing, we have to adjust the expected values for ImageItems, ColorItems as well as DateTimeItems
690             if (item instanceof ImageItem) {
691                 item.setState(new RawType(new byte[0], "application/octet-stream"));
692             } else if (item instanceof ColorItem) {
693                 item.setState(new HSBType("0,0,0"));
694             }
695
696             // Execution
697             FilterCriteria filter = DataCreationHelper.createFilterCriteria(item.getName());
698             Iterable<HistoricItem> result = service.query(filter);
699             // Verification
700
701             if (item instanceof DateTimeItem) {
702                 // verify just the date part
703                 assertEquals(((DateTimeType) item.getState()).getZonedDateTime().toLocalDate(),
704                         ((DateTimeType) result.iterator().next().getState()).getZonedDateTime().toLocalDate());
705             } else {
706                 VerificationHelper.verifyQueryResult(result, item.getState());
707             }
708         } finally {
709             dbContainer.stop();
710         }
711     }
712
713     /**
714      * Tests the writting of NumberItems including units
715      * Each item should be written to the database with the unit information
716      *
717      * @param item The item to store in the database.
718      */
719     @Test
720     public void testStoreNumberItemWithUnit() {
721         // Preparation
722         DatabaseTestContainer dbContainer = new DatabaseTestContainer(new MemoryBackend());
723         try {
724             SetupResult setupResult = DataCreationHelper.setupMongoDB("testCollection", dbContainer);
725             MongoDBPersistenceService service = setupResult.service;
726             MongoDatabase database = setupResult.database;
727
728             service.activate(setupResult.bundleContext, setupResult.config);
729             MongoCollection<Document> collection = database.getCollection("testCollection");
730
731             NumberItem item = DataCreationHelper.createNumberItem("Number:Energy", "TestItem",
732                     new QuantityType<>("10.1 kWh"));
733
734             // Execution
735             service.store(item, null);
736
737             // Verification
738             List<Document> documents = (ArrayList<Document>) collection.find().into(new ArrayList<>());
739
740             assertEquals(1, documents.size()); // Assert that there is only one document
741
742             Document insertedDocument = documents.get(0); // Get the first (and only) document
743
744             assertEquals(10.1, insertedDocument.get(MongoDBFields.FIELD_VALUE));
745             assertEquals("kWh", insertedDocument.get(MongoDBFields.FIELD_UNIT));
746         } finally {
747             dbContainer.stop();
748         }
749     }
750
751     /**
752      * Tests the reading of NumberItems including units
753      * Each item should be written to the database with the unit information
754      *
755      * @param item The item to store in the database.
756      */
757     @Test
758     public void testQueryNumberItemWithUnit() {
759         // Preparation
760         DatabaseTestContainer dbContainer = new DatabaseTestContainer(new MemoryBackend());
761         try {
762             SetupResult setupResult = DataCreationHelper.setupMongoDB("testCollection", dbContainer);
763             MongoDBPersistenceService service = setupResult.service;
764             MongoDatabase database = setupResult.database;
765
766             service.activate(setupResult.bundleContext, setupResult.config);
767             MongoCollection<Document> collection = database.getCollection("testCollection");
768
769             NumberItem item = DataCreationHelper.createNumberItem("Number:Energy", "TestItem",
770                     new QuantityType<>("10.1 MWh"));
771             try {
772                 Mockito.when(setupResult.itemRegistry.getItem("TestItem")).thenReturn(item);
773             } catch (ItemNotFoundException e) {
774             }
775
776             Document obj = new Document();
777             obj.put(MongoDBFields.FIELD_ID, new ObjectId());
778             obj.put(MongoDBFields.FIELD_ITEM, "TestItem");
779             obj.put(MongoDBFields.FIELD_REALNAME, "TestItem");
780             obj.put(MongoDBFields.FIELD_TIMESTAMP, new Date());
781             obj.put(MongoDBFields.FIELD_VALUE, 201.5);
782             obj.put(MongoDBFields.FIELD_UNIT, "Wh");
783             collection.insertOne(obj);
784
785             // Execution
786             FilterCriteria filter = DataCreationHelper.createFilterCriteria("TestItem");
787             Iterable<HistoricItem> result = service.query(filter);
788             VerificationHelper.verifyQueryResult(result, new QuantityType<>("201.5 Wh"));
789         } finally {
790             dbContainer.stop();
791         }
792     }
793
794     /**
795      * Tests the toString of a MongoDBItem
796      * 
797      *
798      * @param item The item to store in the database.
799      */
800     @Test
801     public void testHistoricItemToString() {
802         // Preparation
803         ZonedDateTime now = ZonedDateTime.now();
804         HistoricItem item = new MongoDBItem("TestItem", new DecimalType(10.1), now);
805
806         // Execution
807         String result = item.toString();
808
809         // Verification
810         // Jan 29, 2024, 8:43:26 PM: TestItem -> 10.1
811         String expected = DateFormat.getDateTimeInstance().format(Date.from(now.toInstant())) + ": TestItem -> 10.1";
812         assertEquals(expected, result);
813     }
814
815     /*
816      * Test the store method which stores a item state as well as a timestampe (ZonedDateTime) and check the result in
817      * the database
818      */
819     @Test
820     public void testStoreItemWithTimestamp() {
821         // Preparation
822         DatabaseTestContainer dbContainer = new DatabaseTestContainer(new MemoryBackend());
823         try {
824             SetupResult setupResult = DataCreationHelper.setupMongoDB(null, dbContainer);
825             MongoDBPersistenceService service = setupResult.service;
826             MongoDatabase database = setupResult.database;
827
828             service.activate(setupResult.bundleContext, setupResult.config);
829             try {
830                 Mockito.when(setupResult.itemRegistry.getItem("TestItem"))
831                         .thenReturn(DataCreationHelper.createNumberItem("TestItem", 0));
832             } catch (ItemNotFoundException e) {
833             }
834
835             // Execution
836             NumberItem item = DataCreationHelper.createNumberItem("TestItem", 10.1);
837             DecimalType historicState = new DecimalType(11110.1);
838             ZonedDateTime now = ZonedDateTime.now();
839             service.store(item, now, historicState);
840
841             // Verification
842             MongoCollection<Document> collection = database.getCollection("TestItem");
843             List<Document> documents = (ArrayList<Document>) collection.find().into(new ArrayList<>());
844
845             assertEquals(1, documents.size()); // Assert that there is only one document
846
847             Document insertedDocument = documents.get(0); // Get the first (and only) document
848
849             VerificationHelper.verifyDocument(insertedDocument, "TestItem", historicState);
850             assertEquals(Date.from(now.toInstant()), insertedDocument.get(MongoDBFields.FIELD_TIMESTAMP));
851         } finally {
852             dbContainer.stop();
853         }
854     }
855
856     /*
857      * Test the store method which stores a item state as well as a timestampe (ZonedDateTime) and check the result in
858      * the database
859      */
860     @Test
861     public void testStoreItemWithTimestampAndAlias() {
862         // Preparation
863         DatabaseTestContainer dbContainer = new DatabaseTestContainer(new MemoryBackend());
864         try {
865             SetupResult setupResult = DataCreationHelper.setupMongoDB(null, dbContainer);
866             MongoDBPersistenceService service = setupResult.service;
867             MongoDatabase database = setupResult.database;
868
869             service.activate(setupResult.bundleContext, setupResult.config);
870             try {
871                 Mockito.when(setupResult.itemRegistry.getItem("TestItem"))
872                         .thenReturn(DataCreationHelper.createNumberItem("TestItem", 0));
873             } catch (ItemNotFoundException e) {
874             }
875
876             // Execution
877             NumberItem item = DataCreationHelper.createNumberItem("TestItem", 10.1);
878             DecimalType historicState = new DecimalType(11110.1);
879             ZonedDateTime now = ZonedDateTime.now();
880             service.store(item, now, historicState, "AliasName");
881
882             // Verification
883             MongoCollection<Document> collection = database.getCollection("TestItem");
884             List<Document> documents = (ArrayList<Document>) collection.find().into(new ArrayList<>());
885
886             assertEquals(1, documents.size()); // Assert that there is only one document
887
888             Document insertedDocument = documents.get(0); // Get the first (and only) document
889
890             VerificationHelper.verifyDocumentWithAlias(insertedDocument, "AliasName", "TestItem", historicState);
891             assertEquals(Date.from(now.toInstant()), insertedDocument.get(MongoDBFields.FIELD_TIMESTAMP));
892         } finally {
893             dbContainer.stop();
894         }
895     }
896
897     /*
898      * Test the remove method to remove one item from the database
899      */
900     @Test
901     public void testremoveOneItem() {
902         // Preparation
903         DatabaseTestContainer dbContainer = new DatabaseTestContainer(new MemoryBackend());
904         try {
905             SetupResult setupResult = DataCreationHelper.setupMongoDB("testcollection", dbContainer);
906             MongoDBPersistenceService service = setupResult.service;
907             MongoDatabase database = setupResult.database;
908
909             service.activate(setupResult.bundleContext, setupResult.config);
910
911             for (double i = 0; i < 10.00; i += 0.3) {
912                 service.store(DataCreationHelper.createNumberItem("TestItem", i));
913             }
914             service.store(DataCreationHelper.createNumberItem("TestItemOther", 10.1));
915
916             // Execution
917             service.remove(DataCreationHelper.createFilterCriteria("TestItem", null, null));
918
919             // Verification
920             MongoCollection<Document> collection = database.getCollection("testcollection");
921
922             List<Document> documents = (ArrayList<Document>) collection.find().into(new ArrayList<>());
923
924             assertEquals(1, documents.size()); // Assert that there is the other document
925
926             VerificationHelper.verifyDocument(documents.get(0), "TestItemOther", 10.1);
927         } finally {
928             dbContainer.stop();
929         }
930     }
931
932     /*
933      * Test the remove method to remove values of a given timerange for one item
934      */
935     @Test
936     public void testremoveATimeRangeFromOneItem() {
937         // Preparation
938         DatabaseTestContainer dbContainer = new DatabaseTestContainer(new MemoryBackend());
939         try {
940             SetupResult setupResult = DataCreationHelper.setupMongoDB("testcollection", dbContainer);
941             MongoDBPersistenceService service = setupResult.service;
942             MongoDatabase database = setupResult.database;
943
944             service.activate(setupResult.bundleContext, setupResult.config);
945
946             List<PersistenceTestItem> testDataList = DataCreationHelper.createTestData(service, "TestItem",
947                     "TestItemOther");
948
949             // Execution
950             // Calculate the start and end dates
951             ZonedDateTime startDate = ZonedDateTime.now().plusDays(3).truncatedTo(ChronoUnit.DAYS);
952             ZonedDateTime endDate = ZonedDateTime.now().plusDays(17).truncatedTo(ChronoUnit.DAYS).plusDays(1)
953                     .minusNanos(1);
954
955             // Create the filter and remove the data
956             service.remove(DataCreationHelper.createFilterCriteria("TestItem", startDate, endDate));
957
958             // Verification
959             MongoCollection<Document> collection = database.getCollection("testcollection");
960
961             // Query the database for all data points
962             List<Document> documents = (ArrayList<Document>) collection.find().into(new ArrayList<>());
963
964             // Create a set of the returned data points
965             Set<PersistenceTestItem> returnedData = documents.stream()
966                     .map(doc -> new PersistenceTestItem(doc.getString(MongoDBFields.FIELD_ITEM),
967                             ZonedDateTime.ofInstant(doc.getDate(MongoDBFields.FIELD_TIMESTAMP).toInstant(),
968                                     ZoneId.systemDefault()),
969                             doc.getDouble(MongoDBFields.FIELD_VALUE)))
970                     .collect(Collectors.toSet());
971
972             // Create a set of the expected data points
973             Set<PersistenceTestItem> expectedData = testDataList
974                     .stream().filter(testData -> !(testData.itemName.equals("TestItem")
975                             && testData.date.isAfter(startDate) && testData.date.isBefore(endDate)))
976                     .collect(Collectors.toSet());
977
978             for (PersistenceTestItem expectedItem : expectedData) {
979                 // Assert that this item is in the returned data
980                 assertTrue(returnedData.contains(expectedItem),
981                         "Expected item not found in returned data: " + expectedItem);
982             }
983
984             // Iterate over the returned data
985             for (PersistenceTestItem returnedItem : returnedData) {
986                 // Assert that this item is in the expected data
987                 assertTrue(expectedData.contains(returnedItem),
988                         "Unexpected item found in returned data: " + returnedItem);
989             }
990         } finally {
991             dbContainer.stop();
992         }
993     }
994 }