Skip to content

Commit 0a2811d

Browse files
committed
add tests
1 parent c7f0099 commit 0a2811d

File tree

2 files changed

+107
-31
lines changed

2 files changed

+107
-31
lines changed

packages/utils/src/baggage.ts

Lines changed: 35 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
11
export type AllowedBaggageKeys = 'environment' | 'release'; // TODO: Add remaining allowed baggage keys | 'transaction' | 'userid' | 'usersegment';
2-
export type Baggage = Partial<Record<AllowedBaggageKeys, string> & Record<string, string>>;
2+
export type BaggageObj = Partial<Record<AllowedBaggageKeys, string> & Record<string, string>>;
3+
export type Baggage = [BaggageObj, string];
34

45
export const BAGGAGE_HEADER_NAME = 'baggage';
56

7+
export const SENTRY_BAGGAGE_KEY_PREFIX = 'sentry-';
8+
9+
export const SENTRY_BAGGAGE_KEY_PREFIX_REGEX = /^sentry-/;
10+
11+
// baggage = sentry-environment=prod;my-info=true;
12+
613
/**
714
* Max length of a serialized baggage string
815
*
@@ -11,44 +18,46 @@ export const BAGGAGE_HEADER_NAME = 'baggage';
1118
export const MAX_BAGGAGE_STRING_LENGTH = 8192;
1219

1320
/** Create an instance of Baggage */
14-
export function createBaggage(initItems: Baggage): Baggage {
15-
return { ...initItems };
21+
export function createBaggage(initItems: BaggageObj, baggageString: string = ''): Baggage {
22+
return [{ ...initItems }, baggageString];
1623
}
1724

1825
/** Add a value to baggage */
19-
export function getBaggageValue(baggage: Baggage, key: keyof Baggage): Baggage[keyof Baggage] {
20-
return baggage[key];
26+
export function getBaggageValue(baggage: Baggage, key: keyof BaggageObj): BaggageObj[keyof BaggageObj] {
27+
return baggage[0][key];
2128
}
2229

2330
/** Add a value to baggage */
24-
export function setBaggageValue(baggage: Baggage, key: keyof Baggage, value: Baggage[keyof Baggage]): void {
25-
baggage[key] = value;
31+
export function setBaggageValue(baggage: Baggage, key: keyof BaggageObj, value: BaggageObj[keyof BaggageObj]): void {
32+
baggage[0][key] = value;
2633
}
2734

28-
/** remove a value from baggage */
29-
// TODO: Is this even useful?
30-
// export function removeBaggageValue(baggage: Baggage, key: keyof Baggage): void {
31-
// // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
32-
// delete baggage[key];
33-
// }
34-
3535
/** Serialize a baggage object */
3636
export function serializeBaggage(baggage: Baggage): string {
37-
return Object.keys(baggage).reduce((prev, key) => {
38-
const newVal = `${prev};${key}=${baggage[key as keyof Baggage]}`;
37+
return Object.keys(baggage[0]).reduce((prev, key) => {
38+
const baggageEntry = `${SENTRY_BAGGAGE_KEY_PREFIX}${key}=${baggage[0][key as keyof BaggageObj]}`;
39+
const newVal = prev === '' ? baggageEntry : `${prev},${baggageEntry}`;
3940
return newVal.length > MAX_BAGGAGE_STRING_LENGTH ? prev : newVal;
40-
}, '');
41+
}, baggage[1]);
4142
}
4243

4344
/** Parse a baggage header to a string */
4445
export function parseBaggageString(baggageString: string): Baggage {
45-
// TODO: Should we check validity with regex? How much should we check for malformed baggage keys?
46-
// Perhaps we shouldn't worry about this being used in the frontend, so bundle size isn't that much of a concern
47-
return baggageString.split(';').reduce((prevBaggage, curr) => {
48-
const [key, val] = curr.split('=');
49-
return {
50-
...prevBaggage,
51-
[key]: val,
52-
};
53-
}, {} as Baggage);
46+
return baggageString.split(',').reduce(
47+
([baggageObj, baggageString], curr) => {
48+
const [key, val] = curr.split('=');
49+
if (SENTRY_BAGGAGE_KEY_PREFIX_REGEX.test(key)) {
50+
return [
51+
{
52+
...baggageObj,
53+
[key.split('-')[1]]: val,
54+
},
55+
baggageString,
56+
];
57+
} else {
58+
return [baggageObj, baggageString === '' ? curr : `${baggageString},${curr}`];
59+
}
60+
},
61+
[{}, ''],
62+
);
5463
}

packages/utils/test/baggage.test.ts

Lines changed: 72 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
import { createBaggage, getBaggageValue } from '../src/baggage';
1+
import { createBaggage, getBaggageValue, parseBaggageString, serializeBaggage, setBaggageValue } from '../src/baggage';
22

33
describe('Baggage', () => {
44
describe('createBaggage', () => {
55
it.each([
6-
['creates an empty baggage instance', {}, {}],
6+
['creates an empty baggage instance', {}, [{}, '']],
77
[
88
'creates a baggage instance with initial values',
99
{ environment: 'production', anyKey: 'anyValue' },
10-
{ environment: 'production', anyKey: 'anyValue' },
10+
[{ environment: 'production', anyKey: 'anyValue' }, ''],
1111
],
1212
])('%s', (_: string, input, output) => {
1313
expect(createBaggage(input)).toEqual(output);
@@ -16,10 +16,77 @@ describe('Baggage', () => {
1616

1717
describe('getBaggageValue', () => {
1818
it.each([
19-
['gets a baggage item', { environment: 'production', anyKey: 'anyValue' }, 'environment', 'production'],
20-
['finds undefined items', {}, 'environment', undefined],
19+
[
20+
'gets a baggage item',
21+
createBaggage({ environment: 'production', anyKey: 'anyValue' }),
22+
'environment',
23+
'production',
24+
],
25+
['finds undefined items', createBaggage({}), 'environment', undefined],
2126
])('%s', (_: string, baggage, key, value) => {
2227
expect(getBaggageValue(baggage, key)).toEqual(value);
2328
});
2429
});
30+
31+
describe('setBaggageValue', () => {
32+
it.each([
33+
['sets a baggage item', createBaggage({}), 'environment', 'production'],
34+
['overwrites a baggage item', createBaggage({ environment: 'development' }), 'environment', 'production'],
35+
])('%s', (_: string, baggage, key, value) => {
36+
setBaggageValue(baggage, key, value);
37+
expect(getBaggageValue(baggage, key)).toEqual(value);
38+
});
39+
});
40+
41+
describe('serializeBaggage', () => {
42+
it.each([
43+
['serializes empty baggage', createBaggage({}), ''],
44+
[
45+
'serializes baggage with a single value',
46+
createBaggage({ environment: 'production' }),
47+
'sentry-environment=production',
48+
],
49+
[
50+
'serializes baggage with multiple values',
51+
createBaggage({ environment: 'production', release: '10.0.2' }),
52+
'sentry-environment=production,sentry-release=10.0.2',
53+
],
54+
[
55+
'keeps non-sentry prefixed baggage items',
56+
createBaggage(
57+
{ environment: 'production', release: '10.0.2' },
58+
'userId=alice,serverNode=DF%2028,isProduction=false',
59+
),
60+
'userId=alice,serverNode=DF%2028,isProduction=false,sentry-environment=production,sentry-release=10.0.2',
61+
],
62+
[
63+
'can only use non-sentry prefixed baggage items',
64+
createBaggage({}, 'userId=alice,serverNode=DF%2028,isProduction=false'),
65+
'userId=alice,serverNode=DF%2028,isProduction=false',
66+
],
67+
])('%s', (_: string, baggage, serializedBaggage) => {
68+
expect(serializeBaggage(baggage)).toEqual(serializedBaggage);
69+
});
70+
});
71+
72+
describe('parseBaggageString', () => {
73+
it.each([
74+
['parses an empty string', '', createBaggage({})],
75+
[
76+
'parses sentry values into baggage',
77+
'sentry-environment=production,sentry-release=10.0.2',
78+
createBaggage({ environment: 'production', release: '10.0.2' }),
79+
],
80+
[
81+
'parses arbitrary baggage headers',
82+
'userId=alice,serverNode=DF%2028,isProduction=false,sentry-environment=production,sentry-release=10.0.2',
83+
createBaggage(
84+
{ environment: 'production', release: '10.0.2' },
85+
'userId=alice,serverNode=DF%2028,isProduction=false',
86+
),
87+
],
88+
])('%s', (_: string, baggageString, baggage) => {
89+
expect(parseBaggageString(baggageString)).toEqual(baggage);
90+
});
91+
});
2592
});

0 commit comments

Comments
 (0)