273 lines
8.4 KiB
TypeScript
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();
|
|
});
|
|
});
|
|
});
|