Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions apps/x/packages/core/src/application/lib/bus.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { describe, expect, it } from "vitest";
import z from "zod";
import { RunEvent } from "@x/shared/dist/runs.js";
import { InMemoryBus } from "./bus.js";

function makeEvent(runId: string): z.infer<typeof RunEvent> {
return {
type: "run-processing-start",
runId,
subflow: [],
};
}

describe("InMemoryBus", () => {
it("keeps other handlers subscribed when a handler is unsubscribed twice", async () => {
const bus = new InMemoryBus();
const runId = "run-1";

const receivedByA: z.infer<typeof RunEvent>[] = [];
const receivedByB: z.infer<typeof RunEvent>[] = [];

const unsubscribeA = await bus.subscribe(runId, async (event) => {
receivedByA.push(event);
});
await bus.subscribe(runId, async (event) => {
receivedByB.push(event);
});

// Double-unsubscribe of A must be a no-op the second time. The buggy
// implementation ran splice(indexOf(A), 1) where indexOf(A) === -1 on
// the second call, which removed the last handler (B) instead.
unsubscribeA();
unsubscribeA();

await bus.publish(makeEvent(runId));

expect(receivedByA).toHaveLength(0);
expect(receivedByB).toHaveLength(1);
});
});
5 changes: 4 additions & 1 deletion apps/x/packages/core/src/application/lib/bus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@ export class InMemoryBus implements IBus {
}
this.subscribers.get(runId)!.push(handler);
return () => {
this.subscribers.get(runId)!.splice(this.subscribers.get(runId)!.indexOf(handler), 1);
const handlers = this.subscribers.get(runId);
if (!handlers) return;
const idx = handlers.indexOf(handler);
if (idx >= 0) handlers.splice(idx, 1);
};
}
}