From 2ad6b21f17baacd1ed32072d38853405fff6e4d4 Mon Sep 17 00:00:00 2001 From: Danijel Martinek Date: Tue, 5 May 2026 00:40:09 +0200 Subject: [PATCH] feat(auth): add per-feature InversifyJS container with constructor-injected service --- packages/auth/src/di/container.test.ts | 51 ++++++++++++++++++++++++++ packages/auth/src/di/container.ts | 6 +++ packages/auth/src/di/module.ts | 14 +++++++ packages/auth/src/di/symbols.ts | 4 ++ 4 files changed, 75 insertions(+) create mode 100644 packages/auth/src/di/container.test.ts create mode 100644 packages/auth/src/di/container.ts create mode 100644 packages/auth/src/di/module.ts create mode 100644 packages/auth/src/di/symbols.ts diff --git a/packages/auth/src/di/container.test.ts b/packages/auth/src/di/container.test.ts new file mode 100644 index 0000000..73399e3 --- /dev/null +++ b/packages/auth/src/di/container.test.ts @@ -0,0 +1,51 @@ +import { afterEach, beforeEach, describe, expect, it } from "vitest"; +import { authContainer } from "./container"; +import { AUTH_SYMBOLS } from "./symbols"; +import { AuthModule } from "./module"; +import { MockUsersRepository } from "@/infrastructure/repositories/mock-users.repository"; +import { MockAuthenticationService } from "@/infrastructure/services/mock-authentication.service"; +import type { IUsersRepository } from "@/application/repositories/users-repository.interface"; +import type { IAuthenticationService } from "@/application/services/authentication-service.interface"; + +describe("authContainer", () => { + beforeEach(() => { + authContainer.unbindAll(); + authContainer.load(AuthModule); + }); + + afterEach(() => { + authContainer.unbindAll(); + }); + + it("resolves IUsersRepository to MockUsersRepository by default", () => { + const repo = authContainer.get( + AUTH_SYMBOLS.IUsersRepository, + ); + expect(repo).toBeInstanceOf(MockUsersRepository); + }); + + it("resolves IAuthenticationService to MockAuthenticationService by default", () => { + const service = authContainer.get( + AUTH_SYMBOLS.IAuthenticationService, + ); + expect(service).toBeInstanceOf(MockAuthenticationService); + }); + + it("authentication service receives users repository via constructor injection", async () => { + const service = authContainer.get( + AUTH_SYMBOLS.IAuthenticationService, + ); + // The service should be able to validate against the seeded users + const { session, cookie } = await service.createSession({ + id: "1", + username: "alice", + passwordHash: "hashed_password_alice", + }); + expect(session.userId).toBe("1"); + expect(cookie.value).toBe(session.id); + + // After session creation, validateSession should resolve user via the repo + const validated = await service.validateSession(session.id); + expect(validated.user.username).toBe("alice"); + }); +}); diff --git a/packages/auth/src/di/container.ts b/packages/auth/src/di/container.ts new file mode 100644 index 0000000..209254b --- /dev/null +++ b/packages/auth/src/di/container.ts @@ -0,0 +1,6 @@ +import "reflect-metadata"; +import { Container } from "inversify"; +import { AuthModule } from "./module"; + +export const authContainer = new Container({ defaultScope: "Singleton" }); +authContainer.load(AuthModule); diff --git a/packages/auth/src/di/module.ts b/packages/auth/src/di/module.ts new file mode 100644 index 0000000..a6cbd2c --- /dev/null +++ b/packages/auth/src/di/module.ts @@ -0,0 +1,14 @@ +import { ContainerModule, type interfaces } from "inversify"; + +import type { IUsersRepository } from "../application/repositories/users-repository.interface"; +import type { IAuthenticationService } from "../application/services/authentication-service.interface"; +import { MockUsersRepository } from "../infrastructure/repositories/mock-users.repository"; +import { MockAuthenticationService } from "../infrastructure/services/mock-authentication.service"; +import { AUTH_SYMBOLS } from "./symbols"; + +export const AuthModule = new ContainerModule((bind: interfaces.Bind) => { + bind(AUTH_SYMBOLS.IUsersRepository).to(MockUsersRepository); + bind(AUTH_SYMBOLS.IAuthenticationService).to( + MockAuthenticationService, + ); +}); diff --git a/packages/auth/src/di/symbols.ts b/packages/auth/src/di/symbols.ts new file mode 100644 index 0000000..759f31d --- /dev/null +++ b/packages/auth/src/di/symbols.ts @@ -0,0 +1,4 @@ +export const AUTH_SYMBOLS = { + IUsersRepository: Symbol.for("auth:IUsersRepository"), + IAuthenticationService: Symbol.for("auth:IAuthenticationService"), +} as const;