From fdd01dfc9d41ec7890b68ce62d6421a7f95a1dc5 Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Thu, 7 Aug 2025 12:01:52 +0300 Subject: [PATCH 01/16] fixed flaky edqs tests --- .../controller/EntityQueryControllerTest.java | 34 +++++++++++++------ 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/controller/EntityQueryControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/EntityQueryControllerTest.java index ee18543796..7b33c88062 100644 --- a/application/src/test/java/org/thingsboard/server/controller/EntityQueryControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/EntityQueryControllerTest.java @@ -278,8 +278,7 @@ public class EntityQueryControllerTest extends AbstractControllerTest { assetTypeFilter.setEntityType(EntityType.ASSET); AlarmCountQuery assetAlarmQuery = new AlarmCountQuery(assetTypeFilter); - Long assetAlamCount = doPostWithResponse("/api/alarmsQuery/count", assetAlarmQuery, Long.class); - Assert.assertEquals(assets.size(), assetAlamCount.longValue()); + countAlarmsByQueryAndCheck(assetAlarmQuery, assets.size()); KeyFilter nameFilter = buildStringKeyFilter(EntityKeyType.ENTITY_FIELD, "name", StringFilterPredicate.StringOperation.STARTS_WITH, "Asset1"); List keyFilters = Collections.singletonList(nameFilter); @@ -369,8 +368,7 @@ public class EntityQueryControllerTest extends AbstractControllerTest { assetTypeFilter.setEntityType(EntityType.ASSET); AlarmCountQuery assetAlarmQuery = new AlarmCountQuery(assetTypeFilter); - Long assetAlamCount = doPostWithResponse("/api/alarmsQuery/count", assetAlarmQuery, Long.class); - Assert.assertEquals(10, assetAlamCount.longValue()); + countAlarmsByQueryAndCheck(assetAlarmQuery, 10); KeyFilter nameFilter = buildStringKeyFilter(EntityKeyType.ENTITY_FIELD, "name", StringFilterPredicate.StringOperation.STARTS_WITH, "Asset1"); List keyFilters = Collections.singletonList(nameFilter); @@ -438,9 +436,7 @@ public class EntityQueryControllerTest extends AbstractControllerTest { assetTypeFilter.setEntityType(EntityType.ASSET); AlarmDataQuery assetAlarmQuery = new AlarmDataQuery(assetTypeFilter, pageLink, null, null, null, alarmFields); - PageData alarmPageData = doPostWithTypedResponse("/api/alarmsQuery/find", assetAlarmQuery, new TypeReference<>() { - }); - Assert.assertEquals(10, alarmPageData.getTotalElements()); + PageData alarmPageData = findAlarmsByQueryAndCheck(assetAlarmQuery, 10); List retrievedAlarmTypes = alarmPageData.getData().stream().map(Alarm::getType).toList(); assertThat(retrievedAlarmTypes).containsExactlyInAnyOrderElementsOf(assetAlarmTypes); @@ -511,9 +507,7 @@ public class EntityQueryControllerTest extends AbstractControllerTest { assetTypeFilter.setEntityType(EntityType.ASSET); AlarmDataQuery assetAlarmQuery = new AlarmDataQuery(assetTypeFilter, pageLink, null, null, null, Collections.emptyList()); - PageData alarmPageData = doPostWithTypedResponse("/api/alarmsQuery/find", assetAlarmQuery, new TypeReference<>() { - }); - Assert.assertEquals(10, alarmPageData.getTotalElements()); + PageData alarmPageData = findAlarmsByQueryAndCheck(assetAlarmQuery, 10); List retrievedAlarmTypes = alarmPageData.getData().stream().map(Alarm::getType).toList(); assertThat(retrievedAlarmTypes).containsExactlyInAnyOrderElementsOf(assetAlarmTypes); @@ -1141,22 +1135,42 @@ public class EntityQueryControllerTest extends AbstractControllerTest { }); } + protected PageData findAlarmsByQuery(AlarmDataQuery query) throws Exception { + return doPostWithTypedResponse("/api/alarmsQuery/find", query, new TypeReference<>() {}); + } + protected PageData findByQueryAndCheck(EntityDataQuery query, int expectedResultSize) throws Exception { PageData result = findByQuery(query); assertThat(result.getTotalElements()).isEqualTo(expectedResultSize); return result; } + protected PageData findAlarmsByQueryAndCheck(AlarmDataQuery query, int expectedResultSize) throws Exception { + PageData result = findAlarmsByQuery(query); + assertThat(result.getTotalElements()).isEqualTo(expectedResultSize); + return result; + } + protected Long countByQuery(EntityCountQuery countQuery) throws Exception { return doPostWithResponse("/api/entitiesQuery/count", countQuery, Long.class); } + protected Long countAlarmsByQuery(AlarmCountQuery countQuery) throws Exception { + return doPostWithResponse("/api/alarmsQuery/count", countQuery, Long.class); + } + protected Long countByQueryAndCheck(EntityCountQuery query, long expectedResult) throws Exception { Long result = countByQuery(query); assertThat(result).isEqualTo(expectedResult); return result; } + protected Long countAlarmsByQueryAndCheck(AlarmCountQuery query, long expectedResult) throws Exception { + Long result = countAlarmsByQuery(query); + assertThat(result).isEqualTo(expectedResult); + return result; + } + private KeyFilter getEntityFieldStringEqualToKeyFilter(String keyName, String value) { KeyFilter tenantOwnerNameFilter = new KeyFilter(); tenantOwnerNameFilter.setKey(new EntityKey(EntityKeyType.ENTITY_FIELD, keyName)); From 559b67b9216ff20b5383c1f75422e4560c9930df Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Fri, 8 Aug 2025 10:57:56 +0300 Subject: [PATCH 02/16] UI: minor fix for ai-models --- .../ai-model/ai-model-dialog.component.ts | 3 ++- .../external/ai-config.component.html | 27 +++++++++---------- .../rule-node/external/ai-config.component.ts | 9 +++---- .../ai-model-table-header.component.ts | 5 ++++ 4 files changed, 24 insertions(+), 20 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/ai-model/ai-model-dialog.component.ts b/ui-ngx/src/app/modules/home/components/ai-model/ai-model-dialog.component.ts index db5d1d7e23..c459d66f12 100644 --- a/ui-ngx/src/app/modules/home/components/ai-model/ai-model-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/ai-model/ai-model-dialog.component.ts @@ -36,6 +36,7 @@ import { import { AiModelService } from '@core/http/ai-model.service'; import { CheckConnectivityDialogComponent } from '@home/components/ai-model/check-connectivity-dialog.component'; import { map } from 'rxjs/operators'; +import { deepTrim } from '@core/utils'; export interface AIModelDialogData { AIModel?: AiModel; @@ -162,6 +163,6 @@ export class AIModelDialogComponent extends DialogComponent this.dialogRef.close(aiModel)); + this.aiModelService.saveAiModel(deepTrim(aiModel)).subscribe(aiModel => this.dialogRef.close(aiModel)); } } diff --git a/ui-ngx/src/app/modules/home/components/rule-node/external/ai-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/external/ai-config.component.html index 80519cea28..f590dae84f 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/external/ai-config.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/external/ai-config.component.html @@ -79,25 +79,24 @@
{{ 'rule-node-config.ai.response-format' | translate }}
- + {{ 'rule-node-config.ai.response-text' | translate }} {{ 'rule-node-config.ai.response-json' | translate }} {{ 'rule-node-config.ai.response-json-schema' | translate }} - @if (aiConfigForm.get('responseFormat.type').value === responseFormat.JSON_SCHEMA) { - - - - } + + +
diff --git a/ui-ngx/src/app/modules/home/components/rule-node/external/ai-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/external/ai-config.component.ts index 47313da81d..1ef8ebca72 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/external/ai-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/external/ai-config.component.ts @@ -23,6 +23,7 @@ import { AIModelDialogComponent, AIModelDialogData } from '@home/components/ai-m import { AiModel, AiRuleNodeResponseFormatTypeOnlyText, ResponseFormat } from '@shared/models/ai-model.models'; import { deepTrim } from '@core/utils'; import { TranslateService } from '@ngx-translate/core'; +import { jsonRequired } from '@shared/components/json-object-edit.component'; @Component({ selector: 'tb-external-node-ai-config', @@ -37,8 +38,6 @@ export class AiConfigComponent extends RuleNodeConfigurationComponent { responseFormat = ResponseFormat; - disabledResponseFormatType: boolean; - constructor(private fb: UntypedFormBuilder, private translate: TranslateService, private dialog: MatDialog) { @@ -56,7 +55,7 @@ export class AiConfigComponent extends RuleNodeConfigurationComponent { userPrompt: [configuration?.userPrompt ?? '', [Validators.required, Validators.maxLength(10000), Validators.pattern(/.*\S.*/)]], responseFormat: this.fb.group({ type: [configuration?.responseFormat?.type ?? ResponseFormat.JSON, []], - schema: [configuration?.responseFormat?.schema ?? null, [Validators.required]], + schema: [configuration?.responseFormat?.schema ?? null, [jsonRequired]], }), timeoutSeconds: [configuration?.timeoutSeconds ?? 60, []], forceAck: [configuration?.forceAck ?? true, []] @@ -88,10 +87,10 @@ export class AiConfigComponent extends RuleNodeConfigurationComponent { if (this.aiConfigForm.get('responseFormat.type').value !== ResponseFormat.TEXT) { this.aiConfigForm.get('responseFormat.type').patchValue(ResponseFormat.TEXT, {emitEvent: true}); } - this.disabledResponseFormatType = true; + this.aiConfigForm.get('responseFormat.type').disable(); } } else { - this.disabledResponseFormatType = false; + this.aiConfigForm.get('responseFormat.type').enable(); } } diff --git a/ui-ngx/src/app/modules/home/pages/ai-model/ai-model-table-header.component.ts b/ui-ngx/src/app/modules/home/pages/ai-model/ai-model-table-header.component.ts index 48889dc877..013642dcb2 100644 --- a/ui-ngx/src/app/modules/home/pages/ai-model/ai-model-table-header.component.ts +++ b/ui-ngx/src/app/modules/home/pages/ai-model/ai-model-table-header.component.ts @@ -23,6 +23,11 @@ import { AiModel } from '@shared/models/ai-model.models'; @Component({ selector: 'tb-ai-model-table-header', templateUrl: './ai-model-table-header.component.html', + styles: [` + :host { + width: 100%; + } + `], styleUrls: [] }) export class AiModelTableHeaderComponent extends EntityTableHeaderComponent { From c127b96bb30eceaf37ecb43e594900e9a8d223aa Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Fri, 8 Aug 2025 12:30:10 +0300 Subject: [PATCH 03/16] fixed flaky edqs tests --- .../controller/EdqsEntityQueryControllerTest.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/application/src/test/java/org/thingsboard/server/controller/EdqsEntityQueryControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/EdqsEntityQueryControllerTest.java index 89bbe5e499..655ab417e6 100644 --- a/application/src/test/java/org/thingsboard/server/controller/EdqsEntityQueryControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/EdqsEntityQueryControllerTest.java @@ -25,6 +25,9 @@ import org.thingsboard.server.common.data.edqs.EdqsState; import org.thingsboard.server.common.data.edqs.EdqsState.EdqsApiMode; import org.thingsboard.server.common.data.edqs.ToCoreEdqsRequest; import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.query.AlarmCountQuery; +import org.thingsboard.server.common.data.query.AlarmData; +import org.thingsboard.server.common.data.query.AlarmDataQuery; import org.thingsboard.server.common.data.query.EntityCountQuery; import org.thingsboard.server.common.data.query.EntityData; import org.thingsboard.server.common.data.query.EntityDataQuery; @@ -70,12 +73,24 @@ public class EdqsEntityQueryControllerTest extends EntityQueryControllerTest { result -> result.getTotalElements() == expectedResultSize); } + @Override + protected PageData findAlarmsByQueryAndCheck(AlarmDataQuery query, int expectedResultSize) { + return await().atMost(TIMEOUT, TimeUnit.SECONDS).until(() -> findAlarmsByQuery(query), + result -> result.getTotalElements() == expectedResultSize); + } + @Override protected Long countByQueryAndCheck(EntityCountQuery query, long expectedResult) { return await().atMost(TIMEOUT, TimeUnit.SECONDS).until(() -> countByQuery(query), result -> result == expectedResult); } + @Override + protected Long countAlarmsByQueryAndCheck(AlarmCountQuery query, long expectedResult) { + return await().atMost(TIMEOUT, TimeUnit.SECONDS).until(() -> countAlarmsByQuery(query), + result -> result == expectedResult); + } + @Test public void testEdqsState() throws Exception { loginSysAdmin(); From 13c0926e67f8435cb412b4c27921482f9edefc2c Mon Sep 17 00:00:00 2001 From: VIacheslavKlimov Date: Mon, 11 Aug 2025 12:18:27 +0300 Subject: [PATCH 04/16] Fix some MSA tests not running --- msa/black-box-tests/src/test/resources/connectivity.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/msa/black-box-tests/src/test/resources/connectivity.xml b/msa/black-box-tests/src/test/resources/connectivity.xml index 425fbd67eb..a366522ae0 100644 --- a/msa/black-box-tests/src/test/resources/connectivity.xml +++ b/msa/black-box-tests/src/test/resources/connectivity.xml @@ -1,7 +1,7 @@