autotel
Version:
Write Once, Observe Anywhere
324 lines (262 loc) • 9.54 kB
text/typescript
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import {
AlwaysOffSampler,
AlwaysOnSampler,
ParentBasedSampler,
TraceIdRatioBasedSampler,
} from '@opentelemetry/sdk-trace-base';
import {
resolveOtelEnv,
parseResourceAttributes,
parseOtlpHeaders,
createSamplerFromEnv,
envToConfig,
resolveConfigFromEnv,
} from './env-config';
describe('env-config', () => {
const originalEnv = process.env;
beforeEach(() => {
// Reset environment before each test
process.env = { ...originalEnv };
});
afterEach(() => {
// Restore original environment
process.env = originalEnv;
});
describe('resolveOtelEnv', () => {
it('should resolve standard OTEL env vars', () => {
process.env.OTEL_SERVICE_NAME = 'test-service';
process.env.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://localhost:4318';
process.env.OTEL_EXPORTER_OTLP_PROTOCOL = 'http';
const env = resolveOtelEnv();
expect(env.OTEL_SERVICE_NAME).toBe('test-service');
expect(env.OTEL_EXPORTER_OTLP_ENDPOINT).toBe('http://localhost:4318');
expect(env.OTEL_EXPORTER_OTLP_PROTOCOL).toBe('http');
});
it('should return undefined for unset env vars', () => {
const env = resolveOtelEnv();
expect(env.OTEL_SERVICE_NAME).toBeUndefined();
expect(env.OTEL_EXPORTER_OTLP_ENDPOINT).toBeUndefined();
});
it('should validate protocol enum', () => {
process.env.OTEL_EXPORTER_OTLP_PROTOCOL = 'grpc';
const env = resolveOtelEnv();
expect(env.OTEL_EXPORTER_OTLP_PROTOCOL).toBe('grpc');
});
it('should resolve trace sampler env vars', () => {
process.env.OTEL_TRACES_SAMPLER = 'traceidratio';
process.env.OTEL_TRACES_SAMPLER_ARG = '0.5';
const env = resolveOtelEnv();
expect(env.OTEL_TRACES_SAMPLER).toBe('traceidratio');
expect(env.OTEL_TRACES_SAMPLER_ARG).toBe('0.5');
});
});
describe('parseResourceAttributes', () => {
it('should parse comma-separated key=value pairs', () => {
const input = 'service.version=1.0.0,deployment.environment=production';
const result = parseResourceAttributes(input);
expect(result).toEqual({
'service.version': '1.0.0',
'deployment.environment': 'production',
});
});
it('should handle single attribute', () => {
const result = parseResourceAttributes('team=backend');
expect(result).toEqual({ team: 'backend' });
});
it('should handle empty string', () => {
expect(parseResourceAttributes('')).toEqual({});
expect(parseResourceAttributes(' ')).toEqual({});
});
it('should handle undefined', () => {
expect(parseResourceAttributes()).toEqual({});
});
it('should skip invalid pairs without =', () => {
const result = parseResourceAttributes('valid=value,invalid,another=ok');
expect(result).toEqual({
valid: 'value',
another: 'ok',
});
});
it('should handle whitespace around keys and values', () => {
const result = parseResourceAttributes(' key1 = value1 , key2 = value2 ');
expect(result).toEqual({
key1: 'value1',
key2: 'value2',
});
});
it('should skip empty pairs', () => {
const result = parseResourceAttributes('key1=value1,,key2=value2');
expect(result).toEqual({
key1: 'value1',
key2: 'value2',
});
});
it('should handle values with = in them (takes first = as delimiter)', () => {
const result = parseResourceAttributes('key=value=with=equals');
expect(result).toEqual({
key: 'value=with=equals',
});
});
});
describe('parseOtlpHeaders', () => {
it('should parse comma-separated header pairs', () => {
const input = 'api-key=secret123,x-custom-header=value';
const result = parseOtlpHeaders(input);
expect(result).toEqual({
'api-key': 'secret123',
'x-custom-header': 'value',
});
});
it('should handle single header', () => {
const result = parseOtlpHeaders('authorization=Bearer token');
expect(result).toEqual({ authorization: 'Bearer token' });
});
it('should handle empty string', () => {
expect(parseOtlpHeaders('')).toEqual({});
expect(parseOtlpHeaders(' ')).toEqual({});
});
it('should handle undefined', () => {
expect(parseOtlpHeaders()).toEqual({});
});
it('should skip invalid pairs', () => {
const result = parseOtlpHeaders('valid=value,invalid,another=ok');
expect(result).toEqual({
valid: 'value',
another: 'ok',
});
});
it('should handle whitespace', () => {
const result = parseOtlpHeaders(' key1 = value1 , key2 = value2 ');
expect(result).toEqual({
key1: 'value1',
key2: 'value2',
});
});
});
describe('envToConfig', () => {
it('should map OTEL_SERVICE_NAME to service', () => {
const config = envToConfig({
OTEL_SERVICE_NAME: 'test-service',
});
expect(config.service).toBe('test-service');
});
it('should map OTEL_EXPORTER_OTLP_ENDPOINT to endpoint', () => {
const config = envToConfig({
OTEL_EXPORTER_OTLP_ENDPOINT: 'http://localhost:4318',
});
expect(config.endpoint).toBe('http://localhost:4318');
});
it('should map OTEL_EXPORTER_OTLP_PROTOCOL to protocol', () => {
const config = envToConfig({
OTEL_EXPORTER_OTLP_PROTOCOL: 'grpc',
});
expect(config.protocol).toBe('grpc');
});
it('should parse OTEL_EXPORTER_OTLP_HEADERS', () => {
const config = envToConfig({
OTEL_EXPORTER_OTLP_HEADERS: 'api-key=secret,x-custom=value',
});
expect(config.headers).toEqual({
'api-key': 'secret',
'x-custom': 'value',
});
});
it('should parse OTEL_RESOURCE_ATTRIBUTES', () => {
const config = envToConfig({
OTEL_RESOURCE_ATTRIBUTES:
'service.version=1.0.0,deployment.environment=prod',
});
expect(config.resourceAttributes).toEqual({
'service.version': '1.0.0',
'deployment.environment': 'prod',
});
});
it('should return empty config for no env vars', () => {
const config = envToConfig({});
expect(config).toEqual({});
});
it('should map OTEL_TRACES_SAMPLER to an SDK sampler', () => {
const config = envToConfig({
OTEL_TRACES_SAMPLER: 'always_on',
});
expect(config.otelSampler).toBeInstanceOf(AlwaysOnSampler);
});
});
describe('createSamplerFromEnv', () => {
it('supports always_on', () => {
const sampler = createSamplerFromEnv({
OTEL_TRACES_SAMPLER: 'always_on',
});
expect(sampler).toBeInstanceOf(AlwaysOnSampler);
});
it('supports always_off', () => {
const sampler = createSamplerFromEnv({
OTEL_TRACES_SAMPLER: 'always_off',
});
expect(sampler).toBeInstanceOf(AlwaysOffSampler);
});
it('supports traceidratio', () => {
const sampler = createSamplerFromEnv({
OTEL_TRACES_SAMPLER: 'traceidratio',
OTEL_TRACES_SAMPLER_ARG: '0.25',
});
expect(sampler).toBeInstanceOf(TraceIdRatioBasedSampler);
});
it('supports parentbased_traceidratio', () => {
const sampler = createSamplerFromEnv({
OTEL_TRACES_SAMPLER: 'parentbased_traceidratio',
OTEL_TRACES_SAMPLER_ARG: '0.25',
});
expect(sampler).toBeInstanceOf(ParentBasedSampler);
});
it('returns undefined for unsupported remote samplers', () => {
const sampler = createSamplerFromEnv({
OTEL_TRACES_SAMPLER: 'jaeger_remote',
});
expect(sampler).toBeUndefined();
});
});
describe('resolveConfigFromEnv', () => {
it('should return config from env vars', () => {
process.env.OTEL_SERVICE_NAME = 'test-service';
process.env.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://localhost:4318';
const config = resolveConfigFromEnv();
expect(config.service).toBe('test-service');
expect(config.endpoint).toBe('http://localhost:4318');
});
it('should work with no env vars set', () => {
const config = resolveConfigFromEnv();
expect(config).toEqual({});
});
it('should parse complex real-world scenario', () => {
process.env.OTEL_SERVICE_NAME = 'my-api';
process.env.OTEL_EXPORTER_OTLP_ENDPOINT = 'https://api.honeycomb.io';
process.env.OTEL_EXPORTER_OTLP_HEADERS =
'x-honeycomb-team=abc123,x-honeycomb-dataset=production';
process.env.OTEL_RESOURCE_ATTRIBUTES =
'service.version=1.2.3,deployment.environment=production,team=backend';
process.env.OTEL_EXPORTER_OTLP_PROTOCOL = 'http';
const config = resolveConfigFromEnv();
expect(config).toEqual({
service: 'my-api',
endpoint: 'https://api.honeycomb.io',
protocol: 'http',
headers: {
'x-honeycomb-team': 'abc123',
'x-honeycomb-dataset': 'production',
},
resourceAttributes: {
'service.version': '1.2.3',
'deployment.environment': 'production',
team: 'backend',
},
});
});
it('should include otelSampler when OTEL_TRACES_SAMPLER is set', () => {
process.env.OTEL_TRACES_SAMPLER = 'parentbased_always_off';
const config = resolveConfigFromEnv();
expect(config.otelSampler).toBeInstanceOf(ParentBasedSampler);
});
});
});