From 7e3ced771bf958e9f3ae61f46487735723886835 Mon Sep 17 00:00:00 2001 From: Nick Date: Fri, 20 Mar 2026 16:02:35 +0200 Subject: [PATCH] Issue-103 Karate: Improve rid generation logic fix. Testng: Exclude disabled tests from artifact metadata collection --- java-reporter-core/pom.xml | 2 +- .../core/constants/CommonConstants.java | 2 +- .../methods/artifact/ReportedTestStorage.java | 18 ++-- .../artifact/ReportedTestStorageTest.java | 32 +++++-- java-reporter-cucumber/pom.xml | 2 +- java-reporter-junit/pom.xml | 2 +- java-reporter-karate/pom.xml | 4 +- .../karate/extractor/TestDataExtractor.java | 33 ++++--- .../extractor/TestDataExtractorTest.java | 96 +++++++++++++++++++ java-reporter-testng/pom.xml | 2 +- 10 files changed, 153 insertions(+), 40 deletions(-) diff --git a/java-reporter-core/pom.xml b/java-reporter-core/pom.xml index 582e396..e92b44d 100644 --- a/java-reporter-core/pom.xml +++ b/java-reporter-core/pom.xml @@ -7,7 +7,7 @@ io.testomat java-reporter-core - 0.9.2 + 0.9.3 jar Testomat.io Reporter Core diff --git a/java-reporter-core/src/main/java/io/testomat/core/constants/CommonConstants.java b/java-reporter-core/src/main/java/io/testomat/core/constants/CommonConstants.java index f0a1768..5a4032f 100644 --- a/java-reporter-core/src/main/java/io/testomat/core/constants/CommonConstants.java +++ b/java-reporter-core/src/main/java/io/testomat/core/constants/CommonConstants.java @@ -1,7 +1,7 @@ package io.testomat.core.constants; public class CommonConstants { - public static final String REPORTER_VERSION = "0.9.2"; + public static final String REPORTER_VERSION = "0.9.3"; public static final String TESTS_STRING = "tests"; public static final String API_KEY_STRING = "api_key"; diff --git a/java-reporter-core/src/main/java/io/testomat/core/facade/methods/artifact/ReportedTestStorage.java b/java-reporter-core/src/main/java/io/testomat/core/facade/methods/artifact/ReportedTestStorage.java index 7d0db84..f4cf799 100644 --- a/java-reporter-core/src/main/java/io/testomat/core/facade/methods/artifact/ReportedTestStorage.java +++ b/java-reporter-core/src/main/java/io/testomat/core/facade/methods/artifact/ReportedTestStorage.java @@ -21,19 +21,25 @@ public class ReportedTestStorage { /** * Stores test execution metadata in the internal storage. *

- * The method adds a new entry containing {@code test_id} and {@code rid} - * only if an entry with the same values does not already exist. - * The check and insertion are performed atomically using synchronization - * to prevent duplicates in concurrent environments. + * The method extracts {@code test_id} and {@code rid} from the provided body + * and stores them as a new entry if an identical pair does not already exist. + * If {@code rid} is {@code null}, the entry is ignored and nothing is stored. + *

+ * The existence check and insertion are performed inside a synchronized block + * to guarantee atomicity and prevent duplicate entries in concurrent environments. * - * @param body a map containing test execution data; expected to include - * {@code "test_id"} and {@code "rid"} keys + * @param body a map containing test execution data. Expected to contain + * {@code "rid"} and optionally {@code "test_id"}. */ public static void store(Map body) { Object testId = body.get("test_id"); Object rid = body.get("rid"); + if (rid == null) { + return; + } + synchronized (STORAGE) { boolean exists = STORAGE.stream().anyMatch(m -> diff --git a/java-reporter-core/src/test/java/io/testomat/core/artifact/ReportedTestStorageTest.java b/java-reporter-core/src/test/java/io/testomat/core/artifact/ReportedTestStorageTest.java index cc66b82..9f8526c 100644 --- a/java-reporter-core/src/test/java/io/testomat/core/artifact/ReportedTestStorageTest.java +++ b/java-reporter-core/src/test/java/io/testomat/core/artifact/ReportedTestStorageTest.java @@ -3,7 +3,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Arrays; @@ -177,20 +176,33 @@ void testEmptyArtifactLinkList() { } @Test - @DisplayName("Should handle null values in test data") - void testNullValuesInTestData() { + @DisplayName("Should not store duplicate entries with same test_id and rid") + void testNoDuplicateEntries() { Map body = new HashMap<>(); - body.put("test_id", null); - body.put("rid", null); + body.put("test_id", "test-1"); + body.put("rid", "rid-1"); + ReportedTestStorage.store(body); ReportedTestStorage.store(body); List> storage = ReportedTestStorage.getStorage(); - Map stored = storage.get(storage.size() - 1); - assertTrue(stored.containsKey("test_id")); - assertTrue(stored.containsKey("rid")); - assertNull(stored.get("test_id")); - assertNull(stored.get("rid")); + assertEquals(1, storage.size()); + } + + @Test + @DisplayName("Should not store entry with null rid") + void testRidNullNotStored() { + + Map body = new HashMap<>(); + body.put("test_id", "test-1"); + body.put("rid", null); + + ReportedTestStorage.store(body); + + List> storage = + ReportedTestStorage.getStorage(); + + assertEquals(0, storage.size()); } } diff --git a/java-reporter-cucumber/pom.xml b/java-reporter-cucumber/pom.xml index 70b214f..084fd09 100644 --- a/java-reporter-cucumber/pom.xml +++ b/java-reporter-cucumber/pom.xml @@ -51,7 +51,7 @@ io.testomat java-reporter-core - 0.9.2 + 0.9.3 org.slf4j diff --git a/java-reporter-junit/pom.xml b/java-reporter-junit/pom.xml index 1ed0342..05f7018 100644 --- a/java-reporter-junit/pom.xml +++ b/java-reporter-junit/pom.xml @@ -51,7 +51,7 @@ io.testomat java-reporter-core - 0.9.2 + 0.9.3 org.slf4j diff --git a/java-reporter-karate/pom.xml b/java-reporter-karate/pom.xml index 133eec4..0e8b9ce 100644 --- a/java-reporter-karate/pom.xml +++ b/java-reporter-karate/pom.xml @@ -6,7 +6,7 @@ io.testomat java-reporter-karate - 0.2.4 + 0.2.5 jar Testomat.io Java Reporter Karate @@ -52,7 +52,7 @@ io.testomat java-reporter-core - 0.9.2 + 0.9.3 io.karatelabs diff --git a/java-reporter-karate/src/main/java/io/testomat/karate/extractor/TestDataExtractor.java b/java-reporter-karate/src/main/java/io/testomat/karate/extractor/TestDataExtractor.java index 2deb2ff..71bf2ba 100644 --- a/java-reporter-karate/src/main/java/io/testomat/karate/extractor/TestDataExtractor.java +++ b/java-reporter-karate/src/main/java/io/testomat/karate/extractor/TestDataExtractor.java @@ -10,6 +10,7 @@ import io.testomat.core.model.ExceptionDetails; import java.io.PrintWriter; import java.io.StringWriter; +import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Objects; import java.util.UUID; @@ -135,27 +136,25 @@ public String getNormalizedStatus(ScenarioRuntime sr) { } /** - * Generates a stable runtime identifier (rId) for a Karate test scenario execution. - *

- * The identifier is deterministically derived from the scenario location - * (feature file path and scenario line number). This ensures that the same - * scenario always produces the same rId across multiple test runs. - *

- * The rId is generated using a name-based UUID calculated from the string: - *

-     *     <feature-relative-path>:<scenario-line-number>
-     * 
+ * Generates a report identifier (rId) for a Karate scenario. + * The identifier is based on the feature path, scenario line, and example index + * (if the scenario is a Scenario Outline). * - * @param sr the {@link ScenarioRuntime} representing the executed Karate test scenario - * @return a deterministic UUID string uniquely identifying the scenario execution + * @param sr scenario runtime + * @return stable UUID identifying this scenario execution */ public String getRid(ScenarioRuntime sr) { - String raw = String.format("%s:%s", - sr.scenario.getFeature().getResource().getRelativePath(), - sr.scenario.getLine() - ); + StringBuilder raw = new StringBuilder() + .append(sr.scenario.getFeature().getResource().getRelativePath()) + .append(":") + .append(sr.scenario.getLine()); + + int exampleIndex = sr.scenario.getExampleIndex(); + if (exampleIndex >= 0) { + raw.append(":").append(exampleIndex); + } - return UUID.nameUUIDFromBytes(raw.getBytes()).toString(); + return UUID.nameUUIDFromBytes(raw.toString().getBytes(StandardCharsets.UTF_8)).toString(); } private ExceptionDetails createExceptionDetails(Throwable throwable) { diff --git a/java-reporter-karate/src/test/java/io/testomat/karate/extractor/TestDataExtractorTest.java b/java-reporter-karate/src/test/java/io/testomat/karate/extractor/TestDataExtractorTest.java index 8e7330a..b54c596 100644 --- a/java-reporter-karate/src/test/java/io/testomat/karate/extractor/TestDataExtractorTest.java +++ b/java-reporter-karate/src/test/java/io/testomat/karate/extractor/TestDataExtractorTest.java @@ -9,9 +9,11 @@ import com.intuit.karate.core.Tags; import com.intuit.karate.resource.Resource; import io.testomat.core.model.ExceptionDetails; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.UUID; import java.util.stream.Collectors; import org.junit.jupiter.api.Test; @@ -143,13 +145,107 @@ void getRidGeneratesStableUuid() { ScenarioRuntime sr = mockScenarioRuntime(); when(sr.scenario.getLine()).thenReturn(4); + when(sr.scenario.getExampleIndex()).thenReturn(-1); String rid1 = extractor.getRid(sr); String rid2 = extractor.getRid(sr); + assertThat(rid1) + .isNotNull() + .isEqualTo(rid2); + } + + @Test + void getRidSameWithoutExampleIndex() { + ScenarioRuntime sr1 = mockScenarioRuntime(); + ScenarioRuntime sr2 = mockScenarioRuntime(); + + when(sr1.scenario.getLine()).thenReturn(5); + when(sr2.scenario.getLine()).thenReturn(5); + + when(sr1.scenario.getExampleIndex()).thenReturn(-1); + when(sr2.scenario.getExampleIndex()).thenReturn(-1); + + String rid1 = extractor.getRid(sr1); + String rid2 = extractor.getRid(sr2); + assertThat(rid1).isEqualTo(rid2); } + @Test + void getRidDifferentLinesProduceDifferentRid() { + ScenarioRuntime sr1 = mockScenarioRuntime(); + ScenarioRuntime sr2 = mockScenarioRuntime(); + + when(sr1.scenario.getLine()).thenReturn(1); + when(sr2.scenario.getLine()).thenReturn(2); + + when(sr1.scenario.getExampleIndex()).thenReturn(-1); + when(sr2.scenario.getExampleIndex()).thenReturn(-1); + + String rid1 = extractor.getRid(sr1); + String rid2 = extractor.getRid(sr2); + + assertThat(rid1).isNotEqualTo(rid2); + } + + @Test + void getRidIncludesExampleIndex() { + ScenarioRuntime sr1 = mockScenarioRuntime(); + ScenarioRuntime sr2 = mockScenarioRuntime(); + + when(sr1.scenario.getLine()).thenReturn(10); + when(sr2.scenario.getLine()).thenReturn(10); + + when(sr1.scenario.getExampleIndex()).thenReturn(0); + when(sr2.scenario.getExampleIndex()).thenReturn(1); + + String rid1 = extractor.getRid(sr1); + String rid2 = extractor.getRid(sr2); + + assertThat(rid1).isNotEqualTo(rid2); + } + + @Test + void getRidDifferentFeatureFilesProduceDifferentRid() { + ScenarioRuntime sr1 = mockScenarioRuntime(); + ScenarioRuntime sr2 = mockScenarioRuntime(); + + when(sr1.scenario.getLine()).thenReturn(1); + when(sr2.scenario.getLine()).thenReturn(1); + + when(sr1.scenario.getExampleIndex()).thenReturn(-1); + when(sr2.scenario.getExampleIndex()).thenReturn(-1); + + when(sr2.scenario.getFeature() + .getResource() + .getRelativePath()) + .thenReturn("features/Other.feature"); + + String rid1 = extractor.getRid(sr1); + String rid2 = extractor.getRid(sr2); + + assertThat(rid1).isNotEqualTo(rid2); + } + + @Test + void getRidMatchesExpectedUuid() { + ScenarioRuntime sr = mockScenarioRuntime(); + + when(sr.scenario.getLine()).thenReturn(4); + when(sr.scenario.getExampleIndex()).thenReturn(-1); + + String expected = + UUID.nameUUIDFromBytes( + "features/KarateTest.feature:4" + .getBytes(StandardCharsets.UTF_8) + ).toString(); + + String rid = extractor.getRid(sr); + + assertThat(rid).isEqualTo(expected); + } + private ScenarioRuntime mockScenarioRuntime() { ScenarioRuntime sr = mock(ScenarioRuntime.class); diff --git a/java-reporter-testng/pom.xml b/java-reporter-testng/pom.xml index 6221211..f18217e 100644 --- a/java-reporter-testng/pom.xml +++ b/java-reporter-testng/pom.xml @@ -47,7 +47,7 @@ io.testomat java-reporter-core - 0.9.2 + 0.9.3 org.slf4j