diff --git a/packages/core-testing/package.json b/packages/core-testing/package.json index 87b8599..25a76b7 100644 --- a/packages/core-testing/package.json +++ b/packages/core-testing/package.json @@ -21,8 +21,10 @@ "test": "vitest run" }, "dependencies": { + "@repo/core-events": "workspace:*", "@repo/core-shared": "workspace:*", "@testing-library/jest-dom": "^6.5.0", + "zod": "^3.23.0", "@testing-library/react": "^16.0.0", "@testing-library/user-event": "^14.5.0", "@trpc/client": "^11.0.0", diff --git a/packages/core-testing/src/instrumentation/index.ts b/packages/core-testing/src/instrumentation/index.ts index cd045e9..053357c 100644 --- a/packages/core-testing/src/instrumentation/index.ts +++ b/packages/core-testing/src/instrumentation/index.ts @@ -1,3 +1,4 @@ export { RecordingTracer, type RecordedSpan } from "./recording-tracer"; export { RecordingLogger, type RecordedCapture } from "./recording-logger"; export { RecordingJobQueue } from "./recording-job-queue"; +export { RecordingEventBus } from "./recording-event-bus"; diff --git a/packages/core-testing/src/instrumentation/recording-event-bus.test.ts b/packages/core-testing/src/instrumentation/recording-event-bus.test.ts new file mode 100644 index 0000000..2325032 --- /dev/null +++ b/packages/core-testing/src/instrumentation/recording-event-bus.test.ts @@ -0,0 +1,39 @@ +import { describe, it, expect } from "vitest"; +import { z } from "zod"; +import { defineEvent } from "@repo/core-events"; +import { RecordingEventBus } from "@/instrumentation/recording-event-bus"; + +const evt = defineEvent("test.evt", z.object({ id: z.string() }).strict()); + +describe("RecordingEventBus", () => { + it("records every publish call after schema validation", async () => { + const bus = new RecordingEventBus(); + await bus.publish(evt, { id: "a" }); + await bus.publish(evt, { id: "b" }); + expect(bus.published).toEqual([ + { name: "test.evt", payload: { id: "a" } }, + { name: "test.evt", payload: { id: "b" } }, + ]); + }); + + it("rejects invalid payloads", async () => { + const bus = new RecordingEventBus(); + await expect( + bus.publish(evt, { id: 1 } as unknown as { id: string }), + ).rejects.toThrow(); + expect(bus.published).toHaveLength(0); + }); + + it("invokes registered handlers sequentially in subscription order", async () => { + const bus = new RecordingEventBus(); + const order: string[] = []; + bus.subscribe(evt, "consumer-a", async () => { + order.push("a"); + }); + bus.subscribe(evt, "consumer-b", async () => { + order.push("b"); + }); + await bus.publish(evt, { id: "x" }); + expect(order).toEqual(["a", "b"]); + }); +}); diff --git a/packages/core-testing/src/instrumentation/recording-event-bus.ts b/packages/core-testing/src/instrumentation/recording-event-bus.ts new file mode 100644 index 0000000..c4ab87f --- /dev/null +++ b/packages/core-testing/src/instrumentation/recording-event-bus.ts @@ -0,0 +1,26 @@ +import type { z } from "zod"; +import type { EventDescriptor, EventHandler, IEventBus } from "@repo/core-events"; + +export class RecordingEventBus implements IEventBus { + readonly published: { name: string; payload: unknown }[] = []; + private readonly handlers = new Map[]>(); + + async publish( + descriptor: EventDescriptor>, + payload: T, + ): Promise { + descriptor.schema.parse(payload); + this.published.push({ name: descriptor.name, payload }); + for (const h of this.handlers.get(descriptor.name) ?? []) await h(payload); + } + + subscribe( + descriptor: EventDescriptor>, + _consumerFeature: string, + handler: EventHandler, + ): void { + const arr = this.handlers.get(descriptor.name) ?? []; + arr.push(handler as EventHandler); + this.handlers.set(descriptor.name, arr); + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9cf473f..57fa3fe 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -592,6 +592,9 @@ importers: packages/core-testing: dependencies: + '@repo/core-events': + specifier: workspace:* + version: link:../core-events '@repo/core-shared': specifier: workspace:* version: link:../core-shared @@ -628,6 +631,9 @@ importers: vitest: specifier: ^3.0.0 version: 3.2.4(@types/debug@4.1.13)(@types/node@25.5.2)(happy-dom@20.8.9)(jiti@2.6.1)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0) + zod: + specifier: ^3.23.0 + version: 3.25.76 devDependencies: '@repo/core-eslint': specifier: workspace:*