autotel
Version:
Write Once, Observe Anywhere
1,061 lines (885 loc) • 31.4 kB
text/typescript
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { TraceFlags } from '@opentelemetry/api';
import type { Link, SpanContext } from '@opentelemetry/api';
import {
RandomSampler,
AlwaysSampler,
NeverSampler,
AdaptiveSampler,
UserIdSampler,
CompositeSampler,
FeatureFlagSampler,
createLinkFromHeaders,
extractLinksFromBatch,
samplingPresets,
resolveSamplingPreset,
type SamplingContext,
} from './sampling';
import { type ILogger } from './logger';
describe('Sampling', () => {
let mockLogger: ILogger;
let context: SamplingContext;
beforeEach(() => {
mockLogger = {
info: vi.fn(),
warn: vi.fn(),
error: vi.fn(),
debug: vi.fn(),
};
context = {
operationName: 'test.operation',
args: [{ userId: '123', email: 'test@example.com' }],
};
});
describe('RandomSampler', () => {
it('should throw error for invalid sample rates', () => {
expect(() => new RandomSampler(-0.1)).toThrow();
expect(() => new RandomSampler(1.1)).toThrow();
});
it('should sample at 100%', () => {
const sampler = new RandomSampler(1);
const results = Array.from({ length: 100 }, () =>
sampler.shouldSample(context),
);
expect(results.every((r) => r === true)).toBe(true);
});
it('should never sample at 0%', () => {
const sampler = new RandomSampler(0);
const results = Array.from({ length: 100 }, () =>
sampler.shouldSample(context),
);
expect(results.every((r) => r === false)).toBe(true);
});
it('should sample approximately at the specified rate', () => {
const sampler = new RandomSampler(0.5);
const results = Array.from({ length: 1000 }, () =>
sampler.shouldSample(context),
);
const sampleCount = results.filter(Boolean).length;
// Allow 20% margin of error — random sampling is inherently noisy
expect(sampleCount).toBeGreaterThan(400);
expect(sampleCount).toBeLessThan(600);
});
});
describe('AlwaysSampler', () => {
it('should always sample', () => {
const sampler = new AlwaysSampler();
const results = Array.from({ length: 100 }, () =>
sampler.shouldSample(context),
);
expect(results.every((r) => r === true)).toBe(true);
});
});
describe('NeverSampler', () => {
it('should never sample', () => {
const sampler = new NeverSampler();
const results = Array.from({ length: 100 }, () =>
sampler.shouldSample(context),
);
expect(results.every((r) => r === false)).toBe(true);
});
});
describe('AdaptiveSampler', () => {
it('should throw error for invalid baseline sample rate', () => {
expect(
() =>
new AdaptiveSampler({
baselineSampleRate: -0.1,
}),
).toThrow();
});
it('should indicate it needs tail sampling', () => {
const sampler = new AdaptiveSampler({
baselineSampleRate: 0.1,
});
expect(sampler.needsTailSampling()).toBe(true);
});
it('should always create spans (optimistic sampling)', () => {
const sampler = new AdaptiveSampler({
baselineSampleRate: 0, // Even with 0% baseline
logger: mockLogger,
});
// FIX: shouldSample now always returns true for tail sampling
const result = sampler.shouldSample(context);
expect(result).toBe(true);
});
it('should keep error traces even when baseline would drop them', () => {
const sampler = new AdaptiveSampler({
baselineSampleRate: 0, // 0% baseline sampling
alwaysSampleErrors: true,
logger: mockLogger,
});
// FIX: Span is created (shouldSample returns true)
const shouldSample = sampler.shouldSample(context);
expect(shouldSample).toBe(true);
// Tail sampling keeps error traces
const shouldKeep = sampler.shouldKeepTrace(context, {
success: false,
duration: 100,
error: new Error('Test error'),
});
expect(shouldKeep).toBe(true);
// Pino-native: (extra, message)
expect(mockLogger.debug).toHaveBeenCalledWith(
expect.objectContaining({
operation: 'test.operation',
error: 'Test error',
}),
'Adaptive sampling: Keeping error trace',
);
});
it('should keep slow traces even when baseline would drop them', () => {
const sampler = new AdaptiveSampler({
baselineSampleRate: 0, // 0% baseline sampling
slowThresholdMs: 1000,
alwaysSampleSlow: true,
logger: mockLogger,
});
// FIX: Span is created (shouldSample returns true)
const shouldSample = sampler.shouldSample(context);
expect(shouldSample).toBe(true);
// Tail sampling keeps slow traces
const shouldKeep = sampler.shouldKeepTrace(context, {
success: true,
duration: 1500, // > 1000ms threshold
});
expect(shouldKeep).toBe(true);
// Pino-native: (extra, message)
expect(mockLogger.debug).toHaveBeenCalledWith(
expect.objectContaining({
operation: 'test.operation',
duration: 1500,
}),
'Adaptive sampling: Keeping slow trace',
);
});
it('should drop fast successful traces when baseline sampling says no', () => {
const sampler = new AdaptiveSampler({
baselineSampleRate: 0, // 0% baseline
slowThresholdMs: 1000,
logger: mockLogger,
});
// Span is created optimistically
const shouldSample = sampler.shouldSample(context);
expect(shouldSample).toBe(true);
// Tail sampling drops fast/successful traces
const shouldKeep = sampler.shouldKeepTrace(context, {
success: true,
duration: 100, // < 1000ms threshold
});
expect(shouldKeep).toBe(false);
});
it('should keep fast successful traces when baseline sampling says yes', () => {
const sampler = new AdaptiveSampler({
baselineSampleRate: 1, // 100% baseline
slowThresholdMs: 1000,
logger: mockLogger,
});
const shouldSample = sampler.shouldSample(context);
expect(shouldSample).toBe(true);
// Baseline sampled it, so keep it
const shouldKeep = sampler.shouldKeepTrace(context, {
success: true,
duration: 100,
});
expect(shouldKeep).toBe(true);
});
it('should respect alwaysSampleErrors flag', () => {
const sampler = new AdaptiveSampler({
baselineSampleRate: 0,
alwaysSampleErrors: false, // Don't force-sample errors
logger: mockLogger,
});
const shouldKeep = sampler.shouldKeepTrace(context, {
success: false,
duration: 100,
error: new Error('Test error'),
});
// With alwaysSampleErrors=false and baseline=0, errors are dropped
expect(shouldKeep).toBe(false);
});
it('should respect alwaysSampleSlow flag', () => {
const sampler = new AdaptiveSampler({
baselineSampleRate: 0,
slowThresholdMs: 1000,
alwaysSampleSlow: false, // Don't force-sample slow requests
logger: mockLogger,
});
const shouldKeep = sampler.shouldKeepTrace(context, {
success: true,
duration: 1500,
});
// With alwaysSampleSlow=false and baseline=0, slow requests are dropped
expect(shouldKeep).toBe(false);
});
});
describe('UserIdSampler', () => {
const extractUserId = (args: unknown[]) => {
const firstArg = args[0] as { userId?: string };
return firstArg?.userId;
};
it('should always sample specific users', () => {
const sampler = new UserIdSampler({
baselineSampleRate: 0,
alwaysSampleUsers: ['vip_123'],
extractUserId,
logger: mockLogger,
});
const vipContext: SamplingContext = {
operationName: 'test.operation',
args: [{ userId: 'vip_123' }],
};
expect(sampler.shouldSample(vipContext)).toBe(true);
// Pino-native: (extra, message)
expect(mockLogger.debug).toHaveBeenCalledWith(
{
operation: 'test.operation',
userId: 'vip_123',
},
'Sampling user request',
);
});
it('should use consistent per-user sampling', () => {
const sampler = new UserIdSampler({
baselineSampleRate: 0.5,
extractUserId,
logger: mockLogger,
});
const user123Context: SamplingContext = {
operationName: 'test.operation',
args: [{ userId: 'user_123' }],
};
// Same user should always get same result
const result1 = sampler.shouldSample(user123Context);
const result2 = sampler.shouldSample(user123Context);
const result3 = sampler.shouldSample(user123Context);
expect(result1).toBe(result2);
expect(result2).toBe(result3);
});
it('should add and remove users from always-sample list', () => {
const sampler = new UserIdSampler({
baselineSampleRate: 0,
extractUserId,
});
sampler.addAlwaysSampleUsers('user_1', 'user_2');
const user1Context: SamplingContext = {
operationName: 'test.operation',
args: [{ userId: 'user_1' }],
};
expect(sampler.shouldSample(user1Context)).toBe(true);
sampler.removeAlwaysSampleUsers('user_1');
expect(sampler.shouldSample(user1Context)).toBe(false);
});
it('should fallback to random sampling when no user ID', () => {
const sampler = new UserIdSampler({
baselineSampleRate: 1,
extractUserId: (args) => (args[0] as { userId?: string })?.userId,
});
const noUserContext: SamplingContext = {
operationName: 'test.operation',
args: [{}],
};
expect(sampler.shouldSample(noUserContext)).toBe(true);
});
});
describe('CompositeSampler', () => {
it('should throw error with no child samplers', () => {
expect(() => new CompositeSampler([])).toThrow();
});
it('should sample if any child sampler returns true', () => {
const sampler = new CompositeSampler([
new NeverSampler(),
new AlwaysSampler(),
new NeverSampler(),
]);
expect(sampler.shouldSample(context)).toBe(true);
});
it('should not sample if all child samplers return false', () => {
const sampler = new CompositeSampler([
new NeverSampler(),
new NeverSampler(),
]);
expect(sampler.shouldSample(context)).toBe(false);
});
});
describe('FeatureFlagSampler', () => {
const extractFlags = (
args: unknown[],
metadata?: Record<string, unknown>,
) => {
const firstArg = args[0] as { flags?: string[] };
return firstArg?.flags || (metadata?.featureFlags as string[]);
};
it('should always sample requests with monitored flags', () => {
const sampler = new FeatureFlagSampler({
baselineSampleRate: 0,
alwaysSampleFlags: ['new_checkout', 'experimental_ui'],
extractFlags,
logger: mockLogger,
});
const flagContext: SamplingContext = {
operationName: 'test.operation',
args: [{ flags: ['new_checkout'] }],
};
expect(sampler.shouldSample(flagContext)).toBe(true);
// Pino-native: (extra, message)
expect(mockLogger.debug).toHaveBeenCalledWith(
{
operation: 'test.operation',
flags: ['new_checkout'],
},
'Sampling feature flag request',
);
});
it('should use baseline sampling for non-monitored flags', () => {
const sampler = new FeatureFlagSampler({
baselineSampleRate: 0,
alwaysSampleFlags: ['monitored_flag'],
extractFlags,
});
const flagContext: SamplingContext = {
operationName: 'test.operation',
args: [{ flags: ['other_flag'] }],
};
expect(sampler.shouldSample(flagContext)).toBe(false);
});
it('should add and remove flags', () => {
const sampler = new FeatureFlagSampler({
baselineSampleRate: 0,
extractFlags,
});
sampler.addAlwaysSampleFlags('flag_1', 'flag_2');
const flag1Context: SamplingContext = {
operationName: 'test.operation',
args: [{ flags: ['flag_1'] }],
};
expect(sampler.shouldSample(flag1Context)).toBe(true);
sampler.removeAlwaysSampleFlags('flag_1');
expect(sampler.shouldSample(flag1Context)).toBe(false);
});
});
describe('Real-world QA in Production scenarios', () => {
it('should always capture failed email deliveries from article example', () => {
// From article: "We set up another alert that let us know if our
// email-sending microservice was unable to process a request"
const sampler = new AdaptiveSampler({
baselineSampleRate: 0.1, // 10% baseline
alwaysSampleErrors: true, // Always capture failures
});
const emailContext: SamplingContext = {
operationName: 'email.send',
args: [{ to: 'school@example.com' }],
};
sampler.shouldSample(emailContext);
// Email fails due to invalid address
const shouldKeep = sampler.shouldKeepTrace(emailContext, {
success: false,
duration: 100,
error: new Error('Invalid email address'),
});
expect(shouldKeep).toBe(true);
});
it('should always trace slow job applications from article example', () => {
// From article: Monitor if teachers are able to submit applications
const sampler = new AdaptiveSampler({
baselineSampleRate: 0.05, // 5% baseline
slowThresholdMs: 2000, // Slow if > 2s
alwaysSampleSlow: true,
});
const applicationContext: SamplingContext = {
operationName: 'application.submit',
args: [{ jobId: '123', teacherId: '456' }],
};
sampler.shouldSample(applicationContext);
// Application takes too long
const shouldKeep = sampler.shouldKeepTrace(applicationContext, {
success: true,
duration: 3000, // > 2s threshold
});
expect(shouldKeep).toBe(true);
});
it('should always trace VIP users', () => {
const extractUserId = (args: unknown[]) => {
const firstArg = args[0] as { userId?: string };
return firstArg?.userId;
};
const sampler = new UserIdSampler({
baselineSampleRate: 0.01, // 1% of normal users
alwaysSampleUsers: ['vip_school_123'], // Always trace VIP schools
extractUserId,
});
const vipContext: SamplingContext = {
operationName: 'application.receive',
args: [{ userId: 'vip_school_123' }],
};
expect(sampler.shouldSample(vipContext)).toBe(true);
});
it('should always trace A/B test variants for correlation', () => {
const extractFlags = (args: unknown[]) => {
const firstArg = args[0] as { experimentFlags?: string[] };
return firstArg?.experimentFlags;
};
const sampler = new FeatureFlagSampler({
baselineSampleRate: 0.05,
alwaysSampleFlags: ['new_application_form'], // Always trace experiment
extractFlags,
});
const experimentContext: SamplingContext = {
operationName: 'application.submit',
args: [{ experimentFlags: ['new_application_form'] }],
};
expect(sampler.shouldSample(experimentContext)).toBe(true);
});
});
describe('AdaptiveSampler - Links-based sampling', () => {
let mockLogger: ILogger;
beforeEach(() => {
mockLogger = {
info: vi.fn(),
warn: vi.fn(),
error: vi.fn(),
debug: vi.fn(),
};
});
// Helper to create a SpanContext
const createSpanContext = (sampled: boolean): SpanContext => ({
traceId: '0af7651916cd43dd8448eb211c80319c',
spanId: 'b7ad6b7169203331',
traceFlags: sampled ? TraceFlags.SAMPLED : TraceFlags.NONE,
isRemote: true,
});
// Helper to create a Link
const createLink = (
sampled: boolean,
attributes?: Record<string, unknown>,
): Link => ({
context: createSpanContext(sampled),
attributes: attributes ?? {},
});
it('should have linksBased disabled by default', () => {
const sampler = new AdaptiveSampler({
baselineSampleRate: 0.1,
});
const context: SamplingContext = {
operationName: 'test.operation',
args: [],
links: [createLink(true)], // Sampled link
};
sampler.shouldSample(context);
// With linksBased=false (default), sampled links don't affect decision
// With baselineSampleRate=0.1 and random chance, we can't deterministically test
// So we test with 0% baseline - the link should NOT cause it to be kept
const samplerStrict = new AdaptiveSampler({
baselineSampleRate: 0,
linksBased: false, // Explicitly disabled
});
samplerStrict.shouldSample(context);
const shouldKeep = samplerStrict.shouldKeepTrace(context, {
success: true,
duration: 100,
});
expect(shouldKeep).toBe(false);
});
it('should throw error for invalid linksRate', () => {
expect(
() =>
new AdaptiveSampler({
linksRate: -0.1,
}),
).toThrow('Links rate must be between 0 and 1');
expect(
() =>
new AdaptiveSampler({
linksRate: 1.5,
}),
).toThrow('Links rate must be between 0 and 1');
});
it('should keep traces with sampled links when linksBased=true', () => {
const sampler = new AdaptiveSampler({
baselineSampleRate: 0, // 0% baseline
linksBased: true,
linksRate: 1, // 100% of linked spans kept
logger: mockLogger,
});
const context: SamplingContext = {
operationName: 'consumer.process',
args: [],
links: [createLink(true)], // Sampled link
};
sampler.shouldSample(context);
const shouldKeep = sampler.shouldKeepTrace(context, {
success: true,
duration: 100,
});
expect(shouldKeep).toBe(true);
// Pino-native: (extra, message)
expect(mockLogger.debug).toHaveBeenCalledWith(
expect.objectContaining({
operation: 'consumer.process',
linkCount: 1,
}),
'Adaptive sampling: Keeping trace due to sampled link',
);
});
it('should drop traces with unsampled links when linksBased=true', () => {
const sampler = new AdaptiveSampler({
baselineSampleRate: 0,
linksBased: true,
linksRate: 1,
});
const context: SamplingContext = {
operationName: 'consumer.process',
args: [],
links: [createLink(false)], // NOT sampled
};
sampler.shouldSample(context);
const shouldKeep = sampler.shouldKeepTrace(context, {
success: true,
duration: 100,
});
expect(shouldKeep).toBe(false);
});
it('should keep trace if ANY link is sampled (fan-in)', () => {
const sampler = new AdaptiveSampler({
baselineSampleRate: 0,
linksBased: true,
linksRate: 1,
});
const context: SamplingContext = {
operationName: 'batch.process',
args: [],
links: [
createLink(false), // Not sampled
createLink(true), // Sampled
createLink(false), // Not sampled
],
};
sampler.shouldSample(context);
const shouldKeep = sampler.shouldKeepTrace(context, {
success: true,
duration: 100,
});
expect(shouldKeep).toBe(true);
});
it('should respect linksRate for probabilistic link sampling', () => {
const sampler = new AdaptiveSampler({
baselineSampleRate: 0,
linksBased: true,
linksRate: 0, // 0% - never keep linked spans
});
const context: SamplingContext = {
operationName: 'consumer.process',
args: [],
links: [createLink(true)],
};
sampler.shouldSample(context);
const shouldKeep = sampler.shouldKeepTrace(context, {
success: true,
duration: 100,
});
expect(shouldKeep).toBe(false);
});
it('should prioritize errors over links-based sampling', () => {
const sampler = new AdaptiveSampler({
baselineSampleRate: 0,
linksBased: true,
linksRate: 0, // Would drop linked spans
alwaysSampleErrors: true,
});
const context: SamplingContext = {
operationName: 'consumer.process',
args: [],
links: [createLink(false)], // No sampled links
};
sampler.shouldSample(context);
const shouldKeep = sampler.shouldKeepTrace(context, {
success: false, // Error!
duration: 100,
error: new Error('Processing failed'),
});
expect(shouldKeep).toBe(true); // Errors always kept
});
it('should handle empty links array', () => {
const sampler = new AdaptiveSampler({
baselineSampleRate: 0,
linksBased: true,
linksRate: 1,
});
const context: SamplingContext = {
operationName: 'test.operation',
args: [],
links: [],
};
sampler.shouldSample(context);
const shouldKeep = sampler.shouldKeepTrace(context, {
success: true,
duration: 100,
});
expect(shouldKeep).toBe(false); // No links, baseline=0
});
it('should handle undefined links', () => {
const sampler = new AdaptiveSampler({
baselineSampleRate: 0,
linksBased: true,
linksRate: 1,
});
const context: SamplingContext = {
operationName: 'test.operation',
args: [],
// links is undefined
};
sampler.shouldSample(context);
const shouldKeep = sampler.shouldKeepTrace(context, {
success: true,
duration: 100,
});
expect(shouldKeep).toBe(false);
});
describe('hasSampledLink helper', () => {
it('should return false for empty links', () => {
const sampler = new AdaptiveSampler({ linksBased: true });
expect(sampler.hasSampledLink([])).toBe(false);
});
it('should return true if any link is sampled', () => {
const sampler = new AdaptiveSampler({ linksBased: true });
const links = [createLink(false), createLink(true)];
expect(sampler.hasSampledLink(links)).toBe(true);
});
it('should return false if no links are sampled', () => {
const sampler = new AdaptiveSampler({ linksBased: true });
const links = [createLink(false), createLink(false)];
expect(sampler.hasSampledLink(links)).toBe(false);
});
});
});
describe('Link Helper Functions', () => {
describe('createLinkFromHeaders', () => {
it('should create link from valid W3C traceparent header', () => {
const headers = {
traceparent:
'00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01',
};
const link = createLinkFromHeaders(headers);
expect(link).not.toBeNull();
expect(link?.context.traceId).toBe('0af7651916cd43dd8448eb211c80319c');
expect(link?.context.spanId).toBe('b7ad6b7169203331');
expect(link?.context.traceFlags).toBe(TraceFlags.SAMPLED);
});
it('should create link from unsampled traceparent', () => {
const headers = {
traceparent:
'00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-00',
};
const link = createLinkFromHeaders(headers);
expect(link).not.toBeNull();
expect(link?.context.traceFlags).toBe(TraceFlags.NONE);
});
it('should include custom attributes in link', () => {
const headers = {
traceparent:
'00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01',
};
const attributes = {
'messaging.system': 'kafka',
'custom.attr': 'value',
};
const link = createLinkFromHeaders(headers, attributes);
expect(link?.attributes).toEqual(attributes);
});
it('should return null for invalid/missing traceparent', () => {
expect(createLinkFromHeaders({})).toBeNull();
expect(createLinkFromHeaders({ traceparent: 'invalid' })).toBeNull();
expect(createLinkFromHeaders({ traceparent: '' })).toBeNull();
});
it('should return null for all-zero trace IDs', () => {
// All zeros is an invalid trace context
const headers = {
traceparent:
'00-00000000000000000000000000000000-0000000000000000-01',
};
const link = createLinkFromHeaders(headers);
expect(link).toBeNull();
});
});
describe('extractLinksFromBatch', () => {
it('should extract links from batch of messages', () => {
const messages = [
{
body: 'message1',
headers: {
traceparent:
'00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01',
},
},
{
body: 'message2',
headers: {
traceparent:
'00-1af7651916cd43dd8448eb211c80319c-c7ad6b7169203331-01',
},
},
];
const links = extractLinksFromBatch(messages);
expect(links).toHaveLength(2);
expect(links[0].context.traceId).toBe(
'0af7651916cd43dd8448eb211c80319c',
);
expect(links[1].context.traceId).toBe(
'1af7651916cd43dd8448eb211c80319c',
);
});
it('should add message index as link attribute', () => {
const messages = [
{
body: 'message1',
headers: {
traceparent:
'00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01',
},
},
{
body: 'message2',
headers: {
traceparent:
'00-1af7651916cd43dd8448eb211c80319c-c7ad6b7169203331-01',
},
},
];
const links = extractLinksFromBatch(messages);
expect(links[0].attributes?.['messaging.batch.message_index']).toBe(0);
expect(links[1].attributes?.['messaging.batch.message_index']).toBe(1);
});
it('should use custom headers key', () => {
const messages = [
{
body: 'message1',
metadata: {
traceparent:
'00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01',
},
},
];
const links = extractLinksFromBatch(messages, 'metadata');
expect(links).toHaveLength(1);
});
it('should skip messages without headers', () => {
const messages = [
{ body: 'message1' }, // No headers
{
body: 'message2',
headers: {
traceparent:
'00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01',
},
},
{ body: 'message3', headers: null }, // Null headers
];
const links = extractLinksFromBatch(messages);
expect(links).toHaveLength(1);
});
it('should skip messages with invalid trace context', () => {
const messages = [
{
body: 'message1',
headers: { traceparent: 'invalid-traceparent' },
},
{
body: 'message2',
headers: {
traceparent:
'00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01',
},
},
];
const links = extractLinksFromBatch(messages);
expect(links).toHaveLength(1);
expect(links[0].context.traceId).toBe(
'0af7651916cd43dd8448eb211c80319c',
);
});
it('should return empty array for empty batch', () => {
const links = extractLinksFromBatch([]);
expect(links).toEqual([]);
});
});
});
describe('samplingPresets', () => {
it('development() returns AlwaysSampler', () => {
const sampler = samplingPresets.development();
expect(sampler).toBeInstanceOf(AlwaysSampler);
expect(sampler.shouldSample(context)).toBe(true);
});
it('errorsOnly() returns AdaptiveSampler that drops healthy baseline', () => {
const sampler = samplingPresets.errorsOnly();
expect(sampler).toBeInstanceOf(AdaptiveSampler);
sampler.shouldSample(context); // prime the WeakMap baseline decision
// Baseline is 0, so shouldKeepTrace for successful fast requests = false
expect(
sampler.shouldKeepTrace!(context, { success: true, duration: 50 }),
).toBe(false);
});
it('errorsOnly() keeps errors', () => {
const sampler = samplingPresets.errorsOnly();
sampler.shouldSample(context); // prime the baseline decision
expect(
sampler.shouldKeepTrace!(context, {
success: false,
duration: 50,
error: new Error('fail'),
}),
).toBe(true);
});
it('production() returns AdaptiveSampler with 10% baseline', () => {
const sampler = samplingPresets.production();
expect(sampler).toBeInstanceOf(AdaptiveSampler);
expect(sampler.needsTailSampling!()).toBe(true);
});
it('production() keeps errors', () => {
const sampler = samplingPresets.production();
sampler.shouldSample(context);
expect(
sampler.shouldKeepTrace!(context, {
success: false,
duration: 50,
error: new Error('fail'),
}),
).toBe(true);
});
it('production() accepts overrides', () => {
const sampler = samplingPresets.production({ baselineSampleRate: 1.0 });
sampler.shouldSample(context);
// With 100% baseline, all healthy traffic is kept
expect(
sampler.shouldKeepTrace!(context, { success: true, duration: 50 }),
).toBe(true);
});
it('off() returns NeverSampler', () => {
const sampler = samplingPresets.off();
expect(sampler).toBeInstanceOf(NeverSampler);
expect(sampler.shouldSample(context)).toBe(false);
});
});
describe('resolveSamplingPreset', () => {
it('resolves development', () => {
const sampler = resolveSamplingPreset('development');
expect(sampler).toBeInstanceOf(AlwaysSampler);
});
it('resolves errors-only', () => {
const sampler = resolveSamplingPreset('errors-only');
expect(sampler).toBeInstanceOf(AdaptiveSampler);
});
it('resolves production', () => {
const sampler = resolveSamplingPreset('production');
expect(sampler).toBeInstanceOf(AdaptiveSampler);
});
it('resolves off', () => {
const sampler = resolveSamplingPreset('off');
expect(sampler).toBeInstanceOf(NeverSampler);
});
it('throws on invalid preset with helpful message', () => {
expect(() => resolveSamplingPreset('banana' as any)).toThrow(
/Unknown sampling preset: "banana".*Valid presets: development, errors-only, production, off/,
);
});
});
});