diff --git a/java-reporter-core/pom.xml b/java-reporter-core/pom.xml index bb604de..e7162ca 100644 --- a/java-reporter-core/pom.xml +++ b/java-reporter-core/pom.xml @@ -7,7 +7,7 @@ io.testomat java-reporter-core - 0.11.5 + 0.11.6 jar Testomat.io Reporter Core diff --git a/java-reporter-core/src/main/java/io/testomat/core/client/request/NativeRequestBodyBuilder.java b/java-reporter-core/src/main/java/io/testomat/core/client/request/NativeRequestBodyBuilder.java index 331467b..70a9d23 100644 --- a/java-reporter-core/src/main/java/io/testomat/core/client/request/NativeRequestBodyBuilder.java +++ b/java-reporter-core/src/main/java/io/testomat/core/client/request/NativeRequestBodyBuilder.java @@ -27,6 +27,8 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Default implementation of {@link RequestBodyBuilder} for Testomat.io API requests. @@ -34,6 +36,8 @@ * with support for parameterized tests, shared runs, and configurable properties. */ public class NativeRequestBodyBuilder implements RequestBodyBuilder { + private static final Logger log = LoggerFactory.getLogger(NativeRequestBodyBuilder.class); + private static final String TRUE = "true"; private final Boolean createParam; private final String sharedRun; @@ -162,7 +166,7 @@ private Map buildTestResultMap(TestResult result) throws JsonPro result.getSteps().forEach(this::processStepArtifacts); List> stepsMap = convertStepsToMap(result.getSteps()); body.put("steps", stepsMap); - System.out.println("DEBUG: Adding " + result.getSteps().size() + " steps to request body for test: " + result.getTitle()); + log.debug("Adding {} steps to request body for test: {}", result.getSteps().size(), result.getTitle()); } if (createParam) { 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 7738be8..ede8d97 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.11.5"; + public static final String REPORTER_VERSION = "0.11.6"; 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/client/AwsService.java b/java-reporter-core/src/main/java/io/testomat/core/facade/methods/artifact/client/AwsService.java index aa367d9..e87083a 100644 --- a/java-reporter-core/src/main/java/io/testomat/core/facade/methods/artifact/client/AwsService.java +++ b/java-reporter-core/src/main/java/io/testomat/core/facade/methods/artifact/client/AwsService.java @@ -75,16 +75,19 @@ public void uploadAllArtifactsForTest(String testName, String rid, String testId } S3Credentials credentials = CredentialsManager.getCredentials(); - List uploadedArtifactsLinks = processArtifacts(artifactDirectories, testName, rid, credentials); if (!TempArtifactDirectoriesStorage.STEP_DIRECTORIES.isEmpty()) { - processStepArtifacts(testName, rid, credentials); + List directories = prepareStepArtifactsForUpload(testName, rid, credentials); + processArtifacts(directories, testName, rid, credentials); } - storeArtifactLinkData(testName, rid, testId, uploadedArtifactsLinks); + if (!artifactDirectories.isEmpty()) { + List uploadedArtifactsLinks = processArtifacts(artifactDirectories, testName, rid, credentials); + storeArtifactLinkData(testName, rid, testId, uploadedArtifactsLinks); - // Clear artifact directories after processing - TempArtifactDirectoriesStorage.DIRECTORIES.remove(); + // Clear artifact directories after processing + TempArtifactDirectoriesStorage.DIRECTORIES.remove(); + } } private List processArtifacts(List artifactDirectories, String testName, String rid, S3Credentials credentials) { @@ -99,16 +102,30 @@ private List processArtifacts(List artifactDirectories, String t return uploadedLinks; } - private void processStepArtifacts(String testName, String rid, S3Credentials credentials) { - TempArtifactDirectoriesStorage.STEP_DIRECTORIES - .forEach((stepId, list) -> list.replaceAll(dir -> { - if (dir.startsWith("http")) { - return dir; + /** + * Replaces local artifact paths with S3 URLs in STEP_DIRECTORIES + * and returns the list of directories that need to be uploaded. + * + *

Only non-HTTP paths are processed, since HTTP entries are already uploaded.

+ * + *

Mutates underlying lists by updating entries in place.

+ */ + private List prepareStepArtifactsForUpload(String testName, String rid, S3Credentials credentials) { + List artifactDirectories = new ArrayList<>(); + + for (List list : TempArtifactDirectoriesStorage.STEP_DIRECTORIES.values()) { + for (int i = 0; i < list.size(); i++) { + String dir = list.get(i); + + if (!dir.startsWith("http")) { + artifactDirectories.add(dir); + String key = keyGenerator.generateKey(dir, rid, testName); + list.set(i, urlGenerator.generateUrl(credentials.getBucket(), key)); } - String key = keyGenerator.generateKey(dir, rid, testName); - uploadArtifact(dir, key, credentials); - return urlGenerator.generateUrl(credentials.getBucket(), key); - })); + } + } + + return artifactDirectories; } private void storeArtifactLinkData(String testName, String rid, String testId, List uploadedLinks) { diff --git a/java-reporter-core/src/main/java/io/testomat/core/runmanager/GlobalRunManager.java b/java-reporter-core/src/main/java/io/testomat/core/runmanager/GlobalRunManager.java index 13e7b4d..8062f08 100644 --- a/java-reporter-core/src/main/java/io/testomat/core/runmanager/GlobalRunManager.java +++ b/java-reporter-core/src/main/java/io/testomat/core/runmanager/GlobalRunManager.java @@ -28,7 +28,7 @@ */ public class GlobalRunManager { private static final Logger log = LoggerFactory.getLogger(GlobalRunManager.class); - private static final int DELAY_BEFORE_ARTIFACTS_SENDING_MS = 10000; + private static final int DELAY_BEFORE_ARTIFACTS_SENDING_MS = getDelayBeforeArtifactsSendingMs(); private static volatile GlobalRunManager INSTANCE; private final PropertyProvider provider; @@ -347,4 +347,25 @@ private boolean isReportingDisabled() { return false; } } + + /** + * Returns the delay before sending artifacts in milliseconds. + * + *

Reads the value from the {@code artifacts.sending.delay} system property. + * If the property is missing, non-numeric, or not positive, a default value is used.

+ */ + private static int getDelayBeforeArtifactsSendingMs() { + int defaultDelayMs = 10000; + String value = System.getProperty("artifacts.sending.delay"); + if (value == null) { + return defaultDelayMs; + } + try { + int delayMs = Integer.parseInt(value.trim()); + return delayMs > 0 ? delayMs : defaultDelayMs; + } catch (NumberFormatException nfe) { + log.warn("Invalid artifacts.sending.delay value: {}, using default {}", value, defaultDelayMs); + return defaultDelayMs; + } + } } diff --git a/java-reporter-cucumber/pom.xml b/java-reporter-cucumber/pom.xml index 3d8526c..7cba4e5 100644 --- a/java-reporter-cucumber/pom.xml +++ b/java-reporter-cucumber/pom.xml @@ -51,7 +51,7 @@ io.testomat java-reporter-core - 0.11.5 + 0.11.6 org.slf4j diff --git a/java-reporter-junit/pom.xml b/java-reporter-junit/pom.xml index 095a5ab..e026196 100644 --- a/java-reporter-junit/pom.xml +++ b/java-reporter-junit/pom.xml @@ -51,7 +51,7 @@ io.testomat java-reporter-core - 0.11.5 + 0.11.6 org.slf4j diff --git a/java-reporter-karate/pom.xml b/java-reporter-karate/pom.xml index d7a8ef5..e4532e9 100644 --- a/java-reporter-karate/pom.xml +++ b/java-reporter-karate/pom.xml @@ -52,7 +52,7 @@ io.testomat java-reporter-core - 0.11.5 + 0.11.6 io.karatelabs diff --git a/java-reporter-testng/pom.xml b/java-reporter-testng/pom.xml index 03672af..de70013 100644 --- a/java-reporter-testng/pom.xml +++ b/java-reporter-testng/pom.xml @@ -47,7 +47,7 @@ io.testomat java-reporter-core - 0.11.5 + 0.11.6 org.slf4j diff --git a/testomat-allure-adapter/pom.xml b/testomat-allure-adapter/pom.xml index 9cd95c4..2434a2e 100644 --- a/testomat-allure-adapter/pom.xml +++ b/testomat-allure-adapter/pom.xml @@ -6,7 +6,7 @@ io.testomat testomat-allure-adapter - 0.0.2 + 0.0.3 jar Testomat.io Testomat Allure adapter @@ -66,7 +66,7 @@ io.testomat java-reporter-core - 0.11.5 + 0.11.6 io.qameta.allure diff --git a/testomat-allure-adapter/src/main/java/io/testomat/reporter/TestomatStepReporter.java b/testomat-allure-adapter/src/main/java/io/testomat/reporter/TestomatStepReporter.java index 4df99af..6a02e99 100644 --- a/testomat-allure-adapter/src/main/java/io/testomat/reporter/TestomatStepReporter.java +++ b/testomat-allure-adapter/src/main/java/io/testomat/reporter/TestomatStepReporter.java @@ -1,10 +1,12 @@ package io.testomat.reporter; import io.qameta.allure.listener.StepLifecycleListener; +import io.qameta.allure.model.StatusDetails; import io.qameta.allure.model.StepResult; import io.testomat.core.step.StepLifecycle; import io.testomat.core.step.StepStatus; import io.testomat.core.step.TestStep; +import java.util.Objects; /** * Step lifecycle listener that reports Allure steps to Testomat. @@ -21,11 +23,19 @@ public void afterStepStart(StepResult result) { @Override public void afterStepStop(StepResult result) { TestStep testStep = StepLifecycle.current(); + + if (testStep == null) { + return; + } + testStep.setCategory("user"); - testStep.setStepTitle(result.getName()); + testStep.setStepTitle(Objects.requireNonNullElse(result.getName(), "")); - long durationMillis = result.getStop() - result.getStart(); - testStep.setDuration(durationMillis); + Long start = result.getStart(); + Long stop = result.getStop(); + long duration = (start != null && stop != null) ? (stop - start) : 0L; + + testStep.setDuration(duration); switch (result.getStatus()) { case PASSED: @@ -34,8 +44,16 @@ public void afterStepStop(StepResult result) { case FAILED: case BROKEN: testStep.setStatus(StepStatus.failed); - testStep.setError(result.getStatusDetails().getMessage()); - testStep.setLog(result.getStatusDetails().getTrace()); + + StatusDetails details = result.getStatusDetails(); + if (details != null) { + if (details.getMessage() != null) { + testStep.setError(details.getMessage()); + } + if (details.getTrace() != null) { + testStep.setLog(details.getTrace()); + } + } break; default: testStep.setStatus(StepStatus.none); diff --git a/testomat-allure-adapter/src/test/java/reporter/TestomatStepReporterTest.java b/testomat-allure-adapter/src/test/java/reporter/TestomatStepReporterTest.java index 885ab34..98e415f 100644 --- a/testomat-allure-adapter/src/test/java/reporter/TestomatStepReporterTest.java +++ b/testomat-allure-adapter/src/test/java/reporter/TestomatStepReporterTest.java @@ -8,14 +8,17 @@ import io.testomat.core.step.TestStep; import io.testomat.reporter.TestomatStepReporter; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; import org.mockito.MockedStatic; - +import java.util.Objects; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.Mockito.*; class TestomatStepReporterTest { - TestomatStepReporter reporter = new TestomatStepReporter(); + private final TestomatStepReporter reporter = new TestomatStepReporter(); @Test void shouldStartStep() { @@ -24,51 +27,40 @@ void shouldStartStep() { try (MockedStatic lifecycle = mockStatic(StepLifecycle.class)) { reporter.afterStepStart(result); - lifecycle.verify(() -> StepLifecycle.start(any(TestStep.class))); + lifecycle.verify(() -> + StepLifecycle.start(argThat(Objects::nonNull)) + ); } } @Test void shouldHandlePassedStep() { - StepResult result = - new StepResult() - .setName("step") - .setStart(10L) - .setStop(20L) - .setStatus(Status.PASSED); - - TestStep step = mock(TestStep.class); - - try (MockedStatic lifecycle = mockStatic(StepLifecycle.class)) { - lifecycle.when(StepLifecycle::current).thenReturn(step); + StepResult result = createStep(Status.PASSED, 10, 20); + TestStep step = new TestStep(); + try (MockedStatic lifecycle = mockLifecycle(step)) { reporter.afterStepStop(result); - verify(step).setCategory("user"); - verify(step).setStepTitle("step"); - verify(step).setDuration(10); - verify(step).setStatus(StepStatus.passed); + assertEquals("user", step.getCategory()); + assertEquals("step", step.getStepTitle()); + assertEquals(10, step.getDuration()); + assertEquals(StepStatus.passed, step.getStatus()); lifecycle.verify(StepLifecycle::finish); } } - @Test - void shouldHandleFailedStep() { - StepResult result = - new StepResult() - .setName("step") - .setStart(0L) - .setStop(50L) - .setStatus(Status.FAILED) - .setStatusDetails(new StatusDetails() - .setMessage("error") - .setTrace("stack")); + @ParameterizedTest + @EnumSource(value = Status.class, names = {"FAILED", "BROKEN"}) + void shouldHandleFailedAndBrokenSteps(Status status) { + StepResult result = createStep(status, 0, 50) + .setStatusDetails(new StatusDetails() + .setMessage("error") + .setTrace("stack")); TestStep step = new TestStep(); - try (MockedStatic lifecycle = mockStatic(StepLifecycle.class)) { - lifecycle.when(StepLifecycle::current).thenReturn(step); + try (MockedStatic lifecycle = mockLifecycle(step)) { reporter.afterStepStop(result); assertEquals("user", step.getCategory()); @@ -83,58 +75,52 @@ void shouldHandleFailedStep() { } @Test - void shouldHandleBrokenStep() { - StepResult result = - new StepResult() - .setName("step") - .setStart(0L) - .setStop(1L) - .setStatus(Status.BROKEN) - .setStatusDetails( - new StatusDetails() - .setMessage("error") - .setTrace("stack") - ); - + void shouldHandleUnknownStatus() { + StepResult result = createStep(Status.SKIPPED, 0, 1); TestStep step = new TestStep(); - try (MockedStatic lifecycle = mockStatic(StepLifecycle.class)) { - lifecycle.when(StepLifecycle::current).thenReturn(step); + try (MockedStatic lifecycle = mockLifecycle(step)) { reporter.afterStepStop(result); assertEquals("user", step.getCategory()); assertEquals("step", step.getStepTitle()); assertEquals(1, step.getDuration()); - assertEquals(StepStatus.failed, step.getStatus()); - assertEquals("error", step.getError()); - assertEquals("stack", step.getLog()); + assertEquals(StepStatus.none, step.getStatus()); lifecycle.verify(StepLifecycle::finish); } } @Test - void shouldHandleUnknownStatus() { - StepResult result = - new StepResult() - .setName("step") - .setStart(0L) - .setStop(1L) - .setStatus(Status.SKIPPED); + void shouldHandleNullTimestampsGracefully() { + StepResult result = new StepResult() + .setName("step") + .setStatus(Status.PASSED); TestStep step = new TestStep(); - try (MockedStatic lifecycle = mockStatic(StepLifecycle.class)) { - lifecycle.when(StepLifecycle::current).thenReturn(step); + try (MockedStatic lifecycle = mockLifecycle(step)) { reporter.afterStepStop(result); assertEquals("user", step.getCategory()); assertEquals("step", step.getStepTitle()); - assertEquals(1, step.getDuration()); - assertEquals(StepStatus.none, step.getStatus()); + assertEquals(StepStatus.passed, step.getStatus()); lifecycle.verify(StepLifecycle::finish); } } -} + private StepResult createStep(Status status, long start, long stop) { + return new StepResult() + .setName("step") + .setStart(start) + .setStop(stop) + .setStatus(status); + } + + private MockedStatic mockLifecycle(TestStep step) { + MockedStatic lifecycle = mockStatic(StepLifecycle.class); + lifecycle.when(StepLifecycle::current).thenReturn(step); + return lifecycle; + } +} \ No newline at end of file