diff --git a/packages/core/src/client.ts b/packages/core/src/client.ts index c3ff126732f8..53e0328965a4 100644 --- a/packages/core/src/client.ts +++ b/packages/core/src/client.ts @@ -1037,16 +1037,18 @@ export abstract class Client { /** Updates existing session based on the provided event */ protected _updateSessionFromEvent(session: Session, event: Event): void { + // initially, set `crashed` based on the event level and update from exceptions if there are any later on let crashed = event.level === 'fatal'; let errored = false; const exceptions = event.exception?.values; if (exceptions) { errored = true; + // reset crashed to false if there are exceptions, to ensure `mechanism.handled` is respected. + crashed = false; for (const ex of exceptions) { - const mechanism = ex.mechanism; - if (mechanism?.handled === false) { + if (ex.mechanism?.handled === false) { crashed = true; break; } diff --git a/packages/core/test/lib/client.test.ts b/packages/core/test/lib/client.test.ts index db25793ccf7b..acb4197cf4cf 100644 --- a/packages/core/test/lib/client.test.ts +++ b/packages/core/test/lib/client.test.ts @@ -1,4 +1,5 @@ import { afterEach, beforeEach, describe, expect, it, test, vi } from 'vitest'; +import type { SeverityLevel } from '../../src'; import { addBreadcrumb, dsnToString, @@ -2308,6 +2309,108 @@ describe('Client', () => { }); }); + describe('_updateSessionFromEvent()', () => { + describe('event has no exceptions', () => { + it('sets status to crashed if level is fatal', () => { + const client = new TestClient(getDefaultTestClientOptions()); + const session = makeSession(); + getCurrentScope().setSession(session); + + client.captureEvent({ message: 'test', level: 'fatal' }); + + const updatedSession = client.session; + + expect(updatedSession).toMatchObject({ + duration: expect.any(Number), + errors: 1, + init: false, + sid: expect.any(String), + started: expect.any(Number), + status: 'crashed', + timestamp: expect.any(Number), + }); + }); + + it.each(['error', 'warning', 'log', 'info', 'debug'] as const)( + 'sets status to ok if level is %s', + (level: SeverityLevel) => { + const client = new TestClient(getDefaultTestClientOptions()); + const session = makeSession(); + getCurrentScope().setSession(session); + + client.captureEvent({ message: 'test', level }); + + const updatedSession = client.session; + + expect(updatedSession?.status).toEqual('ok'); + }, + ); + }); + + describe('event has exceptions', () => { + it.each(['fatal', 'error', 'warning', 'log', 'info', 'debug'] as const)( + 'sets status ok for handled exceptions and ignores event level %s', + (level: SeverityLevel) => { + const client = new TestClient(getDefaultTestClientOptions()); + const session = makeSession(); + getCurrentScope().setSession(session); + + client.captureException(new Error('test'), { captureContext: { level } }); + + const updatedSession = client.session; + + expect(updatedSession?.status).toEqual('ok'); + }, + ); + + it.each(['fatal', 'error', 'warning', 'log', 'info', 'debug'] as const)( + 'sets status crashed for unhandled exceptions and ignores event level %s', + (level: SeverityLevel) => { + const client = new TestClient(getDefaultTestClientOptions()); + const session = makeSession(); + getCurrentScope().setSession(session); + + client.captureException(new Error('test'), { captureContext: { level }, mechanism: { handled: false } }); + + const updatedSession = client.session; + + expect(updatedSession?.status).toEqual('crashed'); + }, + ); + + it('sets status crashed if at least one exception is unhandled', () => { + const client = new TestClient(getDefaultTestClientOptions()); + const session = makeSession(); + getCurrentScope().setSession(session); + + const event: Event = { + exception: { + values: [ + { + mechanism: { type: 'generic', handled: true }, + }, + { + mechanism: { type: 'generic', handled: false }, + }, + { + mechanism: { type: 'generic', handled: true }, + }, + ], + }, + }; + + client.captureEvent(event); + + const updatedSession = client.session; + + expect(updatedSession).toMatchObject({ + status: 'crashed', + errors: 1, // an event with multiple exceptions still counts as one error in the session + }); + }); + }); + }); + describe('recordDroppedEvent()/_clearOutcomes()', () => { test('records and returns outcomes', () => { const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN });