feat(redis): add channel prefix and error handling
This commit is contained in:
@@ -394,6 +394,169 @@ describe("createRedisEventTarget", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("channel prefix", () => {
|
||||
it("publishes to prefixed channel when prefix is set", () => {
|
||||
const publishClient = createMockRedis();
|
||||
const subscribeClient = createMockRedis();
|
||||
const eventTarget = createRedisEventTarget<TestEvent>({
|
||||
publishClient: publishClient as any,
|
||||
subscribeClient: subscribeClient as any,
|
||||
prefix: "alk:events:",
|
||||
});
|
||||
|
||||
const event = new CustomEvent("call.responded:uuid-123", {
|
||||
detail: { type: "call.responded", id: "uuid-123", payload: { status: "ok" } },
|
||||
}) as TestEvent;
|
||||
|
||||
eventTarget.dispatchEvent(event);
|
||||
|
||||
expect(publishClient.publish).toHaveBeenCalledWith(
|
||||
"alk:events:call.responded:uuid-123",
|
||||
expect.any(String),
|
||||
);
|
||||
});
|
||||
|
||||
it("subscribes to prefixed channel when prefix is set", () => {
|
||||
const publishClient = createMockRedis();
|
||||
const subscribeClient = createMockRedis();
|
||||
const eventTarget = createRedisEventTarget<TestEvent>({
|
||||
publishClient: publishClient as any,
|
||||
subscribeClient: subscribeClient as any,
|
||||
prefix: "alk:events:",
|
||||
});
|
||||
|
||||
const listener = vi.fn();
|
||||
eventTarget.addEventListener("call.responded:uuid-123", listener);
|
||||
|
||||
expect(subscribeClient.subscribe).toHaveBeenCalledWith("alk:events:call.responded:uuid-123");
|
||||
});
|
||||
|
||||
it("unsubscribes from prefixed channel when prefix is set", () => {
|
||||
const publishClient = createMockRedis();
|
||||
const subscribeClient = createMockRedis();
|
||||
const eventTarget = createRedisEventTarget<TestEvent>({
|
||||
publishClient: publishClient as any,
|
||||
subscribeClient: subscribeClient as any,
|
||||
prefix: "alk:events:",
|
||||
});
|
||||
|
||||
const listener = vi.fn();
|
||||
eventTarget.addEventListener("call.responded:uuid-123", listener);
|
||||
eventTarget.removeEventListener("call.responded:uuid-123", listener);
|
||||
|
||||
expect(subscribeClient.unsubscribe).toHaveBeenCalledWith("alk:events:call.responded:uuid-123");
|
||||
});
|
||||
|
||||
it("delivers messages on prefixed channels to listeners", () => {
|
||||
const publishClient = createMockRedis();
|
||||
const subscribeClient = createMockRedis();
|
||||
const eventTarget = createRedisEventTarget<TestEvent>({
|
||||
publishClient: publishClient as any,
|
||||
subscribeClient: subscribeClient as any,
|
||||
prefix: "alk:events:",
|
||||
});
|
||||
|
||||
const listener = vi.fn();
|
||||
eventTarget.addEventListener("call.responded:uuid-123", listener);
|
||||
|
||||
const envelope: EventEnvelope = {
|
||||
type: "call.responded",
|
||||
id: "uuid-123",
|
||||
payload: { status: "ok" },
|
||||
};
|
||||
subscribeClient.simulateMessage("alk:events:call.responded:uuid-123", JSON.stringify(envelope));
|
||||
|
||||
expect(listener).toHaveBeenCalledTimes(1);
|
||||
expect((listener.mock.calls[0][0] as TestEvent).detail).toEqual(envelope);
|
||||
});
|
||||
|
||||
it("ignores messages on non-prefixed channels when prefix is set", () => {
|
||||
const publishClient = createMockRedis();
|
||||
const subscribeClient = createMockRedis();
|
||||
const eventTarget = createRedisEventTarget<TestEvent>({
|
||||
publishClient: publishClient as any,
|
||||
subscribeClient: subscribeClient as any,
|
||||
prefix: "alk:events:",
|
||||
});
|
||||
|
||||
const listener = vi.fn();
|
||||
eventTarget.addEventListener("call.responded:uuid-123", listener);
|
||||
|
||||
subscribeClient.simulateMessage("call.responded:uuid-123", JSON.stringify({ type: "call.responded", id: "uuid-123", payload: null }));
|
||||
|
||||
expect(listener).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("defaults prefix to empty string", () => {
|
||||
const publishClient = createMockRedis();
|
||||
const subscribeClient = createMockRedis();
|
||||
const eventTarget = createRedisEventTarget<TestEvent>({
|
||||
publishClient: publishClient as any,
|
||||
subscribeClient: subscribeClient as any,
|
||||
});
|
||||
|
||||
const event = new CustomEvent("test:1", {
|
||||
detail: { type: "test", id: "1", payload: null },
|
||||
}) as TestEvent;
|
||||
|
||||
eventTarget.dispatchEvent(event);
|
||||
|
||||
expect(publishClient.publish).toHaveBeenCalledWith("test:1", expect.any(String));
|
||||
});
|
||||
});
|
||||
|
||||
describe("error handling", () => {
|
||||
it("skips messages that fail to parse and logs a warning", () => {
|
||||
const publishClient = createMockRedis();
|
||||
const subscribeClient = createMockRedis();
|
||||
const eventTarget = createRedisEventTarget<TestEvent>({
|
||||
publishClient: publishClient as any,
|
||||
subscribeClient: subscribeClient as any,
|
||||
});
|
||||
|
||||
const listener = vi.fn();
|
||||
eventTarget.addEventListener("topic:a", listener);
|
||||
|
||||
const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
|
||||
|
||||
subscribeClient.simulateMessage("topic:a", "not valid json{{");
|
||||
|
||||
expect(listener).not.toHaveBeenCalled();
|
||||
expect(warnSpy).toHaveBeenCalledTimes(1);
|
||||
expect(warnSpy).toHaveBeenCalledWith(
|
||||
'Failed to parse message on channel "topic:a": not valid json{{',
|
||||
);
|
||||
|
||||
warnSpy.mockRestore();
|
||||
});
|
||||
|
||||
it("continues delivering valid messages after a parse error", () => {
|
||||
const publishClient = createMockRedis();
|
||||
const subscribeClient = createMockRedis();
|
||||
const eventTarget = createRedisEventTarget<TestEvent>({
|
||||
publishClient: publishClient as any,
|
||||
subscribeClient: subscribeClient as any,
|
||||
});
|
||||
|
||||
const listener = vi.fn();
|
||||
eventTarget.addEventListener("topic:a", listener);
|
||||
|
||||
const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
|
||||
|
||||
subscribeClient.simulateMessage("topic:a", "broken{{{");
|
||||
|
||||
expect(listener).not.toHaveBeenCalled();
|
||||
|
||||
const envelope: EventEnvelope = { type: "topic", id: "a", payload: "good" };
|
||||
subscribeClient.simulateMessage("topic:a", JSON.stringify(envelope));
|
||||
|
||||
expect(listener).toHaveBeenCalledTimes(1);
|
||||
expect((listener.mock.calls[0][0] as TestEvent).detail).toEqual(envelope);
|
||||
|
||||
warnSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
|
||||
describe("EventListenerObject support", () => {
|
||||
it("addEventListener accepts EventListenerObject with handleEvent", () => {
|
||||
const publishClient = createMockRedis();
|
||||
|
||||
Reference in New Issue
Block a user