Commit 37d3b424f969642e2eb7d76ac6e19afd81e3a58b
Committed by
GitHub
Merge pull request #5364 from smatvienko-tb/relation-query-improvements
Relation query improvements (handle many relations between two entities, break loops, limit max level)
Showing
17 changed files
with
497 additions
and
53 deletions
1 | +/** | |
2 | + * Copyright © 2016-2021 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.common.data.id; | |
17 | + | |
18 | +import org.junit.Assert; | |
19 | +import org.junit.Test; | |
20 | + | |
21 | +public class EntityIdTest { | |
22 | + | |
23 | + @Test | |
24 | + public void givenConstantNullUuid_whenCompare_thenToStringEqualsPredefinedUuid() { | |
25 | + Assert.assertEquals("13814000-1dd2-11b2-8080-808080808080", EntityId.NULL_UUID.toString()); | |
26 | + } | |
27 | + | |
28 | +} | |
\ No newline at end of file | ... | ... |
... | ... | @@ -202,6 +202,11 @@ |
202 | 202 | <artifactId>spring-boot-starter-data-jpa</artifactId> |
203 | 203 | </dependency> |
204 | 204 | <dependency> |
205 | + <groupId>org.springframework.boot</groupId> | |
206 | + <artifactId>spring-boot-starter-test</artifactId> | |
207 | + <scope>test</scope> | |
208 | + </dependency> | |
209 | + <dependency> | |
205 | 210 | <groupId>org.springframework</groupId> |
206 | 211 | <artifactId>spring-test</artifactId> |
207 | 212 | <scope>test</scope> |
... | ... | @@ -212,6 +217,16 @@ |
212 | 217 | <scope>test</scope> |
213 | 218 | </dependency> |
214 | 219 | <dependency> |
220 | + <groupId>org.testcontainers</groupId> | |
221 | + <artifactId>postgresql</artifactId> | |
222 | + <scope>test</scope> | |
223 | + </dependency> | |
224 | + <dependency> | |
225 | + <groupId>org.testcontainers</groupId> | |
226 | + <artifactId>jdbc</artifactId> | |
227 | + <scope>test</scope> | |
228 | + </dependency> | |
229 | + <dependency> | |
215 | 230 | <groupId>org.springframework</groupId> |
216 | 231 | <artifactId>spring-context-support</artifactId> |
217 | 232 | </dependency> |
... | ... | @@ -239,7 +254,14 @@ |
239 | 254 | <artifactId>maven-surefire-plugin</artifactId> |
240 | 255 | <version>${surfire.version}</version> |
241 | 256 | <configuration> |
257 | + <excludes> | |
258 | + <exclude>**/sql/*Test.java</exclude> | |
259 | + <exclude>**/sql/*/*Test.java</exclude> | |
260 | + <exclude>**/psql/*Test.java</exclude> | |
261 | + <exclude>**/nosql/*Test.java</exclude> | |
262 | + </excludes> | |
242 | 263 | <includes> |
264 | + <include>**/*Test.java</include> | |
243 | 265 | <include>**/*TestSuite.java</include> |
244 | 266 | </includes> |
245 | 267 | </configuration> | ... | ... |
... | ... | @@ -223,6 +223,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { |
223 | 223 | private static final String SELECT_API_USAGE_STATE = "(select aus.id, aus.created_time, aus.tenant_id, aus.entity_id, " + |
224 | 224 | "coalesce((select title from tenant where id = aus.entity_id), (select title from customer where id = aus.entity_id)) as name " + |
225 | 225 | "from api_usage_state as aus)"; |
226 | + static final int MAX_LEVEL_DEFAULT = 50; //This value has to be reasonable small to prevent infinite recursion as early as possible | |
226 | 227 | |
227 | 228 | static { |
228 | 229 | entityTableMap.put(EntityType.ASSET, "asset"); |
... | ... | @@ -239,18 +240,30 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { |
239 | 240 | public static EntityType[] RELATION_QUERY_ENTITY_TYPES = new EntityType[]{ |
240 | 241 | EntityType.TENANT, EntityType.CUSTOMER, EntityType.USER, EntityType.DASHBOARD, EntityType.ASSET, EntityType.DEVICE, EntityType.ENTITY_VIEW}; |
241 | 242 | |
242 | - private static final String HIERARCHICAL_QUERY_TEMPLATE = " FROM (WITH RECURSIVE related_entities(from_id, from_type, to_id, to_type, relation_type, lvl) AS (" + | |
243 | - " SELECT from_id, from_type, to_id, to_type, relation_type, 1 as lvl" + | |
244 | - " FROM relation" + | |
243 | + private static final String HIERARCHICAL_QUERY_TEMPLATE = " FROM (WITH RECURSIVE related_entities(from_id, from_type, to_id, to_type, lvl, path) AS (" + | |
244 | + " SELECT from_id, from_type, to_id, to_type," + | |
245 | + " 1 as lvl," + | |
246 | + " ARRAY[$in_id] as path" + // initial path | |
247 | + " FROM relation " + | |
245 | 248 | " WHERE $in_id = :relation_root_id and $in_type = :relation_root_type and relation_type_group = 'COMMON'" + |
249 | + " GROUP BY from_id, from_type, to_id, to_type, lvl, path" + | |
246 | 250 | " UNION ALL" + |
247 | - " SELECT r.from_id, r.from_type, r.to_id, r.to_type, r.relation_type, lvl + 1" + | |
251 | + " SELECT r.from_id, r.from_type, r.to_id, r.to_type," + | |
252 | + " (re.lvl + 1) as lvl, " + | |
253 | + " (re.path || ARRAY[r.$in_id]) as path" + | |
248 | 254 | " FROM relation r" + |
249 | 255 | " INNER JOIN related_entities re ON" + |
250 | 256 | " r.$in_id = re.$out_id and r.$in_type = re.$out_type and" + |
251 | - " relation_type_group = 'COMMON' %s)" + | |
252 | - " SELECT re.$out_id entity_id, re.$out_type entity_type, max(re.lvl) lvl" + | |
253 | - " from related_entities re" + | |
257 | + " relation_type_group = 'COMMON' " + | |
258 | + " AND r.$in_id NOT IN (SELECT * FROM unnest(re.path)) " + | |
259 | + " %s" + | |
260 | + " GROUP BY r.from_id, r.from_type, r.to_id, r.to_type, (re.lvl + 1), (re.path || ARRAY[r.$in_id])" + | |
261 | + " )" + | |
262 | + " SELECT re.$out_id entity_id, re.$out_type entity_type, max(r_int.lvl) lvl" + | |
263 | + " from related_entities r_int" + | |
264 | + " INNER JOIN relation re ON re.from_id = r_int.from_id AND re.from_type = r_int.from_type" + | |
265 | + " AND re.to_id = r_int.to_id AND re.to_type = r_int.to_type" + | |
266 | + " AND re.relation_type_group = 'COMMON'" + | |
254 | 267 | " %s GROUP BY entity_id, entity_type) entity"; |
255 | 268 | private static final String HIERARCHICAL_TO_QUERY_TEMPLATE = HIERARCHICAL_QUERY_TEMPLATE.replace("$in", "to").replace("$out", "from"); |
256 | 269 | private static final String HIERARCHICAL_FROM_QUERY_TEMPLATE = HIERARCHICAL_QUERY_TEMPLATE.replace("$in", "from").replace("$out", "to"); |
... | ... | @@ -580,7 +593,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { |
580 | 593 | .append("nr.").append(fromOrTo).append("_type").append(" = re.").append(toOrFrom).append("_type"); |
581 | 594 | |
582 | 595 | notExistsPart.append(")"); |
583 | - whereFilter += " and ( re.lvl = " + entityFilter.getMaxLevel() + " OR " + notExistsPart.toString() + ")"; | |
596 | + whereFilter += " and ( r_int.lvl = " + entityFilter.getMaxLevel() + " OR " + notExistsPart.toString() + ")"; | |
584 | 597 | } |
585 | 598 | from = String.format(from, lvlFilter, whereFilter); |
586 | 599 | String query = "( " + selectFields + from + ")"; |
... | ... | @@ -659,7 +672,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { |
659 | 672 | .append(whereFilter.toString().replaceAll("re\\.", "nr\\.")); |
660 | 673 | |
661 | 674 | notExistsPart.append(")"); |
662 | - whereFilter.append(" and ( re.lvl = ").append(entityFilter.getMaxLevel()).append(" OR ").append(notExistsPart.toString()).append(")"); | |
675 | + whereFilter.append(" and ( r_int.lvl = ").append(entityFilter.getMaxLevel()).append(" OR ").append(notExistsPart.toString()).append(")"); | |
663 | 676 | } |
664 | 677 | from = String.format(from, lvlFilter, " WHERE " + whereFilter); |
665 | 678 | return "( " + selectFields + from + ")"; |
... | ... | @@ -693,8 +706,12 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { |
693 | 706 | return whereFilter.toString(); |
694 | 707 | } |
695 | 708 | |
696 | - private String getLvlFilter(int maxLevel) { | |
697 | - return maxLevel > 0 ? ("and lvl <= " + (maxLevel - 1)) : ""; | |
709 | + String getLvlFilter(int maxLevel) { | |
710 | + return "and re.lvl <= " + (getMaxLevel(maxLevel) - 1); | |
711 | + } | |
712 | + | |
713 | + int getMaxLevel(int maxLevel) { | |
714 | + return maxLevel > 0 ? maxLevel : MAX_LEVEL_DEFAULT; | |
698 | 715 | } |
699 | 716 | |
700 | 717 | private String getQueryTemplate(EntitySearchDirection direction) { | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2021 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.dao; | |
17 | + | |
18 | +import org.junit.extensions.cpsuite.ClasspathSuite; | |
19 | +import org.junit.extensions.cpsuite.ClasspathSuite.ClassnameFilters; | |
20 | +import org.junit.runner.RunWith; | |
21 | + | |
22 | +@RunWith(ClasspathSuite.class) | |
23 | +@ClassnameFilters({ | |
24 | + "org.thingsboard.server.dao.service.psql.*SqlTest", | |
25 | + "org.thingsboard.server.dao.service.attributes.psql.*SqlTest", | |
26 | + "org.thingsboard.server.dao.service.event.psql.*SqlTest", | |
27 | + "org.thingsboard.server.dao.service.timeseries.psql.*SqlTest" | |
28 | +}) | |
29 | +public class PostgreSqlDaoServiceTestSuite { | |
30 | +} | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2021 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.dao; | |
17 | + | |
18 | +import com.google.common.base.Charsets; | |
19 | +import com.google.common.io.Resources; | |
20 | +import lombok.extern.slf4j.Slf4j; | |
21 | + | |
22 | +import java.io.IOException; | |
23 | +import java.net.URL; | |
24 | +import java.sql.Connection; | |
25 | +import java.sql.SQLException; | |
26 | +import java.util.List; | |
27 | + | |
28 | +@Slf4j | |
29 | +public class PostgreSqlInitializer { | |
30 | + | |
31 | + private static final List<String> sqlFiles = List.of( | |
32 | + "sql/schema-ts-psql.sql", | |
33 | + "sql/schema-entities.sql", | |
34 | + "sql/schema-entities-idx.sql", | |
35 | + "sql/system-data.sql", | |
36 | + "sql/system-test-psql.sql"); | |
37 | + private static final String dropAllTablesSqlFile = "sql/psql/drop-all-tables.sql"; | |
38 | + | |
39 | + public static void initDb(Connection conn) { | |
40 | + cleanUpDb(conn); | |
41 | + log.info("initialize Postgres DB..."); | |
42 | + try { | |
43 | + for (String sqlFile : sqlFiles) { | |
44 | + URL sqlFileUrl = Resources.getResource(sqlFile); | |
45 | + String sql = Resources.toString(sqlFileUrl, Charsets.UTF_8); | |
46 | + conn.createStatement().execute(sql); | |
47 | + } | |
48 | + } catch (IOException | SQLException e) { | |
49 | + throw new RuntimeException("Unable to init the Postgres database. Reason: " + e.getMessage(), e); | |
50 | + } | |
51 | + log.info("Postgres DB is initialized!"); | |
52 | + } | |
53 | + | |
54 | + private static void cleanUpDb(Connection conn) { | |
55 | + log.info("clean up Postgres DB..."); | |
56 | + try { | |
57 | + URL dropAllTableSqlFileUrl = Resources.getResource(dropAllTablesSqlFile); | |
58 | + String dropAllTablesSql = Resources.toString(dropAllTableSqlFileUrl, Charsets.UTF_8); | |
59 | + conn.createStatement().execute(dropAllTablesSql); | |
60 | + } catch (IOException | SQLException e) { | |
61 | + throw new RuntimeException("Unable to clean up the Postgres database. Reason: " + e.getMessage(), e); | |
62 | + } | |
63 | + } | |
64 | +} | ... | ... |
... | ... | @@ -47,7 +47,7 @@ import java.util.stream.Collectors; |
47 | 47 | |
48 | 48 | import static org.thingsboard.server.common.data.ota.OtaPackageType.FIRMWARE; |
49 | 49 | |
50 | -public class BaseDeviceProfileServiceTest extends AbstractServiceTest { | |
50 | +public abstract class BaseDeviceProfileServiceTest extends AbstractServiceTest { | |
51 | 51 | |
52 | 52 | private IdComparator<DeviceProfile> idComparator = new IdComparator<>(); |
53 | 53 | private IdComparator<DeviceProfileInfo> deviceProfileInfoIdComparator = new IdComparator<>(); | ... | ... |
... | ... | @@ -17,14 +17,17 @@ package org.thingsboard.server.dao.service; |
17 | 17 | |
18 | 18 | import com.google.common.util.concurrent.Futures; |
19 | 19 | import com.google.common.util.concurrent.ListenableFuture; |
20 | +import lombok.extern.slf4j.Slf4j; | |
20 | 21 | import org.apache.commons.lang3.RandomStringUtils; |
21 | 22 | import org.apache.commons.lang3.RandomUtils; |
23 | +import org.hamcrest.Matchers; | |
22 | 24 | import org.junit.After; |
23 | 25 | import org.junit.Assert; |
24 | 26 | import org.junit.Before; |
25 | 27 | import org.junit.Test; |
26 | 28 | import org.springframework.beans.factory.annotation.Autowired; |
27 | 29 | import org.springframework.jdbc.core.JdbcTemplate; |
30 | +import org.springframework.jdbc.core.ResultSetExtractor; | |
28 | 31 | import org.thingsboard.server.common.data.DataConstants; |
29 | 32 | import org.thingsboard.server.common.data.Device; |
30 | 33 | import org.thingsboard.server.common.data.EntityType; |
... | ... | @@ -69,6 +72,7 @@ import org.thingsboard.server.common.data.relation.RelationEntityTypeFilter; |
69 | 72 | import org.thingsboard.server.common.data.relation.RelationTypeGroup; |
70 | 73 | import org.thingsboard.server.dao.attributes.AttributesService; |
71 | 74 | import org.thingsboard.server.dao.model.sqlts.ts.TsKvEntity; |
75 | +import org.thingsboard.server.dao.sql.relation.RelationRepository; | |
72 | 76 | import org.thingsboard.server.dao.timeseries.TimeseriesService; |
73 | 77 | |
74 | 78 | import java.util.ArrayList; |
... | ... | @@ -82,9 +86,13 @@ import java.util.stream.Collectors; |
82 | 86 | import java.util.stream.Stream; |
83 | 87 | |
84 | 88 | import static org.junit.Assert.assertEquals; |
89 | +import static org.hamcrest.MatcherAssert.assertThat; | |
85 | 90 | |
91 | +@Slf4j | |
86 | 92 | public abstract class BaseEntityServiceTest extends AbstractServiceTest { |
87 | 93 | |
94 | + static final int ENTITY_COUNT = 5; | |
95 | + | |
88 | 96 | @Autowired |
89 | 97 | private AttributesService attributesService; |
90 | 98 | |
... | ... | @@ -96,6 +104,9 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest { |
96 | 104 | @Autowired |
97 | 105 | private JdbcTemplate template; |
98 | 106 | |
107 | + @Autowired | |
108 | + private RelationRepository relationRepository; | |
109 | + | |
99 | 110 | @Before |
100 | 111 | public void before() { |
101 | 112 | Tenant tenant = new Tenant(); |
... | ... | @@ -110,7 +121,7 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest { |
110 | 121 | tenantService.deleteTenant(tenantId); |
111 | 122 | } |
112 | 123 | |
113 | - | |
124 | + | |
114 | 125 | @Test |
115 | 126 | public void testCountEntitiesByQuery() throws InterruptedException { |
116 | 127 | List<Device> devices = new ArrayList<>(); |
... | ... | @@ -154,12 +165,12 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest { |
154 | 165 | Assert.assertEquals(0, count); |
155 | 166 | } |
156 | 167 | |
157 | - | |
168 | + | |
158 | 169 | @Test |
159 | 170 | public void testCountHierarchicalEntitiesByQuery() throws InterruptedException { |
160 | 171 | List<Asset> assets = new ArrayList<>(); |
161 | 172 | List<Device> devices = new ArrayList<>(); |
162 | - createTestHierarchy(assets, devices, new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), new ArrayList<>()); | |
173 | + createTestHierarchy(tenantId, assets, devices, new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), new ArrayList<>()); | |
163 | 174 | |
164 | 175 | RelationsQueryFilter filter = new RelationsQueryFilter(); |
165 | 176 | filter.setRootEntity(tenantId); |
... | ... | @@ -168,7 +179,7 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest { |
168 | 179 | EntityCountQuery countQuery = new EntityCountQuery(filter); |
169 | 180 | |
170 | 181 | long count = entityService.countEntitiesByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), countQuery); |
171 | - Assert.assertEquals(30, count); | |
182 | + Assert.assertEquals(31, count); //due to the loop relations in hierarchy, the TenantId included in total count (1*Tenant + 5*Asset + 5*5*Devices = 31) | |
172 | 183 | |
173 | 184 | filter.setFilters(Collections.singletonList(new RelationEntityTypeFilter("Contains", Collections.singletonList(EntityType.DEVICE)))); |
174 | 185 | count = entityService.countEntitiesByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), countQuery); |
... | ... | @@ -304,11 +315,25 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest { |
304 | 315 | |
305 | 316 | @Test |
306 | 317 | public void testHierarchicalFindEntityDataWithAttributesByQuery() throws ExecutionException, InterruptedException { |
318 | + doTestHierarchicalFindEntityDataWithAttributesByQuery(0, false); | |
319 | + } | |
320 | + | |
321 | + @Test | |
322 | + public void testHierarchicalFindEntityDataWithAttributesByQueryWithLevel() throws ExecutionException, InterruptedException { | |
323 | + doTestHierarchicalFindEntityDataWithAttributesByQuery(2, false); | |
324 | + } | |
325 | + | |
326 | + @Test | |
327 | + public void testHierarchicalFindEntityDataWithAttributesByQueryWithLastLevelOnly() throws ExecutionException, InterruptedException { | |
328 | + doTestHierarchicalFindEntityDataWithAttributesByQuery(2, true); | |
329 | + } | |
330 | + | |
331 | + private void doTestHierarchicalFindEntityDataWithAttributesByQuery(final int maxLevel, final boolean fetchLastLevelOnly) throws ExecutionException, InterruptedException { | |
307 | 332 | List<Asset> assets = new ArrayList<>(); |
308 | 333 | List<Device> devices = new ArrayList<>(); |
309 | 334 | List<Long> temperatures = new ArrayList<>(); |
310 | 335 | List<Long> highTemperatures = new ArrayList<>(); |
311 | - createTestHierarchy(assets, devices, new ArrayList<>(), new ArrayList<>(), temperatures, highTemperatures); | |
336 | + createTestHierarchy(tenantId, assets, devices, new ArrayList<>(), new ArrayList<>(), temperatures, highTemperatures); | |
312 | 337 | |
313 | 338 | List<ListenableFuture<List<Void>>> attributeFutures = new ArrayList<>(); |
314 | 339 | for (int i = 0; i < devices.size(); i++) { |
... | ... | @@ -321,6 +346,8 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest { |
321 | 346 | filter.setRootEntity(tenantId); |
322 | 347 | filter.setDirection(EntitySearchDirection.FROM); |
323 | 348 | filter.setFilters(Collections.singletonList(new RelationEntityTypeFilter("Contains", Collections.singletonList(EntityType.DEVICE)))); |
349 | + filter.setMaxLevel(maxLevel); | |
350 | + filter.setFetchLastLevelOnly(fetchLastLevelOnly); | |
324 | 351 | |
325 | 352 | EntityDataSortOrder sortOrder = new EntityDataSortOrder( |
326 | 353 | new EntityKey(EntityKeyType.ENTITY_FIELD, "createdTime"), EntityDataSortOrder.Direction.ASC |
... | ... | @@ -373,14 +400,13 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest { |
373 | 400 | deviceService.deleteDevicesByTenantId(tenantId); |
374 | 401 | } |
375 | 402 | |
376 | - | |
377 | 403 | @Test |
378 | 404 | public void testHierarchicalFindDevicesWithAttributesByQuery() throws ExecutionException, InterruptedException { |
379 | 405 | List<Asset> assets = new ArrayList<>(); |
380 | 406 | List<Device> devices = new ArrayList<>(); |
381 | 407 | List<Long> temperatures = new ArrayList<>(); |
382 | 408 | List<Long> highTemperatures = new ArrayList<>(); |
383 | - createTestHierarchy(assets, devices, new ArrayList<>(), new ArrayList<>(), temperatures, highTemperatures); | |
409 | + createTestHierarchy(tenantId, assets, devices, new ArrayList<>(), new ArrayList<>(), temperatures, highTemperatures); | |
384 | 410 | |
385 | 411 | List<ListenableFuture<List<Void>>> attributeFutures = new ArrayList<>(); |
386 | 412 | for (int i = 0; i < devices.size(); i++) { |
... | ... | @@ -393,6 +419,8 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest { |
393 | 419 | filter.setRootEntity(tenantId); |
394 | 420 | filter.setDirection(EntitySearchDirection.FROM); |
395 | 421 | filter.setRelationType("Contains"); |
422 | + filter.setMaxLevel(2); | |
423 | + filter.setFetchLastLevelOnly(true); | |
396 | 424 | |
397 | 425 | EntityDataSortOrder sortOrder = new EntityDataSortOrder( |
398 | 426 | new EntityKey(EntityKeyType.ENTITY_FIELD, "createdTime"), EntityDataSortOrder.Direction.ASC |
... | ... | @@ -446,14 +474,14 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest { |
446 | 474 | deviceService.deleteDevicesByTenantId(tenantId); |
447 | 475 | } |
448 | 476 | |
449 | - | |
477 | + | |
450 | 478 | @Test |
451 | 479 | public void testHierarchicalFindAssetsWithAttributesByQuery() throws ExecutionException, InterruptedException { |
452 | 480 | List<Asset> assets = new ArrayList<>(); |
453 | 481 | List<Device> devices = new ArrayList<>(); |
454 | 482 | List<Long> consumptions = new ArrayList<>(); |
455 | 483 | List<Long> highConsumptions = new ArrayList<>(); |
456 | - createTestHierarchy(assets, devices, consumptions, highConsumptions, new ArrayList<>(), new ArrayList<>()); | |
484 | + createTestHierarchy(tenantId, assets, devices, consumptions, highConsumptions, new ArrayList<>(), new ArrayList<>()); | |
457 | 485 | |
458 | 486 | List<ListenableFuture<List<Void>>> attributeFutures = new ArrayList<>(); |
459 | 487 | for (int i = 0; i < assets.size(); i++) { |
... | ... | @@ -518,8 +546,8 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest { |
518 | 546 | deviceService.deleteDevicesByTenantId(tenantId); |
519 | 547 | } |
520 | 548 | |
521 | - private void createTestHierarchy(List<Asset> assets, List<Device> devices, List<Long> consumptions, List<Long> highConsumptions, List<Long> temperatures, List<Long> highTemperatures) throws InterruptedException { | |
522 | - for (int i = 0; i < 5; i++) { | |
549 | + private void createTestHierarchy(TenantId tenantId, List<Asset> assets, List<Device> devices, List<Long> consumptions, List<Long> highConsumptions, List<Long> temperatures, List<Long> highTemperatures) throws InterruptedException { | |
550 | + for (int i = 0; i < ENTITY_COUNT; i++) { | |
523 | 551 | Asset asset = new Asset(); |
524 | 552 | asset.setTenantId(tenantId); |
525 | 553 | asset.setName("Asset" + i); |
... | ... | @@ -529,18 +557,19 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest { |
529 | 557 | //TO make sure devices have different created time |
530 | 558 | Thread.sleep(1); |
531 | 559 | assets.add(asset); |
532 | - EntityRelation er = new EntityRelation(); | |
533 | - er.setFrom(tenantId); | |
534 | - er.setTo(asset.getId()); | |
535 | - er.setType("Manages"); | |
536 | - er.setTypeGroup(RelationTypeGroup.COMMON); | |
537 | - relationService.saveRelation(tenantId, er); | |
560 | + createRelation(tenantId, "Manages", tenantId, asset.getId()); | |
538 | 561 | long consumption = (long) (Math.random() * 100); |
539 | 562 | consumptions.add(consumption); |
540 | 563 | if (consumption > 50) { |
541 | 564 | highConsumptions.add(consumption); |
542 | 565 | } |
543 | - for (int j = 0; j < 5; j++) { | |
566 | + | |
567 | + //tenant -> asset : one-to-one but many edges | |
568 | + for (int n = 0; n < ENTITY_COUNT; n++) { | |
569 | + createRelation(tenantId, "UseCase-" + n, tenantId, asset.getId()); | |
570 | + } | |
571 | + | |
572 | + for (int j = 0; j < ENTITY_COUNT; j++) { | |
544 | 573 | Device device = new Device(); |
545 | 574 | device.setTenantId(tenantId); |
546 | 575 | device.setName("A" + i + "Device" + j); |
... | ... | @@ -550,22 +579,125 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest { |
550 | 579 | //TO make sure devices have different created time |
551 | 580 | Thread.sleep(1); |
552 | 581 | devices.add(device); |
553 | - er = new EntityRelation(); | |
554 | - er.setFrom(asset.getId()); | |
555 | - er.setTo(device.getId()); | |
556 | - er.setType("Contains"); | |
557 | - er.setTypeGroup(RelationTypeGroup.COMMON); | |
558 | - relationService.saveRelation(tenantId, er); | |
582 | + createRelation(tenantId, "Contains", asset.getId(), device.getId()); | |
559 | 583 | long temperature = (long) (Math.random() * 100); |
560 | 584 | temperatures.add(temperature); |
561 | 585 | if (temperature > 45) { |
562 | 586 | highTemperatures.add(temperature); |
563 | 587 | } |
588 | + | |
589 | + //asset -> device : one-to-one but many edges | |
590 | + for (int n = 0; n < ENTITY_COUNT; n++) { | |
591 | + createRelation(tenantId, "UseCase-" + n, asset.getId(), device.getId()); | |
592 | + } | |
593 | + } | |
594 | + } | |
595 | + | |
596 | + //asset -> device one-to-many shared with other assets | |
597 | + for (int n = 0; n < devices.size(); n = n + ENTITY_COUNT) { | |
598 | + createRelation(tenantId, "SharedWithAsset0", assets.get(0).getId(), devices.get(n).getId()); | |
599 | + } | |
600 | + | |
601 | + createManyCustomRelationsBetweenTwoNodes(tenantId, "UseCase", assets, devices); | |
602 | + createHorizontalRingRelations(tenantId, "Ring(Loop)-Ast", assets); | |
603 | + createLoopRelations(tenantId, "Loop-Tnt-Ast-Dev", tenantId, assets.get(0).getId(), devices.get(0).getId()); | |
604 | + createLoopRelations(tenantId, "Loop-Tnt-Ast", tenantId, assets.get(1).getId()); | |
605 | + createLoopRelations(tenantId, "Loop-Ast-Tnt-Ast", assets.get(2).getId(), tenantId, assets.get(3).getId()); | |
606 | + | |
607 | + //printAllRelations(); | |
608 | + } | |
609 | + | |
610 | + private ResultSetExtractor<List<List<String>>> getListResultSetExtractor() { | |
611 | + return rs -> { | |
612 | + List<List<String>> list = new ArrayList<>(); | |
613 | + final int columnCount = rs.getMetaData().getColumnCount(); | |
614 | + List<String> columns = new ArrayList<>(columnCount); | |
615 | + for (int i = 1; i <= columnCount; i++) { | |
616 | + columns.add(rs.getMetaData().getColumnName(i)); | |
617 | + } | |
618 | + list.add(columns); | |
619 | + while (rs.next()) { | |
620 | + List<String> data = new ArrayList<>(columnCount); | |
621 | + for (int i = 1; i <= columnCount; i++) { | |
622 | + data.add(rs.getString(i)); | |
623 | + } | |
624 | + list.add(data); | |
564 | 625 | } |
626 | + return list; | |
627 | + }; | |
628 | + } | |
629 | + | |
630 | + /* | |
631 | + * This useful to reproduce exact data in the PostgreSQL and play around with pgadmin query and analyze tool | |
632 | + * */ | |
633 | + private void printAllRelations() { | |
634 | + System.out.println("" + | |
635 | + "DO\n" + | |
636 | + "$$\n" + | |
637 | + " DECLARE\n" + | |
638 | + " someint integer;\n" + | |
639 | + " BEGIN\n" + | |
640 | + " DROP TABLE IF EXISTS relation_test;\n" + | |
641 | + " CREATE TABLE IF NOT EXISTS relation_test\n" + | |
642 | + " (\n" + | |
643 | + " from_id uuid,\n" + | |
644 | + " from_type varchar(255),\n" + | |
645 | + " to_id uuid,\n" + | |
646 | + " to_type varchar(255),\n" + | |
647 | + " relation_type_group varchar(255),\n" + | |
648 | + " relation_type varchar(255),\n" + | |
649 | + " additional_info varchar,\n" + | |
650 | + " CONSTRAINT relation_test_pkey PRIMARY KEY (from_id, from_type, relation_type_group, relation_type, to_id, to_type)\n" + | |
651 | + " );"); | |
652 | + | |
653 | + relationRepository.findAll().forEach(r -> | |
654 | + System.out.printf("INSERT INTO relation_test (from_id, from_type, to_id, to_type, relation_type_group, relation_type, additional_info)" + | |
655 | + " VALUES (%s, %s, %s, %s, %s, %s, %s);\n", | |
656 | + quote(r.getFromId()), quote(r.getFromType()), quote(r.getToId()), quote(r.getToType()), | |
657 | + quote(r.getRelationTypeGroup()), quote(r.getRelationType()), quote(r.getAdditionalInfo())) | |
658 | + ); | |
659 | + | |
660 | + System.out.println("" + | |
661 | + " END\n" + | |
662 | + "$$;"); | |
663 | + } | |
664 | + | |
665 | + private String quote(Object s) { | |
666 | + return s == null ? null : "'" + s + "'"; | |
667 | + } | |
668 | + | |
669 | + void createLoopRelations(TenantId tenantId, String type, EntityId... ids) { | |
670 | + assertThat("ids lenght", ids.length, Matchers.greaterThanOrEqualTo(1)); | |
671 | + //chain all from the head to the tail | |
672 | + for (int i = 1; i < ids.length; i++) { | |
673 | + relationService.saveRelation(tenantId, new EntityRelation(ids[i - 1], ids[i], type, RelationTypeGroup.COMMON)); | |
674 | + } | |
675 | + //chain tail -> head | |
676 | + relationService.saveRelation(tenantId, new EntityRelation(ids[ids.length - 1], ids[0], type, RelationTypeGroup.COMMON)); | |
677 | + } | |
678 | + | |
679 | + void createHorizontalRingRelations(TenantId tenantId, String type, List<Asset> assets) { | |
680 | + createLoopRelations(tenantId, type, assets.stream().map(Asset::getId).toArray(EntityId[]::new)); | |
681 | + } | |
682 | + | |
683 | + void createManyCustomRelationsBetweenTwoNodes(TenantId tenantId, String type, List<Asset> assets, List<Device> devices) { | |
684 | + for (int i = 1; i <= 5; i++) { | |
685 | + final String typeI = type + i; | |
686 | + createOneToManyRelations(tenantId, typeI, tenantId, assets.stream().map(Asset::getId).collect(Collectors.toList())); | |
687 | + assets.forEach(asset -> | |
688 | + createOneToManyRelations(tenantId, typeI, asset.getId(), devices.stream().map(Device::getId).collect(Collectors.toList()))); | |
565 | 689 | } |
566 | 690 | } |
567 | 691 | |
568 | - | |
692 | + void createOneToManyRelations(TenantId tenantId, String type, EntityId from, List<EntityId> toIds) { | |
693 | + toIds.forEach(toId -> createRelation(tenantId, type, from, toId)); | |
694 | + } | |
695 | + | |
696 | + void createRelation(TenantId tenantId, String type, EntityId from, EntityId toId) { | |
697 | + relationService.saveRelation(tenantId, new EntityRelation(from, toId, type, RelationTypeGroup.COMMON)); | |
698 | + } | |
699 | + | |
700 | + | |
569 | 701 | @Test |
570 | 702 | public void testSimpleFindEntityDataByQuery() throws InterruptedException { |
571 | 703 | List<Device> devices = new ArrayList<>(); |
... | ... | @@ -871,7 +1003,7 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest { |
871 | 1003 | } |
872 | 1004 | |
873 | 1005 | @Test |
874 | - public void testBuildNumericPredicateQueryOperations() throws ExecutionException, InterruptedException{ | |
1006 | + public void testBuildNumericPredicateQueryOperations() throws ExecutionException, InterruptedException { | |
875 | 1007 | |
876 | 1008 | List<Device> devices = new ArrayList<>(); |
877 | 1009 | List<Long> temperatures = new ArrayList<>(); |
... | ... | @@ -1031,7 +1163,7 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest { |
1031 | 1163 | |
1032 | 1164 | deviceService.deleteDevicesByTenantId(tenantId); |
1033 | 1165 | } |
1034 | - | |
1166 | + | |
1035 | 1167 | @Test |
1036 | 1168 | public void testFindEntityDataByQueryWithTimeseries() throws ExecutionException, InterruptedException { |
1037 | 1169 | |
... | ... | @@ -1122,7 +1254,7 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest { |
1122 | 1254 | } |
1123 | 1255 | |
1124 | 1256 | @Test |
1125 | - public void testBuildStringPredicateQueryOperations() throws ExecutionException, InterruptedException{ | |
1257 | + public void testBuildStringPredicateQueryOperations() throws ExecutionException, InterruptedException { | |
1126 | 1258 | |
1127 | 1259 | List<Device> devices = new ArrayList<>(); |
1128 | 1260 | List<String> attributeStrings = new ArrayList<>(); |
... | ... | @@ -1142,11 +1274,11 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest { |
1142 | 1274 | devices.add(deviceService.saveDevice(device)); |
1143 | 1275 | //TO make sure devices have different created time |
1144 | 1276 | Thread.sleep(1); |
1145 | - List<StringFilterPredicate.StringOperation> operationValues= Arrays.asList(StringFilterPredicate.StringOperation.values()); | |
1277 | + List<StringFilterPredicate.StringOperation> operationValues = Arrays.asList(StringFilterPredicate.StringOperation.values()); | |
1146 | 1278 | StringFilterPredicate.StringOperation operation = operationValues.get(new Random().nextInt(operationValues.size())); |
1147 | 1279 | String operationName = operation.name(); |
1148 | 1280 | attributeStrings.add(operationName); |
1149 | - switch(operation){ | |
1281 | + switch (operation) { | |
1150 | 1282 | case EQUAL: |
1151 | 1283 | equalStrings.add(operationName); |
1152 | 1284 | notContainsStrings.add(operationName); |
... | ... | @@ -1302,7 +1434,7 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest { |
1302 | 1434 | } |
1303 | 1435 | |
1304 | 1436 | @Test |
1305 | - public void testBuildStringPredicateQueryOperationsForEntityType() throws ExecutionException, InterruptedException{ | |
1437 | + public void testBuildStringPredicateQueryOperationsForEntityType() throws ExecutionException, InterruptedException { | |
1306 | 1438 | |
1307 | 1439 | List<Device> devices = new ArrayList<>(); |
1308 | 1440 | |
... | ... | @@ -1419,7 +1551,7 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest { |
1419 | 1551 | } |
1420 | 1552 | |
1421 | 1553 | @Test |
1422 | - public void testBuildSimplePredicateQueryOperations() throws InterruptedException{ | |
1554 | + public void testBuildSimplePredicateQueryOperations() throws InterruptedException { | |
1423 | 1555 | |
1424 | 1556 | List<Device> devices = new ArrayList<>(); |
1425 | 1557 | |
... | ... | @@ -1492,7 +1624,7 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest { |
1492 | 1624 | return loadedEntities; |
1493 | 1625 | } |
1494 | 1626 | |
1495 | - private List<KeyFilter> createStringKeyFilters(String key, EntityKeyType keyType, StringFilterPredicate.StringOperation operation, String value){ | |
1627 | + private List<KeyFilter> createStringKeyFilters(String key, EntityKeyType keyType, StringFilterPredicate.StringOperation operation, String value) { | |
1496 | 1628 | KeyFilter filter = new KeyFilter(); |
1497 | 1629 | filter.setKey(new EntityKey(keyType, key)); |
1498 | 1630 | StringFilterPredicate predicate = new StringFilterPredicate(); |
... | ... | @@ -1503,7 +1635,7 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest { |
1503 | 1635 | return Collections.singletonList(filter); |
1504 | 1636 | } |
1505 | 1637 | |
1506 | - private KeyFilter createNumericKeyFilter(String key, EntityKeyType keyType, NumericFilterPredicate.NumericOperation operation, double value){ | |
1638 | + private KeyFilter createNumericKeyFilter(String key, EntityKeyType keyType, NumericFilterPredicate.NumericOperation operation, double value) { | |
1507 | 1639 | KeyFilter filter = new KeyFilter(); |
1508 | 1640 | filter.setKey(new EntityKey(keyType, key)); |
1509 | 1641 | NumericFilterPredicate predicate = new NumericFilterPredicate(); | ... | ... |
... | ... | @@ -31,7 +31,7 @@ import org.thingsboard.server.dao.oauth2.OAuth2ConfigTemplateService; |
31 | 31 | import java.util.Arrays; |
32 | 32 | import java.util.UUID; |
33 | 33 | |
34 | -public class BaseOAuth2ConfigTemplateServiceTest extends AbstractServiceTest { | |
34 | +public abstract class BaseOAuth2ConfigTemplateServiceTest extends AbstractServiceTest { | |
35 | 35 | |
36 | 36 | @Autowired |
37 | 37 | protected OAuth2ConfigTemplateService oAuth2ConfigTemplateService; | ... | ... |
... | ... | @@ -43,7 +43,7 @@ import java.util.List; |
43 | 43 | import java.util.UUID; |
44 | 44 | import java.util.stream.Collectors; |
45 | 45 | |
46 | -public class BaseOAuth2ServiceTest extends AbstractServiceTest { | |
46 | +public abstract class BaseOAuth2ServiceTest extends AbstractServiceTest { | |
47 | 47 | private static final OAuth2Info EMPTY_PARAMS = new OAuth2Info(false, Collections.emptyList()); |
48 | 48 | |
49 | 49 | @Autowired | ... | ... |
... | ... | @@ -34,7 +34,7 @@ import java.util.Collections; |
34 | 34 | import java.util.List; |
35 | 35 | import java.util.stream.Collectors; |
36 | 36 | |
37 | -public class BaseTenantProfileServiceTest extends AbstractServiceTest { | |
37 | +public abstract class BaseTenantProfileServiceTest extends AbstractServiceTest { | |
38 | 38 | |
39 | 39 | private IdComparator<TenantProfile> idComparator = new IdComparator<>(); |
40 | 40 | private IdComparator<EntityInfo> tenantProfileInfoIdComparator = new IdComparator<>(); | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2021 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.dao.service; | |
17 | + | |
18 | +import org.springframework.test.context.TestPropertySource; | |
19 | + | |
20 | +import java.lang.annotation.Documented; | |
21 | +import java.lang.annotation.ElementType; | |
22 | +import java.lang.annotation.Inherited; | |
23 | +import java.lang.annotation.Retention; | |
24 | +import java.lang.annotation.RetentionPolicy; | |
25 | +import java.lang.annotation.Target; | |
26 | + | |
27 | +@Target(ElementType.TYPE) | |
28 | +@Retention(RetentionPolicy.RUNTIME) | |
29 | +@Inherited | |
30 | +@Documented | |
31 | +@TestPropertySource(locations = {"classpath:application-test.properties", "classpath:psql-test.properties"}) | |
32 | +public @interface DaoPostgreSqlTest { | |
33 | +} | ... | ... |
dao/src/test/java/org/thingsboard/server/dao/service/psql/EntityServicePostgreSqlTest.java
renamed from
dao/src/test/java/org/thingsboard/server/dao/service/sql/EntityServiceSqlTest.java
... | ... | @@ -13,11 +13,11 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | -package org.thingsboard.server.dao.service.sql; | |
16 | +package org.thingsboard.server.dao.service.psql; | |
17 | 17 | |
18 | 18 | import org.thingsboard.server.dao.service.BaseEntityServiceTest; |
19 | -import org.thingsboard.server.dao.service.DaoSqlTest; | |
19 | +import org.thingsboard.server.dao.service.DaoPostgreSqlTest; | |
20 | 20 | |
21 | -@DaoSqlTest | |
22 | -public class EntityServiceSqlTest extends BaseEntityServiceTest { | |
21 | +@DaoPostgreSqlTest | |
22 | +public class EntityServicePostgreSqlTest extends BaseEntityServiceTest { | |
23 | 23 | } | ... | ... |
dao/src/test/java/org/thingsboard/server/dao/sql/query/DefaultEntityQueryRepositoryTest.java
0 → 100644
1 | +/** | |
2 | + * Copyright © 2016-2021 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.dao.sql.query; | |
17 | + | |
18 | +import org.junit.Test; | |
19 | +import org.thingsboard.server.common.data.id.CustomerId; | |
20 | + | |
21 | +import static org.hamcrest.MatcherAssert.assertThat; | |
22 | +import static org.hamcrest.Matchers.containsString; | |
23 | +import static org.hamcrest.Matchers.equalTo; | |
24 | +import static org.mockito.ArgumentMatchers.anyInt; | |
25 | +import static org.mockito.BDDMockito.willCallRealMethod; | |
26 | +import static org.mockito.Mockito.mock; | |
27 | + | |
28 | +public class DefaultEntityQueryRepositoryTest { | |
29 | + | |
30 | + /* | |
31 | + * This value has to be reasonable small to prevent infinite recursion as early as possible | |
32 | + * */ | |
33 | + @Test | |
34 | + public void givenDefaultMaxLevel_whenStaticConstant_thenEqualsTo() { | |
35 | + assertThat(DefaultEntityQueryRepository.MAX_LEVEL_DEFAULT, equalTo(10)); | |
36 | + } | |
37 | + | |
38 | + @Test | |
39 | + public void givenMaxLevelZeroOrNegative_whenGetMaxLevel_thenReturnDefaultMaxLevel() { | |
40 | + DefaultEntityQueryRepository repo = mock(DefaultEntityQueryRepository.class); | |
41 | + willCallRealMethod().given(repo).getMaxLevel(anyInt()); | |
42 | + assertThat(repo.getMaxLevel(0), equalTo(DefaultEntityQueryRepository.MAX_LEVEL_DEFAULT)); | |
43 | + assertThat(repo.getMaxLevel(-1), equalTo(DefaultEntityQueryRepository.MAX_LEVEL_DEFAULT)); | |
44 | + assertThat(repo.getMaxLevel(-2), equalTo(DefaultEntityQueryRepository.MAX_LEVEL_DEFAULT)); | |
45 | + assertThat(repo.getMaxLevel(Integer.MIN_VALUE), equalTo(DefaultEntityQueryRepository.MAX_LEVEL_DEFAULT)); | |
46 | + } | |
47 | + | |
48 | + @Test | |
49 | + public void givenMaxLevelPositive_whenGetMaxLevel_thenValueTheSame() { | |
50 | + DefaultEntityQueryRepository repo = mock(DefaultEntityQueryRepository.class); | |
51 | + willCallRealMethod().given(repo).getMaxLevel(anyInt()); | |
52 | + assertThat(repo.getMaxLevel(1), equalTo(1)); | |
53 | + assertThat(repo.getMaxLevel(2), equalTo(2)); | |
54 | + assertThat(repo.getMaxLevel(Integer.MAX_VALUE), equalTo(Integer.MAX_VALUE)); | |
55 | + } | |
56 | + | |
57 | +} | ... | ... |
dao/src/test/resources/psql-test.properties
0 → 100644
1 | +database.ts.type=sql | |
2 | +database.ts_latest.type=sql | |
3 | +sql.ts_inserts_executor_type=fixed | |
4 | +sql.ts_inserts_fixed_thread_pool_size=200 | |
5 | +sql.ts_key_value_partitioning=MONTHS | |
6 | +# | |
7 | +spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true | |
8 | +spring.jpa.properties.hibernate.order_by.default_null_ordering=last | |
9 | +spring.jpa.properties.hibernate.jdbc.log.warnings=false | |
10 | +spring.jpa.show-sql=false | |
11 | +spring.jpa.hibernate.ddl-auto=none | |
12 | +spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect | |
13 | +spring.datasource.username=postgres | |
14 | +spring.datasource.password=postgres | |
15 | +spring.datasource.url=jdbc:tc:postgresql:12.8:///thingsboard?TC_DAEMON=true&TC_TMPFS=/testtmpfs:rw&?TC_INITFUNCTION=org.thingsboard.server.dao.PostgreSqlInitializer::initDb | |
16 | +spring.datasource.driverClassName=org.testcontainers.jdbc.ContainerDatabaseDriver | |
17 | +#org.postgresql.Driver | |
18 | +spring.datasource.hikari.maximumPoolSize=50 | |
19 | +service.type=monolith | |
20 | +#database.ts.type=timescale | |
21 | +#database.ts.type=sql | |
22 | +#database.entities.type=sql | |
23 | +# | |
24 | +#sql.ts_inserts_executor_type=fixed | |
25 | +#sql.ts_inserts_fixed_thread_pool_size=200 | |
26 | +#sql.ts_key_value_partitioning=MONTHS | |
27 | +# | |
28 | +#spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true | |
29 | +#spring.jpa.show-sql=false | |
30 | +#spring.jpa.hibernate.ddl-auto=none | |
31 | +#spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect | |
32 | +# | |
33 | +#spring.datasource.username=postgres | |
34 | +#spring.datasource.password=postgres | |
35 | +#spring.datasource.url=jdbc:postgresql://localhost:5432/sqltest | |
36 | +#spring.datasource.driverClassName=org.postgresql.Driver | |
37 | +#spring.datasource.hikari.maximumPoolSize = 50 | |
38 | +queue.core.pack-processing-timeout=3000 | |
39 | +queue.rule-engine.pack-processing-timeout=3000 | |
40 | +queue.rule-engine.queues[0].name=Main | |
41 | +queue.rule-engine.queues[0].topic=tb_rule_engine.main | |
42 | +queue.rule-engine.queues[0].poll-interval=25 | |
43 | +queue.rule-engine.queues[0].partitions=3 | |
44 | +queue.rule-engine.queues[0].pack-processing-timeout=3000 | |
45 | +queue.rule-engine.queues[0].processing-strategy.type=SKIP_ALL_FAILURES | |
46 | +queue.rule-engine.queues[0].submit-strategy.type=BURST | |
47 | +sql.log_entity_queries=true | ... | ... |
... | ... | @@ -1643,6 +1643,17 @@ |
1643 | 1643 | <groupId>org.hsqldb</groupId> |
1644 | 1644 | <artifactId>hsqldb</artifactId> |
1645 | 1645 | <version>${hsqldb.version}</version> |
1646 | + </dependency> | |
1647 | + <dependency> | |
1648 | + <groupId>org.testcontainers</groupId> | |
1649 | + <artifactId>postgresql</artifactId> | |
1650 | + <version>${testcontainers.version}</version> | |
1651 | + <scope>test</scope> | |
1652 | + </dependency> | |
1653 | + <dependency> | |
1654 | + <groupId>org.testcontainers</groupId> | |
1655 | + <artifactId>jdbc</artifactId> | |
1656 | + <version>${testcontainers.version}</version> | |
1646 | 1657 | <scope>test</scope> |
1647 | 1658 | </dependency> |
1648 | 1659 | <dependency> | ... | ... |