Skip to content

Commit e00e79b

Browse files
committed
added tests
1 parent ce26883 commit e00e79b

5 files changed

Lines changed: 617 additions & 0 deletions

File tree

ce/src/test/java/org/thingsboard/client/api/CalculatedFieldApiTest.java

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,13 @@
1717

1818
import org.junit.jupiter.api.Test;
1919
import org.thingsboard.client.ApiException;
20+
import org.thingsboard.client.model.AlarmCalculatedFieldConfiguration;
21+
import org.thingsboard.client.model.AlarmConditionValueAlarmRuleSchedule;
22+
import org.thingsboard.client.model.AlarmRuleDefinition;
23+
import org.thingsboard.client.model.AlarmRuleSchedule;
24+
import org.thingsboard.client.model.AlarmRuleSimpleCondition;
25+
import org.thingsboard.client.model.AlarmRuleSpecificTimeSchedule;
26+
import org.thingsboard.client.model.AlarmSeverity;
2027
import org.thingsboard.client.model.Argument;
2128
import org.thingsboard.client.model.ArgumentType;
2229
import org.thingsboard.client.model.CalculatedField;
@@ -27,14 +34,18 @@
2734
import org.thingsboard.client.model.PageDataCalculatedField;
2835
import org.thingsboard.client.model.ReferencedEntityKey;
2936
import org.thingsboard.client.model.SimpleCalculatedFieldConfiguration;
37+
import org.thingsboard.client.model.TbelAlarmConditionExpression;
3038
import org.thingsboard.client.model.TimeSeriesOutput;
3139

3240
import java.util.ArrayList;
3341
import java.util.List;
42+
import java.util.Map;
43+
import java.util.Set;
3444
import java.util.UUID;
3545

3646
import static org.junit.jupiter.api.Assertions.assertEquals;
3747
import static org.junit.jupiter.api.Assertions.assertNotNull;
48+
import static org.junit.jupiter.api.Assertions.assertTrue;
3849

3950

4051
public class CalculatedFieldApiTest extends AbstractApiTest {
@@ -172,4 +183,116 @@ void testCalculatedFieldLifecycle() throws ApiException {
172183
assertEquals(4, device1FieldsAfterDelete.getData().size());
173184
}
174185

186+
@Test
187+
void testAlarmCalculatedFieldLifecycle() throws ApiException {
188+
long timestamp = System.currentTimeMillis();
189+
190+
// create a device to attach the alarm calculated field to
191+
Device device = new Device();
192+
device.setName("AlarmCalcFieldDevice_" + timestamp);
193+
device.setType("default");
194+
Device createdDevice = client.saveDevice(device, null, null, null, null, null);
195+
196+
// build the alarm calculated field configuration
197+
AlarmCalculatedFieldConfiguration config = new AlarmCalculatedFieldConfiguration();
198+
199+
// argument: temperature time-series
200+
Argument tempArg = new Argument();
201+
ReferencedEntityKey refKey = new ReferencedEntityKey();
202+
refKey.setKey("temperature");
203+
refKey.setType(ArgumentType.TS_LATEST);
204+
tempArg.setRefEntityKey(refKey);
205+
config.putArgumentsItem("temp", tempArg);
206+
207+
// create rule: HIGH_TEMPERATURE when temp > 50 (TBEL expression)
208+
TbelAlarmConditionExpression createExpression = new TbelAlarmConditionExpression();
209+
createExpression.setExpression("return temp > 50;");
210+
AlarmRuleSimpleCondition createCondition = new AlarmRuleSimpleCondition();
211+
createCondition.setExpression(createExpression);
212+
AlarmRuleSpecificTimeSchedule specificTimeSchedule = new AlarmRuleSpecificTimeSchedule().addDaysOfWeekItem(3);
213+
AlarmConditionValueAlarmRuleSchedule schedule = new AlarmConditionValueAlarmRuleSchedule().staticValue(specificTimeSchedule);
214+
createCondition.setSchedule(schedule);
215+
AlarmRuleDefinition createRule = new AlarmRuleDefinition();
216+
createRule.setCondition(createCondition);
217+
createRule.setAlarmDetails("Temperature is too high: ${temp}");
218+
config.setCreateRules(Map.of(
219+
AlarmSeverity.CRITICAL.name(), createRule
220+
));
221+
222+
// clear rule: when temp drops below 30
223+
TbelAlarmConditionExpression clearExpression = new TbelAlarmConditionExpression();
224+
clearExpression.setExpression("return temp < 30;");
225+
AlarmRuleSimpleCondition clearCondition = new AlarmRuleSimpleCondition();
226+
clearCondition.setExpression(clearExpression);
227+
AlarmRuleDefinition clearRule = new AlarmRuleDefinition();
228+
clearRule.setCondition(clearCondition);
229+
config.setClearRule(clearRule);
230+
231+
config.setPropagate(true);
232+
config.setPropagateToOwner(false);
233+
234+
// create calculated field
235+
CalculatedField cf = new CalculatedField();
236+
cf.setName(TEST_PREFIX + "AlarmCalcField_" + timestamp);
237+
cf.setType(CalculatedFieldType.ALARM);
238+
239+
EntityId entityId = new EntityId();
240+
entityId.setEntityType(EntityType.DEVICE);
241+
entityId.setId(createdDevice.getId().getId());
242+
cf.setEntityId(entityId);
243+
cf.setConfiguration(config);
244+
245+
CalculatedField created = client.saveCalculatedField(cf);
246+
assertNotNull(created);
247+
assertNotNull(created.getId());
248+
assertEquals(cf.getName(), created.getName());
249+
assertEquals(CalculatedFieldType.ALARM, created.getType());
250+
AlarmCalculatedFieldConfiguration configuration = (AlarmCalculatedFieldConfiguration) created.getConfiguration();
251+
AlarmConditionValueAlarmRuleSchedule createdSchedule = configuration.getCreateRules().get(AlarmSeverity.CRITICAL.name()).getCondition().getSchedule();
252+
AlarmRuleSpecificTimeSchedule staticSchedule = (AlarmRuleSpecificTimeSchedule)createdSchedule.getStaticValue();
253+
assertEquals(Set.of(3), staticSchedule.getDaysOfWeek());
254+
255+
// get by id and verify configuration
256+
CalculatedField fetched = client.getCalculatedFieldById(created.getId().getId().toString());
257+
assertNotNull(fetched);
258+
assertEquals(created.getName(), fetched.getName());
259+
assertEquals(CalculatedFieldType.ALARM, fetched.getType());
260+
assertNotNull(fetched.getConfiguration());
261+
AlarmCalculatedFieldConfiguration fetchedConfig =
262+
(AlarmCalculatedFieldConfiguration) fetched.getConfiguration();
263+
assertNotNull(fetchedConfig.getCreateRules());
264+
assertEquals(1, fetchedConfig.getCreateRules().size());
265+
assertTrue(fetchedConfig.getCreateRules().containsKey("CRITICAL"));
266+
assertNotNull(fetchedConfig.getClearRule());
267+
assertEquals(Boolean.TRUE, fetchedConfig.getPropagate());
268+
269+
// update: add a second create rule for CRITICAL_TEMPERATURE
270+
TbelAlarmConditionExpression criticalExpression = new TbelAlarmConditionExpression();
271+
criticalExpression.setExpression("return temp > 80;");
272+
AlarmRuleSimpleCondition criticalCondition = new AlarmRuleSimpleCondition();
273+
criticalCondition.setExpression(criticalExpression);
274+
AlarmRuleDefinition criticalRule = new AlarmRuleDefinition();
275+
criticalRule.setCondition(criticalCondition);
276+
fetchedConfig.putCreateRulesItem(AlarmSeverity.INDETERMINATE.name(), criticalRule);
277+
fetched.setConfiguration(fetchedConfig);
278+
279+
CalculatedField updated = client.saveCalculatedField(fetched);
280+
AlarmCalculatedFieldConfiguration updatedConfig =
281+
(AlarmCalculatedFieldConfiguration) updated.getConfiguration();
282+
assertEquals(2, updatedConfig.getCreateRules().size());
283+
assertTrue(updatedConfig.getCreateRules().containsKey("INDETERMINATE"));
284+
285+
// filter by entity and ALARM type
286+
PageDataCalculatedField deviceFields = client.getCalculatedFieldsByEntityIdV2(
287+
EntityType.DEVICE.toString(), createdDevice.getId().getId().toString(),
288+
100, 0, CalculatedFieldType.ALARM, null, null, null);
289+
assertNotNull(deviceFields);
290+
assertEquals(1, deviceFields.getData().size());
291+
292+
// delete and verify
293+
UUID fieldId = created.getId().getId();
294+
client.deleteCalculatedField(fieldId.toString());
295+
assertReturns404(() -> client.getCalculatedFieldById(fieldId.toString()));
296+
}
297+
175298
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/**
2+
* Copyright © 2026-2026 ThingsBoard, Inc.
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.client.api;
17+
18+
import org.junit.jupiter.api.Test;
19+
import org.thingsboard.client.ApiException;
20+
import org.thingsboard.client.model.Device;
21+
22+
import static org.junit.jupiter.api.Assertions.assertEquals;
23+
24+
public class RpcV1ApiTest extends AbstractApiTest {
25+
26+
// Persistent RPC body: the server stores the RPC and immediately returns
27+
// {"rpcId": "..."} without waiting for the device to be online.
28+
private static final String ONE_WAY_BODY =
29+
"{\"method\":\"setGpio\",\"params\":{\"pin\":7,\"value\":1},\"persistent\":true}";
30+
private static final String TWO_WAY_BODY =
31+
"{\"method\":\"getGpio\",\"params\":{\"pin\":7},\"persistent\":true}";
32+
33+
private Device createDevice(String name) throws ApiException {
34+
Device device = new Device();
35+
device.setName(name);
36+
device.setType("default");
37+
return client.saveDevice(device, null, null, null, null, null);
38+
}
39+
40+
// -------------------------------------------------------------------------
41+
// handleOneWayDeviceRPCRequest
42+
// -------------------------------------------------------------------------
43+
44+
@Test
45+
void testHandleOneWayDeviceRPCRequest() throws ApiException {
46+
long ts = System.currentTimeMillis();
47+
Device device = createDevice(TEST_PREFIX + ts);
48+
String deviceId = device.getId().getId().toString();
49+
50+
// With persistent=true the server stores the RPC and immediately
51+
// responds 200 with {"rpcId":"..."}. The generated client declares
52+
// String as the return type and fails to deserialize the JSON object,
53+
// wrapping the IOException in ApiException with code 0.
54+
try {
55+
client.handleOneWayDeviceRPCRequest(deviceId, ONE_WAY_BODY);
56+
} catch (ApiException e) {
57+
assertEquals(0, e.getCode(),
58+
"handleOneWayDeviceRPCRequest got an unexpected HTTP error: " + e.getCode());
59+
}
60+
61+
client.deleteDevice(deviceId);
62+
}
63+
64+
// -------------------------------------------------------------------------
65+
// handleTwoWayDeviceRPCRequest
66+
// -------------------------------------------------------------------------
67+
68+
@Test
69+
void testHandleTwoWayDeviceRPCRequest() throws ApiException {
70+
long ts = System.currentTimeMillis();
71+
Device device = createDevice(TEST_PREFIX + ts);
72+
String deviceId = device.getId().getId().toString();
73+
74+
// Same behaviour as one-way: persistent=true returns {"rpcId":"..."}
75+
// immediately; client type mismatch produces ApiException(code=0).
76+
try {
77+
client.handleTwoWayDeviceRPCRequest(deviceId, TWO_WAY_BODY);
78+
} catch (ApiException e) {
79+
assertEquals(0, e.getCode(),
80+
"handleTwoWayDeviceRPCRequest got an unexpected HTTP error: " + e.getCode());
81+
}
82+
83+
client.deleteDevice(deviceId);
84+
}
85+
86+
}

0 commit comments

Comments
 (0)