From bba80758c6854183c70edea81c16f41cc873f826 Mon Sep 17 00:00:00 2001 From: HazA Date: Thu, 14 Jun 2018 16:00:23 +0200 Subject: [PATCH 01/10] feat: Move get global hub to global function --- packages/hub/src/global.ts | 37 +++++++++++++++++++++++++++++++++ packages/hub/src/hub.ts | 38 +--------------------------------- packages/hub/src/index.ts | 3 ++- packages/hub/src/interfaces.ts | 1 - packages/minimal/src/index.ts | 18 ++++++++-------- 5 files changed, 49 insertions(+), 48 deletions(-) diff --git a/packages/hub/src/global.ts b/packages/hub/src/global.ts index 21d4d3bba24a..aa0a96e947fa 100644 --- a/packages/hub/src/global.ts +++ b/packages/hub/src/global.ts @@ -1,4 +1,13 @@ import { Carrier } from './interfaces'; +import { Hub } from './hub'; + +/** + * API compatibility version of this hub. + * + * WARNING: This number should only be incresed when the global interface + * changes a and new methods are introduced. + */ +export const API_VERSION = 2; /** Global interface helper for type safety. */ interface Global { @@ -15,3 +24,31 @@ global.__SENTRY__ = global.__SENTRY__ || { export function getGlobalCarrier(): Carrier { return global.__SENTRY__; } + +/** + * Returns the latest global hum instance. + * + * If a hub is already registered in the global carrier but this module + * contains a more recent version, it replaces the registered version. + * Otherwise, the currently registered hub will be returned. + */ +export function getGlobalHub(): Hub { + const registry = getGlobalCarrier(); + + if (!registry.hub || registry.hub.isOlderThan(API_VERSION)) { + registry.hub = new Hub(); + } + + return registry.hub; +} + +/** TODO */ +export function getHubFromCarrier(carrier: any): Hub { + if (carrier && carrier.__SENTRY__ && carrier.__SENTRY__.hub) { + return carrier.__SENTRY__.hub; + } else { + carrier.__SENTRY__ = {}; + carrier.__SENTRY__.hub = new Hub(); + return carrier.__SENTRY__.hub; + } +} diff --git a/packages/hub/src/hub.ts b/packages/hub/src/hub.ts index 0b566fd9a725..cc1c42feb727 100644 --- a/packages/hub/src/hub.ts +++ b/packages/hub/src/hub.ts @@ -1,16 +1,8 @@ import { Breadcrumb, SentryEvent } from '@sentry/types'; -import { getGlobalCarrier } from './global'; +import { API_VERSION } from './global'; import { Layer } from './interfaces'; import { Scope } from './scope'; -/** - * API compatibility version of this hub. - * - * WARNING: This number should only be incresed when the global interface - * changes a and new methods are introduced. - */ -export const API_VERSION = 2; - /** * Internal class used to make sure we always have the latest internal functions * working in case we have a version conflict. @@ -30,23 +22,6 @@ export class Hub { } } - /** - * Returns the latest global hum instance. - * - * If a hub is already registered in the global carrier but this module - * contains a more recent version, it replaces the registered version. - * Otherwise, the currently registered hub will be returned. - */ - public static getGlobal(): Hub { - const registry = getGlobalCarrier(); - - if (!registry.hub || registry.hub.isOlderThan(API_VERSION)) { - registry.hub = new Hub(); - } - - return registry.hub; - } - /** * Internal helper function to call a method on the top client if it exists. * @@ -234,14 +209,3 @@ export class Hub { } } } - -/** TODO */ -export function hubFromCarrier(carrier: any): Hub { - if (carrier && carrier.__SENTRY__ && carrier.__SENTRY__.hub) { - return carrier.__SENTRY__.hub; - } else { - carrier.__SENTRY__ = {}; - carrier.__SENTRY__.hub = new Hub(); - return carrier.__SENTRY__.hub; - } -} diff --git a/packages/hub/src/index.ts b/packages/hub/src/index.ts index 002889ea031d..2372d9f6767d 100644 --- a/packages/hub/src/index.ts +++ b/packages/hub/src/index.ts @@ -1,3 +1,4 @@ export { Carrier, Layer } from './interfaces'; export { Scope } from './scope'; -export { hubFromCarrier, Hub } from './hub'; +export { Hub } from './hub'; +export { getGlobalHub, getHubFromCarrier } from './global'; diff --git a/packages/hub/src/interfaces.ts b/packages/hub/src/interfaces.ts index 3ddeaca3c67c..3021786d3939 100644 --- a/packages/hub/src/interfaces.ts +++ b/packages/hub/src/interfaces.ts @@ -13,6 +13,5 @@ export interface Layer { /** An object that contains a hub and maintains a scope stack. */ export interface Carrier { - stack: Layer[]; hub?: Hub; } diff --git a/packages/minimal/src/index.ts b/packages/minimal/src/index.ts index 1289fddb6610..6bcaa42dc6ec 100644 --- a/packages/minimal/src/index.ts +++ b/packages/minimal/src/index.ts @@ -1,9 +1,9 @@ -import { Hub, Scope } from '@sentry/hub'; +import { getGlobalHub, Scope } from '@sentry/hub'; import { Breadcrumb, SentryEvent } from '@sentry/types'; /** Returns the current client, if any. */ export function getCurrentClient(): any | undefined { - return Hub.getGlobal().getCurrentClient(); + return getGlobalHub().getCurrentClient(); } /** @@ -11,7 +11,7 @@ export function getCurrentClient(): any | undefined { * @param client An SDK client (client) instance. */ export function bindClient(client: any): void { - const hub = Hub.getGlobal(); + const hub = getGlobalHub(); const top = hub.getStackTop(); top.client = client; top.scope = hub.createScope(); @@ -34,7 +34,7 @@ export function bindClient(client: any): void { * @param exception An exception-like object. */ export function captureException(exception: any): void { - Hub.getGlobal().captureException(exception); + getGlobalHub().captureException(exception); } /** @@ -43,7 +43,7 @@ export function captureException(exception: any): void { * @param message The message to send to Sentry. */ export function captureMessage(message: string): void { - Hub.getGlobal().captureMessage(message); + getGlobalHub().captureMessage(message); } /** @@ -52,7 +52,7 @@ export function captureMessage(message: string): void { * @param event The event to send to Sentry. */ export function captureEvent(event: SentryEvent): void { - Hub.getGlobal().captureEvent(event); + getGlobalHub().captureEvent(event); } /** @@ -64,7 +64,7 @@ export function captureEvent(event: SentryEvent): void { * @param breadcrumb The breadcrumb to record. */ export function addBreadcrumb(breadcrumb: Breadcrumb): void { - Hub.getGlobal().addBreadcrumb(breadcrumb); + getGlobalHub().addBreadcrumb(breadcrumb); } /** @@ -73,7 +73,7 @@ export function addBreadcrumb(breadcrumb: Breadcrumb): void { * @param callback Callback function that receives Scope. */ export function configureScope(callback: (scope: Scope) => void): void { - Hub.getGlobal().configureScope(callback); + getGlobalHub().configureScope(callback); } /** @@ -86,5 +86,5 @@ export function configureScope(callback: (scope: Scope) => void): void { * @param args Arguments to pass to the client/fontend. */ export function _callOnClient(method: string, ...args: any[]): void { - Hub.getGlobal()._invokeClient(method, ...args); + getGlobalHub()._invokeClient(method, ...args); } From e27a348773b05e4d0beb4b65159801289ab4dbc0 Mon Sep 17 00:00:00 2001 From: HazA Date: Thu, 14 Jun 2018 16:01:09 +0200 Subject: [PATCH 02/10] feat: Fix domain handling in node --- packages/node/src/hub.ts | 60 +++++++------------ packages/node/src/index.ts | 4 +- .../src/integrations/onunhandledrejection.ts | 56 ++++++++++------- packages/node/test/hub.test.ts | 55 +++++++++++++++++ packages/node/test/index.test.ts | 25 ++++---- packages/node/test/integrations.test.ts | 34 +++++++++++ 6 files changed, 160 insertions(+), 74 deletions(-) create mode 100644 packages/node/test/hub.test.ts create mode 100644 packages/node/test/integrations.test.ts diff --git a/packages/node/src/hub.ts b/packages/node/src/hub.ts index 1e99e508da2a..29d885b7c26b 100644 --- a/packages/node/src/hub.ts +++ b/packages/node/src/hub.ts @@ -1,48 +1,34 @@ -import { Hub as BaseHub, Layer } from '@sentry/hub'; +import { Carrier, getGlobalHub as getGlobalHubBase, Hub } from '@sentry/hub'; +import * as domain from 'domain'; -/** - * Node specific implemention of Hub. - */ -export class Hub extends BaseHub { +declare module 'domain' { + export let active: Domain; /** - * @inheritDoc + * Extension for domain interface */ - public getStackTop(): Layer { - return this.getDomainStackTop(); - } - - /** Tries to return the top most ScopeLayer from the domainStack. */ - private getDomainStackTop(): Layer { - const stack = getDomainStack(); - - if (stack.length === 0) { - const client = this.getCurrentClient(); - stack.push({ - client, - scope: this.createScope(), - type: 'domain', - }); - } - - return stack[stack.length - 1]; + export interface Domain { + __SENTRY__?: Carrier; } } -/** Checks for an active domain and returns its stack, if present. */ -function getDomainStack(): Layer[] { - const domain = require('domain'); - // tslint:disable-next-line:no-unsafe-any - const active = domain.active; - if (!active) { - return []; +/** + * TODO + */ +export function getGlobalHub(): Hub { + // const domain = require('domain'); + const globalHub = getGlobalHubBase(); + if (!domain.active) { + return globalHub; } - // tslint:disable-next-line:no-unsafe-any - let carrier = active.__SENTRY__; + + let carrier = domain.active.__SENTRY__; if (!carrier) { - // tslint:disable-next-line:no-unsafe-any - active.__SENTRY__ = carrier = { hub: {} }; + domain.active.__SENTRY__ = carrier = {}; + } + + if (!carrier.hub) { + carrier.hub = new Hub(globalHub.getStack().slice()); } - // tslint:disable-next-line:no-unsafe-any - return carrier.stack; + return carrier.hub; } diff --git a/packages/node/src/index.ts b/packages/node/src/index.ts index ccb1330f50ac..c994f25e0988 100644 --- a/packages/node/src/index.ts +++ b/packages/node/src/index.ts @@ -19,9 +19,9 @@ export { configureScope, } from '@sentry/minimal'; -export { Scope } from '@sentry/hub'; +export { Hub, Scope } from '@sentry/hub'; -export { Hub } from './hub'; +export { getGlobalHub } from './hub'; export { NodeBackend, NodeOptions } from './backend'; export { NodeClient } from './client'; export { init, getCurrentClient } from './sdk'; diff --git a/packages/node/src/integrations/onunhandledrejection.ts b/packages/node/src/integrations/onunhandledrejection.ts index 53cc7b94fcdd..18195b1f474b 100644 --- a/packages/node/src/integrations/onunhandledrejection.ts +++ b/packages/node/src/integrations/onunhandledrejection.ts @@ -1,5 +1,5 @@ -import { Hub } from '@sentry/hub'; import { Integration } from '@sentry/types'; +import { getGlobalHub } from '../hub'; /** Global Promise Rejection handler */ export class OnUnhandledRejection implements Integration { @@ -11,29 +11,39 @@ export class OnUnhandledRejection implements Integration { * @inheritDoc */ public install(): void { - global.process.on('unhandledRejection', (reason, promise: any = {}) => { - const context = (promise.domain && promise.domain.sentryContext) || {}; - const hub = Hub.getGlobal(); - hub.withScope(() => { - hub.configureScope(scope => { - // Preserve backwards compatibility with raven-node for now - if (context.user) { - scope.setUser(context.user); - } - if (context.tags) { - Object.keys(context.tags).forEach(key => { - scope.setTag(key, context.tags[key]); - }); - } - if (context.extra) { - Object.keys(context.extra).forEach(key => { - scope.setExtra(key, context.extra[key]); - }); - } - scope.setExtra('unhandledPromiseRejection', true); - }); - hub.captureException(reason); + global.process.on( + 'unhandledRejection', + this.sendUnhandledPromise.bind(this), + ); + } + + /** + * Send an exception with reason + * @param reason string + * @param promise promise + */ + public sendUnhandledPromise(reason: any, promise: any): void { + const context = (promise.domain && promise.domain.sentryContext) || {}; + const hub = getGlobalHub(); + hub.withScope(() => { + hub.configureScope(scope => { + // Preserve backwards compatibility with raven-node for now + if (context.user) { + scope.setUser(context.user); + } + if (context.tags) { + Object.keys(context.tags).forEach(key => { + scope.setTag(key, context.tags[key]); + }); + } + if (context.extra) { + Object.keys(context.extra).forEach(key => { + scope.setExtra(key, context.extra[key]); + }); + } + scope.setExtra('unhandledPromiseRejection', true); }); + hub.captureException(reason); }); } } diff --git a/packages/node/test/hub.test.ts b/packages/node/test/hub.test.ts new file mode 100644 index 000000000000..e98dfb5250b3 --- /dev/null +++ b/packages/node/test/hub.test.ts @@ -0,0 +1,55 @@ +import * as domain from 'domain'; + +const mockGetGlobalHub = jest.fn(); + +class Hub { + public constructor(public stack: any[] = []) { + Hub.instance = this; + } + public static instance: Hub; + + public getStack(): any[] { + return this.stack; + } +} + +const mockHub = Hub; +jest.mock('@sentry/hub', () => ({ + Hub: mockHub, + getGlobalHub: mockGetGlobalHub, +})); + +import { getGlobalHub } from '../src'; + +describe('getGlobalHub', () => { + let globalHub: Hub; + + beforeEach(() => { + globalHub = new Hub(); + mockGetGlobalHub.mockReturnValue(globalHub); + }); + + afterEach(() => { + if (domain.active) { + domain.active.exit(); + } + jest.resetAllMocks(); + }); + + test('without domain', () => { + expect(domain.active).toBeFalsy(); + const hub = getGlobalHub(); + expect(hub).toBe(globalHub); + }); + + test('inherit global hub', done => { + globalHub.stack = ['abc']; + const d = domain.create(); + d.run(() => { + const hub = getGlobalHub(); + expect(globalHub).not.toBe(hub); + expect(globalHub.getStack()).toEqual(hub.getStack()); + done(); + }); + }); +}); diff --git a/packages/node/test/index.test.ts b/packages/node/test/index.test.ts index d07fd78411e4..4811d02388a6 100644 --- a/packages/node/test/index.test.ts +++ b/packages/node/test/index.test.ts @@ -16,7 +16,7 @@ import { captureException, captureMessage, configureScope, - Hub, + getGlobalHub, init, NodeBackend, NodeClient, @@ -34,11 +34,11 @@ describe('SentryNode', () => { }); beforeEach(() => { - Hub.getGlobal().pushScope(); + getGlobalHub().pushScope(); }); afterEach(() => { - Hub.getGlobal().popScope(); + getGlobalHub().popScope(); }); describe('getContext() / setContext()', () => { @@ -84,7 +84,7 @@ describe('SentryNode', () => { }); test('record auto breadcrumbs', done => { - Hub.getGlobal().pushScope( + getGlobalHub().pushScope( new NodeClient({ afterSend: (event: SentryEvent) => { expect(event.breadcrumbs!).toHaveLength(3); @@ -123,7 +123,7 @@ describe('SentryNode', () => { }); test('capture an exception', done => { - Hub.getGlobal().pushScope( + getGlobalHub().pushScope( new NodeClient({ afterSend: (event: SentryEvent) => { expect(event.tags).toEqual({ test: '1' }); @@ -145,11 +145,11 @@ describe('SentryNode', () => { } catch (e) { captureException(e); } - Hub.getGlobal().popScope(); + getGlobalHub().popScope(); }); test('capture a message', done => { - Hub.getGlobal().pushScope( + getGlobalHub().pushScope( new NodeClient({ afterSend: (event: SentryEvent) => { expect(event.message).toBe('test'); @@ -160,11 +160,11 @@ describe('SentryNode', () => { }), ); captureMessage('test'); - Hub.getGlobal().popScope(); + getGlobalHub().popScope(); }); test('capture an event', done => { - Hub.getGlobal().pushScope( + getGlobalHub().pushScope( new NodeClient({ afterSend: (event: SentryEvent) => { expect(event.message).toBe('test'); @@ -175,14 +175,14 @@ describe('SentryNode', () => { }), ); captureEvent({ message: 'test' }); - Hub.getGlobal().popScope(); + getGlobalHub().popScope(); }); test('capture an event in a domain', async () => { new Promise(resolve => { const d = domain.create(); d.run(() => { - Hub.getGlobal().pushScope( + getGlobalHub().pushScope( new NodeClient({ afterSend: (event: SentryEvent) => { expect(event.message).toBe('test'); @@ -194,9 +194,10 @@ describe('SentryNode', () => { }), ); captureEvent({ message: 'test' }); - Hub.getGlobal().popScope(); + getGlobalHub().popScope(); }); }); }); }); + }); diff --git a/packages/node/test/integrations.test.ts b/packages/node/test/integrations.test.ts new file mode 100644 index 000000000000..fe568fe78443 --- /dev/null +++ b/packages/node/test/integrations.test.ts @@ -0,0 +1,34 @@ +import { init, Integrations } from '../src'; +// import { Hub } from '../src/hub'; + +// Hub.getGlobal = jest.fn(jest.fn); +// jest.mock('../src/hub'); + +const dsn = 'https://53039209a22b4ec1bcc296a3c9fdecd6@sentry.io/4291'; + +describe('integrations', () => { + // test('unhandled promise listener installed', () => { + // init({ dsn }); + // expect(process.listeners('unhandledRejection')).toHaveLength(1); + // }); + + test('sendUnhandledPromise', () => { + init({ dsn }); + const int = new Integrations.OnUnhandledRejection(); + const promise = { + domain: { + sentryContext: { + extra: { tag: '1' }, + tags: { extra: 1 }, + user: { id: 1 }, + }, + }, + }; + // const mockSoundPlayerInstance = Hub.mock.instances[0]; + // console.log(Hub); + // console.log(mockSoundPlayerInstance); + int.sendUnhandledPromise(new Error('bla'), promise); + + // console.log(global.__SENTRY__.hub.stack[0]); + }); +}); From e9ded6df5aa0ec31262eec4ed2bd07b0103e3ff8 Mon Sep 17 00:00:00 2001 From: HazA Date: Thu, 14 Jun 2018 16:18:41 +0200 Subject: [PATCH 03/10] feat: More domain tests --- packages/hub/src/index.ts | 2 +- packages/hub/src/interfaces.ts | 2 +- packages/node/src/hub.ts | 1 - packages/node/test/domain.test.ts | 98 +++++++++++++++++++++++++++++++ packages/node/test/hub.test.ts | 55 ----------------- 5 files changed, 100 insertions(+), 58 deletions(-) create mode 100644 packages/node/test/domain.test.ts delete mode 100644 packages/node/test/hub.test.ts diff --git a/packages/hub/src/index.ts b/packages/hub/src/index.ts index 2372d9f6767d..61380df199db 100644 --- a/packages/hub/src/index.ts +++ b/packages/hub/src/index.ts @@ -1,4 +1,4 @@ -export { Carrier, Layer } from './interfaces'; +export { Carrier, Layer, LayerType } from './interfaces'; export { Scope } from './scope'; export { Hub } from './hub'; export { getGlobalHub, getHubFromCarrier } from './global'; diff --git a/packages/hub/src/interfaces.ts b/packages/hub/src/interfaces.ts index 3021786d3939..49786c5247e1 100644 --- a/packages/hub/src/interfaces.ts +++ b/packages/hub/src/interfaces.ts @@ -2,7 +2,7 @@ import { Hub } from './hub'; import { Scope } from './scope'; /** The type of a process stack layer. */ -export type LayerType = 'process' | 'domain' | 'local'; +export type LayerType = 'process' | 'local'; /** A layer in the process stack. */ export interface Layer { diff --git a/packages/node/src/hub.ts b/packages/node/src/hub.ts index 29d885b7c26b..814df3560ebb 100644 --- a/packages/node/src/hub.ts +++ b/packages/node/src/hub.ts @@ -15,7 +15,6 @@ declare module 'domain' { * TODO */ export function getGlobalHub(): Hub { - // const domain = require('domain'); const globalHub = getGlobalHubBase(); if (!domain.active) { return globalHub; diff --git a/packages/node/test/domain.test.ts b/packages/node/test/domain.test.ts new file mode 100644 index 000000000000..4e19963ddc92 --- /dev/null +++ b/packages/node/test/domain.test.ts @@ -0,0 +1,98 @@ +import { Layer } from '@sentry/hub'; +import * as domain from 'domain'; + +const mockGetGlobalHub = jest.fn(); + +class MockHub { + public constructor(public stack: Layer[] = []) { + MockHub.instance = this; + } + public static instance: MockHub; + + public getStack(): Layer[] { + return this.stack; + } +} + +const mockHub = MockHub; +jest.mock('@sentry/hub', () => ({ + Hub: mockHub, + getGlobalHub: mockGetGlobalHub, +})); + +import { getGlobalHub } from '../src'; + +describe('domains', () => { + let globalHub: MockHub; + + beforeEach(() => { + globalHub = new MockHub(); + mockGetGlobalHub.mockReturnValue(globalHub); + }); + + afterEach(() => { + if (domain.active) { + domain.active.exit(); + } + jest.resetAllMocks(); + }); + + test('without domain', () => { + expect(domain.active).toBeFalsy(); + const hub = getGlobalHub(); + expect(hub).toBe(globalHub); + }); + + test('domain hub inheritance', () => { + globalHub.stack = [{ type: 'process' }]; + const d = domain.create(); + d.run(() => { + const hub = getGlobalHub(); + expect(globalHub).not.toBe(hub); + expect(globalHub.getStack()).toEqual(hub.getStack()); + }); + }); + + test('domain hub isolation', () => { + const d = domain.create(); + d.run(() => { + const hub = getGlobalHub(); + hub.getStack().push({ type: 'process' }); + expect(hub.getStack()).toEqual([{ type: 'process' }]); + expect(globalHub.getStack()).toEqual([]); + }); + }); + + test('domain hub single instance', () => { + const d = domain.create(); + d.run(() => { + expect(getGlobalHub()).toBe(getGlobalHub()); + }); + }); + + test('concurrent domain hubs', done => { + const d1 = domain.create(); + const d2 = domain.create(); + + d1.run(() => { + getGlobalHub() + .getStack() + .push({ type: 'process' }); + + setTimeout(() => { + expect(getGlobalHub().getStack()).toEqual([{ type: 'process' }]); + }, 50); + }); + + d2.run(() => { + getGlobalHub() + .getStack() + .push({ type: 'local' }); + + setTimeout(() => { + expect(getGlobalHub().getStack()).toEqual([{ type: 'local' }]); + done(); + }, 100); + }); + }); +}); diff --git a/packages/node/test/hub.test.ts b/packages/node/test/hub.test.ts deleted file mode 100644 index e98dfb5250b3..000000000000 --- a/packages/node/test/hub.test.ts +++ /dev/null @@ -1,55 +0,0 @@ -import * as domain from 'domain'; - -const mockGetGlobalHub = jest.fn(); - -class Hub { - public constructor(public stack: any[] = []) { - Hub.instance = this; - } - public static instance: Hub; - - public getStack(): any[] { - return this.stack; - } -} - -const mockHub = Hub; -jest.mock('@sentry/hub', () => ({ - Hub: mockHub, - getGlobalHub: mockGetGlobalHub, -})); - -import { getGlobalHub } from '../src'; - -describe('getGlobalHub', () => { - let globalHub: Hub; - - beforeEach(() => { - globalHub = new Hub(); - mockGetGlobalHub.mockReturnValue(globalHub); - }); - - afterEach(() => { - if (domain.active) { - domain.active.exit(); - } - jest.resetAllMocks(); - }); - - test('without domain', () => { - expect(domain.active).toBeFalsy(); - const hub = getGlobalHub(); - expect(hub).toBe(globalHub); - }); - - test('inherit global hub', done => { - globalHub.stack = ['abc']; - const d = domain.create(); - d.run(() => { - const hub = getGlobalHub(); - expect(globalHub).not.toBe(hub); - expect(globalHub.getStack()).toEqual(hub.getStack()); - done(); - }); - }); -}); From fdb352f86f24ffe7c9d495294595bed6fc824daf Mon Sep 17 00:00:00 2001 From: HazA Date: Thu, 14 Jun 2018 16:49:35 +0200 Subject: [PATCH 04/10] feat: Add tests for unhandled promises --- packages/hub/src/global.ts | 8 ++- packages/minimal/test/lib/minimal.test.ts | 65 ++++++++----------- .../src/integrations/onunhandledrejection.ts | 7 +- packages/node/test/integrations.test.ts | 34 ---------- .../node/test/onunhandledrejection.test.ts | 35 ++++++++++ 5 files changed, 71 insertions(+), 78 deletions(-) delete mode 100644 packages/node/test/integrations.test.ts create mode 100644 packages/node/test/onunhandledrejection.test.ts diff --git a/packages/hub/src/global.ts b/packages/hub/src/global.ts index aa0a96e947fa..023394cfd0c0 100644 --- a/packages/hub/src/global.ts +++ b/packages/hub/src/global.ts @@ -1,5 +1,5 @@ -import { Carrier } from './interfaces'; import { Hub } from './hub'; +import { Carrier } from './interfaces'; /** * API compatibility version of this hub. @@ -42,7 +42,11 @@ export function getGlobalHub(): Hub { return registry.hub; } -/** TODO */ +/** + * This will create a new {@link Hub} and add to the passed object on + * __SENTRY__.hub. + * @param carrier object + */ export function getHubFromCarrier(carrier: any): Hub { if (carrier && carrier.__SENTRY__ && carrier.__SENTRY__.hub) { return carrier.__SENTRY__.hub; diff --git a/packages/minimal/test/lib/minimal.test.ts b/packages/minimal/test/lib/minimal.test.ts index 6b3446ae6965..c64166ecd9c5 100644 --- a/packages/minimal/test/lib/minimal.test.ts +++ b/packages/minimal/test/lib/minimal.test.ts @@ -1,4 +1,4 @@ -import { Hub, hubFromCarrier, Scope } from '@sentry/hub'; +import { Hub, getHubFromCarrier, getGlobalHub, Scope } from '@sentry/hub'; import { _callOnClient, addBreadcrumb, @@ -24,8 +24,7 @@ describe('Minimal', () => { const client = { captureException: jest.fn(async () => Promise.resolve()), }; - const hub = Hub.getGlobal(); - hub.withScope(client, () => { + getGlobalHub().withScope(client, () => { const e = new Error('test exception'); captureException(e); expect(client.captureException.mock.calls[0][0]).toBe(e); @@ -34,8 +33,7 @@ describe('Minimal', () => { test('Message', () => { const client = { captureMessage: jest.fn(async () => Promise.resolve()) }; - const hub = Hub.getGlobal(); - hub.withScope(client, () => { + getGlobalHub().withScope(client, () => { const message = 'yo'; captureMessage(message); expect(client.captureMessage.mock.calls[0][0]).toBe(message); @@ -44,8 +42,7 @@ describe('Minimal', () => { test('Event', () => { const client = { captureEvent: jest.fn(async () => Promise.resolve()) }; - const hub = Hub.getGlobal(); - hub.withScope(client, () => { + getGlobalHub().withScope(client, () => { const e = { message: 'test' }; captureEvent(e); expect(client.captureEvent.mock.calls[0][0]).toBe(e); @@ -56,28 +53,26 @@ describe('Minimal', () => { describe('configureScope', () => { test('User Context', () => { const client = new TestClient({}); - const hub = Hub.getGlobal(); - hub.pushScope(client); + getGlobalHub().pushScope(client); configureScope((scope: Scope) => { scope.setUser({ id: '1234' }); }); - expect(global.__SENTRY__.hub.stack[1].scope.user).toEqual({ + expect(global.__SENTRY__.getGlobalHub().stack[1].scope.user).toEqual({ id: '1234', }); - hub.popScope(); + getGlobalHub().popScope(); }); test('Extra Context', () => { const client = new TestClient({}); - const hub = Hub.getGlobal(); - hub.pushScope(client); + getGlobalHub().pushScope(client); configureScope((scope: Scope) => { scope.setExtra('id', '1234'); }); - expect(global.__SENTRY__.hub.stack[1].scope.extra).toEqual({ + expect(global.__SENTRY__.getGlobalHub().stack[1].scope.extra).toEqual({ id: '1234', }); - hub.popScope(); + getGlobalHub().popScope(); }); test('Tags Context', () => { @@ -85,19 +80,18 @@ describe('Minimal', () => { configureScope((scope: Scope) => { scope.setTag('id', '1234'); }); - expect(global.__SENTRY__.hub.stack[0].scope.tags).toEqual({ + expect(global.__SENTRY__.getGlobalHub().stack[0].scope.tags).toEqual({ id: '1234', }); }); test('Fingerprint', () => { const client = new TestClient({}); - const hub = Hub.getGlobal(); - hub.pushScope(client); + getGlobalHub().pushScope(client); configureScope((scope: Scope) => { scope.setFingerprint(['abcd']); }); - expect(global.__SENTRY__.hub.stack[1].scope.fingerprint).toEqual([ + expect(global.__SENTRY__.getGlobalHub().stack[1].scope.fingerprint).toEqual([ 'abcd', ]); }); @@ -105,19 +99,18 @@ describe('Minimal', () => { test('Clear Scope', () => { const client = new TestClient({}); - const hub = Hub.getGlobal(); - hub.withScope(client, () => { - expect(global.__SENTRY__.hub.stack.length).toBe(2); + getGlobalHub().withScope(client, () => { + expect(global.__SENTRY__.getGlobalHub().stack.length).toBe(2); configureScope((scope: Scope) => { scope.setUser({ id: '1234' }); }); - expect(global.__SENTRY__.hub.stack[1].scope.user).toEqual({ + expect(global.__SENTRY__.getGlobalHub().stack[1].scope.user).toEqual({ id: '1234', }); configureScope((scope: Scope) => { scope.clear(); }); - expect(global.__SENTRY__.hub.stack[1].scope.user).toEqual({}); + expect(global.__SENTRY__.getGlobalHub().stack[1].scope.user).toEqual({}); }); }); @@ -125,13 +118,12 @@ describe('Minimal', () => { const client = { addBreadcrumb: jest.fn(), }; - const hub = Hub.getGlobal(); - hub.pushScope(client); + getGlobalHub().pushScope(client); addBreadcrumb({ message: 'world' }); expect(client.addBreadcrumb.mock.calls[0][0]).toEqual({ message: 'world', }); - hub.popScope(); + getGlobalHub().popScope(); }); test('returns undefined before binding a client', () => { @@ -145,8 +137,7 @@ describe('Minimal', () => { test('Calls function on the client', done => { const s = jest.spyOn(TestClient.prototype, 'mySecretPublicMethod'); - const hub = Hub.getGlobal(); - hub.withScope(new TestClient({}), () => { + getGlobalHub().withScope(new TestClient({}), () => { _callOnClient('mySecretPublicMethod', 'test'); expect(s.mock.calls[0][0]).toBe('test'); s.mockRestore(); @@ -157,8 +148,7 @@ describe('Minimal', () => { test('does not throw an error when pushing different clients', () => { init({}); expect(() => { - const hub = Hub.getGlobal(); - hub.withScope(new TestClient2(), () => { + getGlobalHub().withScope(new TestClient2(), () => { // }); }).not.toThrow(); @@ -167,8 +157,7 @@ describe('Minimal', () => { test('does not throw an error when pushing same clients', () => { init({}); expect(() => { - const hub = Hub.getGlobal(); - hub.withScope(new TestClient({}), () => { + getGlobalHub().withScope(new TestClient({}), () => { // }); }).not.toThrow(); @@ -178,18 +167,18 @@ describe('Minimal', () => { const iAmSomeGlobalVarTheUserHasToManage = { state: {}, }; - const hub = hubFromCarrier(iAmSomeGlobalVarTheUserHasToManage.state); - hub.pushScope(new TestClient({})); - hub.configureScope((scope: Scope) => { + const hub = getHubFromCarrier, getGlobalHub(iAmSomeGlobalVarTheUserHasToManage.state); + getGlobalHub().pushScope(new TestClient({})); + getGlobalHub().configureScope((scope: Scope) => { scope.setUser({ id: '1234' }); }); expect( ((iAmSomeGlobalVarTheUserHasToManage.state as any).__SENTRY__.hub .stack[1] as any).scope.user, ).toEqual({ id: '1234' }); - hub.popScope(); + getGlobalHub().popScope(); expect( - (iAmSomeGlobalVarTheUserHasToManage.state as any).__SENTRY__.hub.stack[1], + (iAmSomeGlobalVarTheUserHasToManage.state as any).__SENTRY__.getGlobalHub().stack[1], ).toBeUndefined(); }); }); diff --git a/packages/node/src/integrations/onunhandledrejection.ts b/packages/node/src/integrations/onunhandledrejection.ts index 18195b1f474b..a9ca4f9a819d 100644 --- a/packages/node/src/integrations/onunhandledrejection.ts +++ b/packages/node/src/integrations/onunhandledrejection.ts @@ -24,9 +24,8 @@ export class OnUnhandledRejection implements Integration { */ public sendUnhandledPromise(reason: any, promise: any): void { const context = (promise.domain && promise.domain.sentryContext) || {}; - const hub = getGlobalHub(); - hub.withScope(() => { - hub.configureScope(scope => { + getGlobalHub().withScope(() => { + getGlobalHub().configureScope(scope => { // Preserve backwards compatibility with raven-node for now if (context.user) { scope.setUser(context.user); @@ -43,7 +42,7 @@ export class OnUnhandledRejection implements Integration { } scope.setExtra('unhandledPromiseRejection', true); }); - hub.captureException(reason); + getGlobalHub().captureException(reason); }); } } diff --git a/packages/node/test/integrations.test.ts b/packages/node/test/integrations.test.ts deleted file mode 100644 index fe568fe78443..000000000000 --- a/packages/node/test/integrations.test.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { init, Integrations } from '../src'; -// import { Hub } from '../src/hub'; - -// Hub.getGlobal = jest.fn(jest.fn); -// jest.mock('../src/hub'); - -const dsn = 'https://53039209a22b4ec1bcc296a3c9fdecd6@sentry.io/4291'; - -describe('integrations', () => { - // test('unhandled promise listener installed', () => { - // init({ dsn }); - // expect(process.listeners('unhandledRejection')).toHaveLength(1); - // }); - - test('sendUnhandledPromise', () => { - init({ dsn }); - const int = new Integrations.OnUnhandledRejection(); - const promise = { - domain: { - sentryContext: { - extra: { tag: '1' }, - tags: { extra: 1 }, - user: { id: 1 }, - }, - }, - }; - // const mockSoundPlayerInstance = Hub.mock.instances[0]; - // console.log(Hub); - // console.log(mockSoundPlayerInstance); - int.sendUnhandledPromise(new Error('bla'), promise); - - // console.log(global.__SENTRY__.hub.stack[0]); - }); -}); diff --git a/packages/node/test/onunhandledrejection.test.ts b/packages/node/test/onunhandledrejection.test.ts new file mode 100644 index 000000000000..4f95b2ad2293 --- /dev/null +++ b/packages/node/test/onunhandledrejection.test.ts @@ -0,0 +1,35 @@ +import { Hub, init, Integrations, Scope } from '../src'; + +const dsn = 'https://53039209a22b4ec1bcc296a3c9fdecd6@sentry.io/4291'; + +describe('unhandled promises', () => { + test('install global listener', () => { + init({ dsn }); + expect(process.listeners('unhandledRejection')).toHaveLength(1); + }); + + test('sendUnhandledPromise', () => { + const integration = new Integrations.OnUnhandledRejection(); + const promise = { + domain: { + sentryContext: { + extra: { extra: '1' }, + tags: { tag: '2' }, + user: { id: 1 }, + }, + }, + }; + + const captureException = jest.spyOn(Hub.prototype, 'captureException'); + const setUser = jest.spyOn(Scope.prototype, 'setUser'); + const setExtra = jest.spyOn(Scope.prototype, 'setExtra'); + const setTag = jest.spyOn(Scope.prototype, 'setTag'); + + integration.sendUnhandledPromise('bla', promise); + + expect(captureException.mock.calls[0][0]).toBe('bla'); + expect(setUser.mock.calls[0][0]).toEqual({ id: 1 }); + expect(setExtra.mock.calls[0]).toEqual(['extra', '1']); + expect(setTag.mock.calls[0]).toEqual(['tag', '2']); + }); +}); From fc944813ff06cbd52127e9e0d265c8cb1db20d00 Mon Sep 17 00:00:00 2001 From: HazA Date: Thu, 14 Jun 2018 16:54:31 +0200 Subject: [PATCH 05/10] fix: Browser tests --- packages/browser/test/index.test.ts | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/browser/test/index.test.ts b/packages/browser/test/index.test.ts index 5ce74935c411..39215411a497 100644 --- a/packages/browser/test/index.test.ts +++ b/packages/browser/test/index.test.ts @@ -9,7 +9,7 @@ import { captureException, captureMessage, configureScope, - Hub, + getGlobalHub, init, Scope, SentryEvent, @@ -25,11 +25,11 @@ describe('SentryBrowser', () => { }); beforeEach(() => { - Hub.getGlobal().pushScope(); + getGlobalHub().pushScope(); }); afterEach(() => { - Hub.getGlobal().popScope(); + getGlobalHub().popScope(); }); describe('getContext() / setContext()', () => { @@ -75,7 +75,7 @@ describe('SentryBrowser', () => { }); it('should record auto breadcrumbs', done => { - Hub.getGlobal().pushScope( + getGlobalHub().pushScope( new BrowserClient({ afterSend: (event: SentryEvent) => { expect(event.breadcrumbs!).to.have.lengthOf(3); @@ -97,7 +97,7 @@ describe('SentryBrowser', () => { addBreadcrumb({ message: 'test2' }); captureMessage('event'); - Hub.getGlobal().popScope(); + getGlobalHub().popScope(); }); }); @@ -115,7 +115,7 @@ describe('SentryBrowser', () => { }); it('should capture an exception', done => { - Hub.getGlobal().pushScope( + getGlobalHub().pushScope( new BrowserClient({ afterSend: (event: SentryEvent) => { expect(event.exception).to.not.be.undefined; @@ -133,11 +133,11 @@ describe('SentryBrowser', () => { } catch (e) { captureException(e); } - Hub.getGlobal().popScope(); + getGlobalHub().popScope(); }); it('should capture a message', done => { - Hub.getGlobal().pushScope( + getGlobalHub().pushScope( new BrowserClient({ afterSend: (event: SentryEvent) => { expect(event.message).to.equal('test'); @@ -148,11 +148,11 @@ describe('SentryBrowser', () => { }), ); captureMessage('test'); - Hub.getGlobal().popScope(); + getGlobalHub().popScope(); }); it('should capture an event', done => { - Hub.getGlobal().pushScope( + getGlobalHub().pushScope( new BrowserClient({ afterSend: (event: SentryEvent) => { expect(event.message).to.equal('test'); @@ -163,7 +163,7 @@ describe('SentryBrowser', () => { }), ); captureEvent({ message: 'test' }); - Hub.getGlobal().popScope(); + getGlobalHub().popScope(); }); }); }); From 68791687fef075ef028e8132f8101fd3fe7e8acc Mon Sep 17 00:00:00 2001 From: HazA Date: Thu, 14 Jun 2018 17:27:02 +0200 Subject: [PATCH 06/10] fix: tests --- packages/browser/src/index.ts | 2 +- packages/minimal/test/lib/minimal.test.ts | 24 +++++++++++------------ packages/node/test/index.test.ts | 1 - 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/packages/browser/src/index.ts b/packages/browser/src/index.ts index 2e8bdcd7eaf1..1041bfa140a0 100644 --- a/packages/browser/src/index.ts +++ b/packages/browser/src/index.ts @@ -19,7 +19,7 @@ export { configureScope, } from '@sentry/minimal'; -export { Hub, Scope } from '@sentry/hub'; +export { getGlobalHub, Hub, Scope } from '@sentry/hub'; export { BrowserBackend, BrowserOptions } from './backend'; export { BrowserClient } from './client'; diff --git a/packages/minimal/test/lib/minimal.test.ts b/packages/minimal/test/lib/minimal.test.ts index c64166ecd9c5..c83daa2f189a 100644 --- a/packages/minimal/test/lib/minimal.test.ts +++ b/packages/minimal/test/lib/minimal.test.ts @@ -57,7 +57,7 @@ describe('Minimal', () => { configureScope((scope: Scope) => { scope.setUser({ id: '1234' }); }); - expect(global.__SENTRY__.getGlobalHub().stack[1].scope.user).toEqual({ + expect(global.__SENTRY__.hub.stack[1].scope.user).toEqual({ id: '1234', }); getGlobalHub().popScope(); @@ -69,7 +69,7 @@ describe('Minimal', () => { configureScope((scope: Scope) => { scope.setExtra('id', '1234'); }); - expect(global.__SENTRY__.getGlobalHub().stack[1].scope.extra).toEqual({ + expect(global.__SENTRY__.hub.stack[1].scope.extra).toEqual({ id: '1234', }); getGlobalHub().popScope(); @@ -80,7 +80,7 @@ describe('Minimal', () => { configureScope((scope: Scope) => { scope.setTag('id', '1234'); }); - expect(global.__SENTRY__.getGlobalHub().stack[0].scope.tags).toEqual({ + expect(global.__SENTRY__.hub.stack[0].scope.tags).toEqual({ id: '1234', }); }); @@ -91,7 +91,7 @@ describe('Minimal', () => { configureScope((scope: Scope) => { scope.setFingerprint(['abcd']); }); - expect(global.__SENTRY__.getGlobalHub().stack[1].scope.fingerprint).toEqual([ + expect(global.__SENTRY__.hub.stack[1].scope.fingerprint).toEqual([ 'abcd', ]); }); @@ -100,17 +100,17 @@ describe('Minimal', () => { test('Clear Scope', () => { const client = new TestClient({}); getGlobalHub().withScope(client, () => { - expect(global.__SENTRY__.getGlobalHub().stack.length).toBe(2); + expect(global.__SENTRY__.hub.stack.length).toBe(2); configureScope((scope: Scope) => { scope.setUser({ id: '1234' }); }); - expect(global.__SENTRY__.getGlobalHub().stack[1].scope.user).toEqual({ + expect(global.__SENTRY__.hub.stack[1].scope.user).toEqual({ id: '1234', }); configureScope((scope: Scope) => { scope.clear(); }); - expect(global.__SENTRY__.getGlobalHub().stack[1].scope.user).toEqual({}); + expect(global.__SENTRY__.hub.stack[1].scope.user).toEqual({}); }); }); @@ -167,18 +167,18 @@ describe('Minimal', () => { const iAmSomeGlobalVarTheUserHasToManage = { state: {}, }; - const hub = getHubFromCarrier, getGlobalHub(iAmSomeGlobalVarTheUserHasToManage.state); - getGlobalHub().pushScope(new TestClient({})); - getGlobalHub().configureScope((scope: Scope) => { + const hub = getHubFromCarrier(iAmSomeGlobalVarTheUserHasToManage.state); + hub.pushScope(new TestClient({})); + hub.configureScope((scope: Scope) => { scope.setUser({ id: '1234' }); }); expect( ((iAmSomeGlobalVarTheUserHasToManage.state as any).__SENTRY__.hub .stack[1] as any).scope.user, ).toEqual({ id: '1234' }); - getGlobalHub().popScope(); + hub.popScope(); expect( - (iAmSomeGlobalVarTheUserHasToManage.state as any).__SENTRY__.getGlobalHub().stack[1], + (iAmSomeGlobalVarTheUserHasToManage.state as any).__SENTRY__.hub.stack[1], ).toBeUndefined(); }); }); diff --git a/packages/node/test/index.test.ts b/packages/node/test/index.test.ts index 4811d02388a6..fabcde80593d 100644 --- a/packages/node/test/index.test.ts +++ b/packages/node/test/index.test.ts @@ -199,5 +199,4 @@ describe('SentryNode', () => { }); }); }); - }); From bc8d68cee9504730455bd6406b8eb34e9ae34e5c Mon Sep 17 00:00:00 2001 From: HazA Date: Fri, 15 Jun 2018 10:11:15 +0200 Subject: [PATCH 07/10] feat: Add tests for hub --- packages/hub/src/hub.ts | 4 +- packages/hub/src/scope.ts | 9 +- packages/hub/test/lib/global.test.ts | 22 +++ packages/hub/test/lib/hub.test.ts | 243 +++++++++++++++++++++++++++ packages/hub/test/lib/scope.test.ts | 62 +++++++ 5 files changed, 334 insertions(+), 6 deletions(-) create mode 100644 packages/hub/test/lib/global.test.ts create mode 100644 packages/hub/test/lib/hub.test.ts diff --git a/packages/hub/src/hub.ts b/packages/hub/src/hub.ts index cc1c42feb727..88102bfd5639 100644 --- a/packages/hub/src/hub.ts +++ b/packages/hub/src/hub.ts @@ -15,7 +15,7 @@ export class Hub { */ public constructor( private readonly stack: Layer[] = [], - public readonly version: number = API_VERSION, + private readonly version: number = API_VERSION, ) { if (stack.length === 0) { this.stack.push({ scope: this.createScope(), type: 'process' }); @@ -72,7 +72,7 @@ export class Hub { */ public pushScope(client?: any): void { const usedClient = client || this.getCurrentClient(); - // We want to clone the last scope and not create a new one + // We want to clone the content of prev scope const stack = this.getStack(); const parentScope = stack.length > 0 ? stack[stack.length - 1].scope : undefined; diff --git a/packages/hub/src/scope.ts b/packages/hub/src/scope.ts index bacd9e3acc6e..d4065f06d33a 100644 --- a/packages/hub/src/scope.ts +++ b/packages/hub/src/scope.ts @@ -141,8 +141,8 @@ export class Scope { /** * Sets the breadcrumbs in the scope - * @param breadcrumbs - * @param maxBreadcrumbs + * @param breadcrumbs Breadcrumb + * @param maxBreadcrumbs number of max breadcrumbs to merged into event. */ public addBreadcrumb(breadcrumb: Breadcrumb, maxBreadcrumbs?: number): void { this.breadcrumbs = @@ -155,8 +155,9 @@ export class Scope { /** * Applies the current context and fingerprint to the event. * Note that breadcrumbs will be added by the client. - * @param event - * @param maxBreadcrumbs + * Also if the event has already breadcrumbs on it, we do not merge them. + * @param event SentryEvent + * @param maxBreadcrumbs number of max breadcrumbs to merged into event. */ public applyToEvent(event: SentryEvent, maxBreadcrumbs?: number): void { if (this.extra && Object.keys(this.extra).length) { diff --git a/packages/hub/test/lib/global.test.ts b/packages/hub/test/lib/global.test.ts new file mode 100644 index 000000000000..c325816bbe7c --- /dev/null +++ b/packages/hub/test/lib/global.test.ts @@ -0,0 +1,22 @@ +import { getGlobalHub, getHubFromCarrier, Hub } from '../../src'; + +describe('global', () => { + test('getGlobalHub', () => { + expect(getGlobalHub()).toBeTruthy(); + expect((global as any).__SENTRY__.hub).toBeTruthy(); + }); + + test('getHubFromCarrier', () => { + const bla = { a: 'b' }; + getHubFromCarrier(bla); + expect((bla as any).__SENTRY__.hub).toBeTruthy(); + expect((bla as any).__SENTRY__.hub).toBe((bla as any).__SENTRY__.hub); + getHubFromCarrier(bla); + }); + + test('getGlobalHub', () => { + const newestHub = new Hub([], 999999); + (global as any).__SENTRY__.hub = newestHub; + expect(getGlobalHub()).toBe(newestHub); + }); +}); diff --git a/packages/hub/test/lib/hub.test.ts b/packages/hub/test/lib/hub.test.ts new file mode 100644 index 000000000000..7e369155d61b --- /dev/null +++ b/packages/hub/test/lib/hub.test.ts @@ -0,0 +1,243 @@ +import { Breadcrumb, SentryEvent } from '@sentry/types'; +import { Hub, Layer, Scope } from '../../src'; + +const clientFn = jest.fn(); +// const asyncClientFn = jest.fn(async () => Promise.resolve({})); +// const asyncClientFn = async () => jest.fn(); +const asyncClientFn = async () => Promise.reject('error'); +const scope = new Scope(); + +const filledLayer: Layer = { + client: { + asyncClientFn, + clientFn, + }, + scope, + type: 'local', +}; + +describe('Hub', () => { + afterEach(() => { + jest.resetAllMocks(); + }); + + test('push process into stack', () => { + const hub = new Hub(); + expect(hub.getStack()).toHaveLength(1); + }); + + test('pass in filled layer', () => { + const hub = new Hub([filledLayer]); + expect(hub.getStack()).toHaveLength(1); + }); + + test('invoke client sync', () => { + const hub = new Hub([filledLayer]); + hub._invokeClient('clientFn', true); + expect(clientFn).toHaveBeenCalled(); + expect(clientFn.mock.calls[0][0]).toBe(true); + expect(clientFn.mock.calls[0][1]).toBe(scope); + }); + + test("don't invoke client sync with wrong func", () => { + const hub = new Hub([filledLayer]); + hub._invokeClient('funca', true); + expect(clientFn).not.toHaveBeenCalled(); + }); + + test('invoke client async catch error in case', done => { + // @ts-ignore + global.console = { error: jest.fn() }; + const hub = new Hub([filledLayer]); + (hub as any).invokeClientAsync('asyncClientFn', true); + setTimeout(() => { + // tslint:disable-next-line + expect(console.error).toHaveBeenCalled(); + done(); + }); + }); + + test('isOlderThan', () => { + const hub = new Hub(); + expect(hub.isOlderThan(0)).toBeFalsy(); + }); + + test('pushScope', () => { + const localScope = new Scope(); + localScope.setExtra('a', 'b'); + const hub = new Hub([ + { + scope: localScope, + type: 'local', + }, + ]); + hub.pushScope(); + expect(hub.getStack()).toHaveLength(2); + expect(hub.getStack()[1].scope).not.toBe(localScope); + expect((hub.getStack()[1].scope as Scope).getExtra()).toEqual({ a: 'b' }); + }); + + test('pushScope inherit client', () => { + const testClient = { bla: 'a' }; + const hub = new Hub([ + { + client: testClient, + type: 'local', + }, + ]); + hub.pushScope(); + expect(hub.getStack()).toHaveLength(2); + expect(hub.getStack()[1].client).toBe(testClient); + }); + + test('pushScope with client', () => { + const testClient = { bla: 'a' }; + const hub = new Hub([ + { + client: testClient, + type: 'local', + }, + ]); + const ndClient = { foo: 'bar' }; + hub.pushScope(ndClient); + expect(hub.getStack()).toHaveLength(2); + expect(hub.getStack()[0].client).toBe(testClient); + expect(hub.getStack()[1].client).toBe(ndClient); + }); + + test('popScope', () => { + const hub = new Hub(); + hub.pushScope(); + expect(hub.getStack()).toHaveLength(2); + hub.popScope(); + expect(hub.getStack()).toHaveLength(1); + }); + + test('withScope', () => { + const hub = new Hub(); + hub.withScope(() => { + expect(hub.getStack()).toHaveLength(2); + }); + expect(hub.getStack()).toHaveLength(1); + }); + + test('withScope with client', () => { + const hub = new Hub(); + const testClient = { bla: 'a' }; + hub.withScope(() => { + expect(hub.getStack()).toHaveLength(2); + expect(hub.getStack()[1].client).toBe(testClient); + }, testClient); + expect(hub.getStack()).toHaveLength(1); + + hub.withScope(testClient, () => { + expect(hub.getStack()).toHaveLength(2); + expect(hub.getStack()[1].client).toBe(testClient); + }); + expect(hub.getStack()).toHaveLength(1); + }); + + test('getCurrentClient', () => { + const testClient = { bla: 'a' }; + const hub = new Hub([ + { + client: testClient, + type: 'local', + }, + ]); + expect(hub.getCurrentClient()).toBe(testClient); + }); + + test('getStack', () => { + const testLayer: Layer[] = [ + { + client: { a: 'b' }, + type: 'local', + }, + ]; + const hub = new Hub(testLayer); + expect(hub.getStack()).toBe(testLayer); + }); + + test('getStackTop', () => { + const testClient = { bla: 'a' }; + const hub = new Hub(); + hub.pushScope(); + hub.pushScope(testClient); + expect(hub.getStackTop().client).toEqual({ bla: 'a' }); + }); + + test('createScope', () => { + const hub = new Hub(); + expect(hub.createScope()).toEqual(new Scope()); + }); + + test('createScope with parentScope', () => { + const hub = new Hub(); + const parentScope = new Scope(); + parentScope.setExtra('a', 'b'); + expect(hub.createScope(parentScope).getExtra()).toEqual({ a: 'b' }); + }); + + test('captureException', () => { + const hub = new Hub(); + const spy = jest.spyOn(hub as any, 'invokeClientAsync'); + hub.captureException('a'); + expect(spy).toHaveBeenCalled(); + expect(spy.mock.calls[0][0]).toBe('captureException'); + expect(spy.mock.calls[0][1]).toBe('a'); + }); + + test('captureMessage', () => { + const hub = new Hub(); + const spy = jest.spyOn(hub as any, 'invokeClientAsync'); + hub.captureMessage('a'); + expect(spy).toHaveBeenCalled(); + expect(spy.mock.calls[0][0]).toBe('captureMessage'); + expect(spy.mock.calls[0][1]).toBe('a'); + }); + + test('captureEvent', () => { + const event: SentryEvent = { + extra: { b: 3 }, + }; + const hub = new Hub(); + const spy = jest.spyOn(hub as any, 'invokeClientAsync'); + hub.captureEvent(event); + expect(spy).toHaveBeenCalled(); + expect(spy.mock.calls[0][0]).toBe('captureEvent'); + expect(spy.mock.calls[0][1]).toBe(event); + }); + + test('addBreadcrumb', () => { + const hub = new Hub(); + const spy = jest.spyOn(hub as any, '_invokeClient'); + hub.addBreadcrumb({ message: 'test' }); + expect(spy).toHaveBeenCalled(); + expect(spy.mock.calls[0][0]).toBe('addBreadcrumb'); + expect(spy.mock.calls[0][1]).toEqual({ message: 'test' }); + }); + + test('configureScope', () => { + const hub = new Hub(); + hub.configureScope(_ => { + expect(true).toBeFalsy(); + }); + }); + + test('configureScope', () => { + const localScope = new Scope(); + localScope.setExtra('a', 'b'); + const testLayer: Layer[] = [ + { + client: { a: 'b' }, + scope: localScope, + type: 'local', + }, + ]; + const hub = new Hub(testLayer); + hub.configureScope(confScope => { + expect(confScope.getExtra()).toEqual({ a: 'b' }); + }); + }); +}); diff --git a/packages/hub/test/lib/scope.test.ts b/packages/hub/test/lib/scope.test.ts index d4a446269a24..0b0954847693 100644 --- a/packages/hub/test/lib/scope.test.ts +++ b/packages/hub/test/lib/scope.test.ts @@ -1,3 +1,4 @@ +import { SentryEvent } from '@sentry/types'; import { Scope } from '../../src'; describe('Scope', () => { @@ -59,4 +60,65 @@ describe('Scope', () => { expect(parentScope.getExtra()).toEqual({ a: 1 }); expect(scope.getExtra()).toEqual({ a: 2 }); }); + + test('listeners', () => { + jest.useFakeTimers(); + const scope = new Scope(); + const listener = jest.fn(); + scope.addScopeListener(listener); + scope.setExtra('a', 2); + jest.runAllTimers(); + expect(listener).toHaveBeenCalled(); + expect(listener.mock.calls[0][0].extra).toEqual({ a: 2 }); + }); + + test('applyToEvent', () => { + const scope = new Scope(); + scope.setExtra('a', 2); + scope.setTag('a', 'b'); + scope.setUser({ id: '1' }); + scope.setFingerprint(['abcd']); + scope.addBreadcrumb({ message: 'test' }, 100); + const event: SentryEvent = {}; + scope.applyToEvent(event); + expect(event.extra).toEqual({ a: 2 }); + expect(event.tags).toEqual({ a: 'b' }); + expect(event.user).toEqual({ id: '1' }); + expect(event.fingerprint).toEqual(['abcd']); + expect(event.breadcrumbs).toEqual([{ message: 'test' }]); + }); + + test('applyToEvent merge', () => { + const scope = new Scope(); + scope.setExtra('a', 2); + scope.setTag('a', 'b'); + scope.setUser({ id: '1' }); + scope.setFingerprint(['abcd']); + scope.addBreadcrumb({ message: 'test' }, 100); + const event: SentryEvent = { + breadcrumbs: [{ message: 'test2' }], + extra: { b: 3 }, + fingerprint: ['efgh'], + tags: { b: 'c' }, + user: { id: '3' }, + }; + scope.applyToEvent(event); + expect(event.extra).toEqual({ a: 2, b: 3 }); + expect(event.tags).toEqual({ a: 'b', b: 'c' }); + expect(event.user).toEqual({ id: '3' }); + expect(event.fingerprint).toEqual(['efgh']); + expect(event.breadcrumbs).toEqual([{ message: 'test2' }]); + }); + + test('clear', () => { + const scope = new Scope(); + scope.setExtra('a', 2); + scope.setTag('a', 'b'); + scope.setUser({ id: '1' }); + scope.setFingerprint(['abcd']); + scope.addBreadcrumb({ message: 'test' }, 100); + expect(scope.getExtra()).toEqual({ a: 2 }); + scope.clear(); + expect(scope.getExtra()).toEqual({}); + }); }); From 3d8dc4f9dbbf70fab49a6da993d406d4c28e11cc Mon Sep 17 00:00:00 2001 From: HazA Date: Fri, 15 Jun 2018 11:06:42 +0200 Subject: [PATCH 08/10] ref: Minor code changes --- packages/hub/test/lib/hub.test.ts | 2 +- packages/minimal/test/lib/minimal.test.ts | 2 +- packages/minimal/tsconfig.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/hub/test/lib/hub.test.ts b/packages/hub/test/lib/hub.test.ts index 7e369155d61b..c418a93f60c7 100644 --- a/packages/hub/test/lib/hub.test.ts +++ b/packages/hub/test/lib/hub.test.ts @@ -1,4 +1,4 @@ -import { Breadcrumb, SentryEvent } from '@sentry/types'; +import { SentryEvent } from '@sentry/types'; import { Hub, Layer, Scope } from '../../src'; const clientFn = jest.fn(); diff --git a/packages/minimal/test/lib/minimal.test.ts b/packages/minimal/test/lib/minimal.test.ts index c83daa2f189a..f1ab975fca4f 100644 --- a/packages/minimal/test/lib/minimal.test.ts +++ b/packages/minimal/test/lib/minimal.test.ts @@ -1,4 +1,4 @@ -import { Hub, getHubFromCarrier, getGlobalHub, Scope } from '@sentry/hub'; +import { getHubFromCarrier, getGlobalHub, Scope } from '@sentry/hub'; import { _callOnClient, addBreadcrumb, diff --git a/packages/minimal/tsconfig.json b/packages/minimal/tsconfig.json index 3f9fafbd5678..d7fb0f4c8a45 100644 --- a/packages/minimal/tsconfig.json +++ b/packages/minimal/tsconfig.json @@ -4,7 +4,7 @@ "exclude": ["dist"], "compilerOptions": { "rootDir": ".", - "types": ["node"], + "types": ["node", "jest"], "plugins": [{ "name": "tslint-language-service" }] } } From 7b91f24fe24307d0483d25e02d5a2eb7e74db663 Mon Sep 17 00:00:00 2001 From: HazA Date: Fri, 15 Jun 2018 11:14:51 +0200 Subject: [PATCH 09/10] ref: Only clone top stack --- packages/node/src/hub.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/node/src/hub.ts b/packages/node/src/hub.ts index 814df3560ebb..ad72e6cc04d5 100644 --- a/packages/node/src/hub.ts +++ b/packages/node/src/hub.ts @@ -12,7 +12,11 @@ declare module 'domain' { } /** - * TODO + * Returns the latest global hum instance. + * + * If a hub is already registered in the global carrier but this module + * contains a more recent version, it replaces the registered version. + * Otherwise, the currently registered hub will be returned. */ export function getGlobalHub(): Hub { const globalHub = getGlobalHubBase(); @@ -26,7 +30,7 @@ export function getGlobalHub(): Hub { } if (!carrier.hub) { - carrier.hub = new Hub(globalHub.getStack().slice()); + carrier.hub = new Hub([globalHub.getStackTop()]); } return carrier.hub; From 2b2e87e507a16fd06828ee84cbd7dd0e8b1498d8 Mon Sep 17 00:00:00 2001 From: HazA Date: Fri, 15 Jun 2018 11:23:21 +0200 Subject: [PATCH 10/10] fix: Node hub --- packages/node/src/hub.ts | 4 +++- packages/node/test/domain.test.ts | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/node/src/hub.ts b/packages/node/src/hub.ts index ad72e6cc04d5..53fce5b62819 100644 --- a/packages/node/src/hub.ts +++ b/packages/node/src/hub.ts @@ -30,7 +30,9 @@ export function getGlobalHub(): Hub { } if (!carrier.hub) { - carrier.hub = new Hub([globalHub.getStackTop()]); + carrier.hub = new Hub( + globalHub.getStackTop() ? [globalHub.getStackTop()] : [], + ); } return carrier.hub; diff --git a/packages/node/test/domain.test.ts b/packages/node/test/domain.test.ts index 4e19963ddc92..ad32450e4d1c 100644 --- a/packages/node/test/domain.test.ts +++ b/packages/node/test/domain.test.ts @@ -12,6 +12,10 @@ class MockHub { public getStack(): Layer[] { return this.stack; } + + public getStackTop(): Layer { + return this.stack[this.stack.length - 1]; + } } const mockHub = MockHub;