/**
* @jest-environment node
*/
import type {
Context,
ContextManager,
TextMapGetter,
TextMapPropagator,
} from '@opentelemetry/api'
import {
ROOT_CONTEXT,
context,
createContextKey,
propagation,
trace,
} from '@opentelemetry/api'
import { getTracer } from './tracer'
const customContextKey = createContextKey('next.tracer.test.custom-context')
const getter: TextMapGetter<Record<string, string | undefined>> = {
keys: (carrier) => Object.keys(carrier),
get: (carrier, key) => carrier[key],
}
class TestContextManager implements ContextManager {
private currentContext: Context = ROOT_CONTEXT
active(): Context {
return this.currentContext
}
with<A extends unknown[], F extends (...args: A) => ReturnType<F>>(
newContext: Context,
fn: F,
thisArg?: ThisParameterType<F>,
...args: A
): ReturnType<F> {
const previousContext = this.currentContext
this.currentContext = newContext
try {
return fn.apply(thisArg, args)
} finally {
this.currentContext = previousContext
}
}
bind<T>(bindContext: Context, target: T): T {
if (typeof target !== 'function') {
return target
}
return ((...args: unknown[]) => {
return this.with(
bindContext,
target as (...args: unknown[]) => unknown,
undefined,
...args
)
}) as T
}
enable(): this {
return this
}
disable(): this {
this.currentContext = ROOT_CONTEXT
return this
}
}
class CustomPropagator implements TextMapPropagator {
fields(): string[] {
return ['x-custom']
}
inject(): void {}
extract(
extractedContext: Context,
carrier: Record<string, string | undefined>,
mapGetter: TextMapGetter<Record<string, string | undefined>>
): Context {
const value = mapGetter.get(carrier, 'x-custom')
if (!value || Array.isArray(value)) {
return extractedContext
}
return extractedContext.setValue(customContextKey, value)
}
}
describe('withPropagatedContext', () => {
beforeEach(() => {
context.disable()
propagation.disable()
context.setGlobalContextManager(new TestContextManager())
propagation.setGlobalPropagator(new CustomPropagator())
})
afterEach(() => {
propagation.disable()
context.disable()
})
it('merges extracted context in force mode when no remote span exists', () => {
const activeSpan = trace.wrapSpanContext({
traceId: '0123456789abcdef0123456789abcdef',
spanId: '0123456789abcdef',
traceFlags: 1,
isRemote: false,
})
const activeContext = trace.setSpan(ROOT_CONTEXT, activeSpan)
const result = context.with(activeContext, () =>
getTracer().withPropagatedContext(
{ 'x-custom': 'custom1' },
() => {
const scopedContext = context.active()
return {
customValue: scopedContext.getValue(customContextKey),
activeSpanId: trace.getSpanContext(scopedContext)?.spanId,
}
},
getter,
true
)
)
expect(result).toEqual({
customValue: 'custom1',
activeSpanId: '0123456789abcdef',
})
})
})