light-sentry-sdk/tests/SamplingPlugin.test.ts

273 lines
8.4 KiB
TypeScript

import { describe, it, expect, vi, beforeEach } from 'vitest';
import SamplingPlugin from '../src/plugins/SamplingPlugin';
import type { LightClient, LightConfig, SentryEvent, ErrorEvent } from '../src/types';
describe('SamplingPlugin', () => {
function createMockClient(configOverrides: Partial<LightConfig> = {}): LightClient {
const config: LightConfig = {
dsn: 'https://abc@sentry.io/123',
sampleRate: 1,
sampling: {
rates: {
error: 1,
performance: 1,
network: 1,
behavior: 1,
},
},
...configOverrides,
} as LightConfig;
return {
config,
dsn: { protocol: 'https', publicKey: 'abc', host: 'sentry.io', projectId: '123' },
on: vi.fn(),
off: vi.fn(),
emit: vi.fn(),
captureException: vi.fn(),
captureMessage: vi.fn(),
captureEvent: vi.fn(),
setUser: vi.fn(),
setAnonymousId: vi.fn(),
setTag: vi.fn(),
setTags: vi.fn(),
setExtra: vi.fn(),
addBreadcrumb: vi.fn(),
startTransaction: vi.fn(),
getCurrentTransaction: vi.fn(),
getCurrentSpan: vi.fn(),
setSpan: vi.fn(),
startSpan: vi.fn(),
flush: vi.fn(),
disable: vi.fn(),
enable: vi.fn(),
use: vi.fn(),
init: vi.fn(),
destroy: vi.fn(),
} as unknown as LightClient;
}
function createErrorEvent(): ErrorEvent {
return {
type: 'error',
level: 'error',
message: 'Test error',
timestamp: Date.now(),
};
}
function createEvent(type: string): SentryEvent {
return {
type: type as any,
level: 'info',
timestamp: Date.now(),
} as SentryEvent;
}
describe('constructor', () => {
it('should create plugin with correct name and version', () => {
const plugin = new SamplingPlugin();
expect(plugin.name).toBe('sampling');
expect(plugin.version).toBe('1.0.0');
});
});
describe('setup', () => {
it('should call setup with client', () => {
const plugin = new SamplingPlugin();
const client = createMockClient();
expect(() => plugin.setup(client)).not.toThrow();
});
it('should load default rates when no sampling config', () => {
const plugin = new SamplingPlugin();
const client = createMockClient({ sampling: undefined });
plugin.setup(client);
const event = createErrorEvent();
const result = plugin.beforeReport(event);
expect(result).not.toBeNull();
});
it('should load custom rates from config', () => {
const plugin = new SamplingPlugin();
const client = createMockClient({
sampleRate: 1,
sampling: {
rates: {
error: 0.5,
performance: 0.1,
},
},
});
plugin.setup(client);
const randomSpy = vi.spyOn(Math, 'random').mockReturnValue(0.1);
expect(plugin.beforeReport(createEvent('error'))).toBeTruthy();
randomSpy.mockRestore();
});
});
describe('beforeReport', () => {
it('should return event when sampling rate is 1', () => {
const plugin = new SamplingPlugin();
const client = createMockClient({ sampleRate: 1 });
plugin.setup(client);
const event = createErrorEvent();
const result = plugin.beforeReport(event);
expect(result).toBe(event);
});
it('should return null when global sampleRate is 0', () => {
const plugin = new SamplingPlugin();
const client = createMockClient({ sampleRate: 0 });
plugin.setup(client);
const event = createErrorEvent();
const result = plugin.beforeReport(event);
expect(result).toBeNull();
});
it('should return null when global sampleRate is negative', () => {
const plugin = new SamplingPlugin();
const client = createMockClient({ sampleRate: -1 });
plugin.setup(client);
const event = createErrorEvent();
const result = plugin.beforeReport(event);
expect(result).toBeNull();
});
it('should return event when type rate is 1', () => {
const plugin = new SamplingPlugin();
const client = createMockClient({
sampleRate: 1,
sampling: { rates: { error: 1 } },
});
plugin.setup(client);
const event = createErrorEvent();
const result = plugin.beforeReport(event);
expect(result).toBe(event);
});
it('should return null when type rate is 0', () => {
const plugin = new SamplingPlugin();
const client = createMockClient({
sampleRate: 1,
sampling: { rates: { error: 0 } },
});
plugin.setup(client);
const event = createErrorEvent();
const result = plugin.beforeReport(event);
expect(result).toBeNull();
});
it('should always sample count events (rate = 1)', () => {
const plugin = new SamplingPlugin();
const client = createMockClient({
sampleRate: 0,
sampling: { rates: { error: 0 } },
});
plugin.setup(client);
const countEvent = {
type: 'count' as const,
level: 'info' as const,
timestamp: Date.now(),
fingerprint: 'test',
count: 1,
ts_start: Date.now(),
ts_end: Date.now(),
};
const result = plugin.beforeReport(countEvent);
expect(result).toBeNull();
});
it('should handle unknown event types (default rate = 1)', () => {
const plugin = new SamplingPlugin();
const client = createMockClient({ sampleRate: 1 });
plugin.setup(client);
const unknownEvent = createEvent('unknown_type');
const result = plugin.beforeReport(unknownEvent);
expect(result).not.toBeNull();
});
it('should use combined rate when globalRate < 1', () => {
const plugin = new SamplingPlugin();
const client = createMockClient({
sampleRate: 0.5,
sampling: { rates: { error: 0.5 } },
});
plugin.setup(client);
const randomSpy = vi.spyOn(Math, 'random').mockReturnValue(0.9);
const event = createErrorEvent();
const result = plugin.beforeReport(event);
expect(result).toBeNull();
randomSpy.mockRestore();
});
it('should pass when random is below combined rate', () => {
const plugin = new SamplingPlugin();
const client = createMockClient({
sampleRate: 0.5,
sampling: { rates: { error: 0.5 } },
});
plugin.setup(client);
const randomSpy = vi.spyOn(Math, 'random').mockReturnValue(0.1);
const event = createErrorEvent();
const result = plugin.beforeReport(event);
expect(result).toBe(event);
randomSpy.mockRestore();
});
it('should pass when type rate is 1 and globalRate is 1', () => {
const plugin = new SamplingPlugin();
const client = createMockClient({ sampleRate: 1 });
plugin.setup(client);
const randomSpy = vi.spyOn(Math, 'random').mockReturnValue(0.99);
const event = createErrorEvent();
const result = plugin.beforeReport(event);
expect(result).toBe(event);
randomSpy.mockRestore();
});
});
describe('destroy', () => {
it('should not throw when destroy is called', () => {
const plugin = new SamplingPlugin();
expect(() => plugin.destroy()).not.toThrow();
});
});
describe('config loading', () => {
it('should handle missing sampling config', () => {
const plugin = new SamplingPlugin();
const client = createMockClient({ sampling: undefined });
plugin.setup(client);
const event = createErrorEvent();
const result = plugin.beforeReport(event);
expect(result).not.toBeNull();
});
it('should handle sampling config without rates', () => {
const plugin = new SamplingPlugin();
const client = createMockClient({ sampling: {} });
plugin.setup(client);
const event = createErrorEvent();
const result = plugin.beforeReport(event);
expect(result).not.toBeNull();
});
it('should merge custom rates with defaults', () => {
const plugin = new SamplingPlugin();
const client = createMockClient({
sampling: {
rates: {
error: 0.5,
},
},
});
plugin.setup(client);
const randomSpy = vi.spyOn(Math, 'random').mockReturnValue(0.9);
expect(plugin.beforeReport(createEvent('error'))).toBeNull();
expect(plugin.beforeReport(createEvent('performance'))).not.toBeNull();
randomSpy.mockRestore();
});
});
});