From e8774f29db1cf54278f008f029974fc576a9cbea Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Mon, 13 Jun 2022 11:19:08 -0700 Subject: [PATCH] fix tests --- packages/node/test/handlers.test.ts | 344 +------------ packages/node/test/requestdata.test.ts | 487 ++++++++++++++++++ .../serverless/test/__mocks__/@sentry/node.ts | 1 + 3 files changed, 494 insertions(+), 338 deletions(-) create mode 100644 packages/node/test/requestdata.test.ts diff --git a/packages/node/test/handlers.test.ts b/packages/node/test/handlers.test.ts index bcb5cc515bbd..22ef95fd6fd9 100644 --- a/packages/node/test/handlers.test.ts +++ b/packages/node/test/handlers.test.ts @@ -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'; @@ -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 }); @@ -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 }); @@ -292,6 +134,7 @@ describe('requestHandler', () => { setImmediate(() => { expect(res.finished).toBe(true); + done(); }); }); }); @@ -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://', - }); - }); - }); - - 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'; diff --git a/packages/node/test/requestdata.test.ts b/packages/node/test/requestdata.test.ts new file mode 100644 index 000000000000..7592d1d3b1a3 --- /dev/null +++ b/packages/node/test/requestdata.test.ts @@ -0,0 +1,487 @@ +/* eslint-disable deprecation/deprecation */ + +/* Note: These tests (except for the ones related to cookies) should eventually live in `@sentry/utils`, and can be + * moved there once the the backwards-compatibility-preserving wrappers in `handlers.ts` are removed. + */ + +// TODO (v8 / #5190): Remove everything above + +import { Event, User } from '@sentry/types'; +import { + addRequestDataToEvent, + AddRequestDataToEventOptions, + CrossPlatformRequest, + extractRequestData as newExtractRequestData, +} from '@sentry/utils'; +import * as cookie from 'cookie'; +import * as net from 'net'; +import * as url from 'url'; + +import { + ExpressRequest, + extractRequestData as oldExtractRequestData, + parseRequest, +} from '../src/requestDataDeprecated'; + +const mockCookieModule = { parse: jest.fn() }; + +// TODO (v8 / #5190): Remove `describe.each` wrapper, remove `formatArgs` wrapper, reformat args in tests, use only +// `addRequestDataToEvent`, and move these tests to @sentry/utils +describe.each([parseRequest, addRequestDataToEvent])( + 'backwards compatibility of `parseRequest` rename and move', + fn => { + /** Rearrage and cast args correctly for each version of the function */ + function formatArgs( + fn: typeof parseRequest | typeof addRequestDataToEvent, + event: Event, + req: any, + include?: AddRequestDataToEventOptions['include'], + ): Parameters | Parameters { + if (fn.name === 'parseRequest') { + return [event, req as ExpressRequest, include]; + } else { + return [ + event, + req as CrossPlatformRequest, + { + include, + deps: { + cookie: mockCookieModule, + url, + }, + }, + ]; + } + } + + describe(fn, () => { + let mockEvent: Event; + let mockReq: { [key: string]: any }; + + beforeEach(() => { + mockEvent = {}; + 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(`${fn.name}.user properties`, () => { + const DEFAULT_USER_KEYS = ['id', 'username', 'email']; + const CUSTOM_USER_KEYS = ['custom_property']; + + test(`${fn.name}.user only contains the default properties from the user`, () => { + const [event, req, options] = formatArgs(fn, mockEvent, mockReq); + const parsedRequest: Event = fn(event, req, options); + + expect(Object.keys(parsedRequest.user as User)).toEqual(DEFAULT_USER_KEYS); + }); + + test(`${fn.name}.user only contains the custom properties specified in the options.user array`, () => { + const optionsWithCustomUserKeys = { + user: CUSTOM_USER_KEYS, + }; + + const [event, req, options] = formatArgs(fn, mockEvent, mockReq, optionsWithCustomUserKeys); + const parsedRequest: Event = fn(event, req, options); + + expect(Object.keys(parsedRequest.user as User)).toEqual(CUSTOM_USER_KEYS); + }); + + test(`${fn.name}.user doesnt blow up when someone passes non-object value`, () => { + const reqWithUser = { + ...mockReq, + // @ts-ignore user is not assignable to object + user: 'wat', + }; + + const [event, req, options] = formatArgs(fn, mockEvent, reqWithUser); + const parsedRequest: Event = fn(event, req, options); + + expect(parsedRequest.user).toBeUndefined(); + }); + }); + + describe(`${fn.name}.ip property`, () => { + test('can be extracted from req.ip', () => { + const mockReqWithIP = { + ...mockReq, + ip: '123', + }; + const optionsWithIP = { + ip: true, + }; + + const [event, req, options] = formatArgs(fn, mockEvent, mockReqWithIP, optionsWithIP); + const parsedRequest: Event = fn(event, req, options); + + expect(parsedRequest.user!.ip_address).toEqual('123'); + }); + + test('can extract from req.socket.remoteAddress', () => { + const reqWithIPInSocket = { + ...mockReq, + socket: { + remoteAddress: '321', + } as net.Socket, + }; + const optionsWithIP = { + ip: true, + }; + + const [event, req, options] = formatArgs(fn, mockEvent, reqWithIPInSocket, optionsWithIP); + const parsedRequest: Event = fn(event, req, options); + + expect(parsedRequest.user!.ip_address).toEqual('321'); + }); + }); + + describe(`${fn.name}.request properties`, () => { + test(`${fn.name}.request only contains the default set of properties from the request`, () => { + const DEFAULT_REQUEST_PROPERTIES = ['cookies', 'data', 'headers', 'method', 'query_string', 'url']; + + const [event, req, options] = formatArgs(fn, mockEvent, mockReq); + const parsedRequest: Event = fn(event, req, options); + + expect(Object.keys(parsedRequest.request!)).toEqual(DEFAULT_REQUEST_PROPERTIES); + }); + + test(`${fn.name}.request only contains the specified properties in the options.request array`, () => { + const INCLUDED_PROPERTIES = ['data', 'headers', 'query_string', 'url']; + const optionsWithRequestIncludes = { + request: INCLUDED_PROPERTIES, + }; + + const [event, req, options] = formatArgs(fn, mockEvent, mockReq, optionsWithRequestIncludes); + const parsedRequest: Event = fn(event, req, options); + + expect(Object.keys(parsedRequest.request!)).toEqual(INCLUDED_PROPERTIES); + }); + + test.each([ + [undefined, true], + ['GET', false], + ['HEAD', false], + ])( + `${fn.name}.request skips \`body\` property for GET and HEAD requests - %s method`, + (method, shouldIncludeBodyData) => { + const reqWithMethod = { ...mockReq, method }; + + const [event, req, options] = formatArgs(fn, mockEvent, reqWithMethod); + const parsedRequest: Event = fn(event, req, options); + + if (shouldIncludeBodyData) { + expect(parsedRequest.request).toHaveProperty('data'); + } else { + expect(parsedRequest.request).not.toHaveProperty('data'); + } + }, + ); + }); + + describe(`${fn.name}.transaction property`, () => { + test('extracts method and full route path by default`', () => { + const [event, req, options] = formatArgs(fn, mockEvent, mockReq); + const parsedRequest: Event = fn(event, req, options); + + 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 [event, req, options] = formatArgs(fn, mockEvent, mockReq); + const parsedRequest: Event = fn(event, req, options); + + // `subpath/` 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 [event, req, options] = formatArgs(fn, mockEvent, mockReq); + const parsedRequest: Event = fn(event, req, options); + + expect(parsedRequest.transaction).toEqual('POST /routerMountPath/subpath/specificValue'); + }); + + test('can extract path only instead if configured', () => { + const optionsWithPathTransaction = { transaction: 'path' } as const; + + const [event, req, options] = formatArgs(fn, mockEvent, mockReq, optionsWithPathTransaction); + const parsedRequest: Event = fn(event, req, options); + + expect(parsedRequest.transaction).toEqual('/routerMountPath/subpath/:parameterName'); + }); + + test('can extract handler name instead if configured', () => { + const optionsWithHandlerTransaction = { transaction: 'handler' } as const; + + const [event, req, options] = formatArgs(fn, mockEvent, mockReq, optionsWithHandlerTransaction); + const parsedRequest: Event = fn(event, req, options); + + expect(parsedRequest.transaction).toEqual('parameterNameRouteHandler'); + }); + }); + }); + }, +); + +// TODO (v8 / #5190): Remove `describe.each` wrapper, remove `formatArgs` wrapper, reformat args in tests, use only +// `newExtractRequestData`, rename `newExtractRequestData` to just `extractRequestData`, and move these tests (except +// the ones involving cookies) to @sentry/utils (use `mockCookieModule` for others) +Object.defineProperty(oldExtractRequestData, 'name', { + value: 'oldExtractRequestData', +}); +Object.defineProperty(newExtractRequestData, 'name', { + value: 'newExtractRequestData', +}); +describe.each([oldExtractRequestData, newExtractRequestData])( + 'backwards compatibility of `extractRequestData` move', + fn => { + /** Rearrage and cast args correctly for each version of the function */ + function formatArgs( + fn: typeof oldExtractRequestData | typeof newExtractRequestData, + req: any, + include?: string[], + ): Parameters | Parameters { + if (fn.name === 'oldExtractRequestData') { + return [req as ExpressRequest, include] as Parameters; + } else { + return [ + req as CrossPlatformRequest, + { + include, + deps: { + cookie: include?.includes('cookies') ? cookie : mockCookieModule, + url, + }, + }, + ] as Parameters; + } + } + + describe(fn, () => { + describe('default behaviour', () => { + test('node', () => { + const mockReq = { + headers: { host: 'example.com' }, + method: 'GET', + socket: { encrypted: true }, + originalUrl: '/', + }; + + const [req, options] = formatArgs(fn, mockReq); + + expect(fn(req, options as any)).toEqual({ + cookies: {}, + headers: { + host: 'example.com', + }, + method: 'GET', + query_string: undefined, + url: 'https://example.com/', + }); + }); + + test('degrades gracefully without request data', () => { + const mockReq = {}; + + const [req, options] = formatArgs(fn, mockReq); + + expect(fn(req, options as any)).toEqual({ + cookies: {}, + headers: {}, + method: undefined, + query_string: undefined, + url: 'http://', + }); + }); + }); + + describe('cookies', () => { + it('uses `req.cookies` if available', () => { + const mockReq = { + cookies: { foo: 'bar' }, + }; + const optionsWithCookies = ['cookies']; + + const [req, options] = formatArgs(fn, mockReq, optionsWithCookies); + + expect(fn(req, options as any)).toEqual({ + cookies: { foo: 'bar' }, + }); + }); + + it('parses the cookie header', () => { + const mockReq = { + headers: { + cookie: 'foo=bar;', + }, + }; + const optionsWithCookies = ['cookies']; + + const [req, options] = formatArgs(fn, mockReq, optionsWithCookies); + + expect(fn(req, options as any)).toEqual({ + cookies: { foo: 'bar' }, + }); + }); + + it('falls back if no cookies are defined', () => { + const mockReq = {}; + const optionsWithCookies = ['cookies']; + + const [req, options] = formatArgs(fn, mockReq, optionsWithCookies); + + expect(fn(req, options as any)).toEqual({ + cookies: {}, + }); + }); + }); + + describe('data', () => { + it('includes data from `req.body` if available', () => { + const mockReq = { + method: 'POST', + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + body: 'foo=bar', + }; + const optionsWithData = ['data']; + + const [req, options] = formatArgs(fn, mockReq, optionsWithData); + + expect(fn(req, options as any)).toEqual({ + data: 'foo=bar', + }); + }); + + it('encodes JSON body contents back to a string', () => { + const mockReq = { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: { foo: 'bar' }, + }; + const optionsWithData = ['data']; + + const [req, options] = formatArgs(fn, mockReq, optionsWithData); + + expect(fn(req, options as any)).toEqual({ + data: '{"foo":"bar"}', + }); + }); + }); + + describe('query_string', () => { + it('parses the query parms from the url', () => { + const mockReq = { + headers: { host: 'example.com' }, + secure: true, + originalUrl: '/?foo=bar', + }; + const optionsWithQueryString = ['query_string']; + + const [req, options] = formatArgs(fn, mockReq, optionsWithQueryString); + + expect(fn(req, options as any)).toEqual({ + query_string: 'foo=bar', + }); + }); + + it('gracefully degrades if url cannot be determined', () => { + const mockReq = {}; + const optionsWithQueryString = ['query_string']; + + const [req, options] = formatArgs(fn, mockReq, optionsWithQueryString); + + expect(fn(req, options as any)).toEqual({ + query_string: undefined, + }); + }); + }); + + describe('url', () => { + test('express/koa', () => { + const mockReq = { + host: 'example.com', + protocol: 'https', + url: '/', + }; + const optionsWithURL = ['url']; + + const [req, options] = formatArgs(fn, mockReq, optionsWithURL); + + expect(fn(req, options as any)).toEqual({ + url: 'https://example.com/', + }); + }); + + test('node', () => { + const mockReq = { + headers: { host: 'example.com' }, + socket: { encrypted: true }, + originalUrl: '/', + }; + const optionsWithURL = ['url']; + + const [req, options] = formatArgs(fn, mockReq, optionsWithURL); + + expect(fn(req, options as any)).toEqual({ + url: 'https://example.com/', + }); + }); + }); + + describe('custom key', () => { + it('includes the custom key if present', () => { + const mockReq = { + httpVersion: '1.1', + }; + const optionsWithCustomKey = ['httpVersion']; + + const [req, options] = formatArgs(fn, mockReq, optionsWithCustomKey); + + expect(fn(req, options as any)).toEqual({ + httpVersion: '1.1', + }); + }); + + it('gracefully degrades if the custom key is missing', () => { + const mockReq = {}; + const optionsWithCustomKey = ['httpVersion']; + + const [req, options] = formatArgs(fn, mockReq, optionsWithCustomKey); + + expect(fn(req, options as any)).toEqual({}); + }); + }); + }); + }, +); diff --git a/packages/serverless/test/__mocks__/@sentry/node.ts b/packages/serverless/test/__mocks__/@sentry/node.ts index 769d22f6e2f6..14043efdd42c 100644 --- a/packages/serverless/test/__mocks__/@sentry/node.ts +++ b/packages/serverless/test/__mocks__/@sentry/node.ts @@ -1,6 +1,7 @@ const origSentry = jest.requireActual('@sentry/node'); export const defaultIntegrations = origSentry.defaultIntegrations; // eslint-disable-line @typescript-eslint/no-unsafe-member-access export const Handlers = origSentry.Handlers; // eslint-disable-line @typescript-eslint/no-unsafe-member-access +export const addRequestDataToEvent = origSentry.addRequestDataToEvent; export const SDK_VERSION = '6.6.6'; export const Severity = { Warning: 'warning',