From 3d14882a98ec9d45265c685ad67d94ce835f0206 Mon Sep 17 00:00:00 2001 From: Francesco Gringl-Novy Date: Wed, 20 May 2026 10:44:03 +0200 Subject: [PATCH] test(opentelemetry): Avoid wallclock race in startSpan child end-time test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "converts seconds to milliseconds for startSpan child span" test computed `nowSec = Math.floor(Date.now()/1000) + 1` and then created both an outer `startSpan` and an inner `startInactiveSpan` before calling `innerSpan.end(nowSec)`. When the surrounding setup pushed wallclock past `nowSec*1000`, OTel's `Span.end` saw `endTime < startTime`, hit the `if (duration[0] < 0)` clamp in `sdk-trace-base/Span.js`, and overwrote endTime with startTime — leaving endTime[1] equal to the sub-second nanos of startTime (1000000 in the reported failure) instead of the expected 0. Bumping the offset to +10s gives the test enough headroom that the clamp branch can no longer trip during normal execution. Sibling tests in the same describe block use +1 but do not flake in practice because they create only a single span. Fixes #20962 Co-Authored-By: Claude Opus 4.7 (1M context) --- packages/opentelemetry/test/trace.test.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/opentelemetry/test/trace.test.ts b/packages/opentelemetry/test/trace.test.ts index 0aeacc5284ad..4b6f611d7108 100644 --- a/packages/opentelemetry/test/trace.test.ts +++ b/packages/opentelemetry/test/trace.test.ts @@ -2185,7 +2185,10 @@ describe('span.end() timestamp conversion', () => { }); it('converts seconds to milliseconds for startSpan child span', () => { - const nowSec = Math.floor(Date.now() / 1000) + 1; + // +10s buffer (not +1 like sibling tests): the outer startSpan + inner startInactiveSpan + // adds enough wallclock overhead that nowSec*1000 can slip behind innerSpan's startTime, + // tripping OTel's `endTime < startTime` clamp and copying startTime's nanos onto endTime. + const nowSec = Math.floor(Date.now() / 1000) + 10; let capturedEndTime: [number, number] | undefined; startSpan({ name: 'outer' }, () => { const innerSpan = startInactiveSpan({ name: 'inner' });