2 * Copyright (c) 2010-2023 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.dynamodb.internal;
15 import static org.junit.jupiter.api.Assertions.*;
16 import static org.junit.jupiter.api.Assumptions.assumeTrue;
18 import java.util.ArrayList;
19 import java.util.List;
20 import java.util.concurrent.ExecutionException;
21 import java.util.concurrent.ExecutorService;
22 import java.util.concurrent.Executors;
24 import org.eclipse.jdt.annotation.NonNull;
25 import org.eclipse.jdt.annotation.NonNullByDefault;
26 import org.junit.jupiter.api.Test;
27 import org.openhab.core.library.items.NumberItem;
28 import org.openhab.core.library.types.DecimalType;
29 import org.openhab.core.persistence.FilterCriteria;
31 import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient;
35 * @author Sami Salonen - Initial contribution
39 public class DynamoDBTableNameResolverTest extends BaseIntegrationTest {
41 public static final boolean LEGACY_MODE = false; // not relevant for these tests but required by BaseIntegrationTest
44 public void testLegacyWithDynamoDBBigDecimalItem() {
45 assertEquals("integration-tests-bigdecimal",
46 new DynamoDBTableNameResolver(ExpectedTableSchema.LEGACY, "", "integration-tests-")
47 .fromItem(new DynamoDBBigDecimalItem()));
51 public void testLegacyWithDynamoDBStringItem() {
52 assertEquals("integration-tests-string",
53 new DynamoDBTableNameResolver(ExpectedTableSchema.LEGACY, "", "integration-tests-")
54 .fromItem(new DynamoDBStringItem()));
58 public void testWithDynamoDBBigDecimalItem() {
59 assertEquals("integration-tests",
60 new DynamoDBTableNameResolver(ExpectedTableSchema.NEW, "integration-tests", "")
61 .fromItem(new DynamoDBBigDecimalItem()));
65 public void testWithDynamoDBStringItem() {
66 assertEquals("integration-tests",
67 new DynamoDBTableNameResolver(ExpectedTableSchema.NEW, "integration-tests", "")
68 .fromItem(new DynamoDBStringItem()));
72 public void testBothLegacyAndNewParametersNeedToBeSpecifiedWithUnclearTableSchema() {
73 assertThrows(IllegalArgumentException.class, () -> {
74 assertEquals("integration-tests",
75 new DynamoDBTableNameResolver(ExpectedTableSchema.MAYBE_LEGACY, "integration-tests", "")
76 .fromItem(new DynamoDBStringItem()));
78 assertThrows(IllegalArgumentException.class, () -> {
79 assertEquals("integration-tests", new DynamoDBTableNameResolver(ExpectedTableSchema.MAYBE_LEGACY, "", "bb")
80 .fromItem(new DynamoDBStringItem()));
85 public void testResolveLegacyTablesPresent() throws InterruptedException {
86 // Run test only with embedded server. Otherwise there is risk of writing data using default table names
87 assumeTrue(embeddedServer != null);
88 ExecutorService executor = Executors.newFixedThreadPool(2);
90 DynamoDBPersistenceService maybeLegacyService = null;
91 final DynamoDBPersistenceService legacyService = newService(true, true, null, DynamoDBConfig.DEFAULT_TABLE_NAME,
92 DynamoDBConfig.DEFAULT_TABLE_PREFIX);
93 DynamoDBTableNameResolver tableNameResolver = legacyService.getTableNameResolver();
94 assertNotNull(tableNameResolver);
95 assert tableNameResolver != null; // to get rid of null warning...
96 assertEquals(ExpectedTableSchema.LEGACY, tableNameResolver.getTableSchema());
98 NumberItem item = (@NonNull NumberItem) ITEMS.get("number");
99 final FilterCriteria criteria = new FilterCriteria();
100 criteria.setItemName(item.getName());
103 // Old tables do not exit --> resolves to new schema
104 assertEquals(ExpectedTableSchema.NEW, resolveMaybeLegacy(legacyService, executor));
106 // Write data using legacy tables
107 item.setState(new DecimalType(0));
108 legacyService.store(item);
110 // Since table exist now, DynamoDBTableNameResolver should resolve
111 waitForAssert(() -> {
112 // Old tables are now there --> should resolve to old schema
113 assertEquals(ExpectedTableSchema.LEGACY, resolveMaybeLegacy(legacyService, executor));
116 // Create 2 new services, with unknown schemas (MAYBE_LEGACY), pointing to same database
117 maybeLegacyService = newService(null, false, legacyService.getEndpointOverride(), null, null);
118 DynamoDBTableNameResolver maybeLegacyServiceTableNameResolver = maybeLegacyService.getTableNameResolver();
119 assertNotNull(maybeLegacyServiceTableNameResolver);
120 assert maybeLegacyServiceTableNameResolver != null; // to get rid of null warning...
121 assertEquals(ExpectedTableSchema.MAYBE_LEGACY, maybeLegacyServiceTableNameResolver.getTableSchema());
122 assertEquals(legacyService.getEndpointOverride(), maybeLegacyService.getEndpointOverride());
124 // maybeLegacyService2 still does not know the schema
125 assertEquals(ExpectedTableSchema.MAYBE_LEGACY, maybeLegacyServiceTableNameResolver.getTableSchema());
126 // ... but it will be resolved automatically on query
127 final DynamoDBPersistenceService maybeLegacyServiceFinal = maybeLegacyService;
128 waitForAssert(() -> {
129 assertEquals(1, asList(maybeLegacyServiceFinal.query(criteria)).size());
130 // also the schema gets resolved
131 assertEquals(ExpectedTableSchema.LEGACY, maybeLegacyServiceTableNameResolver.getTableSchema());
136 if (maybeLegacyService != null) {
137 maybeLegacyService.deactivate();
139 legacyService.deactivate();
145 * @param legacyService service that has the client to use
149 private ExpectedTableSchema resolveMaybeLegacy(DynamoDBPersistenceService legacyService, ExecutorService executor) {
150 DynamoDBTableNameResolver resolver = new DynamoDBTableNameResolver(ExpectedTableSchema.MAYBE_LEGACY,
151 DynamoDBConfig.DEFAULT_TABLE_NAME, DynamoDBConfig.DEFAULT_TABLE_PREFIX);
152 assertFalse(resolver.isFullyResolved());
154 DynamoDbAsyncClient localClient = legacyService.getLowLevelClient();
155 if (localClient == null) {
156 fail("local client is null");
157 throw new RuntimeException();
159 boolean resolved = resolver
160 .resolveSchema(localClient, b -> b.overrideConfiguration(legacyService::overrideConfig), executor)
162 assertTrue(resolved);
163 return resolver.getTableSchema();
164 } catch (InterruptedException | ExecutionException e) {
165 fail(e.getMessage());
166 throw new IllegalStateException(); // Make compiler happy
170 private static <T> List<T> asList(Iterable<T> iterable) {
171 var items = new ArrayList<T>();
172 for (T item : iterable) {