import { describe, it, expect } from 'vitest'; import { serializeBaggage, parseBaggage, createBaggageFromTransaction, } from '../src/utils/baggage'; import type { BaggageFields } from '../src/types'; describe('baggage utils', () => { describe('serializeBaggage', () => { it('should serialize empty baggage', () => { const result = serializeBaggage({}); expect(result).toBe(''); }); it('should serialize baggage with all fields', () => { const baggage: BaggageFields = { trace_id: 'abc123', public_key: 'key123', sample_rate: '0.5', sampled: 'true', user_id: 'user1', user_segment: 'premium', release: '1.0.0', environment: 'production', }; const result = serializeBaggage(baggage); expect(result).toContain('sentry-trace_id=abc123'); expect(result).toContain('sentry-public_key=key123'); expect(result).toContain('sentry-sample_rate=0.5'); expect(result).toContain('sentry-sampled=true'); expect(result).toContain('sentry-user_id=user1'); expect(result).toContain('sentry-user_segment=premium'); expect(result).toContain('sentry-release=1.0.0'); expect(result).toContain('sentry-environment=production'); }); it('should skip null, undefined, and empty string values', () => { const baggage: BaggageFields = { trace_id: 'abc123', public_key: undefined as any, sampled: '', }; const result = serializeBaggage(baggage); expect(result).toContain('sentry-trace_id=abc123'); expect(result).not.toContain('public_key'); expect(result).not.toContain('sampled'); }); it('should add sentry- prefix to non-sentry keys', () => { const baggage: BaggageFields = { trace_id: 'abc123', custom_field: 'value', }; const result = serializeBaggage(baggage); expect(result).toContain('sentry-trace_id=abc123'); expect(result).toContain('sentry-custom_field=value'); }); it('should preserve sentry- prefix on sentry keys', () => { const baggage: BaggageFields = { 'sentry-trace_id': 'abc123', }; const result = serializeBaggage(baggage); expect(result).toContain('sentry-trace_id=abc123'); // Should not double-prefix expect(result).not.toContain('sentry-sentry-'); }); it('should truncate long baggage to 8192 characters', () => { const longBaggage: BaggageFields = {}; // Create a very long value const longValue = 'a'.repeat(10000); longBaggage.trace_id = longValue; const result = serializeBaggage(longBaggage); expect(result.length).toBeLessThanOrEqual(8192); }); it('should URL encode values', () => { const baggage: BaggageFields = { user_id: 'user with spaces', custom_field: 'value=special&chars', }; const result = serializeBaggage(baggage); expect(result).toContain(encodeURIComponent('user with spaces')); expect(result).toContain(encodeURIComponent('value=special&chars')); }); }); describe('parseBaggage', () => { it('should parse empty baggage', () => { const result = parseBaggage(''); expect(result).toEqual({}); }); it('should parse valid baggage header', () => { const header = 'sentry-trace_id=abc123,sentry-sampled=true,sentry-user_id=user1'; const result = parseBaggage(header); expect(result.trace_id).toBe('abc123'); expect(result.sampled).toBe('true'); expect(result.user_id).toBe('user1'); }); it('should remove sentry- prefix from keys', () => { const header = 'sentry-public_key=key123,sentry-release=1.0.0'; const result = parseBaggage(header); expect(result.public_key).toBe('key123'); expect(result.release).toBe('1.0.0'); expect(result['sentry-public_key']).toBeUndefined(); }); it('should handle custom fields without prefix', () => { const header = 'custom_field=value,another=data'; const result = parseBaggage(header); expect(result.custom_field).toBe('value'); expect(result.another).toBe('data'); }); it('should URL decode values', () => { const header = `sentry-user_id=${encodeURIComponent('user with spaces')}`; const result = parseBaggage(header); expect(result.user_id).toBe('user with spaces'); }); it('should skip malformed entries', () => { const header = 'valid=key,invalid-entry-no-equals,sentry-trace_id=abc123'; const result = parseBaggage(header); expect(result.valid).toBe('key'); expect(result['sentry-trace_id']).toBeUndefined(); expect(result.trace_id).toBe('abc123'); }); it('should handle whitespace around entries', () => { const header = ' sentry-trace_id=abc123 , sentry-sampled=true '; const result = parseBaggage(header); expect(result.trace_id).toBe('abc123'); expect(result.sampled).toBe('true'); }); }); describe('createBaggageFromTransaction', () => { it('should create baggage with only trace_id', () => { const result = createBaggageFromTransaction('abc123'); expect(result.trace_id).toBe('abc123'); expect(Object.keys(result)).toHaveLength(1); }); it('should create baggage with all options', () => { const result = createBaggageFromTransaction('abc123', { dsnPublicKey: 'key123', sampled: true, sampleRate: 0.5, user: { id: 'user1', segment: 'premium' }, release: '1.0.0', environment: 'production', }); expect(result.trace_id).toBe('abc123'); expect(result.public_key).toBe('key123'); expect(result.sampled).toBe('true'); expect(result.sample_rate).toBe('0.5'); expect(result.user_id).toBe('user1'); expect(result.user_segment).toBe('premium'); expect(result.release).toBe('1.0.0'); expect(result.environment).toBe('production'); }); it('should convert sampled boolean to string', () => { expect(createBaggageFromTransaction('abc', { sampled: true }).sampled).toBe('true'); expect(createBaggageFromTransaction('abc', { sampled: false }).sampled).toBe('false'); }); it('should convert sampleRate number to string', () => { const result = createBaggageFromTransaction('abc', { sampleRate: 0.1 }); expect(result.sample_rate).toBe('0.1'); }); it('should handle optional user object', () => { const result1 = createBaggageFromTransaction('abc', { user: { id: 'user1' } }); expect(result1.user_id).toBe('user1'); expect(result1.user_segment).toBeUndefined(); const result2 = createBaggageFromTransaction('abc', { user: { id: 'user2', segment: 'vip' } }); expect(result2.user_id).toBe('user2'); expect(result2.user_segment).toBe('vip'); }); it('should not include undefined options', () => { const result = createBaggageFromTransaction('abc', { dsnPublicKey: 'key', sampled: undefined, sampleRate: undefined, }); expect(result.trace_id).toBe('abc'); expect(result.public_key).toBe('key'); expect(result.sampled).toBeUndefined(); expect(result.sample_rate).toBeUndefined(); }); it('should work without options', () => { const result = createBaggageFromTransaction('abc'); expect(result.trace_id).toBe('abc'); }); }); describe('serialize/parse roundtrip', () => { it('should preserve baggage through serialize/parse cycle', () => { const original: BaggageFields = { trace_id: 'abc123', public_key: 'key123', sampled: 'true', user_id: 'user1', release: '1.0.0', environment: 'production', custom: 'value', }; const serialized = serializeBaggage(original); const parsed = parseBaggage(serialized); expect(parsed.trace_id).toBe(original.trace_id); expect(parsed.public_key).toBe(original.public_key); expect(parsed.sampled).toBe(original.sampled); expect(parsed.user_id).toBe(original.user_id); expect(parsed.release).toBe(original.release); expect(parsed.environment).toBe(original.environment); expect(parsed.custom).toBe(original.custom); }); }); });