UNPKG

@remediator/core

Version:
227 lines (226 loc) 9.53 kB
import { describe, it, expect, beforeEach, vi } from "vitest"; import { reMediator as ReMediatorClass, reMediatorInstance, } from "../reMediator"; // Test request classes class TestRequest { constructor(name = "test", value = 42) { this.name = name; this.value = value; } } class TestRequest2 { constructor(id = "test-id", count = 10) { this.id = id; this.count = count; } } class AuthRequest { constructor(userId = "user123", action = "read") { this.userId = userId; this.action = action; } } // Test handlers class TestHandler { async handle(request, context) { return `Hello ${request.name}, value: ${request.value}`; } } class TestHandler2 { async handle(request, context) { return request.count * 2; } } class AuthHandler { async handle(request, context) { return request.userId === "user123" && request.action === "read"; } } // Test middleware const testMiddleware = async (request, context, next) => { const result = await next(); return `[${result}]`; }; const authMiddleware = async (request, context, next) => { if (request instanceof AuthRequest && request.userId !== "user123") { throw new Error("Unauthorized"); } return await next(); }; const loggingMiddleware = async (request, context, next) => { const result = await next(); return result; }; describe("reMediator", () => { let mediator; let mockRequest; beforeEach(() => { // Create a fresh instance for each test to ensure isolation mediator = new ReMediatorClass(); mockRequest = new Request("http://localhost:3000/test"); }); describe("register", () => { it("should register a handler for a request type", () => { const handler = new TestHandler(); expect(() => mediator.register(TestRequest, handler)).not.toThrow(); }); it("should allow registering multiple handlers for different request types", () => { const handler1 = new TestHandler(); const handler2 = new TestHandler2(); expect(() => { mediator.register(TestRequest, handler1); mediator.register(TestRequest2, handler2); }).not.toThrow(); }); }); describe("use", () => { it("should add middleware to the pipeline", () => { expect(() => mediator.use(testMiddleware)).not.toThrow(); }); it("should allow adding multiple middleware", () => { expect(() => { mediator.use(testMiddleware); mediator.use(authMiddleware); mediator.use(loggingMiddleware); }).not.toThrow(); }); }); describe("send - instance overload", () => { beforeEach(() => { mediator.register(TestRequest, new TestHandler()); }); it("should handle instance with raw request", async () => { const request = new TestRequest("Alice", 100); const result = await mediator.send(request, mockRequest); expect(result).toBe("Hello Alice, value: 100"); }); it("should handle instance with raw request and middleware", async () => { mediator.use(testMiddleware); const request = new TestRequest("Bob", 50); const result = await mediator.send(request, mockRequest); expect(result).toBe("[Hello Bob, value: 50]"); }); it("should throw error for unregistered request type", async () => { const request = new TestRequest2("test", 5); await expect(mediator.send(request, mockRequest)).rejects.toThrow("No handler registered for 'TestRequest2'"); }); }); describe("send - constructor overload", () => { beforeEach(() => { mediator.register(TestRequest, new TestHandler()); }); it("should handle constructor with data and raw request", async () => { const data = { name: "Charlie", value: 75 }; const result = await mediator.send(TestRequest, data, mockRequest); expect(result).toBe("Hello Charlie, value: 75"); }); it("should handle constructor with data, raw request, and middleware", async () => { mediator.use(testMiddleware); const data = { name: "David", value: 25 }; const result = await mediator.send(TestRequest, data, mockRequest); expect(result).toBe("[Hello David, value: 25]"); }); it("should merge data with constructor defaults", async () => { const data = { name: "Eve" }; // value should default to 42 const result = await mediator.send(TestRequest, data, mockRequest); expect(result).toBe("Hello Eve, value: 42"); }); }); describe("middleware functionality", () => { beforeEach(() => { mediator.register(AuthRequest, new AuthHandler()); }); it("should execute middleware in correct order", async () => { mediator.use(loggingMiddleware); mediator.use(authMiddleware); mediator.use(testMiddleware); const request = new AuthRequest("user123", "read"); const result = await mediator.send(request, mockRequest); expect(result).toBe("[true]"); }); it("should pass middleware names to context", async () => { mediator.use(loggingMiddleware); mediator.use(authMiddleware); const request = new AuthRequest("user123", "read"); const handler = new AuthHandler(); const spy = vi.spyOn(handler, "handle"); mediator.register(AuthRequest, handler); await mediator.send(request, mockRequest); expect(spy).toHaveBeenCalledWith(request, expect.objectContaining({ middlewareNames: ["loggingMiddleware", "authMiddleware"], })); }); it("should handle middleware that throws errors", async () => { mediator.use(authMiddleware); const request = new AuthRequest("unauthorized", "read"); await expect(mediator.send(request, mockRequest)).rejects.toThrow("Unauthorized"); }); it("should handle custom middleware array", async () => { mediator.use(loggingMiddleware); mediator.use(authMiddleware); const request = new AuthRequest("user123", "read"); const result = await mediator.send(request, mockRequest, [ loggingMiddleware, ]); expect(result).toBe(true); }); }); describe("error handling", () => { it("should throw error for invalid arguments", async () => { await expect(mediator.send(undefined, undefined)).rejects.toThrow("Invalid arguments for reMediator.send()"); }); it("should throw error for invalid first argument", async () => { await expect(mediator.send(null, mockRequest)).rejects.toThrow("Invalid arguments for reMediator.send()"); }); it("should warn when handler returns undefined", async () => { const undefinedHandler = { async handle() { return undefined; }, }; mediator.register(TestRequest, undefinedHandler); const request = new TestRequest(); const consoleSpy = vi.spyOn(console, "warn").mockImplementation(() => { }); await mediator.send(request, mockRequest); expect(consoleSpy).toHaveBeenCalledWith("Handler 'Object' returned undefined."); consoleSpy.mockRestore(); }); }); describe("context handling", () => { beforeEach(() => { mediator.register(TestRequest, new TestHandler()); }); it("should pass correct context to handler", async () => { const handler = new TestHandler(); const spy = vi.spyOn(handler, "handle"); mediator.register(TestRequest, handler); const request = new TestRequest("test", 42); await mediator.send(request, mockRequest); expect(spy).toHaveBeenCalledWith(request, expect.objectContaining({ rawRequest: mockRequest, handlerName: "TestRequest", middlewareNames: [], })); }); it("should include middleware names in context", async () => { mediator.use(testMiddleware); mediator.use(authMiddleware); const handler = new TestHandler(); const spy = vi.spyOn(handler, "handle"); mediator.register(TestRequest, handler); const request = new TestRequest("test", 42); await mediator.send(request, mockRequest); expect(spy).toHaveBeenCalledWith(request, expect.objectContaining({ middlewareNames: ["testMiddleware", "authMiddleware"], })); }); }); describe("default export", () => { it("should export a singleton instance", () => { expect(reMediatorInstance).toBeInstanceOf(ReMediatorClass); }); it("should be the same instance across imports", () => { // In ES modules, we can't use require, so we'll test the singleton differently expect(reMediatorInstance).toBeInstanceOf(ReMediatorClass); }); }); });