13 changed files with 725 additions and 56 deletions
@ -0,0 +1,238 @@ |
|||
/** |
|||
* Copyright © 2016-2024 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.cf.ctx.state; |
|||
|
|||
import org.junit.jupiter.api.BeforeEach; |
|||
import org.junit.jupiter.api.Test; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.boot.test.context.SpringBootTest; |
|||
import org.thingsboard.script.api.tbel.DefaultTbelInvokeService; |
|||
import org.thingsboard.script.api.tbel.TbelInvokeService; |
|||
import org.thingsboard.server.common.data.AttributeScope; |
|||
import org.thingsboard.server.common.data.cf.CalculatedField; |
|||
import org.thingsboard.server.common.data.cf.CalculatedFieldType; |
|||
import org.thingsboard.server.common.data.cf.configuration.Argument; |
|||
import org.thingsboard.server.common.data.cf.configuration.ArgumentType; |
|||
import org.thingsboard.server.common.data.cf.configuration.CalculatedFieldConfiguration; |
|||
import org.thingsboard.server.common.data.cf.configuration.Output; |
|||
import org.thingsboard.server.common.data.cf.configuration.OutputType; |
|||
import org.thingsboard.server.common.data.cf.configuration.ReferencedEntityKey; |
|||
import org.thingsboard.server.common.data.cf.configuration.SimpleCalculatedFieldConfiguration; |
|||
import org.thingsboard.server.common.data.id.AssetId; |
|||
import org.thingsboard.server.common.data.id.DeviceId; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.service.cf.CalculatedFieldResult; |
|||
|
|||
import java.util.HashMap; |
|||
import java.util.Map; |
|||
import java.util.TreeMap; |
|||
import java.util.UUID; |
|||
import java.util.concurrent.ExecutionException; |
|||
|
|||
import static org.assertj.core.api.Assertions.assertThat; |
|||
|
|||
@SpringBootTest(classes = DefaultTbelInvokeService.class) |
|||
public class ScriptCalculatedFieldStateTest { |
|||
|
|||
private final TenantId TENANT_ID = TenantId.fromUUID(UUID.fromString("5b18e321-3327-4290-b996-d72a65e90382")); |
|||
private final DeviceId DEVICE_ID = new DeviceId(UUID.fromString("5512071d-5abc-411d-a907-4cdb6539c2eb")); |
|||
private final AssetId ASSET_ID = new AssetId(UUID.fromString("5bc010ae-bcfd-46c8-98b9-8ee8c8955a76")); |
|||
|
|||
private final SingleValueArgumentEntry assetHumidityArgEntry = new SingleValueArgumentEntry(System.currentTimeMillis() - 10, 43, 122L); |
|||
private final TsRollingArgumentEntry deviceTemperatureArgEntry = createRollingArgEntry(); |
|||
|
|||
private final long ts = System.currentTimeMillis(); |
|||
|
|||
private ScriptCalculatedFieldState state; |
|||
private CalculatedFieldCtx ctx; |
|||
|
|||
@Autowired |
|||
private TbelInvokeService tbelInvokeService; |
|||
|
|||
@BeforeEach |
|||
void setUp() { |
|||
ctx = new CalculatedFieldCtx(getCalculatedField(), tbelInvokeService); |
|||
state = new ScriptCalculatedFieldState(ctx.getArgNames()); |
|||
} |
|||
|
|||
@Test |
|||
void testType() { |
|||
assertThat(state.getType()).isEqualTo(CalculatedFieldType.SCRIPT); |
|||
} |
|||
|
|||
@Test |
|||
void testUpdateState() { |
|||
state.arguments = new HashMap<>(Map.of("assetHumidity", assetHumidityArgEntry)); |
|||
|
|||
Map<String, ArgumentEntry> newArgs = Map.of("deviceTemperature", deviceTemperatureArgEntry); |
|||
boolean stateUpdated = state.updateState(newArgs); |
|||
|
|||
assertThat(stateUpdated).isTrue(); |
|||
assertThat(state.getArguments()).containsExactlyInAnyOrderEntriesOf( |
|||
Map.of( |
|||
"assetHumidity", assetHumidityArgEntry, |
|||
"deviceTemperature", deviceTemperatureArgEntry |
|||
) |
|||
); |
|||
} |
|||
|
|||
@Test |
|||
void testUpdateStateWhenUpdateExistingEntry() { |
|||
state.arguments = new HashMap<>(Map.of("deviceTemperature", deviceTemperatureArgEntry, "assetHumidity", assetHumidityArgEntry)); |
|||
|
|||
SingleValueArgumentEntry newArgEntry = new SingleValueArgumentEntry(ts, 41, 349L); |
|||
Map<String, ArgumentEntry> newArgs = Map.of("assetHumidity", newArgEntry); |
|||
boolean stateUpdated = state.updateState(newArgs); |
|||
|
|||
assertThat(stateUpdated).isTrue(); |
|||
assertThat(state.getArguments()).containsExactlyInAnyOrderEntriesOf( |
|||
Map.of( |
|||
"assetHumidity", newArgEntry, |
|||
"deviceTemperature", deviceTemperatureArgEntry |
|||
) |
|||
); |
|||
} |
|||
|
|||
@Test |
|||
void testPerformCalculation() throws ExecutionException, InterruptedException { |
|||
state.arguments = new HashMap<>(Map.of("deviceTemperature", deviceTemperatureArgEntry, "assetHumidity", assetHumidityArgEntry)); |
|||
|
|||
CalculatedFieldResult result = state.performCalculation(ctx).get(); |
|||
|
|||
assertThat(result).isNotNull(); |
|||
Output output = getCalculatedFieldConfig().getOutput(); |
|||
assertThat(result.getType()).isEqualTo(output.getType()); |
|||
assertThat(result.getScope()).isEqualTo(output.getScope()); |
|||
assertThat(result.getResultMap()).isEqualTo(Map.of("averageDeviceTemperature", 13.0, "assetHumidity", 43)); |
|||
} |
|||
|
|||
@Test |
|||
void testPerformCalculationWhenOldTelemetry() throws ExecutionException, InterruptedException { |
|||
TsRollingArgumentEntry argumentEntry = new TsRollingArgumentEntry(); |
|||
|
|||
TreeMap<Long, Object> values = new TreeMap<>(); |
|||
values.put(ts - 40000, 4);// will not be used for calculation
|
|||
values.put(ts - 45000, 2);// will not be used for calculation
|
|||
values.put(ts - 20, 0); |
|||
|
|||
argumentEntry.setTsRecords(values); |
|||
|
|||
state.arguments = new HashMap<>(Map.of("deviceTemperature", argumentEntry, "assetHumidity", assetHumidityArgEntry)); |
|||
|
|||
CalculatedFieldResult result = state.performCalculation(ctx).get(); |
|||
|
|||
assertThat(result).isNotNull(); |
|||
Output output = getCalculatedFieldConfig().getOutput(); |
|||
assertThat(result.getType()).isEqualTo(output.getType()); |
|||
assertThat(result.getScope()).isEqualTo(output.getScope()); |
|||
assertThat(result.getResultMap()).isEqualTo(Map.of("averageDeviceTemperature", 0.0, "assetHumidity", 43)); |
|||
} |
|||
|
|||
@Test |
|||
void testPerformCalculationWhenArgumentsMoreThanLimit() throws ExecutionException, InterruptedException { |
|||
TsRollingArgumentEntry argumentEntry = new TsRollingArgumentEntry(); |
|||
TreeMap<Long, Object> values = new TreeMap<>(); |
|||
values.put(ts - 20, 1000);// will not be used
|
|||
values.put(ts - 18, 0); |
|||
values.put(ts - 16, 0); |
|||
values.put(ts - 14, 0); |
|||
values.put(ts - 12, 0); |
|||
values.put(ts - 10, 0); |
|||
argumentEntry.setTsRecords(values); |
|||
|
|||
state.arguments = new HashMap<>(Map.of("deviceTemperature", argumentEntry, "assetHumidity", assetHumidityArgEntry)); |
|||
|
|||
CalculatedFieldResult result = state.performCalculation(ctx).get(); |
|||
|
|||
assertThat(result).isNotNull(); |
|||
Output output = getCalculatedFieldConfig().getOutput(); |
|||
assertThat(result.getType()).isEqualTo(output.getType()); |
|||
assertThat(result.getScope()).isEqualTo(output.getScope()); |
|||
assertThat(result.getResultMap()).isEqualTo(Map.of("averageDeviceTemperature", 0.0, "assetHumidity", 43)); |
|||
} |
|||
|
|||
@Test |
|||
void testIsReadyWhenNotAllArgPresent() { |
|||
assertThat(state.isReady()).isFalse(); |
|||
} |
|||
|
|||
@Test |
|||
void testIsReadyWhenAllArgPresent() { |
|||
state.arguments = new HashMap<>(Map.of("deviceTemperature", deviceTemperatureArgEntry, "assetHumidity", assetHumidityArgEntry)); |
|||
|
|||
assertThat(state.isReady()).isTrue(); |
|||
} |
|||
|
|||
@Test |
|||
void testIsReadyWhenEmptyEntryPresents() { |
|||
state.arguments = new HashMap<>(Map.of("deviceTemperature", TsRollingArgumentEntry.EMPTY, "assetHumidity", assetHumidityArgEntry)); |
|||
|
|||
assertThat(state.isReady()).isFalse(); |
|||
} |
|||
|
|||
private TsRollingArgumentEntry createRollingArgEntry() { |
|||
TsRollingArgumentEntry argumentEntry = new TsRollingArgumentEntry(); |
|||
long ts = System.currentTimeMillis(); |
|||
|
|||
TreeMap<Long, Object> values = new TreeMap<>(); |
|||
values.put(ts - 40, 10); |
|||
values.put(ts - 30, 12); |
|||
values.put(ts - 20, 17); |
|||
|
|||
argumentEntry.setTsRecords(values); |
|||
return argumentEntry; |
|||
} |
|||
|
|||
private CalculatedField getCalculatedField() { |
|||
CalculatedField calculatedField = new CalculatedField(); |
|||
calculatedField.setTenantId(TENANT_ID); |
|||
calculatedField.setEntityId(ASSET_ID); |
|||
calculatedField.setType(CalculatedFieldType.SCRIPT); |
|||
calculatedField.setName("Test Calculated Field"); |
|||
calculatedField.setConfigurationVersion(1); |
|||
calculatedField.setConfiguration(getCalculatedFieldConfig()); |
|||
calculatedField.setVersion(1L); |
|||
return calculatedField; |
|||
} |
|||
|
|||
private CalculatedFieldConfiguration getCalculatedFieldConfig() { |
|||
SimpleCalculatedFieldConfiguration config = new SimpleCalculatedFieldConfiguration(); |
|||
|
|||
Argument argument1 = new Argument(); |
|||
argument1.setRefEntityId(DEVICE_ID); |
|||
ReferencedEntityKey refEntityKey1 = new ReferencedEntityKey("temperature", ArgumentType.TS_ROLLING, null); |
|||
argument1.setRefEntityKey(refEntityKey1); |
|||
argument1.setLimit(5); |
|||
argument1.setTimeWindow(30000); |
|||
|
|||
Argument argument2 = new Argument(); |
|||
ReferencedEntityKey refEntityKey2 = new ReferencedEntityKey("humidity", ArgumentType.TS_LATEST, null); |
|||
argument1.setRefEntityKey(refEntityKey2); |
|||
|
|||
config.setArguments(Map.of("deviceTemperature", argument1, "assetHumidity", argument2)); |
|||
|
|||
config.setExpression("var result = 0; foreach(element : deviceTemperature.entrySet()) { result += element.getValue(); } var map = {}; map.put(\"averageDeviceTemperature\", result / deviceTemperature.size()); map.put(\"assetHumidity\", assetHumidity); return map;"); |
|||
|
|||
Output output = new Output(); |
|||
output.setType(OutputType.ATTRIBUTES); |
|||
output.setScope(AttributeScope.SERVER_SCOPE); |
|||
|
|||
config.setOutput(output); |
|||
|
|||
return config; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,213 @@ |
|||
/** |
|||
* Copyright © 2016-2024 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.cf.ctx.state; |
|||
|
|||
import org.junit.jupiter.api.BeforeEach; |
|||
import org.junit.jupiter.api.Test; |
|||
import org.thingsboard.server.common.data.AttributeScope; |
|||
import org.thingsboard.server.common.data.cf.CalculatedField; |
|||
import org.thingsboard.server.common.data.cf.CalculatedFieldType; |
|||
import org.thingsboard.server.common.data.cf.configuration.Argument; |
|||
import org.thingsboard.server.common.data.cf.configuration.ArgumentType; |
|||
import org.thingsboard.server.common.data.cf.configuration.CalculatedFieldConfiguration; |
|||
import org.thingsboard.server.common.data.cf.configuration.Output; |
|||
import org.thingsboard.server.common.data.cf.configuration.OutputType; |
|||
import org.thingsboard.server.common.data.cf.configuration.ReferencedEntityKey; |
|||
import org.thingsboard.server.common.data.cf.configuration.SimpleCalculatedFieldConfiguration; |
|||
import org.thingsboard.server.common.data.id.AssetId; |
|||
import org.thingsboard.server.common.data.id.DeviceId; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.service.cf.CalculatedFieldResult; |
|||
|
|||
import java.util.HashMap; |
|||
import java.util.Map; |
|||
import java.util.UUID; |
|||
import java.util.concurrent.ExecutionException; |
|||
|
|||
import static org.assertj.core.api.Assertions.assertThat; |
|||
import static org.assertj.core.api.Assertions.assertThatThrownBy; |
|||
|
|||
public class SimpleCalculatedFieldStateTest { |
|||
|
|||
private final TenantId TENANT_ID = TenantId.fromUUID(UUID.fromString("5b18e321-3327-4290-b996-d72a65e90382")); |
|||
private final DeviceId DEVICE_ID = new DeviceId(UUID.fromString("5512071d-5abc-411d-a907-4cdb6539c2eb")); |
|||
private final AssetId ASSET_ID = new AssetId(UUID.fromString("5bc010ae-bcfd-46c8-98b9-8ee8c8955a76")); |
|||
|
|||
private final SingleValueArgumentEntry key1ArgEntry = new SingleValueArgumentEntry(System.currentTimeMillis() - 10, 11, 145L); |
|||
private final SingleValueArgumentEntry key2ArgEntry = new SingleValueArgumentEntry(System.currentTimeMillis() - 6, 15, 165L); |
|||
private final SingleValueArgumentEntry key3ArgEntry = new SingleValueArgumentEntry(System.currentTimeMillis() - 3, 23, 184L); |
|||
|
|||
private SimpleCalculatedFieldState state; |
|||
private CalculatedFieldCtx ctx; |
|||
|
|||
@BeforeEach |
|||
void setUp() { |
|||
ctx = new CalculatedFieldCtx(getCalculatedField(), null); |
|||
state = new SimpleCalculatedFieldState(ctx.getArgNames()); |
|||
} |
|||
|
|||
@Test |
|||
void testType() { |
|||
assertThat(state.getType()).isEqualTo(CalculatedFieldType.SIMPLE); |
|||
} |
|||
|
|||
@Test |
|||
void testUpdateState() { |
|||
state.arguments = new HashMap<>(Map.of( |
|||
"key1", key1ArgEntry, |
|||
"key2", key2ArgEntry |
|||
)); |
|||
|
|||
Map<String, ArgumentEntry> newArgs = Map.of("key3", key3ArgEntry); |
|||
boolean stateUpdated = state.updateState(newArgs); |
|||
|
|||
assertThat(stateUpdated).isTrue(); |
|||
assertThat(state.getArguments()).containsExactlyInAnyOrderEntriesOf( |
|||
Map.of( |
|||
"key1", key1ArgEntry, |
|||
"key2", key2ArgEntry, |
|||
"key3", key3ArgEntry |
|||
) |
|||
); |
|||
} |
|||
|
|||
@Test |
|||
void testUpdateStateWhenUpdateExistingEntry() { |
|||
state.arguments = new HashMap<>(Map.of("key1", key1ArgEntry)); |
|||
|
|||
SingleValueArgumentEntry newArgEntry = new SingleValueArgumentEntry(System.currentTimeMillis(), 18, 190L); |
|||
Map<String, ArgumentEntry> newArgs = Map.of("key1", newArgEntry); |
|||
boolean stateUpdated = state.updateState(newArgs); |
|||
|
|||
assertThat(stateUpdated).isTrue(); |
|||
assertThat(state.getArguments()).containsExactlyInAnyOrderEntriesOf(Map.of("key1", newArgEntry)); |
|||
} |
|||
|
|||
@Test |
|||
void testUpdateStateWhenRollingEntryPassed() { |
|||
state.arguments = new HashMap<>(Map.of( |
|||
"key1", key1ArgEntry, |
|||
"key2", key2ArgEntry |
|||
)); |
|||
|
|||
Map<String, ArgumentEntry> newArgs = Map.of("key3", TsRollingArgumentEntry.EMPTY); |
|||
assertThatThrownBy(() -> state.updateState(newArgs)) |
|||
.isInstanceOf(IllegalArgumentException.class) |
|||
.hasMessage("Rolling argument entry is not supported for simple calculated fields."); |
|||
} |
|||
|
|||
@Test |
|||
void testPerformCalculation() throws ExecutionException, InterruptedException { |
|||
state.arguments = new HashMap<>(Map.of( |
|||
"key1", key1ArgEntry, |
|||
"key2", key2ArgEntry, |
|||
"key3", key3ArgEntry |
|||
)); |
|||
|
|||
CalculatedFieldResult result = state.performCalculation(ctx).get(); |
|||
|
|||
assertThat(result).isNotNull(); |
|||
Output output = getCalculatedFieldConfig().getOutput(); |
|||
assertThat(result.getType()).isEqualTo(output.getType()); |
|||
assertThat(result.getScope()).isEqualTo(output.getScope()); |
|||
assertThat(result.getResultMap()).isEqualTo(Map.of("output", 49.0)); |
|||
} |
|||
|
|||
@Test |
|||
void testPerformCalculationWhenPassedNotNumber() { |
|||
state.arguments = new HashMap<>(Map.of( |
|||
"key1", key1ArgEntry, |
|||
"key2", new SingleValueArgumentEntry(System.currentTimeMillis() - 9, "string", 124L), |
|||
"key3", key3ArgEntry |
|||
)); |
|||
|
|||
assertThatThrownBy(() -> state.performCalculation(ctx)) |
|||
.isInstanceOf(IllegalArgumentException.class) |
|||
.hasMessage("Argument 'key2' is not a number."); |
|||
} |
|||
|
|||
@Test |
|||
void testIsReadyWhenNotAllArgPresent() { |
|||
assertThat(state.isReady()).isFalse(); |
|||
} |
|||
|
|||
@Test |
|||
void testIsReadyWhenAllArgPresent() { |
|||
state.arguments = new HashMap<>(Map.of( |
|||
"key1", key1ArgEntry, |
|||
"key2", key2ArgEntry, |
|||
"key3", key3ArgEntry |
|||
)); |
|||
|
|||
assertThat(state.isReady()).isTrue(); |
|||
} |
|||
|
|||
@Test |
|||
void testIsReadyWhenEmptyEntryPresents() { |
|||
state.arguments = new HashMap<>(Map.of( |
|||
"key1", key1ArgEntry, |
|||
"key2", key2ArgEntry |
|||
)); |
|||
state.getArguments().put("key3", SingleValueArgumentEntry.EMPTY); |
|||
|
|||
assertThat(state.isReady()).isFalse(); |
|||
} |
|||
|
|||
private CalculatedField getCalculatedField() { |
|||
CalculatedField calculatedField = new CalculatedField(); |
|||
calculatedField.setTenantId(TENANT_ID); |
|||
calculatedField.setEntityId(DEVICE_ID); |
|||
calculatedField.setType(CalculatedFieldType.SIMPLE); |
|||
calculatedField.setName("Test Calculated Field"); |
|||
calculatedField.setConfigurationVersion(1); |
|||
calculatedField.setConfiguration(getCalculatedFieldConfig()); |
|||
calculatedField.setVersion(1L); |
|||
return calculatedField; |
|||
} |
|||
|
|||
private CalculatedFieldConfiguration getCalculatedFieldConfig() { |
|||
SimpleCalculatedFieldConfiguration config = new SimpleCalculatedFieldConfiguration(); |
|||
|
|||
Argument argument1 = new Argument(); |
|||
argument1.setRefEntityId(ASSET_ID); |
|||
ReferencedEntityKey refEntityKey1 = new ReferencedEntityKey("temp1", ArgumentType.TS_LATEST, null); |
|||
argument1.setRefEntityKey(refEntityKey1); |
|||
|
|||
Argument argument2 = new Argument(); |
|||
argument2.setRefEntityId(ASSET_ID); |
|||
ReferencedEntityKey refEntityKey2 = new ReferencedEntityKey("temp2", ArgumentType.ATTRIBUTE, null); |
|||
argument2.setRefEntityKey(refEntityKey2); |
|||
|
|||
Argument argument3 = new Argument(); |
|||
argument3.setRefEntityId(ASSET_ID); |
|||
ReferencedEntityKey refEntityKey3 = new ReferencedEntityKey("temp3", ArgumentType.TS_LATEST, null); |
|||
argument3.setRefEntityKey(refEntityKey3); |
|||
|
|||
config.setArguments(Map.of("key1", argument1, "key2", argument2, "key3", argument3)); |
|||
|
|||
config.setExpression("key1 + key2 + key3"); |
|||
|
|||
Output output = new Output(); |
|||
output.setName("output"); |
|||
output.setType(OutputType.ATTRIBUTES); |
|||
output.setScope(AttributeScope.SERVER_SCOPE); |
|||
|
|||
config.setOutput(output); |
|||
|
|||
return config; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,71 @@ |
|||
/** |
|||
* Copyright © 2016-2024 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.cf.ctx.state; |
|||
|
|||
import org.junit.jupiter.api.BeforeEach; |
|||
import org.junit.jupiter.api.Test; |
|||
|
|||
import static org.assertj.core.api.Assertions.assertThat; |
|||
import static org.assertj.core.api.Assertions.assertThatThrownBy; |
|||
|
|||
public class SingleValueArgumentEntryTest { |
|||
|
|||
private SingleValueArgumentEntry entry; |
|||
|
|||
private final long ts = System.currentTimeMillis(); |
|||
|
|||
@BeforeEach |
|||
void setUp() { |
|||
entry = new SingleValueArgumentEntry(ts, 11, 363L); |
|||
} |
|||
|
|||
@Test |
|||
void testArgumentEntryType() { |
|||
assertThat(entry.getType()).isEqualTo(ArgumentEntryType.SINGLE_VALUE); |
|||
} |
|||
|
|||
@Test |
|||
void testUpdateEntryWhenRollingEntryPassed() { |
|||
assertThatThrownBy(() -> entry.updateEntry(TsRollingArgumentEntry.EMPTY)) |
|||
.isInstanceOf(IllegalArgumentException.class) |
|||
.hasMessage("Unsupported argument entry type for single value argument entry: " + ArgumentEntryType.TS_ROLLING); |
|||
} |
|||
|
|||
@Test |
|||
void testUpdateEntryWithThaSameTs() { |
|||
assertThat(entry.updateEntry(new SingleValueArgumentEntry(ts, 13, 363L))).isFalse(); |
|||
} |
|||
|
|||
@Test |
|||
void testUpdateEntryWhenNewVersionIsNull() { |
|||
assertThat(entry.updateEntry(new SingleValueArgumentEntry(ts + 16, 13, null))).isTrue(); |
|||
assertThat(entry.getValue()).isEqualTo(13); |
|||
assertThat(entry.getVersion()).isNull(); |
|||
} |
|||
|
|||
@Test |
|||
void testUpdateEntryWhenNewVersionIsGreaterThanCurrent() { |
|||
assertThat(entry.updateEntry(new SingleValueArgumentEntry(ts + 18, 18, 369L))).isTrue(); |
|||
assertThat(entry.getValue()).isEqualTo(18); |
|||
assertThat(entry.getVersion()).isEqualTo(369L); |
|||
} |
|||
|
|||
@Test |
|||
void testUpdateEntryWhenNewVersionIsLessThanCurrent() { |
|||
assertThat(entry.updateEntry(new SingleValueArgumentEntry(ts + 18, 18, 234L))).isFalse(); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,93 @@ |
|||
/** |
|||
* Copyright © 2016-2024 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.cf.ctx.state; |
|||
|
|||
import org.junit.jupiter.api.BeforeEach; |
|||
import org.junit.jupiter.api.Test; |
|||
|
|||
import java.util.Map; |
|||
import java.util.TreeMap; |
|||
|
|||
import static org.assertj.core.api.Assertions.assertThat; |
|||
import static org.assertj.core.api.Assertions.assertThatThrownBy; |
|||
|
|||
public class TsRollingArgumentEntryTest { |
|||
|
|||
private TsRollingArgumentEntry entry; |
|||
|
|||
private final long ts = System.currentTimeMillis(); |
|||
|
|||
@BeforeEach |
|||
void setUp() { |
|||
TreeMap<Long, Object> values = new TreeMap<>(); |
|||
values.put(ts - 40, 10); |
|||
values.put(ts - 30, 12); |
|||
values.put(ts - 20, 17); |
|||
|
|||
entry = new TsRollingArgumentEntry(values); |
|||
} |
|||
|
|||
@Test |
|||
void testArgumentEntryType() { |
|||
assertThat(entry.getType()).isEqualTo(ArgumentEntryType.TS_ROLLING); |
|||
} |
|||
|
|||
@Test |
|||
void testUpdateEntryWhenSingleValueEntryPassed() { |
|||
SingleValueArgumentEntry newEntry = new SingleValueArgumentEntry(ts - 10, 23, 123L); |
|||
|
|||
assertThat(entry.updateEntry(newEntry)).isTrue(); |
|||
assertThat(entry.getTsRecords()).hasSize(4); |
|||
assertThat(entry.getTsRecords().get(ts - 10)).isEqualTo(23); |
|||
} |
|||
|
|||
@Test |
|||
void testUpdateEntryWhenSingleValueEntryWithTheSameTsPassed() { |
|||
SingleValueArgumentEntry newEntry = new SingleValueArgumentEntry(ts - 20, 23, 123L); |
|||
|
|||
assertThat(entry.updateEntry(newEntry)).isFalse(); |
|||
} |
|||
|
|||
@Test |
|||
void testUpdateEntryWhenRollingEntryPassed() { |
|||
TsRollingArgumentEntry newEntry = new TsRollingArgumentEntry(); |
|||
TreeMap<Long, Object> values = new TreeMap<>(); |
|||
values.put(ts - 20, 16); |
|||
values.put(ts - 10, 7); |
|||
values.put(ts - 5, 1); |
|||
newEntry.setTsRecords(values); |
|||
|
|||
assertThat(entry.updateEntry(newEntry)).isTrue(); |
|||
assertThat(entry.getTsRecords()).hasSize(5); |
|||
assertThat(entry.getTsRecords()).isEqualTo(Map.of( |
|||
ts - 40, 10, |
|||
ts - 30, 12, |
|||
ts - 20, 17, |
|||
ts - 10, 7, |
|||
ts - 5, 1 |
|||
)); |
|||
} |
|||
|
|||
@Test |
|||
void testUpdateEntryWhenValueIsNotNumber() { |
|||
SingleValueArgumentEntry newEntry = new SingleValueArgumentEntry(ts - 10, "string", 123L); |
|||
|
|||
assertThatThrownBy(() -> entry.updateEntry(newEntry)) |
|||
.isInstanceOf(IllegalArgumentException.class) |
|||
.hasMessage("Argument type " + ArgumentEntryType.TS_ROLLING + " only supports numeric values."); |
|||
} |
|||
|
|||
} |
|||
Loading…
Reference in new issue