From b72cbd87700c2f3045fac69e26e8bd4f20002122 Mon Sep 17 00:00:00 2001 From: Zenith1415 Date: Mon, 19 Jan 2026 23:59:58 +0530 Subject: [PATCH 1/8] fixed changes requested --- .../opentelemetry/job/OtelEnvironmentContributor.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/jenkins/plugins/opentelemetry/job/OtelEnvironmentContributor.java b/src/main/java/io/jenkins/plugins/opentelemetry/job/OtelEnvironmentContributor.java index 115f383ef..dc28ffc2f 100644 --- a/src/main/java/io/jenkins/plugins/opentelemetry/job/OtelEnvironmentContributor.java +++ b/src/main/java/io/jenkins/plugins/opentelemetry/job/OtelEnvironmentContributor.java @@ -11,6 +11,7 @@ import hudson.model.EnvironmentContributor; import hudson.model.Run; import hudson.model.TaskListener; +import io.opentelemetry.api.trace.Span; import javax.inject.Inject; /** @@ -25,7 +26,12 @@ public class OtelEnvironmentContributor extends EnvironmentContributor { @Override public void buildEnvironmentFor(@NonNull Run run, @NonNull EnvVars envs, @NonNull TaskListener listener) { - otelEnvironmentContributorService.addEnvironmentVariables(run, envs, otelTraceService.getSpan(run)); + Span span = Span.current(); + // If there is no active span on the thread, it falls back to the run's root span + if (!span.getSpanContext().isValid()) { + span = otelTraceService.getSpan(run); + } + otelEnvironmentContributorService.addEnvironmentVariables(run, envs, span); } @Inject From 7c1d73d3dd457ed750177b8f090a300684200a8b Mon Sep 17 00:00:00 2001 From: Zenith1415 Date: Sun, 8 Feb 2026 10:11:28 +0530 Subject: [PATCH 2/8] Test: Add integration test verifying context propagation between layers --- .../job/TraceParentIntegrationTest.java | 128 ++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/test/java/io/jenkins/plugins/opentelemetry/job/TraceParentIntegrationTest.java diff --git a/src/test/java/io/jenkins/plugins/opentelemetry/job/TraceParentIntegrationTest.java b/src/test/java/io/jenkins/plugins/opentelemetry/job/TraceParentIntegrationTest.java new file mode 100644 index 000000000..966bfb2c2 --- /dev/null +++ b/src/test/java/io/jenkins/plugins/opentelemetry/job/TraceParentIntegrationTest.java @@ -0,0 +1,128 @@ +package io.jenkins.plugins.opentelemetry.job; + +import hudson.EnvVars; +import hudson.model.Job; +import hudson.model.Run; +import hudson.model.TaskListener; +import io.jenkins.plugins.opentelemetry.OtelEnvironmentContributor; +import io.jenkins.plugins.opentelemetry.api.ReconfigurableOpenTelemetry; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.context.Scope; +import io.opentelemetry.sdk.testing.junit4.OpenTelemetryRule; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Answers; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; +import java.lang.reflect.Field; +import java.io.IOException; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class TraceParentIntegrationTest { + + @Rule + public OpenTelemetryRule otelTesting = OpenTelemetryRule.create(); + + @Mock + Run run; + + @Mock + Job job; + + @Mock + TaskListener listener; + + + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + ReconfigurableOpenTelemetry reconfigurableOpenTelemetry; + + private OtelEnvironmentContributor contributor; + private Tracer tracer; + + @Before + public void setup() throws Exception { + this.contributor = new OtelEnvironmentContributor(); + this.tracer = otelTesting.getOpenTelemetry().getTracer("test-tracer"); + + // Setup Mock Interactions + when(run.getParent()).thenReturn(job); + when(job.getFullName()).thenReturn("test-pipeline-job"); + + // Instantiate the Real Service + OtelEnvironmentContributorService service = new OtelEnvironmentContributorService(); + + // Inject 'ReconfigurableOpenTelemetry' into 'Service' + injectDependencyByType(service, reconfigurableOpenTelemetry, ReconfigurableOpenTelemetry.class); + + // Inject 'Service' into 'Contributor' + injectDependencyByType(this.contributor, service, OtelEnvironmentContributorService.class); + } + + @Test + public void testContextPropagatesToNestedLayer() throws IOException, InterruptedException { + // Simulate the Root Layer + Span rootSpan = tracer.spanBuilder("root-build").startSpan(); + String rootSpanId = rootSpan.getSpanContext().getSpanId(); + + try (Scope rootScope = rootSpan.makeCurrent()) { + + // Simulate a Nested Layer + Span stageSpan = tracer.spanBuilder("stage-layer").startSpan(); + String stageSpanId = stageSpan.getSpanContext().getSpanId(); + String stageTraceId = stageSpan.getSpanContext().getTraceId(); + + // ACTIVATE the nested layer + try (Scope stageScope = stageSpan.makeCurrent()) { + + EnvVars envs = new EnvVars(); + contributor.buildEnvironmentFor(run, envs, listener); + + String traceParent = envs.get("TRACEPARENT"); + assertNotNull("TRACEPARENT variable should be injected", traceParent); + + assertTrue("Trace context must match the current active stage layer", + traceParent.contains(stageSpanId)); + + assertFalse("Trace context must NOT match the root build layer", + traceParent.contains(rootSpanId)); + + assertTrue("Trace ID must match the current context", + traceParent.contains(stageTraceId)); + } finally { + stageSpan.end(); + } + } finally { + rootSpan.end(); + } + } + + private void injectDependencyByType(Object target, Object dependency, Class dependencyType) throws Exception { + boolean found = false; + for (Field field : target.getClass().getDeclaredFields()) { + if (field.getType().isAssignableFrom(dependencyType)) { + field.setAccessible(true); + field.set(target, dependency); + found = true; + break; + } + } + if (!found) { + // Fallback: Check parent class just in case + for (Field field : target.getClass().getSuperclass().getDeclaredFields()) { + if (field.getType().isAssignableFrom(dependencyType)) { + field.setAccessible(true); + field.set(target, dependency); + found = true; + break; + } + } + } + } +} \ No newline at end of file From 02f6e9db1ee34ee4b0c53bf898008f8064d83ca0 Mon Sep 17 00:00:00 2001 From: Zenith1415 Date: Sun, 8 Feb 2026 10:29:02 +0530 Subject: [PATCH 3/8] Fix: Remove incorrect imported class in same package --- .../job/TraceParentIntegrationTest.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/test/java/io/jenkins/plugins/opentelemetry/job/TraceParentIntegrationTest.java b/src/test/java/io/jenkins/plugins/opentelemetry/job/TraceParentIntegrationTest.java index 966bfb2c2..c212f6d7c 100644 --- a/src/test/java/io/jenkins/plugins/opentelemetry/job/TraceParentIntegrationTest.java +++ b/src/test/java/io/jenkins/plugins/opentelemetry/job/TraceParentIntegrationTest.java @@ -4,7 +4,6 @@ import hudson.model.Job; import hudson.model.Run; import hudson.model.TaskListener; -import io.jenkins.plugins.opentelemetry.OtelEnvironmentContributor; import io.jenkins.plugins.opentelemetry.api.ReconfigurableOpenTelemetry; import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.Tracer; @@ -14,11 +13,13 @@ import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.Answers; +import org.mockito.Answers; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; + import java.lang.reflect.Field; import java.io.IOException; + import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; @@ -38,7 +39,6 @@ public class TraceParentIntegrationTest { @Mock TaskListener listener; - @Mock(answer = Answers.RETURNS_DEEP_STUBS) ReconfigurableOpenTelemetry reconfigurableOpenTelemetry; @@ -54,7 +54,7 @@ public void setup() throws Exception { // Setup Mock Interactions when(run.getParent()).thenReturn(job); when(job.getFullName()).thenReturn("test-pipeline-job"); - + // Instantiate the Real Service OtelEnvironmentContributorService service = new OtelEnvironmentContributorService(); @@ -67,13 +67,13 @@ public void setup() throws Exception { @Test public void testContextPropagatesToNestedLayer() throws IOException, InterruptedException { - // Simulate the Root Layer + // Simulate the "Root" Layer Span rootSpan = tracer.spanBuilder("root-build").startSpan(); String rootSpanId = rootSpan.getSpanContext().getSpanId(); try (Scope rootScope = rootSpan.makeCurrent()) { - // Simulate a Nested Layer + // Simulate a "Nested" Layer Span stageSpan = tracer.spanBuilder("stage-layer").startSpan(); String stageSpanId = stageSpan.getSpanContext().getSpanId(); String stageTraceId = stageSpan.getSpanContext().getTraceId(); @@ -105,6 +105,7 @@ public void testContextPropagatesToNestedLayer() throws IOException, Interrupted private void injectDependencyByType(Object target, Object dependency, Class dependencyType) throws Exception { boolean found = false; + for (Field field : target.getClass().getDeclaredFields()) { if (field.getType().isAssignableFrom(dependencyType)) { field.setAccessible(true); @@ -114,7 +115,7 @@ private void injectDependencyByType(Object target, Object dependency, Class d } } if (!found) { - // Fallback: Check parent class just in case + // Check parent class fields for (Field field : target.getClass().getSuperclass().getDeclaredFields()) { if (field.getType().isAssignableFrom(dependencyType)) { field.setAccessible(true); From cc87aea970b2171509fe0706cebe6ed31b789440 Mon Sep 17 00:00:00 2001 From: Zenith1415 Date: Sun, 8 Feb 2026 13:12:27 +0530 Subject: [PATCH 4/8] Fix code formatting violations --- .../job/TraceParentIntegrationTest.java | 63 +++++++++---------- 1 file changed, 30 insertions(+), 33 deletions(-) diff --git a/src/test/java/io/jenkins/plugins/opentelemetry/job/TraceParentIntegrationTest.java b/src/test/java/io/jenkins/plugins/opentelemetry/job/TraceParentIntegrationTest.java index c212f6d7c..adc68858a 100644 --- a/src/test/java/io/jenkins/plugins/opentelemetry/job/TraceParentIntegrationTest.java +++ b/src/test/java/io/jenkins/plugins/opentelemetry/job/TraceParentIntegrationTest.java @@ -1,5 +1,10 @@ package io.jenkins.plugins.opentelemetry.job; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.when; + import hudson.EnvVars; import hudson.model.Job; import hudson.model.Run; @@ -9,6 +14,8 @@ import io.opentelemetry.api.trace.Tracer; import io.opentelemetry.context.Scope; import io.opentelemetry.sdk.testing.junit4.OpenTelemetryRule; +import java.io.IOException; +import java.lang.reflect.Field; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -17,14 +24,6 @@ import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; -import java.lang.reflect.Field; -import java.io.IOException; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.when; - @RunWith(MockitoJUnitRunner.class) public class TraceParentIntegrationTest { @@ -39,7 +38,7 @@ public class TraceParentIntegrationTest { @Mock TaskListener listener; - + @Mock(answer = Answers.RETURNS_DEEP_STUBS) ReconfigurableOpenTelemetry reconfigurableOpenTelemetry; @@ -54,7 +53,7 @@ public void setup() throws Exception { // Setup Mock Interactions when(run.getParent()).thenReturn(job); when(job.getFullName()).thenReturn("test-pipeline-job"); - + // Instantiate the Real Service OtelEnvironmentContributorService service = new OtelEnvironmentContributorService(); @@ -72,7 +71,7 @@ public void testContextPropagatesToNestedLayer() throws IOException, Interrupted String rootSpanId = rootSpan.getSpanContext().getSpanId(); try (Scope rootScope = rootSpan.makeCurrent()) { - + // Simulate a "Nested" Layer Span stageSpan = tracer.spanBuilder("stage-layer").startSpan(); String stageSpanId = stageSpan.getSpanContext().getSpanId(); @@ -80,21 +79,19 @@ public void testContextPropagatesToNestedLayer() throws IOException, Interrupted // ACTIVATE the nested layer try (Scope stageScope = stageSpan.makeCurrent()) { - + EnvVars envs = new EnvVars(); contributor.buildEnvironmentFor(run, envs, listener); - + String traceParent = envs.get("TRACEPARENT"); assertNotNull("TRACEPARENT variable should be injected", traceParent); - - assertTrue("Trace context must match the current active stage layer", - traceParent.contains(stageSpanId)); - - assertFalse("Trace context must NOT match the root build layer", - traceParent.contains(rootSpanId)); - - assertTrue("Trace ID must match the current context", - traceParent.contains(stageTraceId)); + + assertTrue( + "Trace context must match the current active stage layer", traceParent.contains(stageSpanId)); + + assertFalse("Trace context must NOT match the root build layer", traceParent.contains(rootSpanId)); + + assertTrue("Trace ID must match the current context", traceParent.contains(stageTraceId)); } finally { stageSpan.end(); } @@ -105,7 +102,7 @@ public void testContextPropagatesToNestedLayer() throws IOException, Interrupted private void injectDependencyByType(Object target, Object dependency, Class dependencyType) throws Exception { boolean found = false; - + for (Field field : target.getClass().getDeclaredFields()) { if (field.getType().isAssignableFrom(dependencyType)) { field.setAccessible(true); @@ -115,15 +112,15 @@ private void injectDependencyByType(Object target, Object dependency, Class d } } if (!found) { - // Check parent class fields - for (Field field : target.getClass().getSuperclass().getDeclaredFields()) { - if (field.getType().isAssignableFrom(dependencyType)) { - field.setAccessible(true); - field.set(target, dependency); - found = true; - break; - } - } + // Check parent class fields + for (Field field : target.getClass().getSuperclass().getDeclaredFields()) { + if (field.getType().isAssignableFrom(dependencyType)) { + field.setAccessible(true); + field.set(target, dependency); + found = true; + break; + } + } } } -} \ No newline at end of file +} From c05890b579f721e0f756190eff59a08d668bff42 Mon Sep 17 00:00:00 2001 From: Zenith1415 Date: Tue, 10 Feb 2026 10:35:43 +0530 Subject: [PATCH 5/8] Added regression test for TRACEPARENT propagation in nested pipeline stages --- .../job/TraceParentIntegrationTest.java | 126 ------------------ .../job/TraceParentPipelineTest.java | 73 ++++++++++ 2 files changed, 73 insertions(+), 126 deletions(-) delete mode 100644 src/test/java/io/jenkins/plugins/opentelemetry/job/TraceParentIntegrationTest.java create mode 100644 src/test/java/io/jenkins/plugins/opentelemetry/job/TraceParentPipelineTest.java diff --git a/src/test/java/io/jenkins/plugins/opentelemetry/job/TraceParentIntegrationTest.java b/src/test/java/io/jenkins/plugins/opentelemetry/job/TraceParentIntegrationTest.java deleted file mode 100644 index adc68858a..000000000 --- a/src/test/java/io/jenkins/plugins/opentelemetry/job/TraceParentIntegrationTest.java +++ /dev/null @@ -1,126 +0,0 @@ -package io.jenkins.plugins.opentelemetry.job; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.when; - -import hudson.EnvVars; -import hudson.model.Job; -import hudson.model.Run; -import hudson.model.TaskListener; -import io.jenkins.plugins.opentelemetry.api.ReconfigurableOpenTelemetry; -import io.opentelemetry.api.trace.Span; -import io.opentelemetry.api.trace.Tracer; -import io.opentelemetry.context.Scope; -import io.opentelemetry.sdk.testing.junit4.OpenTelemetryRule; -import java.io.IOException; -import java.lang.reflect.Field; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Answers; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; - -@RunWith(MockitoJUnitRunner.class) -public class TraceParentIntegrationTest { - - @Rule - public OpenTelemetryRule otelTesting = OpenTelemetryRule.create(); - - @Mock - Run run; - - @Mock - Job job; - - @Mock - TaskListener listener; - - @Mock(answer = Answers.RETURNS_DEEP_STUBS) - ReconfigurableOpenTelemetry reconfigurableOpenTelemetry; - - private OtelEnvironmentContributor contributor; - private Tracer tracer; - - @Before - public void setup() throws Exception { - this.contributor = new OtelEnvironmentContributor(); - this.tracer = otelTesting.getOpenTelemetry().getTracer("test-tracer"); - - // Setup Mock Interactions - when(run.getParent()).thenReturn(job); - when(job.getFullName()).thenReturn("test-pipeline-job"); - - // Instantiate the Real Service - OtelEnvironmentContributorService service = new OtelEnvironmentContributorService(); - - // Inject 'ReconfigurableOpenTelemetry' into 'Service' - injectDependencyByType(service, reconfigurableOpenTelemetry, ReconfigurableOpenTelemetry.class); - - // Inject 'Service' into 'Contributor' - injectDependencyByType(this.contributor, service, OtelEnvironmentContributorService.class); - } - - @Test - public void testContextPropagatesToNestedLayer() throws IOException, InterruptedException { - // Simulate the "Root" Layer - Span rootSpan = tracer.spanBuilder("root-build").startSpan(); - String rootSpanId = rootSpan.getSpanContext().getSpanId(); - - try (Scope rootScope = rootSpan.makeCurrent()) { - - // Simulate a "Nested" Layer - Span stageSpan = tracer.spanBuilder("stage-layer").startSpan(); - String stageSpanId = stageSpan.getSpanContext().getSpanId(); - String stageTraceId = stageSpan.getSpanContext().getTraceId(); - - // ACTIVATE the nested layer - try (Scope stageScope = stageSpan.makeCurrent()) { - - EnvVars envs = new EnvVars(); - contributor.buildEnvironmentFor(run, envs, listener); - - String traceParent = envs.get("TRACEPARENT"); - assertNotNull("TRACEPARENT variable should be injected", traceParent); - - assertTrue( - "Trace context must match the current active stage layer", traceParent.contains(stageSpanId)); - - assertFalse("Trace context must NOT match the root build layer", traceParent.contains(rootSpanId)); - - assertTrue("Trace ID must match the current context", traceParent.contains(stageTraceId)); - } finally { - stageSpan.end(); - } - } finally { - rootSpan.end(); - } - } - - private void injectDependencyByType(Object target, Object dependency, Class dependencyType) throws Exception { - boolean found = false; - - for (Field field : target.getClass().getDeclaredFields()) { - if (field.getType().isAssignableFrom(dependencyType)) { - field.setAccessible(true); - field.set(target, dependency); - found = true; - break; - } - } - if (!found) { - // Check parent class fields - for (Field field : target.getClass().getSuperclass().getDeclaredFields()) { - if (field.getType().isAssignableFrom(dependencyType)) { - field.setAccessible(true); - field.set(target, dependency); - found = true; - break; - } - } - } - } -} diff --git a/src/test/java/io/jenkins/plugins/opentelemetry/job/TraceParentPipelineTest.java b/src/test/java/io/jenkins/plugins/opentelemetry/job/TraceParentPipelineTest.java new file mode 100644 index 000000000..47fd9ad86 --- /dev/null +++ b/src/test/java/io/jenkins/plugins/opentelemetry/job/TraceParentPipelineTest.java @@ -0,0 +1,73 @@ +package io.jenkins.plugins.opentelemetry.job; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; + +import hudson.model.Result; +import io.jenkins.plugins.opentelemetry.JenkinsOpenTelemetryPluginConfiguration; +import jenkins.model.GlobalConfiguration; +import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; +import org.jenkinsci.plugins.workflow.job.WorkflowJob; +import org.jenkinsci.plugins.workflow.job.WorkflowRun; +import org.junit.Rule; +import org.junit.Test; +import org.jvnet.hudson.test.JenkinsRule; + +public class TraceParentPipelineTest { + + @Rule + public JenkinsRule jenkinsRule = new JenkinsRule(); + + @Test + public void traceParentPropagationInNestedStages() throws Exception { + JenkinsOpenTelemetryPluginConfiguration config = + GlobalConfiguration.all().get(JenkinsOpenTelemetryPluginConfiguration.class); + + assertNotNull("OpenTelemetry plugin configuration must exist", config); + + config.setEndpoint("http://localhost:4317"); + config.setExportOtelConfigurationAsEnvironmentVariables(true); + config.save(); + + WorkflowJob job = jenkinsRule.createProject(WorkflowJob.class, "traceparent-nested-test"); + + String pipelineScript = "node {\n" + " stage('Outer') {\n" + + " sh 'echo OUTER_TP=$TRACEPARENT'\n" + + " stage('Inner') {\n" + + " sh 'echo INNER_TP=$TRACEPARENT'\n" + + " }\n" + + " }\n" + + "}"; + + job.setDefinition(new CpsFlowDefinition(pipelineScript, true)); + + WorkflowRun run = jenkinsRule.assertBuildStatus(Result.SUCCESS, job.scheduleBuild2(0)); + + String logs = JenkinsRule.getLog(run); + + String outerTp = extract("OUTER_TP=", logs); + String innerTp = extract("INNER_TP=", logs); + + assertNotNull("Outer TRACEPARENT missing", outerTp); + assertNotNull("Inner TRACEPARENT missing", innerTp); + + assertNotEquals(outerTp, innerTp); + + assertEquals(traceId(outerTp), traceId(innerTp)); + } + + private String extract(String prefix, String log) { + for (String line : log.split("\n")) { + if (line.contains(prefix)) { + return line.substring(line.indexOf(prefix) + prefix.length()).trim(); + } + } + return null; + } + + private String traceId(String traceParent) { + String[] parts = traceParent.split("-"); + return parts.length >= 2 ? parts[1] : null; + } +} From 9522f4773378610fa3da2e134cf15b0ec29710d0 Mon Sep 17 00:00:00 2001 From: Zenith1415 Date: Tue, 10 Feb 2026 17:44:28 +0530 Subject: [PATCH 6/8] Added WithEnv, multi stages and a try/catch case --- .../job/TraceParentPipelineTest.java | 78 +++++++++++++------ 1 file changed, 55 insertions(+), 23 deletions(-) diff --git a/src/test/java/io/jenkins/plugins/opentelemetry/job/TraceParentPipelineTest.java b/src/test/java/io/jenkins/plugins/opentelemetry/job/TraceParentPipelineTest.java index 47fd9ad86..98ee45e0d 100644 --- a/src/test/java/io/jenkins/plugins/opentelemetry/job/TraceParentPipelineTest.java +++ b/src/test/java/io/jenkins/plugins/opentelemetry/job/TraceParentPipelineTest.java @@ -1,9 +1,5 @@ package io.jenkins.plugins.opentelemetry.job; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; - import hudson.model.Result; import io.jenkins.plugins.opentelemetry.JenkinsOpenTelemetryPluginConfiguration; import jenkins.model.GlobalConfiguration; @@ -14,53 +10,89 @@ import org.junit.Test; import org.jvnet.hudson.test.JenkinsRule; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; + public class TraceParentPipelineTest { @Rule public JenkinsRule jenkinsRule = new JenkinsRule(); @Test - public void traceParentPropagationInNestedStages() throws Exception { + public void TraceParentPropagation() throws Exception { JenkinsOpenTelemetryPluginConfiguration config = GlobalConfiguration.all().get(JenkinsOpenTelemetryPluginConfiguration.class); - assertNotNull("OpenTelemetry plugin configuration must exist", config); + assertNotNull(config); config.setEndpoint("http://localhost:4317"); config.setExportOtelConfigurationAsEnvironmentVariables(true); config.save(); - WorkflowJob job = jenkinsRule.createProject(WorkflowJob.class, "traceparent-nested-test"); - - String pipelineScript = "node {\n" + " stage('Outer') {\n" - + " sh 'echo OUTER_TP=$TRACEPARENT'\n" - + " stage('Inner') {\n" - + " sh 'echo INNER_TP=$TRACEPARENT'\n" - + " }\n" - + " }\n" - + "}"; + WorkflowJob job = jenkinsRule.createProject( + WorkflowJob.class, + "traceparent-withenv-trycatch-test" + ); + + String pipelineScript = + "node {\n" + + " stage('Stage-A') {\n" + + " sh 'echo a_tp=$TRACEPARENT'\n" + + " }\n" + + "\n" + + " withEnv(['FOO=bar']) {\n" + + " stage('Stage-B') {\n" + + " sh 'echo b_tp=$TRACEPARENT'\n" + + " try {\n" + + " sh 'echo try_tp=$TRACEPARENT'\n" + + " sh 'exit 1'\n" + + " } catch (err) {\n" + + " sh 'echo catch_tp=$TRACEPARENT'\n" + + " } finally {\n" + + " sh 'echo final_tp=$TRACEPARENT'\n" + + " }\n" + + " }\n" + + " }\n" + + "}"; job.setDefinition(new CpsFlowDefinition(pipelineScript, true)); - WorkflowRun run = jenkinsRule.assertBuildStatus(Result.SUCCESS, job.scheduleBuild2(0)); + WorkflowRun run = jenkinsRule.assertBuildStatus( + Result.SUCCESS, + job.scheduleBuild2(0) + ); String logs = JenkinsRule.getLog(run); - String outerTp = extract("OUTER_TP=", logs); - String innerTp = extract("INNER_TP=", logs); + String aTp = extract("a_tp=", logs); + String bTp = extract("b_tp=", logs); + String tryTp = extract("try_tp=", logs); + String catchTp = extract("catch_tp=", logs); + String finalTp = extract("final_tp=", logs); + + assertNotNull(aTp); + assertNotNull(bTp); + assertNotNull(tryTp); + assertNotNull(catchTp); + assertNotNull(finalTp); - assertNotNull("Outer TRACEPARENT missing", outerTp); - assertNotNull("Inner TRACEPARENT missing", innerTp); + assertNotEquals(aTp, bTp); - assertNotEquals(outerTp, innerTp); + String traceId = traceId(aTp); - assertEquals(traceId(outerTp), traceId(innerTp)); + assertEquals(traceId, traceId(bTp)); + assertEquals(traceId, traceId(tryTp)); + assertEquals(traceId, traceId(catchTp)); + assertEquals(traceId, traceId(finalTp)); } private String extract(String prefix, String log) { for (String line : log.split("\n")) { if (line.contains(prefix)) { - return line.substring(line.indexOf(prefix) + prefix.length()).trim(); + return line.substring( + line.indexOf(prefix) + prefix.length() + ).trim(); } } return null; From 90ced8e8c8921569e21dc29dd02d7b0decc7c4af Mon Sep 17 00:00:00 2001 From: Zenith1415 Date: Tue, 10 Feb 2026 17:47:02 +0530 Subject: [PATCH 7/8] Added WithEnv, multi stages and a try/catch case --- .../job/TraceParentPipelineTest.java | 62 ++++++++----------- 1 file changed, 26 insertions(+), 36 deletions(-) diff --git a/src/test/java/io/jenkins/plugins/opentelemetry/job/TraceParentPipelineTest.java b/src/test/java/io/jenkins/plugins/opentelemetry/job/TraceParentPipelineTest.java index 98ee45e0d..c5d14ca6b 100644 --- a/src/test/java/io/jenkins/plugins/opentelemetry/job/TraceParentPipelineTest.java +++ b/src/test/java/io/jenkins/plugins/opentelemetry/job/TraceParentPipelineTest.java @@ -1,5 +1,9 @@ package io.jenkins.plugins.opentelemetry.job; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; + import hudson.model.Result; import io.jenkins.plugins.opentelemetry.JenkinsOpenTelemetryPluginConfiguration; import jenkins.model.GlobalConfiguration; @@ -10,10 +14,6 @@ import org.junit.Test; import org.jvnet.hudson.test.JenkinsRule; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; - public class TraceParentPipelineTest { @Rule @@ -30,38 +30,30 @@ public void TraceParentPropagation() throws Exception { config.setExportOtelConfigurationAsEnvironmentVariables(true); config.save(); - WorkflowJob job = jenkinsRule.createProject( - WorkflowJob.class, - "traceparent-withenv-trycatch-test" - ); - - String pipelineScript = - "node {\n" + - " stage('Stage-A') {\n" + - " sh 'echo a_tp=$TRACEPARENT'\n" + - " }\n" + - "\n" + - " withEnv(['FOO=bar']) {\n" + - " stage('Stage-B') {\n" + - " sh 'echo b_tp=$TRACEPARENT'\n" + - " try {\n" + - " sh 'echo try_tp=$TRACEPARENT'\n" + - " sh 'exit 1'\n" + - " } catch (err) {\n" + - " sh 'echo catch_tp=$TRACEPARENT'\n" + - " } finally {\n" + - " sh 'echo final_tp=$TRACEPARENT'\n" + - " }\n" + - " }\n" + - " }\n" + - "}"; + WorkflowJob job = jenkinsRule.createProject(WorkflowJob.class, "traceparent-withenv-trycatch-test"); + + String pipelineScript = "node {\n" + " stage('Stage-A') {\n" + + " sh 'echo a_tp=$TRACEPARENT'\n" + + " }\n" + + "\n" + + " withEnv(['FOO=bar']) {\n" + + " stage('Stage-B') {\n" + + " sh 'echo b_tp=$TRACEPARENT'\n" + + " try {\n" + + " sh 'echo try_tp=$TRACEPARENT'\n" + + " sh 'exit 1'\n" + + " } catch (err) {\n" + + " sh 'echo catch_tp=$TRACEPARENT'\n" + + " } finally {\n" + + " sh 'echo final_tp=$TRACEPARENT'\n" + + " }\n" + + " }\n" + + " }\n" + + "}"; job.setDefinition(new CpsFlowDefinition(pipelineScript, true)); - WorkflowRun run = jenkinsRule.assertBuildStatus( - Result.SUCCESS, - job.scheduleBuild2(0) - ); + WorkflowRun run = jenkinsRule.assertBuildStatus(Result.SUCCESS, job.scheduleBuild2(0)); String logs = JenkinsRule.getLog(run); @@ -90,9 +82,7 @@ public void TraceParentPropagation() throws Exception { private String extract(String prefix, String log) { for (String line : log.split("\n")) { if (line.contains(prefix)) { - return line.substring( - line.indexOf(prefix) + prefix.length() - ).trim(); + return line.substring(line.indexOf(prefix) + prefix.length()).trim(); } } return null; From 135aa50afe3ca73b86451d5b28f8fef197b963c0 Mon Sep 17 00:00:00 2001 From: Amanraz Thakur Date: Sat, 14 Feb 2026 08:52:06 +0530 Subject: [PATCH 8/8] Update src/test/java/io/jenkins/plugins/opentelemetry/job/TraceParentPipelineTest.java --- .../plugins/opentelemetry/job/TraceParentPipelineTest.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/test/java/io/jenkins/plugins/opentelemetry/job/TraceParentPipelineTest.java b/src/test/java/io/jenkins/plugins/opentelemetry/job/TraceParentPipelineTest.java index c5d14ca6b..fddf637ac 100644 --- a/src/test/java/io/jenkins/plugins/opentelemetry/job/TraceParentPipelineTest.java +++ b/src/test/java/io/jenkins/plugins/opentelemetry/job/TraceParentPipelineTest.java @@ -70,6 +70,11 @@ public void TraceParentPropagation() throws Exception { assertNotNull(finalTp); assertNotEquals(aTp, bTp); + assertNotEquals(bTp, tryTp); + assertNotEquals(bTp, catchTp); + assertNotEquals(bTp, finalTp); + assertNotEquals(tryTp, catchTp); + assertNotEquals(tryTp, finalTp); String traceId = traceId(aTp);