Skip to content

Commit 133d468

Browse files
matt-aitkenclaude
andcommitted
test(webapp): stop createInMemoryTracing from registering OTel globally
Webapp's vitest config sets `pool: "forks"` and the test script uses `--no-file-parallelism`, so every test file in a shard executes sequentially in the same forked Node process. globalThis persists across files even though vitest clears the module cache between them. Two places call `provider.register()`: - `~/v3/tracer.server.ts` via the `setupTelemetry` singleton — sets the OTel global APIs (trace/context/propagation). - `test/utils/tracing.ts#createInMemoryTracing()` — also calls `provider.register()` to make `trace.getTracer("test-tracer")` return its in-memory provider's tracer. Once the webapp's tracer.server.ts has been loaded by any test in the shard, the globals are set. The next test that calls createInMemoryTracing fails with `@opentelemetry/api: Attempted duplicate registration of API: trace/context/propagation`. Failed test stack pointed at `runsBackfiller.test.ts:30` calling `createInMemoryTracing()` after triggerTask.test.ts (which loads tracer.server.ts via `~/runEngine/services/triggerTask.server`) ran first in shard 6. The same shape failure cascades through the rest of the shard's tests because the consumer keeps trying and tracer.server.ts can't be re-singleton'd cleanly after the cache eviction. Why now: this PR's slim-AuthenticatedEnvironment refactor changed the import graph so that triggerTask test's transitive imports actually reach tracer.server.ts in CI's shard 6 distribution. It worked before because the shard's tests didn't co-load both register() callers. But the latent issue was always there — fixing the test util is the right layer. Fix: drop `provider.register()` from createInMemoryTracing. The exporter still receives spans because the consumer uses the tracer returned from `provider.getTracer(...)` directly (provider-scoped, not global). Tests that genuinely need the global must register their own provider explicitly and clean up — but none of the current callers do. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 8478210 commit 133d468

1 file changed

Lines changed: 13 additions & 5 deletions

File tree

apps/webapp/test/utils/tracing.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
22
import { InMemorySpanExporter, SimpleSpanProcessor } from "@opentelemetry/sdk-trace-base";
3-
import { trace } from "@opentelemetry/api";
43
import {
54
MeterProvider,
65
InMemoryMetricExporter,
@@ -9,15 +8,24 @@ import {
98
} from "@opentelemetry/sdk-metrics";
109

1110
export function createInMemoryTracing() {
12-
// Initialize the tracer provider and exporter
11+
// Initialize the tracer provider and exporter — but do NOT call
12+
// `provider.register()`. Calling register() sets the OTel global APIs
13+
// (trace/context/propagation), and webapp's `~/v3/tracer.server.ts`
14+
// also calls register() via its singleton. Webapp's `vitest.config.ts`
15+
// uses `pool: "forks"` with `--no-file-parallelism`, so all test
16+
// files in a shard share one process — globals set by the first test
17+
// to load tracer.server.ts conflict with subsequent createInMemoryTracing
18+
// calls, throwing "Attempted duplicate registration of API: trace".
19+
//
20+
// The tracer returned from `provider.getTracer(...)` is scoped to
21+
// this provider, so the InMemorySpanExporter still receives the
22+
// spans the consuming test creates — no global registration needed.
1323
const exporter = new InMemorySpanExporter();
1424
const provider = new NodeTracerProvider({
1525
spanProcessors: [new SimpleSpanProcessor(exporter)],
1626
});
17-
provider.register();
1827

19-
// Retrieve the tracer
20-
const tracer = trace.getTracer("test-tracer");
28+
const tracer = provider.getTracer("test-tracer");
2129

2230
return {
2331
exporter,

0 commit comments

Comments
 (0)