1 changed files with 144 additions and 0 deletions
@ -0,0 +1,144 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2026 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.service.install.lts; |
||||
|
|
||||
|
import com.fasterxml.jackson.databind.JsonNode; |
||||
|
import org.junit.After; |
||||
|
import org.junit.Before; |
||||
|
import org.junit.Test; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.jdbc.core.JdbcTemplate; |
||||
|
import org.thingsboard.common.util.JacksonUtil; |
||||
|
import org.thingsboard.server.common.data.id.TenantId; |
||||
|
import org.thingsboard.server.common.data.id.WidgetTypeId; |
||||
|
import org.thingsboard.server.common.data.id.WidgetsBundleId; |
||||
|
import org.thingsboard.server.common.data.widget.WidgetTypeDetails; |
||||
|
import org.thingsboard.server.common.data.widget.WidgetsBundle; |
||||
|
import org.thingsboard.server.controller.AbstractControllerTest; |
||||
|
import org.thingsboard.server.dao.service.DaoSqlTest; |
||||
|
import org.thingsboard.server.dao.widget.WidgetTypeService; |
||||
|
import org.thingsboard.server.dao.widget.WidgetsBundleService; |
||||
|
|
||||
|
import java.util.List; |
||||
|
import java.util.UUID; |
||||
|
|
||||
|
import static org.junit.Assert.assertEquals; |
||||
|
import static org.junit.Assert.assertNotNull; |
||||
|
import static org.junit.Assert.assertNull; |
||||
|
import static org.junit.Assert.assertTrue; |
||||
|
|
||||
|
@DaoSqlTest |
||||
|
public class LtsMigrationIntegrationTest extends AbstractControllerTest { |
||||
|
|
||||
|
private static final long V_4_2_2_2 = 4_002_002_002L; |
||||
|
private static final long V_4_2_2_3 = 4_002_002_003L; |
||||
|
private static final String OBSOLETE_ALIAS = "air_quality"; |
||||
|
|
||||
|
@Autowired |
||||
|
private LtsMigrationService ltsMigrationService; |
||||
|
@Autowired |
||||
|
private WidgetsBundleService widgetsBundleService; |
||||
|
@Autowired |
||||
|
private WidgetTypeService widgetTypeService; |
||||
|
@Autowired |
||||
|
private JdbcTemplate jdbcTemplate; |
||||
|
|
||||
|
private Long originalSchemaVersion; |
||||
|
private WidgetsBundleId bundleId; |
||||
|
private WidgetTypeId widgetTypeId; |
||||
|
|
||||
|
@Before |
||||
|
public void setUp() { |
||||
|
// The test-context DB (DaoSqlTest) creates tb_schema_settings empty, so ensure the baseline
|
||||
|
// version row a real installed DB always has exists before driving the migration.
|
||||
|
originalSchemaVersion = jdbcTemplate.query("SELECT schema_version FROM tb_schema_settings", |
||||
|
rs -> rs.next() ? rs.getLong(1) : null); |
||||
|
if (originalSchemaVersion == null) { |
||||
|
jdbcTemplate.execute("INSERT INTO tb_schema_settings (schema_version, product) VALUES (" + V_4_2_2_2 + ", 'CE')"); |
||||
|
} |
||||
|
|
||||
|
// Seed an obsolete system widget bundle with one (non-deprecated) widget type linked to it.
|
||||
|
WidgetsBundle bundle = new WidgetsBundle(); |
||||
|
bundle.setTenantId(TenantId.SYS_TENANT_ID); |
||||
|
bundle.setAlias(OBSOLETE_ALIAS); |
||||
|
bundle.setTitle("Air quality"); |
||||
|
bundleId = widgetsBundleService.saveWidgetsBundle(bundle).getId(); |
||||
|
|
||||
|
WidgetTypeDetails type = new WidgetTypeDetails(); |
||||
|
type.setTenantId(TenantId.SYS_TENANT_ID); |
||||
|
type.setFqn("air_quality_sample_" + UUID.randomUUID()); |
||||
|
type.setName("Air quality sample"); |
||||
|
type.setDescriptor(JacksonUtil.fromString("{ \"type\": \"latest\" }", JsonNode.class)); |
||||
|
WidgetTypeDetails saved = widgetTypeService.saveWidgetType(type); |
||||
|
widgetTypeId = saved.getId(); |
||||
|
widgetTypeService.updateWidgetsBundleWidgetFqns(TenantId.SYS_TENANT_ID, bundleId, List.of(saved.getFqn())); |
||||
|
|
||||
|
// Pretend the DB is at 4.2.2.2 so the 4.2.2.3 migration is in range (4.2.2.2, 4.2.2.3].
|
||||
|
jdbcTemplate.execute("UPDATE tb_schema_settings SET schema_version = " + V_4_2_2_2); |
||||
|
} |
||||
|
|
||||
|
@After |
||||
|
public void tearDown() { |
||||
|
// Restore the schema version (or drop the row we seeded) and clean up whatever the migration left behind.
|
||||
|
if (originalSchemaVersion != null) { |
||||
|
jdbcTemplate.execute("UPDATE tb_schema_settings SET schema_version = " + originalSchemaVersion); |
||||
|
} else { |
||||
|
jdbcTemplate.execute("DELETE FROM tb_schema_settings"); |
||||
|
} |
||||
|
if (widgetTypeId != null && widgetTypeService.findWidgetTypeDetailsById(TenantId.SYS_TENANT_ID, widgetTypeId) != null) { |
||||
|
widgetTypeService.deleteWidgetType(TenantId.SYS_TENANT_ID, widgetTypeId); |
||||
|
} |
||||
|
WidgetsBundle bundle = widgetsBundleService.findWidgetsBundleByTenantIdAndAlias(TenantId.SYS_TENANT_ID, OBSOLETE_ALIAS); |
||||
|
if (bundle != null) { |
||||
|
widgetsBundleService.deleteWidgetsBundle(TenantId.SYS_TENANT_ID, bundle.getId()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void appliesSqlDeprecatesTypesDeletesBundleAndRecordsVersion() { |
||||
|
ltsMigrationService.applyMigrations("4.2.2.2", "4.2.2.3"); |
||||
|
|
||||
|
// 1. The version's SQL ran: iot_hub_installed_item table now exists.
|
||||
|
assertTrue(tableExists("iot_hub_installed_item")); |
||||
|
// 2. The version was recorded.
|
||||
|
assertEquals(Long.valueOf(V_4_2_2_3), |
||||
|
jdbcTemplate.queryForObject("SELECT schema_version FROM tb_schema_settings", Long.class)); |
||||
|
// 3. The obsolete bundle entity was deleted.
|
||||
|
assertNull(widgetsBundleService.findWidgetsBundleByTenantIdAndAlias(TenantId.SYS_TENANT_ID, OBSOLETE_ALIAS)); |
||||
|
// 4. The widget type was KEPT and marked deprecated.
|
||||
|
WidgetTypeDetails type = widgetTypeService.findWidgetTypeDetailsById(TenantId.SYS_TENANT_ID, widgetTypeId); |
||||
|
assertNotNull(type); |
||||
|
assertTrue(type.isDeprecated()); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void reRunFromCurrentVersionIsNoOp() { |
||||
|
ltsMigrationService.applyMigrations("4.2.2.2", "4.2.2.3"); |
||||
|
// Re-running from the now-current version selects an empty range — nothing changes, nothing throws.
|
||||
|
ltsMigrationService.applyMigrations("4.2.2.3", "4.2.2.3"); |
||||
|
|
||||
|
assertEquals(Long.valueOf(V_4_2_2_3), |
||||
|
jdbcTemplate.queryForObject("SELECT schema_version FROM tb_schema_settings", Long.class)); |
||||
|
assertNull(widgetsBundleService.findWidgetsBundleByTenantIdAndAlias(TenantId.SYS_TENANT_ID, OBSOLETE_ALIAS)); |
||||
|
assertTrue(widgetTypeService.findWidgetTypeDetailsById(TenantId.SYS_TENANT_ID, widgetTypeId).isDeprecated()); |
||||
|
} |
||||
|
|
||||
|
private boolean tableExists(String table) { |
||||
|
Boolean exists = jdbcTemplate.queryForObject( |
||||
|
"SELECT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = ?)", Boolean.class, table); |
||||
|
return Boolean.TRUE.equals(exists); |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue