Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
add tests
  • Loading branch information
lobsterkatie committed Mar 24, 2021
commit ba148349fccf0ff4ee0517d9d8c6ffbe372aaccb
13 changes: 8 additions & 5 deletions packages/core/test/lib/request.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ describe('eventToSentryRequest', () => {
environment: 'dogpark',
event_id: '0908201304152013',
release: 'off.leash.park',
user: { id: '1121', username: 'CharlieDog', ip_address: '11.21.20.12' },
user: { id: '1121', username: 'CharlieDog', ip_address: '11.21.20.12', segment: 'bigs' },
};

describe('error/message events', () => {
Expand Down Expand Up @@ -65,13 +65,15 @@ describe('eventToSentryRequest', () => {
// computeTracestateValue({
// trace_id: '1231201211212012',
// environment: 'dogpark',
// release: 'off.leash.park',
// public_key: 'dogsarebadatkeepingsecrets',
// release: 'off.leash.park',
// user: { id: '1121', segment: 'bigs' },
// }),
tracestate: {
sentry:
'sentry=eyJ0cmFjZV9pZCI6IjEyMzEyMDEyMTEyMTIwMTIiLCJlbnZpcm9ubWVudCI6ImRvZ3BhcmsiLCJyZWxlYXNlIjoib2ZmLmxlYXNo' +
'LnBhcmsiLCJwdWJsaWNfa2V5IjoiZG9nc2FyZWJhZGF0a2VlcGluZ3NlY3JldHMifQ',
'sentry=eyJ0cmFjZV9pZCI6IjEyMzEyMDEyMTEyMTIwMTIiLCJlbnZpcm9ubWVudCI6ImRvZ3BhcmsiLCJwdWJsaWNfa2V5Ijo' +
'iZG9nc2FyZWJhZGF0a2VlcGluZ3NlY3JldHMiLCJyZWxlYXNlIjoib2ZmLmxlYXNoLnBhcmsiLCJ1c2VyIjp7ImlkIjoiMTEyM' +
'SIsInNlZ21lbnQiOiJiaWdzIn19',
},
},
spans: [],
Expand Down Expand Up @@ -160,10 +162,11 @@ describe('eventToSentryRequest', () => {

expect(envelope.envelopeHeader.trace).toBeDefined();
expect(envelope.envelopeHeader.trace).toEqual({
trace_id: '1231201211212012',
environment: 'dogpark',
public_key: 'dogsarebadatkeepingsecrets',
release: 'off.leash.park',
trace_id: '1231201211212012',
user: { id: '1121', segment: 'bigs' },
});
});
});
Expand Down
2 changes: 1 addition & 1 deletion packages/tracing/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ export function secToMs(time: number): number {
// so it can be used in manual instrumentation without necessitating a hard dependency on @sentry/utils
export { stripUrlQueryAndFragment } from '@sentry/utils';

type SentryTracestateData = {
export type SentryTracestateData = {
traceId: string;
environment: string | undefined | null;
release: string | undefined | null;
Expand Down
250 changes: 250 additions & 0 deletions packages/tracing/test/httpheaders.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
import * as sentryCore from '@sentry/core';
import { API } from '@sentry/core';
import { Hub } from '@sentry/hub';
import { SentryRequest } from '@sentry/types';
import * as utilsPackage from '@sentry/utils';
import { base64ToUnicode } from '@sentry/utils';

import { Span } from '../src/span';
import { Transaction } from '../src/transaction';
import { computeTracestateValue, SentryTracestateData } from '../src/utils';

// TODO gather sentry-trace and tracestate tests here

function parseEnvelopeRequest(request: SentryRequest): any {
const [envelopeHeaderString, itemHeaderString, eventString] = request.body.split('\n');

return {
envelopeHeader: JSON.parse(envelopeHeaderString),
itemHeader: JSON.parse(itemHeaderString),
event: JSON.parse(eventString),
};
}

describe('sentry-trace', () => {
// TODO gather relevant tests here
});

describe('tracestate', () => {
// grab these this way rather than importing them individually to get around TS's guards against instantiating
// abstract classes (using the real classes would create a circulr dependency)
const { BaseClient, BaseBackend } = sentryCore as any;

const dsn = 'https://[email protected]/12312012';
const environment = 'dogpark';
const release = 'off.leash.trail';
const hub = new Hub(
new BaseClient(BaseBackend, {
dsn,
environment,
release,
}),
);

describe('sentry tracestate', () => {
describe('lazy creation', () => {
const getNewTracestate = jest
.spyOn(Span.prototype as any, '_getNewTracestate')
.mockReturnValue('sentry=doGsaREgReaT');

beforeEach(() => {
jest.clearAllMocks();
});

afterAll(() => {
jest.restoreAllMocks();
});

describe('when creating a transaction', () => {
it('uses sentry tracestate passed to the transaction constructor rather than creating a new one', () => {
const transaction = new Transaction({
name: 'FETCH /ball',
metadata: { tracestate: { sentry: 'sentry=doGsaREgReaT' } },
});

expect(getNewTracestate).not.toHaveBeenCalled();
expect(transaction.metadata.tracestate?.sentry).toEqual('sentry=doGsaREgReaT');
});

it("doesn't create new sentry tracestate on transaction creation if none provided", () => {
const transaction = new Transaction({
name: 'FETCH /ball',
});

expect(transaction.metadata.tracestate?.sentry).toBeUndefined();
expect(getNewTracestate).not.toHaveBeenCalled();
});
});

describe('when getting outgoing request headers', () => {
it('uses existing sentry tracestate when getting tracing headers rather than creating new one', () => {
const transaction = new Transaction({
name: 'FETCH /ball',
metadata: { tracestate: { sentry: 'sentry=doGsaREgReaT' } },
});

expect(transaction.getTraceHeaders().tracestate).toEqual('sentry=doGsaREgReaT');
expect(getNewTracestate).not.toHaveBeenCalled();
});

it('creates and stores new sentry tracestate when getting tracing headers if none exists', () => {
const transaction = new Transaction({
name: 'FETCH /ball',
});

expect(transaction.metadata.tracestate?.sentry).toBeUndefined();

transaction.getTraceHeaders();

expect(getNewTracestate).toHaveBeenCalled();
expect(transaction.metadata.tracestate?.sentry).toEqual('sentry=doGsaREgReaT');
expect(transaction.getTraceHeaders().tracestate).toEqual('sentry=doGsaREgReaT');
});
});

describe('when getting envelope headers', () => {
// In real life, `transaction.finish()` calls `captureEvent()`, which eventually calls `eventToSentryRequest()`,
// which in turn calls `base64ToUnicode`. Here we're short circuiting that process a little, to avoid having to
// mock out more of the intermediate pieces.
jest
.spyOn(utilsPackage, 'base64ToUnicode')
.mockImplementation(base64 =>
base64 === 'doGsaREgReaT' ? '{"all the":"right stuff here"}' : '{"nope nope nope":"wrong"}',
);
jest.spyOn(hub, 'captureEvent').mockImplementation(event => {
expect(event).toEqual(
expect.objectContaining({ debug_meta: { tracestate: { sentry: 'sentry=doGsaREgReaT' } } }),
);

const envelope = parseEnvelopeRequest(sentryCore.eventToSentryRequest(event, new API(dsn)));
expect(envelope.envelopeHeader).toEqual(
expect.objectContaining({ trace: { 'all the': 'right stuff here' } }),
);

// `captureEvent` normally returns the event id
return '11212012041520131231201209082013'; //
});

it('uses existing sentry tracestate in envelope headers rather than creating a new one', () => {
// one here, and two inside the `captureEvent` implementation above
expect.assertions(3);

const transaction = new Transaction(
{
name: 'FETCH /ball',
metadata: { tracestate: { sentry: 'sentry=doGsaREgReaT' } },
sampled: true,
},
hub,
);

transaction.finish();

expect(getNewTracestate).not.toHaveBeenCalled();
});

it('creates new sentry tracestate for envelope header if none exists', () => {
// two here, and two inside the `captureEvent` implementation above
expect.assertions(4);

const transaction = new Transaction(
{
name: 'FETCH /ball',
sampled: true,
},
hub,
);

expect(transaction.metadata.tracestate?.sentry).toBeUndefined();

transaction.finish();

expect(getNewTracestate).toHaveBeenCalled();
});
});
});

describe('mutibility', () => {
it("won't include data set after transaction is created if there's an inherited value", () => {
expect.assertions(2);

const inheritedTracestate = `sentry=${computeTracestateValue({
traceId: '12312012090820131231201209082013',
environment: 'dogpark',
release: 'off.leash.trail',
publicKey: 'dogsarebadatkeepingsecrets',
} as SentryTracestateData)}`;

const transaction = new Transaction(
{
name: 'FETCH /ball',
metadata: {
tracestate: {
sentry: inheritedTracestate,
},
},
},
hub,
);

hub.withScope(scope => {
scope.setUser({ id: '1121', username: 'CharlieDog', ip_address: '11.21.20.12', segment: 'bigs' });

const tracestateValue = (transaction as any)._toTracestate().replace('sentry=', '');
const reinflatedTracestate = JSON.parse(base64ToUnicode(tracestateValue));

expect(reinflatedTracestate.userId).toBeNull();
expect(reinflatedTracestate.userSegment).toBeNull();
});
});

it("will include data set after transaction is created if there's no inherited value and `getTraceHeaders` hasn't been called", () => {
expect.assertions(2);

const transaction = new Transaction(
{
name: 'FETCH /ball',
},
hub,
);

hub.withScope(scope => {
scope.setUser({ id: '1121', username: 'CharlieDog', ip_address: '11.21.20.12', segment: 'bigs' });

const tracestateValue = (transaction as any)._toTracestate().replace('sentry=', '');
const reinflatedTracestate = JSON.parse(base64ToUnicode(tracestateValue));

expect(reinflatedTracestate.userId).toEqual('1121');
expect(reinflatedTracestate.userSegment).toEqual('bigs');
});
});

it("won't include data set after first call to `getTraceHeaders`", () => {
expect.assertions(2);

const transaction = new Transaction(
{
name: 'FETCH /ball',
},
hub,
);

transaction.getTraceHeaders();

hub.withScope(scope => {
scope.setUser({ id: '1121', username: 'CharlieDog', ip_address: '11.21.20.12', segment: 'bigs' });

const tracestateValue = (transaction as any)._toTracestate().replace('sentry=', '');
const reinflatedTracestate = JSON.parse(base64ToUnicode(tracestateValue));

expect(reinflatedTracestate.userId).toBeNull();
expect(reinflatedTracestate.userSegment).toBeNull();
});
});
});
});

describe('third-party tracestate', () => {
// TODO gather relevant tests here
});
});