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