2 * Copyright (c) 2010-2024 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.persistence.inmemory.internal;
15 import static org.hamcrest.MatcherAssert.assertThat;
16 import static org.hamcrest.Matchers.*;
17 import static org.mockito.Mockito.when;
19 import java.time.ZoneId;
20 import java.time.ZonedDateTime;
21 import java.util.ArrayList;
22 import java.util.Comparator;
23 import java.util.List;
24 import java.util.TreeSet;
26 import org.eclipse.jdt.annotation.NonNullByDefault;
27 import org.junit.jupiter.api.BeforeEach;
28 import org.junit.jupiter.api.Test;
29 import org.junit.jupiter.api.extension.ExtendWith;
30 import org.mockito.Mock;
31 import org.mockito.junit.jupiter.MockitoExtension;
32 import org.mockito.junit.jupiter.MockitoSettings;
33 import org.mockito.quality.Strictness;
34 import org.openhab.core.items.GenericItem;
35 import org.openhab.core.library.types.DecimalType;
36 import org.openhab.core.library.types.HSBType;
37 import org.openhab.core.library.types.PercentType;
38 import org.openhab.core.library.types.StringType;
39 import org.openhab.core.persistence.FilterCriteria;
40 import org.openhab.core.persistence.HistoricItem;
41 import org.openhab.core.types.State;
44 * The {@link InMemoryPersistenceTests} contains tests for the {@link InMemoryPersistenceService}
46 * @author Jan N. Klug - Initial contribution
48 @ExtendWith(MockitoExtension.class)
49 @MockitoSettings(strictness = Strictness.LENIENT)
51 public class InMemoryPersistenceTests {
52 private static final String ITEM_NAME = "testItem";
53 private static final String ALIAS = "alias";
55 private @NonNullByDefault({}) InMemoryPersistenceService service;
56 private @NonNullByDefault({}) @Mock GenericItem item;
58 private @NonNullByDefault({}) FilterCriteria filterCriteria;
62 when(item.getName()).thenReturn(ITEM_NAME);
64 filterCriteria = new FilterCriteria();
65 filterCriteria.setItemName(ITEM_NAME);
67 service = new InMemoryPersistenceService();
71 public void storeDirect() {
72 State state = new DecimalType(1);
73 when(item.getState()).thenReturn(state);
75 ZonedDateTime expectedTime = ZonedDateTime.now();
78 TreeSet<HistoricItem> storedStates = new TreeSet<>(Comparator.comparing(HistoricItem::getTimestamp));
79 service.query(filterCriteria).forEach(storedStates::add);
81 assertThat(storedStates, hasSize(1));
82 assertThat(storedStates.first().getName(), is(ITEM_NAME));
83 assertThat(storedStates.first().getState(), is(state));
84 assertThat((double) storedStates.first().getTimestamp().toEpochSecond(),
85 is(closeTo(expectedTime.toEpochSecond(), 2)));
89 public void storeAlias() {
90 State state = new PercentType(1);
91 when(item.getState()).thenReturn(state);
93 ZonedDateTime expectedTime = ZonedDateTime.now();
94 service.store(item, ALIAS);
96 TreeSet<HistoricItem> storedStates = new TreeSet<>(Comparator.comparing(HistoricItem::getTimestamp));
98 // query with item name should return nothing
99 service.query(filterCriteria).forEach(storedStates::add);
100 assertThat(storedStates, is(empty()));
102 filterCriteria.setItemName(ALIAS);
103 service.query(filterCriteria).forEach(storedStates::add);
105 assertThat(storedStates.size(), is(1));
106 assertThat(storedStates.first().getName(), is(ALIAS));
107 assertThat(storedStates.first().getState(), is(state));
108 assertThat((double) storedStates.first().getTimestamp().toEpochSecond(),
109 is(closeTo(expectedTime.toEpochSecond(), 2)));
113 public void storeHistoric() {
114 State state = new HSBType("120,100,100");
115 when(item.getState()).thenReturn(state);
117 State historicState = new HSBType("40,50,50");
118 ZonedDateTime expectedTime = ZonedDateTime.of(2022, 05, 31, 10, 0, 0, 0, ZoneId.systemDefault());
119 service.store(item, expectedTime, historicState);
121 TreeSet<HistoricItem> storedStates = new TreeSet<>(Comparator.comparing(HistoricItem::getTimestamp));
122 service.query(filterCriteria).forEach(storedStates::add);
124 assertThat(storedStates, hasSize(1));
125 assertThat(storedStates.first().getName(), is(ITEM_NAME));
126 assertThat(storedStates.first().getState(), is(historicState));
127 assertThat(storedStates.first().getTimestamp(), is(expectedTime));
131 public void queryWithoutItemNameReturnsEmptyList() {
132 TreeSet<HistoricItem> storedStates = new TreeSet<>(Comparator.comparing(HistoricItem::getTimestamp));
133 service.query(new FilterCriteria()).forEach(storedStates::add);
135 assertThat(storedStates, is(empty()));
139 public void queryUnknownItemReturnsEmptyList() {
140 TreeSet<HistoricItem> storedStates = new TreeSet<>(Comparator.comparing(HistoricItem::getTimestamp));
141 service.query(filterCriteria).forEach(storedStates::add);
143 assertThat(storedStates, is(empty()));
147 public void querySupportsAscendingOrdering() {
148 ZonedDateTime start = ZonedDateTime.of(2020, 12, 1, 12, 0, 0, 0, ZoneId.systemDefault());
149 service.store(item, start, new DecimalType(1));
150 service.store(item, start.plusHours(1), new DecimalType(2));
151 service.store(item, start.plusHours(2), new DecimalType(3));
153 filterCriteria.setOrdering(FilterCriteria.Ordering.ASCENDING);
154 filterCriteria.setBeginDate(start);
156 List<Integer> resultSet = new ArrayList<>();
157 service.query(filterCriteria).forEach(h -> resultSet.add(((DecimalType) h.getState()).intValue()));
159 assertThat(resultSet, contains(1, 2, 3));
163 public void querySupportsDescendingOrdering() {
164 ZonedDateTime start = ZonedDateTime.of(2020, 12, 1, 12, 0, 0, 0, ZoneId.systemDefault());
165 service.store(item, start, new DecimalType(1));
166 service.store(item, start.plusHours(1), new DecimalType(2));
167 service.store(item, start.plusHours(2), new DecimalType(3));
169 filterCriteria.setOrdering(FilterCriteria.Ordering.DESCENDING);
170 filterCriteria.setBeginDate(start);
172 List<Integer> resultSet = new ArrayList<>();
173 service.query(filterCriteria).forEach(h -> resultSet.add(((DecimalType) h.getState()).intValue()));
175 assertThat(resultSet, contains(3, 2, 1));
179 public void removeBetweenTimes() {
180 State historicState1 = new StringType("value1");
181 State historicState2 = new StringType("value2");
182 State historicState3 = new StringType("value3");
184 ZonedDateTime expectedTime = ZonedDateTime.of(2022, 05, 31, 10, 0, 0, 0, ZoneId.systemDefault());
185 service.store(item, expectedTime, historicState1);
186 service.store(item, expectedTime.plusHours(2), historicState2);
187 service.store(item, expectedTime.plusHours(4), historicState3);
189 // ensure both are stored
190 TreeSet<HistoricItem> storedStates = new TreeSet<>(Comparator.comparing(HistoricItem::getTimestamp));
191 service.query(filterCriteria).forEach(storedStates::add);
193 assertThat(storedStates, hasSize(3));
195 filterCriteria.setBeginDate(expectedTime.plusHours(1));
196 filterCriteria.setEndDate(expectedTime.plusHours(3));
197 service.remove(filterCriteria);
199 filterCriteria = new FilterCriteria();
200 filterCriteria.setItemName(ITEM_NAME);
201 storedStates.clear();
202 service.query(filterCriteria).forEach(storedStates::add);
204 assertThat(storedStates, hasSize(2));
206 assertThat(storedStates.first().getName(), is(ITEM_NAME));
207 assertThat(storedStates.first().getState(), is(historicState1));
208 assertThat(storedStates.first().getTimestamp(), is(expectedTime));
210 assertThat(storedStates.last().getName(), is(ITEM_NAME));
211 assertThat(storedStates.last().getState(), is(historicState3));
212 assertThat(storedStates.last().getTimestamp(), is(expectedTime.plusHours(4)));