@ -25,6 +25,7 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith ;
import org.junit.jupiter.params.ParameterizedTest ;
import org.junit.jupiter.params.provider.Arguments ;
import org.junit.jupiter.params.provider.EnumSource ;
import org.junit.jupiter.params.provider.MethodSource ;
import org.mockito.Mock ;
import org.mockito.junit.jupiter.MockitoExtension ;
@ -35,15 +36,19 @@ import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.common.data.ApiUsageRecordKey ;
import org.thingsboard.server.common.data.ApiUsageState ;
import org.thingsboard.server.common.data.ApiUsageStateValue ;
import org.thingsboard.server.common.data.EntityType ;
import org.thingsboard.server.common.data.EntityView ;
import org.thingsboard.server.common.data.id.ApiUsageStateId ;
import org.thingsboard.server.common.data.id.CustomerId ;
import org.thingsboard.server.common.data.id.DeviceId ;
import org.thingsboard.server.common.data.id.EntityId ;
import org.thingsboard.server.common.data.id.EntityIdFactory ;
import org.thingsboard.server.common.data.id.EntityViewId ;
import org.thingsboard.server.common.data.id.TenantId ;
import org.thingsboard.server.common.data.kv.BasicTsKvEntry ;
import org.thingsboard.server.common.data.kv.DoubleDataEntry ;
import org.thingsboard.server.common.data.kv.KvEntry ;
import org.thingsboard.server.common.data.kv.LongDataEntry ;
import org.thingsboard.server.common.data.kv.TsKvEntry ;
import org.thingsboard.server.common.data.objects.AttributesEntityView ;
import org.thingsboard.server.common.data.objects.TelemetryEntityView ;
@ -72,11 +77,16 @@ import java.util.concurrent.ExecutorService;
import java.util.stream.LongStream ;
import java.util.stream.Stream ;
import static com.google.common.util.concurrent.Futures.immediateFailedFuture ;
import static com.google.common.util.concurrent.Futures.immediateFuture ;
import static org.assertj.core.api.Assertions.assertThat ;
import static org.assertj.core.api.Assertions.assertThatThrownBy ;
import static org.mockito.ArgumentMatchers.any ;
import static org.mockito.ArgumentMatchers.anyLong ;
import static org.mockito.BDDMockito.given ;
import static org.mockito.BDDMockito.then ;
import static org.mockito.Mockito.lenient ;
import static org.mockito.Mockito.never ;
@ExtendWith ( MockitoExtension . class )
class DefaultTelemetrySubscriptionServiceTest {
@ -368,6 +378,154 @@ class DefaultTelemetrySubscriptionServiceTest {
) ;
}
@Test
void shouldThrowErrorWhenTryingToSaveTimeseriesForApiUsageState ( ) {
// GIVEN
var request = TimeseriesSaveRequest . builder ( )
. tenantId ( tenantId )
. customerId ( customerId )
. entityId ( new ApiUsageStateId ( UUID . randomUUID ( ) ) )
. entries ( sampleTelemetry )
. strategy ( TimeseriesSaveRequest . Strategy . SAVE_ALL )
. callback ( emptyCallback )
. build ( ) ;
// WHEN
assertThatThrownBy ( ( ) - > telemetryService . saveTimeseries ( request ) )
. isInstanceOf ( RuntimeException . class )
. hasMessage ( "Can't update API Usage State!" ) ;
// THEN
then ( tsService ) . shouldHaveNoInteractions ( ) ;
then ( deviceStateManager ) . shouldHaveNoInteractions ( ) ;
}
@Test
void shouldNotifyDeviceStateManagerWhenDeviceInactivityTimeoutTimeseriesWasSavedToLatest ( ) {
// GIVEN
var deviceId = DeviceId . fromString ( "cc51e450-53e1-11ee-883e-e56b48fd2088" ) ;
var inactivityTimeout = new BasicTsKvEntry ( 123L , new LongDataEntry ( "inactivityTimeout" , 5000L ) ) ;
var request = TimeseriesSaveRequest . builder ( )
. tenantId ( tenantId )
. customerId ( customerId )
. entityId ( deviceId )
. entry ( inactivityTimeout )
. strategy ( new TimeseriesSaveRequest . Strategy ( false , true , false ) )
. callback ( emptyCallback )
. build ( ) ;
given ( tsService . saveLatest ( tenantId , deviceId , List . of ( inactivityTimeout ) ) ) . willReturn ( immediateFuture ( listOfNNumbers ( 1 ) ) ) ;
// WHEN
telemetryService . saveTimeseries ( request ) ;
// THEN
then ( deviceStateManager ) . should ( ) . onDeviceInactivityTimeoutUpdate ( tenantId , deviceId , 5000L , TbCallback . EMPTY ) ;
}
@ParameterizedTest
@EnumSource (
value = EntityType . class ,
names = { "DEVICE" , "API_USAGE_STATE" } , // API usage state excluded due to coverage in another test
mode = EnumSource . Mode . EXCLUDE
)
void shouldNotNotifyDeviceStateManagerWhenInactivityTimeoutTimeseriesWasUpdatedButEntityTypeIsNotDevice ( EntityType entityType ) {
// GIVEN
var nonDeviceId = EntityIdFactory . getByTypeAndUuid ( entityType , "cc51e450-53e1-11ee-883e-e56b48fd2088" ) ;
var inactivityTimeout = new BasicTsKvEntry ( 123L , new LongDataEntry ( "inactivityTimeout" , 5000L ) ) ;
var request = TimeseriesSaveRequest . builder ( )
. tenantId ( tenantId )
. customerId ( customerId )
. entityId ( nonDeviceId )
. entry ( inactivityTimeout )
. strategy ( new TimeseriesSaveRequest . Strategy ( false , true , false ) )
. callback ( emptyCallback )
. build ( ) ;
given ( tsService . saveLatest ( tenantId , nonDeviceId , List . of ( inactivityTimeout ) ) ) . willReturn ( immediateFuture ( listOfNNumbers ( 1 ) ) ) ;
lenient ( ) . when ( tbEntityViewService . findEntityViewsByTenantIdAndEntityIdAsync ( tenantId , nonDeviceId ) ) . thenReturn ( immediateFuture ( Collections . emptyList ( ) ) ) ;
// WHEN
telemetryService . saveTimeseries ( request ) ;
// THEN
then ( deviceStateManager ) . should ( never ( ) ) . onDeviceInactivityTimeoutUpdate ( any ( ) , any ( ) , anyLong ( ) , any ( ) ) ;
}
@Test
void shouldNotNotifyDeviceStateManagerWhenDeviceInactivityTimeoutTimeseriesWasNotSavedToLatest ( ) {
// GIVEN
var deviceId = DeviceId . fromString ( "cc51e450-53e1-11ee-883e-e56b48fd2088" ) ;
var inactivityTimeout = new BasicTsKvEntry ( 123L , new LongDataEntry ( "inactivityTimeout" , 5000L ) ) ;
var request = TimeseriesSaveRequest . builder ( )
. tenantId ( tenantId )
. customerId ( customerId )
. entityId ( deviceId )
. entry ( inactivityTimeout )
. strategy ( new TimeseriesSaveRequest . Strategy ( true , false , true ) )
. callback ( emptyCallback )
. build ( ) ;
given ( tsService . saveWithoutLatest ( tenantId , deviceId , List . of ( inactivityTimeout ) , 0L ) ) . willReturn ( immediateFuture ( 1 ) ) ;
// WHEN
telemetryService . saveTimeseries ( request ) ;
// THEN
then ( deviceStateManager ) . should ( never ( ) ) . onDeviceInactivityTimeoutUpdate ( any ( ) , any ( ) , anyLong ( ) , any ( ) ) ;
}
@Test
void shouldNotNotifyDeviceStateManagerWhenInactivityTimeoutTimeseriesWasNotUpdated ( ) {
// GIVEN
var deviceId = DeviceId . fromString ( "cc51e450-53e1-11ee-883e-e56b48fd2088" ) ;
var notInactivityTimeout = new BasicTsKvEntry ( 123L , new LongDataEntry ( "notInactivityTimeout" , 5000L ) ) ;
var request = TimeseriesSaveRequest . builder ( )
. tenantId ( tenantId )
. customerId ( customerId )
. entityId ( deviceId )
. entry ( notInactivityTimeout )
. strategy ( new TimeseriesSaveRequest . Strategy ( false , true , false ) )
. callback ( emptyCallback )
. build ( ) ;
given ( tsService . saveLatest ( tenantId , deviceId , List . of ( notInactivityTimeout ) ) ) . willReturn ( immediateFuture ( listOfNNumbers ( 1 ) ) ) ;
// WHEN
telemetryService . saveTimeseries ( request ) ;
// THEN
then ( deviceStateManager ) . should ( never ( ) ) . onDeviceInactivityTimeoutUpdate ( any ( ) , any ( ) , anyLong ( ) , any ( ) ) ;
}
@Test
void shouldNotNotifyDeviceStateManagerWhenDeviceInactivityTimeoutTimeseriesSaveFailed ( ) {
// GIVEN
var deviceId = DeviceId . fromString ( "cc51e450-53e1-11ee-883e-e56b48fd2088" ) ;
var inactivityTimeout = new BasicTsKvEntry ( 123L , new LongDataEntry ( "inactivityTimeout" , 5000L ) ) ;
var request = TimeseriesSaveRequest . builder ( )
. tenantId ( tenantId )
. customerId ( customerId )
. entityId ( deviceId )
. entry ( inactivityTimeout )
. strategy ( new TimeseriesSaveRequest . Strategy ( false , true , false ) )
. callback ( emptyCallback )
. build ( ) ;
given ( tsService . saveLatest ( tenantId , deviceId , List . of ( inactivityTimeout ) ) ) . willReturn ( immediateFailedFuture ( new RuntimeException ( "failed to save" ) ) ) ;
// WHEN
telemetryService . saveTimeseries ( request ) ;
// THEN
then ( deviceStateManager ) . should ( never ( ) ) . onDeviceInactivityTimeoutUpdate ( any ( ) , any ( ) , anyLong ( ) , any ( ) ) ;
}
// used to emulate sequence numbers returned by save latest API
private static List < Long > listOfNNumbers ( int N ) {
return LongStream . range ( 0 , N ) . boxed ( ) . toList ( ) ;