UNPKG

@sentzunhat/zacatl

Version:

A modular, high-performance TypeScript microservice framework for Node.js, featuring layered architecture, dependency injection, and robust validation for building scalable APIs and distributed systems.

201 lines (164 loc) 5.54 kB
import { ConfigService, DatabaseVendor, HandlersType, ServerVendor, Service, ServiceType, strategiesForDatabaseVendor, strategiesForServerVendor, } from "../../../../../../src/micro-service/architecture/platform/service/service"; class FakeFastifyInstance { listen = vi.fn(() => Promise.resolve()); withTypeProvider = vi.fn(() => ({ route: vi.fn() })); addHook = vi.fn(); register = vi.fn(); } class FakeMongoose { connect = vi.fn(() => Promise.resolve()); constructor() { Object.defineProperty(this.constructor, "name", { value: "FakeMongoose" }); } } const dummyRouteHandler = { url: "/test", method: "GET", schema: {}, execute: vi.fn(), constructor: { name: "DummyRouteHandler" }, }; // Dummy hook handler. const dummyHookHandler = { name: "onRequest", execute: vi.fn(), constructor: { name: "DummyHookHandler" }, }; describe("Service", () => { const fakeFastify: FakeFastifyInstance = new FakeFastifyInstance(); const fakeMongoose: FakeMongoose = new FakeMongoose(); let config: ConfigService; beforeEach(() => { config = { name: "TestService", server: { type: ServiceType.SERVER, vendor: ServerVendor.FASTIFY, instance: fakeFastify as any, // Cast as FastifyInstance }, databases: [ { vendor: DatabaseVendor.MONGOOSE, instance: fakeMongoose as any, // Cast as DatabaseInstance connectionString: "mongodb://localhost/test", onDatabaseConnected: vi.fn(), }, ], }; }); describe("registerHandlers", () => { it("should register a route handler using FASTIFY strategy", async () => { const routeSpy = vi.fn(); fakeFastify.withTypeProvider = vi .fn() .mockReturnValue({ route: routeSpy }); const service = new Service(config); await service.registerHandlers({ handlers: [dummyRouteHandler as any], handlersType: HandlersType.ROUTE, }); expect(fakeFastify.withTypeProvider).toHaveBeenCalled(); expect(routeSpy).toHaveBeenCalledWith( expect.objectContaining({ url: dummyRouteHandler.url, method: dummyRouteHandler.method, schema: dummyRouteHandler.schema, handler: expect.any(Function), }) ); }); it("should register a hook handler using FASTIFY strategy", async () => { const service = new Service(config); await service.registerHandlers({ handlers: [dummyHookHandler as any], handlersType: HandlersType.HOOK, }); expect(fakeFastify.addHook).toHaveBeenCalledWith( dummyHookHandler.name, expect.any(Function) ); }); it("should throw an error if registration function does not exist", async () => { const service = new Service(config); await expect( service.registerHandlers({ handlers: [dummyRouteHandler as any], handlersType: "invalid" as HandlersType, }) ).rejects.toThrowError(/is not supported for invalid registration/); }); }); describe("start", () => { let originalDbStrategy: any; let originalServerConfigFn: any; beforeEach(() => { originalDbStrategy = strategiesForDatabaseVendor[DatabaseVendor.MONGOOSE]; strategiesForDatabaseVendor[DatabaseVendor.MONGOOSE] = vi.fn(() => Promise.resolve() ); originalServerConfigFn = strategiesForServerVendor[ServerVendor.FASTIFY].server[ ServiceType.SERVER ]; strategiesForServerVendor[ServerVendor.FASTIFY].server[ ServiceType.SERVER ] = vi.fn(); }); afterEach(() => { strategiesForDatabaseVendor[DatabaseVendor.MONGOOSE] = originalDbStrategy; strategiesForServerVendor[ServerVendor.FASTIFY].server[ ServiceType.SERVER ] = originalServerConfigFn; }); it("should configure databases, configure server, and start the server", async () => { const port = 3000; const service = new Service(config); await service.configureDatabases(); await service.start({ port }); expect( strategiesForDatabaseVendor[DatabaseVendor.MONGOOSE] ).toHaveBeenCalledWith( expect.objectContaining({ serviceName: config.name, database: fakeMongoose, connectionString: config.databases[0]?.connectionString, onDatabaseConnected: expect.any(Function), }) ); expect( strategiesForServerVendor[ServerVendor.FASTIFY].server[ ServiceType.SERVER ] ).toHaveBeenCalledWith(fakeFastify, undefined); expect(fakeFastify.listen).toHaveBeenCalledWith({ host: "0.0.0.0", port, }); }); it("should throw an error if starting the server fails", async () => { const port = 3000; fakeFastify.listen.mockRejectedValue(new Error("listen failed")); const service = new Service(config); await expect(service.start({ port })).rejects.toThrow( "failed to start service" ); }); it("should throw an error if database configuration fails", async () => { strategiesForDatabaseVendor[DatabaseVendor.MONGOOSE] = originalDbStrategy; fakeMongoose.connect.mockRejectedValue(new Error("db connect failed")); const service = new Service(config); await expect(service.configureDatabases()).rejects.toThrow( "failed to configure database for service" ); }); }); });