|
|
|
@ -26,11 +26,14 @@ import org.thingsboard.server.common.data.cf.CalculatedFieldLink; |
|
|
|
import org.thingsboard.server.common.data.cf.CalculatedFieldType; |
|
|
|
import org.thingsboard.server.common.data.cf.configuration.CalculatedFieldConfiguration; |
|
|
|
import org.thingsboard.server.common.data.id.AssetId; |
|
|
|
import org.thingsboard.server.common.data.id.AssetProfileId; |
|
|
|
import org.thingsboard.server.common.data.id.CalculatedFieldId; |
|
|
|
import org.thingsboard.server.common.data.id.CustomerId; |
|
|
|
import org.thingsboard.server.common.data.id.DeviceId; |
|
|
|
import org.thingsboard.server.common.data.id.DeviceProfileId; |
|
|
|
import org.thingsboard.server.common.data.id.EntityId; |
|
|
|
import org.thingsboard.server.common.data.id.TenantId; |
|
|
|
import org.thingsboard.server.common.data.id.TenantProfileId; |
|
|
|
import org.thingsboard.server.common.data.page.PageData; |
|
|
|
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; |
|
|
|
import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; |
|
|
|
@ -51,6 +54,9 @@ import static org.assertj.core.api.Assertions.assertThat; |
|
|
|
import static org.mockito.ArgumentMatchers.any; |
|
|
|
import static org.mockito.ArgumentMatchers.eq; |
|
|
|
import static org.mockito.Mockito.mock; |
|
|
|
import static org.mockito.Mockito.never; |
|
|
|
import static org.mockito.Mockito.spy; |
|
|
|
import static org.mockito.Mockito.verify; |
|
|
|
import static org.mockito.Mockito.when; |
|
|
|
|
|
|
|
@ExtendWith(MockitoExtension.class) |
|
|
|
@ -225,6 +231,112 @@ public class DefaultCalculatedFieldCacheTest { |
|
|
|
assertThat(cache.getDynamicEntities(tenant, customer)).doesNotContain(device); |
|
|
|
} |
|
|
|
|
|
|
|
// --- DeviceProfile/AssetProfile deletion tests ---
|
|
|
|
|
|
|
|
@Test |
|
|
|
public void onComponentLifecycleEvent_deviceProfileDeleted_evictsCfsForThatProfile() { |
|
|
|
TenantId tenant = new TenantId(UUID.randomUUID()); |
|
|
|
DeviceProfileId profileId = new DeviceProfileId(UUID.randomUUID()); |
|
|
|
CalculatedField cf = addCfToCache(tenant, profileId); |
|
|
|
|
|
|
|
cache.onComponentLifecycleEvent(new ComponentLifecycleMsg(tenant, profileId, ComponentLifecycleEvent.DELETED)); |
|
|
|
|
|
|
|
assertThat(cache.getCalculatedField(cf.getId())).isNull(); |
|
|
|
assertThat(cache.getCalculatedFieldsByEntityId(profileId)).isEmpty(); |
|
|
|
} |
|
|
|
|
|
|
|
@Test |
|
|
|
public void onComponentLifecycleEvent_deviceProfileDeleted_removesLinksForLinkedEntities() { |
|
|
|
TenantId tenant = new TenantId(UUID.randomUUID()); |
|
|
|
DeviceProfileId profileId = new DeviceProfileId(UUID.randomUUID()); |
|
|
|
DeviceId linkedDevice = new DeviceId(UUID.randomUUID()); |
|
|
|
addCfToCache(tenant, profileId, linkedDevice); |
|
|
|
|
|
|
|
cache.onComponentLifecycleEvent(new ComponentLifecycleMsg(tenant, profileId, ComponentLifecycleEvent.DELETED)); |
|
|
|
|
|
|
|
assertThat(cache.getCalculatedFieldLinksByEntityId(linkedDevice)).isEmpty(); |
|
|
|
} |
|
|
|
|
|
|
|
@Test |
|
|
|
public void onComponentLifecycleEvent_deviceProfileDeleted_doesNotEvictOtherProfilesCfs() { |
|
|
|
TenantId tenant = new TenantId(UUID.randomUUID()); |
|
|
|
DeviceProfileId profile1 = new DeviceProfileId(UUID.randomUUID()); |
|
|
|
DeviceProfileId profile2 = new DeviceProfileId(UUID.randomUUID()); |
|
|
|
CalculatedField cf1 = addCfToCache(tenant, profile1); |
|
|
|
CalculatedField cf2 = addCfToCache(tenant, profile2); |
|
|
|
|
|
|
|
cache.onComponentLifecycleEvent(new ComponentLifecycleMsg(tenant, profile1, ComponentLifecycleEvent.DELETED)); |
|
|
|
|
|
|
|
assertThat(cache.getCalculatedField(cf1.getId())).isNull(); |
|
|
|
assertThat(cache.getCalculatedFieldsByEntityId(profile1)).isEmpty(); |
|
|
|
assertThat(cache.getCalculatedField(cf2.getId())).isEqualTo(cf2); |
|
|
|
assertThat(cache.getCalculatedFieldsByEntityId(profile2)).containsExactly(cf2); |
|
|
|
} |
|
|
|
|
|
|
|
@Test |
|
|
|
public void onComponentLifecycleEvent_deviceProfileUpdated_doesNotEvictCfs() { |
|
|
|
TenantId tenant = new TenantId(UUID.randomUUID()); |
|
|
|
DeviceProfileId profileId = new DeviceProfileId(UUID.randomUUID()); |
|
|
|
CalculatedField cf = addCfToCache(tenant, profileId); |
|
|
|
|
|
|
|
cache.onComponentLifecycleEvent(new ComponentLifecycleMsg(tenant, profileId, ComponentLifecycleEvent.UPDATED)); |
|
|
|
|
|
|
|
assertThat(cache.getCalculatedField(cf.getId())).isEqualTo(cf); |
|
|
|
assertThat(cache.getCalculatedFieldsByEntityId(profileId)).containsExactly(cf); |
|
|
|
} |
|
|
|
|
|
|
|
@Test |
|
|
|
public void onComponentLifecycleEvent_assetProfileDeleted_evictsCfsForThatProfile() { |
|
|
|
TenantId tenant = new TenantId(UUID.randomUUID()); |
|
|
|
AssetProfileId profileId = new AssetProfileId(UUID.randomUUID()); |
|
|
|
CalculatedField cf = addCfToCache(tenant, profileId); |
|
|
|
|
|
|
|
cache.onComponentLifecycleEvent(new ComponentLifecycleMsg(tenant, profileId, ComponentLifecycleEvent.DELETED)); |
|
|
|
|
|
|
|
assertThat(cache.getCalculatedField(cf.getId())).isNull(); |
|
|
|
assertThat(cache.getCalculatedFieldsByEntityId(profileId)).isEmpty(); |
|
|
|
} |
|
|
|
|
|
|
|
@Test |
|
|
|
public void onComponentLifecycleEvent_assetProfileDeleted_removesLinksForLinkedEntities() { |
|
|
|
TenantId tenant = new TenantId(UUID.randomUUID()); |
|
|
|
AssetProfileId profileId = new AssetProfileId(UUID.randomUUID()); |
|
|
|
AssetId linkedAsset = new AssetId(UUID.randomUUID()); |
|
|
|
addCfToCache(tenant, profileId, linkedAsset); |
|
|
|
|
|
|
|
cache.onComponentLifecycleEvent(new ComponentLifecycleMsg(tenant, profileId, ComponentLifecycleEvent.DELETED)); |
|
|
|
|
|
|
|
assertThat(cache.getCalculatedFieldLinksByEntityId(linkedAsset)).isEmpty(); |
|
|
|
} |
|
|
|
|
|
|
|
@Test |
|
|
|
public void onComponentLifecycleEvent_assetProfileDeleted_doesNotEvictOtherProfilesCfs() { |
|
|
|
TenantId tenant = new TenantId(UUID.randomUUID()); |
|
|
|
AssetProfileId profile1 = new AssetProfileId(UUID.randomUUID()); |
|
|
|
AssetProfileId profile2 = new AssetProfileId(UUID.randomUUID()); |
|
|
|
CalculatedField cf1 = addCfToCache(tenant, profile1); |
|
|
|
CalculatedField cf2 = addCfToCache(tenant, profile2); |
|
|
|
|
|
|
|
cache.onComponentLifecycleEvent(new ComponentLifecycleMsg(tenant, profile1, ComponentLifecycleEvent.DELETED)); |
|
|
|
|
|
|
|
assertThat(cache.getCalculatedField(cf1.getId())).isNull(); |
|
|
|
assertThat(cache.getCalculatedFieldsByEntityId(profile1)).isEmpty(); |
|
|
|
assertThat(cache.getCalculatedField(cf2.getId())).isEqualTo(cf2); |
|
|
|
assertThat(cache.getCalculatedFieldsByEntityId(profile2)).containsExactly(cf2); |
|
|
|
} |
|
|
|
|
|
|
|
@Test |
|
|
|
public void onComponentLifecycleEvent_assetProfileUpdated_doesNotEvictCfs() { |
|
|
|
TenantId tenant = new TenantId(UUID.randomUUID()); |
|
|
|
AssetProfileId profileId = new AssetProfileId(UUID.randomUUID()); |
|
|
|
CalculatedField cf = addCfToCache(tenant, profileId); |
|
|
|
|
|
|
|
cache.onComponentLifecycleEvent(new ComponentLifecycleMsg(tenant, profileId, ComponentLifecycleEvent.UPDATED)); |
|
|
|
|
|
|
|
assertThat(cache.getCalculatedField(cf.getId())).isEqualTo(cf); |
|
|
|
assertThat(cache.getCalculatedFieldsByEntityId(profileId)).containsExactly(cf); |
|
|
|
} |
|
|
|
|
|
|
|
// --- CalculatedField lifecycle tests ---
|
|
|
|
|
|
|
|
@Test |
|
|
|
@ -268,6 +380,77 @@ public class DefaultCalculatedFieldCacheTest { |
|
|
|
assertThat(cache.getCalculatedField(cf.getId())).isEqualTo(updatedCf); |
|
|
|
} |
|
|
|
|
|
|
|
// --- evictOwner recursive traversal tests ---
|
|
|
|
|
|
|
|
@Test |
|
|
|
public void evictOwner_customerDeleted_recursivelyEvictsDevicesOwnedByThatCustomer() { |
|
|
|
TenantId tenant = new TenantId(UUID.randomUUID()); |
|
|
|
CustomerId customer = new CustomerId(UUID.randomUUID()); |
|
|
|
DeviceId device = new DeviceId(UUID.randomUUID()); |
|
|
|
|
|
|
|
stubDeviceOwner(tenant, device, customer); |
|
|
|
when(customerService.findCustomersByTenantId(any(), any())).thenReturn(PageData.emptyPageData()); |
|
|
|
|
|
|
|
// tenant owns customer (getOwner for CUSTOMER returns tenantId)
|
|
|
|
cache.addOwnerEntity(tenant, customer); // ownerEntities[tenant] = {customer}
|
|
|
|
cache.addOwnerEntity(tenant, device); // ownerEntities[customer] = {device}
|
|
|
|
|
|
|
|
assertThat(cache.getDynamicEntities(tenant, tenant)).contains(customer); |
|
|
|
assertThat(cache.getDynamicEntities(tenant, customer)).contains(device); |
|
|
|
|
|
|
|
// deleting the customer evicts the customer key and recursively cleans its owned set
|
|
|
|
cache.onComponentLifecycleEvent(new ComponentLifecycleMsg(tenant, customer, ComponentLifecycleEvent.DELETED)); |
|
|
|
|
|
|
|
assertThat(cache.getDynamicEntities(tenant, customer)).doesNotContain(device); |
|
|
|
} |
|
|
|
|
|
|
|
@Test |
|
|
|
public void evictOwner_tenantDeleted_recursivelyEvictsCustomerAndItsOwnedDevices() { |
|
|
|
TenantId tenant = new TenantId(UUID.randomUUID()); |
|
|
|
CustomerId customer = new CustomerId(UUID.randomUUID()); |
|
|
|
DeviceId device = new DeviceId(UUID.randomUUID()); |
|
|
|
|
|
|
|
stubDeviceOwner(tenant, device, customer); |
|
|
|
when(customerService.findCustomersByTenantId(any(), any())).thenReturn(PageData.emptyPageData()); |
|
|
|
|
|
|
|
cache.addOwnerEntity(tenant, customer); // ownerEntities[tenant] = {customer}
|
|
|
|
cache.addOwnerEntity(tenant, device); // ownerEntities[customer] = {device}
|
|
|
|
|
|
|
|
assertThat(cache.getDynamicEntities(tenant, tenant)).contains(customer); |
|
|
|
assertThat(cache.getDynamicEntities(tenant, customer)).contains(device); |
|
|
|
|
|
|
|
// deleting the tenant: evictOwner(tenant) finds customer (CUSTOMER type) and recurses into it
|
|
|
|
cache.onComponentLifecycleEvent(new ComponentLifecycleMsg(tenant, tenant, ComponentLifecycleEvent.DELETED)); |
|
|
|
|
|
|
|
// both levels must be gone
|
|
|
|
assertThat(cache.getDynamicEntities(tenant, tenant)).doesNotContain(customer); |
|
|
|
assertThat(cache.getDynamicEntities(tenant, customer)).doesNotContain(device); |
|
|
|
} |
|
|
|
|
|
|
|
// --- TenantProfile lifecycle tests ---
|
|
|
|
|
|
|
|
@Test |
|
|
|
public void onComponentLifecycleEvent_tenantProfileUpdated_callsHandleTenantProfileUpdate() { |
|
|
|
TenantId tenant = new TenantId(UUID.randomUUID()); |
|
|
|
TenantProfileId profileId = new TenantProfileId(UUID.randomUUID()); |
|
|
|
DefaultCalculatedFieldCache spyCache = spy(cache); |
|
|
|
|
|
|
|
spyCache.onComponentLifecycleEvent(new ComponentLifecycleMsg(tenant, profileId, ComponentLifecycleEvent.UPDATED)); |
|
|
|
|
|
|
|
verify(spyCache).handleTenantProfileUpdate(profileId); |
|
|
|
} |
|
|
|
|
|
|
|
@Test |
|
|
|
public void onComponentLifecycleEvent_tenantProfileDeleted_doesNotCallHandleTenantProfileUpdate() { |
|
|
|
TenantId tenant = new TenantId(UUID.randomUUID()); |
|
|
|
TenantProfileId profileId = new TenantProfileId(UUID.randomUUID()); |
|
|
|
DefaultCalculatedFieldCache spyCache = spy(cache); |
|
|
|
|
|
|
|
spyCache.onComponentLifecycleEvent(new ComponentLifecycleMsg(tenant, profileId, ComponentLifecycleEvent.DELETED)); |
|
|
|
|
|
|
|
verify(spyCache, never()).handleTenantProfileUpdate(any()); |
|
|
|
} |
|
|
|
|
|
|
|
// --- Helpers ---
|
|
|
|
|
|
|
|
private void stubDeviceOwner(TenantId tenantId, DeviceId deviceId, EntityId ownerId) { |
|
|
|
|