Skip to content

Commit 0848575

Browse files
committed
add http header tests
1 parent c3e8ef9 commit 0848575

File tree

1 file changed

+248
-0
lines changed

1 file changed

+248
-0
lines changed
Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
import * as sentryCore from '@sentry/core';
2+
import { API } from '@sentry/core';
3+
import { Hub } from '@sentry/hub';
4+
import { SentryRequest } from '@sentry/types';
5+
import * as utilsPackage from '@sentry/utils';
6+
import { base64ToUnicode } from '@sentry/utils';
7+
8+
import { Span } from '../src/span';
9+
import { Transaction } from '../src/transaction';
10+
import { computeTracestateValue } from '../src/utils';
11+
12+
// TODO gather sentry-trace and tracestate tests here
13+
14+
function parseEnvelopeRequest(request: SentryRequest): any {
15+
const [envelopeHeaderString, itemHeaderString, eventString] = request.body.split('\n');
16+
17+
return {
18+
envelopeHeader: JSON.parse(envelopeHeaderString),
19+
itemHeader: JSON.parse(itemHeaderString),
20+
event: JSON.parse(eventString),
21+
};
22+
}
23+
24+
describe('sentry-trace', () => {
25+
// TODO gather relevant tests here
26+
});
27+
28+
describe('tracestate', () => {
29+
// grab these this way rather than importing them individually to get around TS's guards against instantiating
30+
// abstract classes (using non-abstract classes would create a circular dependency)
31+
const { BaseClient, BaseBackend } = sentryCore as any;
32+
33+
const dsn = 'https://[email protected]/12312012';
34+
const environment = 'dogpark';
35+
const release = 'off.leash.trail';
36+
const hub = new Hub(
37+
new BaseClient(BaseBackend, {
38+
dsn,
39+
environment,
40+
release,
41+
}),
42+
);
43+
44+
describe('sentry tracestate', () => {
45+
describe('lazy creation', () => {
46+
const getNewTracestate = jest
47+
.spyOn(Span.prototype as any, '_getNewTracestate')
48+
.mockReturnValue('sentry=doGsaREgReaT');
49+
50+
beforeEach(() => {
51+
jest.clearAllMocks();
52+
});
53+
54+
afterAll(() => {
55+
jest.restoreAllMocks();
56+
});
57+
58+
describe('when creating a transaction', () => {
59+
it('uses sentry tracestate passed to the transaction constructor rather than creating a new one', () => {
60+
const transaction = new Transaction({
61+
name: 'FETCH /ball',
62+
metadata: { tracestate: { sentry: 'sentry=doGsaREgReaT' } },
63+
});
64+
65+
expect(getNewTracestate).not.toHaveBeenCalled();
66+
expect(transaction.metadata.tracestate?.sentry).toEqual('sentry=doGsaREgReaT');
67+
});
68+
69+
it("doesn't create new sentry tracestate on transaction creation if none provided", () => {
70+
const transaction = new Transaction({
71+
name: 'FETCH /ball',
72+
});
73+
74+
expect(transaction.metadata.tracestate?.sentry).toBeUndefined();
75+
expect(getNewTracestate).not.toHaveBeenCalled();
76+
});
77+
});
78+
79+
describe('when getting outgoing request headers', () => {
80+
it('uses existing sentry tracestate when getting tracing headers rather than creating new one', () => {
81+
const transaction = new Transaction({
82+
name: 'FETCH /ball',
83+
metadata: { tracestate: { sentry: 'sentry=doGsaREgReaT' } },
84+
});
85+
86+
expect(transaction.getTraceHeaders().tracestate).toEqual('sentry=doGsaREgReaT');
87+
expect(getNewTracestate).not.toHaveBeenCalled();
88+
});
89+
90+
it('creates and stores new sentry tracestate when getting tracing headers if none exists', () => {
91+
const transaction = new Transaction({
92+
name: 'FETCH /ball',
93+
});
94+
95+
expect(transaction.metadata.tracestate?.sentry).toBeUndefined();
96+
97+
transaction.getTraceHeaders();
98+
99+
expect(getNewTracestate).toHaveBeenCalled();
100+
expect(transaction.metadata.tracestate?.sentry).toEqual('sentry=doGsaREgReaT');
101+
expect(transaction.getTraceHeaders().tracestate).toEqual('sentry=doGsaREgReaT');
102+
});
103+
});
104+
105+
describe('when getting envelope headers', () => {
106+
// In real life, `transaction.finish()` calls `captureEvent()`, which eventually calls `eventToSentryRequest()`,
107+
// which in turn calls `base64ToUnicode`. Here we're short circuiting that process a little, to avoid having to
108+
// mock out more of the intermediate pieces.
109+
jest
110+
.spyOn(utilsPackage, 'base64ToUnicode')
111+
.mockImplementation(base64 =>
112+
base64 === 'doGsaREgReaT' ? '{"all the":"right stuff here"}' : '{"nope nope nope":"wrong"}',
113+
);
114+
jest.spyOn(hub, 'captureEvent').mockImplementation(event => {
115+
expect(event).toEqual(
116+
expect.objectContaining({ debug_meta: { tracestate: { sentry: 'sentry=doGsaREgReaT' } } }),
117+
);
118+
119+
const envelope = parseEnvelopeRequest(sentryCore.eventToSentryRequest(event, new API(dsn)));
120+
expect(envelope.envelopeHeader).toEqual(
121+
expect.objectContaining({ trace: { 'all the': 'right stuff here' } }),
122+
);
123+
124+
// `captureEvent` normally returns the event id
125+
return '11212012041520131231201209082013'; //
126+
});
127+
128+
it('uses existing sentry tracestate in envelope headers rather than creating a new one', () => {
129+
// one here, and two inside the `captureEvent` implementation above
130+
expect.assertions(3);
131+
132+
const transaction = new Transaction(
133+
{
134+
name: 'FETCH /ball',
135+
metadata: { tracestate: { sentry: 'sentry=doGsaREgReaT' } },
136+
sampled: true,
137+
},
138+
hub,
139+
);
140+
141+
transaction.finish();
142+
143+
expect(getNewTracestate).not.toHaveBeenCalled();
144+
});
145+
146+
it('creates new sentry tracestate for envelope header if none exists', () => {
147+
// two here, and two inside the `captureEvent` implementation above
148+
expect.assertions(4);
149+
150+
const transaction = new Transaction(
151+
{
152+
name: 'FETCH /ball',
153+
sampled: true,
154+
},
155+
hub,
156+
);
157+
158+
expect(transaction.metadata.tracestate?.sentry).toBeUndefined();
159+
160+
transaction.finish();
161+
162+
expect(getNewTracestate).toHaveBeenCalled();
163+
});
164+
});
165+
});
166+
167+
describe('mutibility', () => {
168+
it("won't include data set after transaction is created if there's an inherited value", () => {
169+
expect.assertions(1);
170+
171+
const inheritedTracestate = `sentry=${computeTracestateValue({
172+
trace_id: '12312012090820131231201209082013',
173+
environment: 'dogpark',
174+
release: 'off.leash.trail',
175+
public_key: 'dogsarebadatkeepingsecrets',
176+
})}`;
177+
178+
const transaction = new Transaction(
179+
{
180+
name: 'FETCH /ball',
181+
metadata: {
182+
tracestate: {
183+
sentry: inheritedTracestate,
184+
},
185+
},
186+
},
187+
hub,
188+
);
189+
190+
hub.withScope(scope => {
191+
scope.setUser({ id: '1121', username: 'CharlieDog', ip_address: '11.21.20.12', segment: 'bigs' });
192+
193+
const tracestateValue = (transaction as any)._toTracestate().replace('sentry=', '');
194+
const reinflatedTracestate = JSON.parse(base64ToUnicode(tracestateValue));
195+
196+
expect(reinflatedTracestate.user).toBeUndefined();
197+
});
198+
});
199+
200+
it("will include data set after transaction is created if there's no inherited value and `getTraceHeaders` hasn't been called", () => {
201+
expect.assertions(2);
202+
203+
const transaction = new Transaction(
204+
{
205+
name: 'FETCH /ball',
206+
},
207+
hub,
208+
);
209+
210+
hub.withScope(scope => {
211+
scope.setUser({ id: '1121', username: 'CharlieDog', ip_address: '11.21.20.12', segment: 'bigs' });
212+
213+
const tracestateValue = (transaction as any)._toTracestate().replace('sentry=', '');
214+
const reinflatedTracestate = JSON.parse(base64ToUnicode(tracestateValue));
215+
216+
expect(reinflatedTracestate.user.id).toEqual('1121');
217+
expect(reinflatedTracestate.user.segment).toEqual('bigs');
218+
});
219+
});
220+
221+
it("won't include data set after first call to `getTraceHeaders`", () => {
222+
expect.assertions(1);
223+
224+
const transaction = new Transaction(
225+
{
226+
name: 'FETCH /ball',
227+
},
228+
hub,
229+
);
230+
231+
transaction.getTraceHeaders();
232+
233+
hub.withScope(scope => {
234+
scope.setUser({ id: '1121', username: 'CharlieDog', ip_address: '11.21.20.12', segment: 'bigs' });
235+
236+
const tracestateValue = (transaction as any)._toTracestate().replace('sentry=', '');
237+
const reinflatedTracestate = JSON.parse(base64ToUnicode(tracestateValue));
238+
239+
expect(reinflatedTracestate.user).toBeUndefined();
240+
});
241+
});
242+
});
243+
});
244+
245+
describe('third-party tracestate', () => {
246+
// TODO gather relevant tests here
247+
});
248+
});

0 commit comments

Comments
 (0)