From ca654c29e0aac43309f4d54307e4c1879a8a00b0 Mon Sep 17 00:00:00 2001 From: Dmitry Ermakovich Date: Tue, 22 Sep 2026 11:26:58 +0300 Subject: [PATCH] fix: TMS-41711 fix testRun api with workaround --- docs/tz-testrun-tags-and-links.md | 5 ++ gradle.properties | 2 +- .../java/ru/testit/clients/Converter.java | 6 +++ .../java/ru/testit/clients/TmsApiClient.java | 36 +++++++++++++- .../services/core/AdapterStartupHelper.java | 2 + .../AdapterStartupHelperTestRunMetaTest.java | 48 ++++++++++++++++++- 6 files changed, 96 insertions(+), 3 deletions(-) diff --git a/docs/tz-testrun-tags-and-links.md b/docs/tz-testrun-tags-and-links.md index ba765c29..17983dd4 100644 --- a/docs/tz-testrun-tags-and-links.md +++ b/docs/tz-testrun-tags-and-links.md @@ -172,3 +172,8 @@ Implemented in `testit-java-commons` (all Java adapters share this): - Config: `testRunTags` / `TMS_TEST_RUN_TAGS` / `tmsTestRunTags`, `testRunLinks` / `TMS_TEST_RUN_LINKS` / `tmsTestRunLinks` - Create path: tags/links on `createTestRun` - Existing run: early merge in `AdapterStartupHelper` at startup +- **TODO — TEMPORARY WORKAROUND (remove after TMS fix):** + `GET /adapters/testRuns/{id}` returns empty `links`/`attachments`. + `TmsApiClient.getTestRun` falls back to `GET /api/v2/testRuns/{id}` for merge-on-update + (adapters `PUT` replaces those collections; tags are merged server-side). + Switch back to adapters GET once it returns links/attachments. diff --git a/gradle.properties b/gradle.properties index 16ca5998..e2faa683 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -version=4.2.1 +version=4.2.2 org.gradle.daemon=true org.gradle.parallel=true diff --git a/testit-java-commons/src/main/java/ru/testit/clients/Converter.java b/testit-java-commons/src/main/java/ru/testit/clients/Converter.java index 4c65d80f..bbb98a4a 100644 --- a/testit-java-commons/src/main/java/ru/testit/clients/Converter.java +++ b/testit-java-commons/src/main/java/ru/testit/clients/Converter.java @@ -537,6 +537,9 @@ public static List convertTestRunUpdateLinks(List } public static List buildAssignAttachmentApiModels(List attachments) { + if (attachments == null || attachments.isEmpty()) { + return Collections.emptyList(); + } return attachments.stream().map( attachment -> { AssignAttachmentApiModel model = new AssignAttachmentApiModel(); @@ -549,6 +552,9 @@ public static List buildAssignAttachmentApiModels(List } public static List buildUpdateLinkApiModels(List links) { + if (links == null || links.isEmpty()) { + return Collections.emptyList(); + } return links.stream().map( link -> { UpdateLinkApiModel model = new UpdateLinkApiModel(); diff --git a/testit-java-commons/src/main/java/ru/testit/clients/TmsApiClient.java b/testit-java-commons/src/main/java/ru/testit/clients/TmsApiClient.java index 4081b6fa..60cafa15 100644 --- a/testit-java-commons/src/main/java/ru/testit/clients/TmsApiClient.java +++ b/testit-java-commons/src/main/java/ru/testit/clients/TmsApiClient.java @@ -2,6 +2,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import jakarta.ws.rs.core.GenericType; import ru.testit.adaptersapi.api.*; import ru.testit.adaptersapi.invoker.ApiClient; import ru.testit.adaptersapi.invoker.ApiException; @@ -25,6 +26,7 @@ public class TmsApiClient implements ITmsApiClient { private static final int TESTS_LIMIT = 100; private static final UUID EMPTY_TEST_POINT_ID = UUID.fromString("00000000-0000-0000-0000-000000000000"); + private final ApiClient apiClient; private final TestRunsApi testRunsApi; private final AutoTestsApi autoTestsApi; private final AttachmentsApi attachmentsApi; @@ -41,6 +43,7 @@ public TmsApiClient(ClientConfiguration config) { apiClient.setApiKey(config.getPrivateToken()); clientConfiguration = config; + this.apiClient = apiClient; testRunsApi = new TestRunsApi(apiClient); autoTestsApi = new AutoTestsApi(apiClient); attachmentsApi = new AttachmentsApi(apiClient); @@ -83,7 +86,38 @@ public TestRunApiResult createTestRun() throws ApiException { @Override public TestRunApiResult getTestRun(String uuid) throws ApiException { - return testRunsApi.adaptersTestRunsIdGet(UUID.fromString(uuid)); + // TODO(TEMPORARY WORKAROUND): remove getTestRunViaPublicApi once + // GET /adapters/testRuns/{id} returns links and attachments. + // Adapters GET currently stubs them as empty; UpdateEmptyTestRun then + // replaces those collections and wipes existing data on merge. + return getTestRunViaPublicApi(UUID.fromString(uuid)); + } + + /** + * FIXME: temporary workaround — delete this method when adapters GET is fixed. + * Until then, public API is the only source of real links/attachments for merge. + */ + private TestRunApiResult getTestRunViaPublicApi(UUID id) throws ApiException { + String path = "/api/v2/testRuns/" + apiClient.escapeString(id.toString()); + String accept = apiClient.selectHeaderAccept("application/json"); + String contentType = apiClient.selectHeaderContentType(); + String[] authNames = new String[]{"PrivateToken", "Identity.Application"}; + GenericType returnType = new GenericType() {}; + return apiClient.invokeAPI( + "TmsApiClient.getTestRunViaPublicApi", + path, + "GET", + new ArrayList<>(), + null, + new LinkedHashMap<>(), + new LinkedHashMap<>(), + new LinkedHashMap<>(), + accept, + contentType, + authNames, + returnType, + false + ).getData(); } @Override diff --git a/testit-java-commons/src/main/java/ru/testit/services/core/AdapterStartupHelper.java b/testit-java-commons/src/main/java/ru/testit/services/core/AdapterStartupHelper.java index ead86c96..3ecfdc93 100644 --- a/testit-java-commons/src/main/java/ru/testit/services/core/AdapterStartupHelper.java +++ b/testit-java-commons/src/main/java/ru/testit/services/core/AdapterStartupHelper.java @@ -95,6 +95,8 @@ private void updateTestRunMetadata() throws ApiException { } TestRunApiResult testRun = this.client.getTestRun(this.clientConfiguration.getTestRunId()); + // getTestRun uses a temporary v2 fallback (see TmsApiClient) until adapters GET + // returns links/attachments; without that, merge would wipe them on PUT. UpdateEmptyTestRunApiModel model = Converter.buildUpdateEmptyTestRunApiModel(testRun); boolean changed = false; diff --git a/testit-java-commons/src/test/java/ru/testit/services/core/AdapterStartupHelperTestRunMetaTest.java b/testit-java-commons/src/test/java/ru/testit/services/core/AdapterStartupHelperTestRunMetaTest.java index 5b3b9141..8344f850 100644 --- a/testit-java-commons/src/test/java/ru/testit/services/core/AdapterStartupHelperTestRunMetaTest.java +++ b/testit-java-commons/src/test/java/ru/testit/services/core/AdapterStartupHelperTestRunMetaTest.java @@ -3,6 +3,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.LoggerFactory; +import ru.testit.adaptersapi.model.AttachmentApiResult; import ru.testit.adaptersapi.model.LinkApiResult; import ru.testit.adaptersapi.model.LinkType; import ru.testit.adaptersapi.model.TestRunApiResult; @@ -54,16 +55,23 @@ void startTests_mergesTagsAndLinksEarly() throws Exception { existing.setName("Old"); existing.setTags(Collections.singletonList("smoke")); LinkApiResult oldLink = new LinkApiResult(); + oldLink.setId(UUID.fromString("aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee")); oldLink.setUrl("https://existing.example"); oldLink.setType(LinkType.RELATED); existing.setLinks(Collections.singletonList(oldLink)); + AttachmentApiResult oldAttachment = new AttachmentApiResult(); + oldAttachment.setId(UUID.fromString("11111111-2222-3333-4444-555555555555")); + oldAttachment.setFileId("file-1"); + oldAttachment.setType("text/plain"); + oldAttachment.setSize(1f); + oldAttachment.setName("note.txt"); + existing.setAttachments(Collections.singletonList(oldAttachment)); when(client.getTestRun(RUN_ID)).thenReturn(existing); AdapterStartupHelper helper = new AdapterStartupHelper( adapterConfig, config, client, LoggerFactory.getLogger(getClass())); helper.startTests(); - verify(client).updateTestRun(any(UpdateEmptyTestRunApiModel.class)); org.mockito.ArgumentCaptor captor = org.mockito.ArgumentCaptor.forClass(UpdateEmptyTestRunApiModel.class); verify(client).updateTestRun(captor.capture()); @@ -71,6 +79,44 @@ void startTests_mergesTagsAndLinksEarly() throws Exception { assertTrue(model.getTags().contains("smoke")); assertTrue(model.getTags().contains("nightly")); assertEquals(2, model.getLinks().size()); + assertEquals(1, model.getAttachments().size()); + assertEquals(oldAttachment.getId(), model.getAttachments().get(0).getId()); + assertEquals(oldLink.getId(), model.getLinks().get(0).getId()); + } + + @Test + void startTests_tagsOnly_keepsExistingLinksAndAttachments() throws Exception { + ClientConfiguration config = configWith(RUN_ID, null, "nightly", null); + + TestRunApiResult existing = new TestRunApiResult(); + existing.setId(UUID.fromString(RUN_ID)); + existing.setName("Old"); + existing.setTags(Collections.singletonList("smoke")); + LinkApiResult oldLink = new LinkApiResult(); + oldLink.setId(UUID.fromString("aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee")); + oldLink.setUrl("https://existing.example"); + oldLink.setType(LinkType.RELATED); + existing.setLinks(Collections.singletonList(oldLink)); + AttachmentApiResult oldAttachment = new AttachmentApiResult(); + oldAttachment.setId(UUID.fromString("11111111-2222-3333-4444-555555555555")); + oldAttachment.setFileId("file-1"); + oldAttachment.setType("text/plain"); + oldAttachment.setSize(1f); + oldAttachment.setName("note.txt"); + existing.setAttachments(Collections.singletonList(oldAttachment)); + when(client.getTestRun(RUN_ID)).thenReturn(existing); + + AdapterStartupHelper helper = new AdapterStartupHelper( + adapterConfig, config, client, LoggerFactory.getLogger(getClass())); + helper.startTests(); + + org.mockito.ArgumentCaptor captor = + org.mockito.ArgumentCaptor.forClass(UpdateEmptyTestRunApiModel.class); + verify(client).updateTestRun(captor.capture()); + UpdateEmptyTestRunApiModel model = captor.getValue(); + assertEquals(1, model.getLinks().size()); + assertEquals(1, model.getAttachments().size()); + assertEquals("https://existing.example", model.getLinks().get(0).getUrl()); } @Test