Skip to content

Commit

Permalink
fix tests
Browse files Browse the repository at this point in the history
  • Loading branch information
lobsterkatie committed Jun 13, 2022
1 parent 8a2d673 commit e8774f2
Show file tree
Hide file tree
Showing 3 changed files with 494 additions and 338 deletions.
344 changes: 6 additions & 338 deletions packages/node/test/handlers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,174 +2,15 @@ import * as sentryCore from '@sentry/core';
import * as sentryHub from '@sentry/hub';
import { Hub } from '@sentry/hub';
import { Transaction } from '@sentry/tracing';
import { Baggage } from '@sentry/types';
import { Baggage, Event } from '@sentry/types';
import { SentryError } from '@sentry/utils';
import * as http from 'http';
import * as net from 'net';

import { Event, Request, User } from '../src';
import { NodeClient } from '../src/client';
import {
errorHandler,
ExpressRequest,
extractRequestData,
parseRequest,
requestHandler,
tracingHandler,
} from '../src/handlers';
import { errorHandler, requestHandler, tracingHandler } from '../src/handlers';
import * as SDK from '../src/sdk';
import { getDefaultNodeClientOptions } from './helper/node-client-options';

describe('parseRequest', () => {
let mockReq: { [key: string]: any };

beforeEach(() => {
mockReq = {
baseUrl: '/routerMountPath',
body: 'foo',
cookies: { test: 'test' },
headers: {
host: 'mattrobenolt.com',
},
method: 'POST',
originalUrl: '/routerMountPath/subpath/specificValue?querystringKey=querystringValue',
path: '/subpath/specificValue',
query: {
querystringKey: 'querystringValue',
},
route: {
path: '/subpath/:parameterName',
stack: [
{
name: 'parameterNameRouteHandler',
},
],
},
url: '/subpath/specificValue?querystringKey=querystringValue',
user: {
custom_property: 'foo',
email: 'tobias@mail.com',
id: 123,
username: 'tobias',
},
};
});

describe('parseRequest.user properties', () => {
const DEFAULT_USER_KEYS = ['id', 'username', 'email'];
const CUSTOM_USER_KEYS = ['custom_property'];

test('parseRequest.user only contains the default properties from the user', () => {
const parsedRequest: Event = parseRequest({}, mockReq as ExpressRequest);
expect(Object.keys(parsedRequest.user as User)).toEqual(DEFAULT_USER_KEYS);
});

test('parseRequest.user only contains the custom properties specified in the options.user array', () => {
const parsedRequest: Event = parseRequest({}, mockReq as ExpressRequest, {
user: CUSTOM_USER_KEYS,
});
expect(Object.keys(parsedRequest.user as User)).toEqual(CUSTOM_USER_KEYS);
});

test('parseRequest.user doesnt blow up when someone passes non-object value', () => {
const parsedRequest: Event = parseRequest(
{},
{
...mockReq,
// @ts-ignore user is not assignable to object
user: 'wat',
},
);
expect(Object.keys(parsedRequest.user as User)).toEqual([]);
});
});

describe('parseRequest.ip property', () => {
test('can be extracted from req.ip', () => {
const parsedRequest: Event = parseRequest(
{},
{
...mockReq,
ip: '123',
} as ExpressRequest,
{
ip: true,
},
);
expect(parsedRequest.user!.ip_address).toEqual('123');
});

test('can extract from req.connection.remoteAddress', () => {
const parsedRequest: Event = parseRequest(
{},
{
...mockReq,
connection: {
remoteAddress: '321',
} as net.Socket,
} as ExpressRequest,
{
ip: true,
},
);
expect(parsedRequest.user!.ip_address).toEqual('321');
});
});

describe('parseRequest.request properties', () => {
test('parseRequest.request only contains the default set of properties from the request', () => {
const DEFAULT_REQUEST_PROPERTIES = ['cookies', 'data', 'headers', 'method', 'query_string', 'url'];
const parsedRequest: Event = parseRequest({}, mockReq as ExpressRequest);
expect(Object.keys(parsedRequest.request as Request)).toEqual(DEFAULT_REQUEST_PROPERTIES);
});

test('parseRequest.request only contains the specified properties in the options.request array', () => {
const INCLUDED_PROPERTIES = ['data', 'headers', 'query_string', 'url'];
const parsedRequest: Event = parseRequest({}, mockReq as ExpressRequest, {
request: INCLUDED_PROPERTIES,
});
expect(Object.keys(parsedRequest.request as Request)).toEqual(INCLUDED_PROPERTIES);
});

test('parseRequest.request skips `body` property for GET and HEAD requests', () => {
expect(parseRequest({}, mockReq as ExpressRequest, {}).request).toHaveProperty('data');
expect(parseRequest({}, { ...mockReq, method: 'GET' } as ExpressRequest, {}).request).not.toHaveProperty('data');
expect(parseRequest({}, { ...mockReq, method: 'HEAD' } as ExpressRequest, {}).request).not.toHaveProperty('data');
});
});

describe('parseRequest.transaction property', () => {
test('extracts method and full route path by default`', () => {
const parsedRequest: Event = parseRequest({}, mockReq as ExpressRequest);
expect(parsedRequest.transaction).toEqual('POST /routerMountPath/subpath/:parameterName');
});

test('extracts method and full path by default when mountpoint is `/`', () => {
mockReq.originalUrl = mockReq.originalUrl.replace('/routerMountpath', '');
mockReq.baseUrl = '';
const parsedRequest: Event = parseRequest({}, mockReq as ExpressRequest);
// "sub"path is the full path here, because there's no router mount path
expect(parsedRequest.transaction).toEqual('POST /subpath/:parameterName');
});

test('fallback to method and `originalUrl` if route is missing', () => {
delete mockReq.route;
const parsedRequest: Event = parseRequest({}, mockReq as ExpressRequest);
expect(parsedRequest.transaction).toEqual('POST /routerMountPath/subpath/specificValue');
});

test('can extract path only instead if configured', () => {
const parsedRequest: Event = parseRequest({}, mockReq as ExpressRequest, { transaction: 'path' });
expect(parsedRequest.transaction).toEqual('/routerMountPath/subpath/:parameterName');
});

test('can extract handler name instead if configured', () => {
const parsedRequest: Event = parseRequest({}, mockReq as ExpressRequest, { transaction: 'handler' });
expect(parsedRequest.transaction).toEqual('parameterNameRouteHandler');
});
});
});

describe('requestHandler', () => {
const headers = { ears: 'furry', nose: 'wet', tongue: 'spotted', cookie: 'favorite=zukes' };
const method = 'wagging';
Expand Down Expand Up @@ -270,7 +111,7 @@ describe('requestHandler', () => {
});
});

it('patches `res.end` when `flushTimeout` is specified', () => {
it('patches `res.end` when `flushTimeout` is specified', done => {
const flush = jest.spyOn(SDK, 'flush').mockResolvedValue(true);

const sentryRequestMiddleware = requestHandler({ flushTimeout: 1337 });
Expand All @@ -280,10 +121,11 @@ describe('requestHandler', () => {
setImmediate(() => {
expect(flush).toHaveBeenCalledWith(1337);
expect(res.finished).toBe(true);
done();
});
});

it('prevents errors thrown during `flush` from breaking the response', async () => {
it('prevents errors thrown during `flush` from breaking the response', done => {
jest.spyOn(SDK, 'flush').mockRejectedValue(new SentryError('HTTP Error (429)'));

const sentryRequestMiddleware = requestHandler({ flushTimeout: 1337 });
Expand All @@ -292,6 +134,7 @@ describe('requestHandler', () => {

setImmediate(() => {
expect(res.finished).toBe(true);
done();
});
});
});
Expand Down Expand Up @@ -530,181 +373,6 @@ describe('tracingHandler', () => {
});
});

describe('extractRequestData()', () => {
describe('default behaviour', () => {
test('node', () => {
expect(
extractRequestData({
headers: { host: 'example.com' },
method: 'GET',
secure: true,
originalUrl: '/',
}),
).toEqual({
cookies: {},
headers: {
host: 'example.com',
},
method: 'GET',
query_string: null,
url: 'https://example.com/',
});
});

test('degrades gracefully without request data', () => {
expect(extractRequestData({})).toEqual({
cookies: {},
headers: {},
method: undefined,
query_string: null,
url: 'http://<no host>',
});
});
});

describe('cookies', () => {
it('uses `req.cookies` if available', () => {
expect(
extractRequestData(
{
cookies: { foo: 'bar' },
},
['cookies'],
),
).toEqual({
cookies: { foo: 'bar' },
});
});

it('parses the cookie header', () => {
expect(
extractRequestData(
{
headers: {
cookie: 'foo=bar;',
},
},
['cookies'],
),
).toEqual({
cookies: { foo: 'bar' },
});
});

it('falls back if no cookies are defined', () => {
expect(extractRequestData({}, ['cookies'])).toEqual({
cookies: {},
});
});
});

describe('data', () => {
it('includes data from `req.body` if available', () => {
expect(
extractRequestData(
{
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: 'foo=bar',
},
['data'],
),
).toEqual({
data: 'foo=bar',
});
});

it('encodes JSON body contents back to a string', () => {
expect(
extractRequestData(
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: { foo: 'bar' },
},
['data'],
),
).toEqual({
data: '{"foo":"bar"}',
});
});
});

describe('query_string', () => {
it('parses the query parms from the url', () => {
expect(
extractRequestData(
{
headers: { host: 'example.com' },
secure: true,
originalUrl: '/?foo=bar',
},
['query_string'],
),
).toEqual({
query_string: 'foo=bar',
});
});

it('gracefully degrades if url cannot be determined', () => {
expect(extractRequestData({}, ['query_string'])).toEqual({
query_string: null,
});
});
});

describe('url', () => {
test('express/koa', () => {
expect(
extractRequestData(
{
host: 'example.com',
protocol: 'https',
url: '/',
},
['url'],
),
).toEqual({
url: 'https://example.com/',
});
});

test('node', () => {
expect(
extractRequestData(
{
headers: { host: 'example.com' },
secure: true,
originalUrl: '/',
},
['url'],
),
).toEqual({
url: 'https://example.com/',
});
});
});

describe('custom key', () => {
it('includes the custom key if present', () => {
expect(
extractRequestData(
{
httpVersion: '1.1',
},
['httpVersion'],
),
).toEqual({
httpVersion: '1.1',
});
});

it('gracefully degrades if the custom key is missing', () => {
expect(extractRequestData({}, ['httpVersion'])).toEqual({});
});
});
});

describe('errorHandler()', () => {
const headers = { ears: 'furry', nose: 'wet', tongue: 'spotted', cookie: 'favorite=zukes' };
const method = 'wagging';
Expand Down
Loading

0 comments on commit e8774f2

Please sign in to comment.