opentelemetry-js/packages/opentelemetry-core/test/common/propagation/composite.test.ts

229 lines
6.5 KiB
TypeScript

/*
* Copyright The OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
defaultTextMapGetter,
defaultTextMapSetter,
TextMapPropagator,
SpanContext,
TextMapGetter,
TextMapSetter,
trace,
} from '@opentelemetry/api';
import { Context, ROOT_CONTEXT } from '@opentelemetry/api';
import * as assert from 'assert';
import { CompositePropagator, W3CTraceContextPropagator } from '../../../src';
import {
TRACE_PARENT_HEADER,
TRACE_STATE_HEADER,
} from '../../../src/trace/W3CTraceContextPropagator';
import { TraceState } from '../../../src/trace/TraceState';
class DummyPropagator implements TextMapPropagator {
inject(context: Context, carrier: any, setter: TextMapSetter<any>): void {
carrier['dummy'] = trace.getSpanContext(context);
}
extract(context: Context, carrier: any, getter: TextMapGetter<any>): Context {
if (carrier['dummy']) {
return trace.setSpanContext(context, carrier['dummy']);
}
return context;
}
fields(): string[] {
return ['dummy'];
}
}
describe('Composite Propagator', () => {
const traceId = 'd4cda95b652f4a1592b449d5929fda1b';
const spanId = '6e0c63257de34c92';
describe('inject', () => {
let carrier: { [key: string]: unknown };
let spanContext: SpanContext;
let ctxWithSpanContext: Context;
beforeEach(() => {
carrier = {};
spanContext = {
spanId,
traceId,
traceFlags: 1,
traceState: new TraceState('foo=bar'),
};
ctxWithSpanContext = trace.setSpanContext(ROOT_CONTEXT, spanContext);
});
it('should inject context using all configured propagators', () => {
const composite = new CompositePropagator({
propagators: [new DummyPropagator(), new W3CTraceContextPropagator()],
});
composite.inject(ctxWithSpanContext, carrier, defaultTextMapSetter);
assert.strictEqual(carrier['dummy'], spanContext);
assert.strictEqual(
carrier[TRACE_PARENT_HEADER],
`00-${traceId}-${spanId}-01`
);
assert.strictEqual(carrier[TRACE_STATE_HEADER], 'foo=bar');
});
it('should not throw', () => {
const composite = new CompositePropagator({
propagators: [
new ThrowingPropagator(),
new W3CTraceContextPropagator(),
],
});
composite.inject(ctxWithSpanContext, carrier, defaultTextMapSetter);
assert.strictEqual(
carrier[TRACE_PARENT_HEADER],
`00-${traceId}-${spanId}-01`
);
});
});
describe('extract', () => {
let carrier: { [key: string]: unknown };
beforeEach(() => {
carrier = {
['dummy']: { traceId, spanId },
[TRACE_PARENT_HEADER]: `00-${traceId}-${spanId}-01`,
[TRACE_STATE_HEADER]: 'foo=bar',
};
});
it('should extract context using all configured propagators', () => {
const composite = new CompositePropagator({
propagators: [new DummyPropagator(), new W3CTraceContextPropagator()],
});
const spanContext = trace.getSpanContext(
composite.extract(ROOT_CONTEXT, carrier, defaultTextMapGetter)
);
if (!spanContext) {
throw new Error('no extracted context');
}
assert.strictEqual(spanContext.traceId, traceId);
assert.strictEqual(spanContext.spanId, spanId);
assert.strictEqual(spanContext.traceFlags, 1);
assert.strictEqual(spanContext.isRemote, true);
assert.strictEqual(spanContext.traceState!.get('foo'), 'bar');
});
it('should not throw', () => {
const composite = new CompositePropagator({
propagators: [
new ThrowingPropagator(),
new W3CTraceContextPropagator(),
],
});
const spanContext = trace.getSpanContext(
composite.extract(ROOT_CONTEXT, carrier, defaultTextMapGetter)
);
if (!spanContext) {
throw new Error('no extracted context');
}
assert.strictEqual(spanContext.traceId, traceId);
assert.strictEqual(spanContext.spanId, spanId);
assert.strictEqual(spanContext.traceFlags, 1);
assert.strictEqual(spanContext.isRemote, true);
assert.strictEqual(spanContext.traceState!.get('foo'), 'bar');
});
});
describe('fields()', () => {
it('should combine fields from both propagators', () => {
const composite = new CompositePropagator({
propagators: [
{
extract: c => c,
inject: () => {},
fields: () => ['p1'],
},
{
extract: c => c,
inject: () => {},
fields: () => ['p2'],
},
],
});
assert.deepStrictEqual(composite.fields(), ['p1', 'p2']);
});
it('should ignore propagators without fields function', () => {
const composite = new CompositePropagator({
propagators: [
{
extract: c => c,
inject: () => {},
fields: () => ['p1'],
},
// @ts-expect-error verify missing fields case
{
extract: c => c,
inject: () => {},
},
],
});
assert.deepStrictEqual(composite.fields(), ['p1']);
});
it('should not allow caller to modify fields', () => {
const composite = new CompositePropagator({
propagators: [
{
extract: c => c,
inject: () => {},
fields: () => ['p1'],
},
{
extract: c => c,
inject: () => {},
fields: () => ['p2'],
},
],
});
const fields = composite.fields();
assert.deepStrictEqual(fields, ['p1', 'p2']);
fields[1] = 'p3';
assert.deepStrictEqual(composite.fields(), ['p1', 'p2']);
});
});
});
class ThrowingPropagator implements TextMapPropagator {
inject(context: Context, carrier: unknown) {
throw new Error('this propagator throws');
}
extract(context: Context, carrier: unknown): Context {
throw new Error('This propagator throws');
}
fields(): string[] {
return [];
}
}