Skip to content

Commit

Permalink
add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
AbhiPrasad committed May 12, 2022
1 parent c7f0099 commit 0a2811d
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 31 deletions.
61 changes: 35 additions & 26 deletions packages/utils/src/baggage.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
export type AllowedBaggageKeys = 'environment' | 'release'; // TODO: Add remaining allowed baggage keys | 'transaction' | 'userid' | 'usersegment';
export type Baggage = Partial<Record<AllowedBaggageKeys, string> & Record<string, string>>;
export type BaggageObj = Partial<Record<AllowedBaggageKeys, string> & Record<string, string>>;
export type Baggage = [BaggageObj, string];

export const BAGGAGE_HEADER_NAME = 'baggage';

export const SENTRY_BAGGAGE_KEY_PREFIX = 'sentry-';

export const SENTRY_BAGGAGE_KEY_PREFIX_REGEX = /^sentry-/;

// baggage = sentry-environment=prod;my-info=true;

/**
* Max length of a serialized baggage string
*
Expand All @@ -11,44 +18,46 @@ export const BAGGAGE_HEADER_NAME = 'baggage';
export const MAX_BAGGAGE_STRING_LENGTH = 8192;

/** Create an instance of Baggage */
export function createBaggage(initItems: Baggage): Baggage {
return { ...initItems };
export function createBaggage(initItems: BaggageObj, baggageString: string = ''): Baggage {
return [{ ...initItems }, baggageString];
}

/** Add a value to baggage */
export function getBaggageValue(baggage: Baggage, key: keyof Baggage): Baggage[keyof Baggage] {
return baggage[key];
export function getBaggageValue(baggage: Baggage, key: keyof BaggageObj): BaggageObj[keyof BaggageObj] {
return baggage[0][key];
}

/** Add a value to baggage */
export function setBaggageValue(baggage: Baggage, key: keyof Baggage, value: Baggage[keyof Baggage]): void {
baggage[key] = value;
export function setBaggageValue(baggage: Baggage, key: keyof BaggageObj, value: BaggageObj[keyof BaggageObj]): void {
baggage[0][key] = value;
}

/** remove a value from baggage */
// TODO: Is this even useful?
// export function removeBaggageValue(baggage: Baggage, key: keyof Baggage): void {
// // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
// delete baggage[key];
// }

/** Serialize a baggage object */
export function serializeBaggage(baggage: Baggage): string {
return Object.keys(baggage).reduce((prev, key) => {
const newVal = `${prev};${key}=${baggage[key as keyof Baggage]}`;
return Object.keys(baggage[0]).reduce((prev, key) => {
const baggageEntry = `${SENTRY_BAGGAGE_KEY_PREFIX}${key}=${baggage[0][key as keyof BaggageObj]}`;
const newVal = prev === '' ? baggageEntry : `${prev},${baggageEntry}`;
return newVal.length > MAX_BAGGAGE_STRING_LENGTH ? prev : newVal;
}, '');
}, baggage[1]);
}

/** Parse a baggage header to a string */
export function parseBaggageString(baggageString: string): Baggage {
// TODO: Should we check validity with regex? How much should we check for malformed baggage keys?
// Perhaps we shouldn't worry about this being used in the frontend, so bundle size isn't that much of a concern
return baggageString.split(';').reduce((prevBaggage, curr) => {
const [key, val] = curr.split('=');
return {
...prevBaggage,
[key]: val,
};
}, {} as Baggage);
return baggageString.split(',').reduce(
([baggageObj, baggageString], curr) => {
const [key, val] = curr.split('=');
if (SENTRY_BAGGAGE_KEY_PREFIX_REGEX.test(key)) {
return [
{
...baggageObj,
[key.split('-')[1]]: val,
},
baggageString,
];
} else {
return [baggageObj, baggageString === '' ? curr : `${baggageString},${curr}`];
}
},
[{}, ''],
);
}
77 changes: 72 additions & 5 deletions packages/utils/test/baggage.test.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { createBaggage, getBaggageValue } from '../src/baggage';
import { createBaggage, getBaggageValue, parseBaggageString, serializeBaggage, setBaggageValue } from '../src/baggage';

describe('Baggage', () => {
describe('createBaggage', () => {
it.each([
['creates an empty baggage instance', {}, {}],
['creates an empty baggage instance', {}, [{}, '']],
[
'creates a baggage instance with initial values',
{ environment: 'production', anyKey: 'anyValue' },
{ environment: 'production', anyKey: 'anyValue' },
[{ environment: 'production', anyKey: 'anyValue' }, ''],
],
])('%s', (_: string, input, output) => {
expect(createBaggage(input)).toEqual(output);
Expand All @@ -16,10 +16,77 @@ describe('Baggage', () => {

describe('getBaggageValue', () => {
it.each([
['gets a baggage item', { environment: 'production', anyKey: 'anyValue' }, 'environment', 'production'],
['finds undefined items', {}, 'environment', undefined],
[
'gets a baggage item',
createBaggage({ environment: 'production', anyKey: 'anyValue' }),
'environment',
'production',
],
['finds undefined items', createBaggage({}), 'environment', undefined],
])('%s', (_: string, baggage, key, value) => {
expect(getBaggageValue(baggage, key)).toEqual(value);
});
});

describe('setBaggageValue', () => {
it.each([
['sets a baggage item', createBaggage({}), 'environment', 'production'],
['overwrites a baggage item', createBaggage({ environment: 'development' }), 'environment', 'production'],
])('%s', (_: string, baggage, key, value) => {
setBaggageValue(baggage, key, value);
expect(getBaggageValue(baggage, key)).toEqual(value);
});
});

describe('serializeBaggage', () => {
it.each([
['serializes empty baggage', createBaggage({}), ''],
[
'serializes baggage with a single value',
createBaggage({ environment: 'production' }),
'sentry-environment=production',
],
[
'serializes baggage with multiple values',
createBaggage({ environment: 'production', release: '10.0.2' }),
'sentry-environment=production,sentry-release=10.0.2',
],
[
'keeps non-sentry prefixed baggage items',
createBaggage(
{ environment: 'production', release: '10.0.2' },
'userId=alice,serverNode=DF%2028,isProduction=false',
),
'userId=alice,serverNode=DF%2028,isProduction=false,sentry-environment=production,sentry-release=10.0.2',
],
[
'can only use non-sentry prefixed baggage items',
createBaggage({}, 'userId=alice,serverNode=DF%2028,isProduction=false'),
'userId=alice,serverNode=DF%2028,isProduction=false',
],
])('%s', (_: string, baggage, serializedBaggage) => {
expect(serializeBaggage(baggage)).toEqual(serializedBaggage);
});
});

describe('parseBaggageString', () => {
it.each([
['parses an empty string', '', createBaggage({})],
[
'parses sentry values into baggage',
'sentry-environment=production,sentry-release=10.0.2',
createBaggage({ environment: 'production', release: '10.0.2' }),
],
[
'parses arbitrary baggage headers',
'userId=alice,serverNode=DF%2028,isProduction=false,sentry-environment=production,sentry-release=10.0.2',
createBaggage(
{ environment: 'production', release: '10.0.2' },
'userId=alice,serverNode=DF%2028,isProduction=false',
),
],
])('%s', (_: string, baggageString, baggage) => {
expect(parseBaggageString(baggageString)).toEqual(baggage);
});
});
});

0 comments on commit 0a2811d

Please sign in to comment.