diff --git a/packages/core/lib/events/DomainEventEmitter.spec.ts b/packages/core/lib/events/DomainEventEmitter.spec.ts index 596ec99e..d72d8167 100644 --- a/packages/core/lib/events/DomainEventEmitter.spec.ts +++ b/packages/core/lib/events/DomainEventEmitter.spec.ts @@ -66,6 +66,14 @@ describe('AutopilotEventEmitter', () => { it('emits event to anyListener - foreground', async () => { const fakeListener = new FakeListener() eventEmitter.onAny(fakeListener) + const transactionManagerStartSpy = vi.spyOn( + diContainer.cradle.transactionObservabilityManager, + 'startWithGroup', + ) + const transactionManagerStopSpy = vi.spyOn( + diContainer.cradle.transactionObservabilityManager, + 'stop', + ) const emittedEvent = await eventEmitter.emit(TestEvents.created, createdEventPayload) @@ -74,6 +82,19 @@ describe('AutopilotEventEmitter', () => { expect(processedEvent.message.type).toBe(TestEvents.created.consumerSchema.shape.type.value) expect(fakeListener.receivedEvents).toHaveLength(1) expect(fakeListener.receivedEvents[0]).toMatchObject(expectedCreatedPayload) + + expect(transactionManagerStartSpy).toHaveBeenCalledOnce() + expect(transactionManagerStartSpy).toHaveBeenCalledWith( + 'fg_event_listener:entity.created:FakeListener', + expect.any(String), + 'entity.created', + ) + + expect(transactionManagerStopSpy).toHaveBeenCalledOnce() + expect(transactionManagerStopSpy).toHaveBeenCalledWith( + transactionManagerStartSpy.mock.calls[0][1], + true, + ) }) it('emits event to anyListener - background', async () => { @@ -213,11 +234,32 @@ describe('AutopilotEventEmitter', () => { it('emits event to singleListener - foreground', async () => { const fakeListener = new FakeListener() eventEmitter.on('entity.created', fakeListener) + const transactionManagerStartSpy = vi.spyOn( + diContainer.cradle.transactionObservabilityManager, + 'startWithGroup', + ) + const transactionManagerStopSpy = vi.spyOn( + diContainer.cradle.transactionObservabilityManager, + 'stop', + ) await eventEmitter.emit(TestEvents.created, createdEventPayload) expect(fakeListener.receivedEvents).toHaveLength(1) expect(fakeListener.receivedEvents[0]).toMatchObject(expectedCreatedPayload) + + expect(transactionManagerStartSpy).toHaveBeenCalledOnce() + expect(transactionManagerStartSpy).toHaveBeenCalledWith( + 'fg_event_listener:entity.created:FakeListener', + expect.any(String), + 'entity.created', + ) + + expect(transactionManagerStopSpy).toHaveBeenCalledOnce() + expect(transactionManagerStopSpy).toHaveBeenCalledWith( + transactionManagerStartSpy.mock.calls[0][1], + true, + ) }) it('emits event to singleListener - background', async () => { @@ -259,6 +301,14 @@ describe('AutopilotEventEmitter', () => { it('emits event to manyListener - foreground', async () => { const fakeListener = new FakeListener() eventEmitter.onMany(['entity.created', 'entity.updated'], fakeListener) + const transactionManagerStartSpy = vi.spyOn( + diContainer.cradle.transactionObservabilityManager, + 'startWithGroup', + ) + const transactionManagerStopSpy = vi.spyOn( + diContainer.cradle.transactionObservabilityManager, + 'stop', + ) await eventEmitter.emit(TestEvents.created, createdEventPayload) await eventEmitter.emit(TestEvents.updated, updatedEventPayload) @@ -266,6 +316,9 @@ describe('AutopilotEventEmitter', () => { expect(fakeListener.receivedEvents).toHaveLength(2) expect(fakeListener.receivedEvents[0]).toMatchObject(expectedCreatedPayload) expect(fakeListener.receivedEvents[1]).toMatchObject(expectedUpdatedPayload) + + expect(transactionManagerStartSpy).toHaveBeenCalledTimes(2) + expect(transactionManagerStopSpy).toHaveBeenCalledTimes(2) }) it('emits event to manyListener - background', async () => { @@ -294,6 +347,39 @@ describe('AutopilotEventEmitter', () => { expect(transactionManagerStopSpy).toHaveBeenCalledTimes(2) }) + it('foreground listener error handling', async () => { + const fakeListener = new ErroredFakeListener() + eventEmitter.onAny(fakeListener) + const transactionManagerStartSpy = vi.spyOn( + diContainer.cradle.transactionObservabilityManager, + 'startWithGroup', + ) + const transactionManagerStopSpy = vi.spyOn( + diContainer.cradle.transactionObservabilityManager, + 'stop', + ) + + await expect(eventEmitter.emit(TestEvents.created, createdEventPayload)).rejects.toThrow( + 'ErroredFakeListener error', + ) + + expect(fakeListener.receivedEvents).toHaveLength(1) + expect(fakeListener.receivedEvents[0]).toMatchObject(expectedCreatedPayload) + + expect(transactionManagerStartSpy).toHaveBeenCalledOnce() + expect(transactionManagerStartSpy).toHaveBeenCalledWith( + 'fg_event_listener:entity.created:ErroredFakeListener', + expect.any(String), + 'entity.created', + ) + + expect(transactionManagerStopSpy).toHaveBeenCalledOnce() + expect(transactionManagerStopSpy).toHaveBeenCalledWith( + transactionManagerStartSpy.mock.calls[0][1], + false, + ) + }) + it('background listener error handling', async () => { const fakeListener = new ErroredFakeListener(100) eventEmitter.onAny(fakeListener, true) diff --git a/packages/core/lib/events/DomainEventEmitter.ts b/packages/core/lib/events/DomainEventEmitter.ts index 6b00d92b..eba9c7ee 100644 --- a/packages/core/lib/events/DomainEventEmitter.ts +++ b/packages/core/lib/events/DomainEventEmitter.ts @@ -175,7 +175,19 @@ export class DomainEventEmitter const fgHandlers = [...eventHandlers.foreground, ...this.anyHandlers.foreground] for (const handler of fgHandlers) { - await handler.handleEvent(event) + const transactionId = randomUUID() + let isSuccessfull = false + try { + this.transactionObservabilityManager?.startWithGroup( + this.buildTransactionKey(event, handler, false), + transactionId, + event.type, + ) + await handler.handleEvent(event) + isSuccessfull = true + } finally { + this.transactionObservabilityManager?.stop(transactionId, isSuccessfull) + } } const bgHandlers = [...eventHandlers.background, ...this.anyHandlers.background] @@ -184,7 +196,7 @@ export class DomainEventEmitter // not sure if we should use startWithGroup or start, using group to group all handlers for the same event type // should it be eventId + eventType or just eventType? this.transactionObservabilityManager?.startWithGroup( - this.buildTransactionKey(event, handler), + this.buildTransactionKey(event, handler, true), transactionId, event.type, ) @@ -212,7 +224,8 @@ export class DomainEventEmitter private buildTransactionKey( event: CommonEventDefinitionPublisherSchemaType, handler: EventHandler>, + isBackgroundHandler: boolean, ): string { - return `bg_event_listener:${event.type}:${handler.eventHandlerId}` + return `${isBackgroundHandler ? 'bg' : 'fg'}_event_listener:${event.type}:${handler.eventHandlerId}` } }