From c989df41d5474c41db41bdaf43c375d666d226fc Mon Sep 17 00:00:00 2001 From: Danijel Martinek Date: Tue, 5 May 2026 00:39:22 +0200 Subject: [PATCH] feat(auth): add signUpUseCase (test red until DI lands) --- .../use-cases/sign-up.use-case.test.ts | 47 +++++++++++++++++++ .../application/use-cases/sign-up.use-case.ts | 46 ++++++++++++++++++ 2 files changed, 93 insertions(+) create mode 100644 packages/auth/src/application/use-cases/sign-up.use-case.test.ts create mode 100644 packages/auth/src/application/use-cases/sign-up.use-case.ts diff --git a/packages/auth/src/application/use-cases/sign-up.use-case.test.ts b/packages/auth/src/application/use-cases/sign-up.use-case.test.ts new file mode 100644 index 0000000..22a40c6 --- /dev/null +++ b/packages/auth/src/application/use-cases/sign-up.use-case.test.ts @@ -0,0 +1,47 @@ +import { beforeEach, describe, expect, it } from "vitest"; +import { authContainer } from "@/di/container"; +import { AUTH_SYMBOLS } from "@/di/symbols"; +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"; +import { AuthenticationError } from "@/entities/errors"; +import { signUpUseCase } from "./sign-up.use-case"; + +describe("signUpUseCase", () => { + let usersRepo: MockUsersRepository; + let authService: MockAuthenticationService; + + beforeEach(() => { + if (authContainer.isBound(AUTH_SYMBOLS.IUsersRepository)) { + authContainer.unbind(AUTH_SYMBOLS.IUsersRepository); + } + if (authContainer.isBound(AUTH_SYMBOLS.IAuthenticationService)) { + authContainer.unbind(AUTH_SYMBOLS.IAuthenticationService); + } + usersRepo = new MockUsersRepository(); + authService = new MockAuthenticationService(usersRepo); + authContainer + .bind(AUTH_SYMBOLS.IUsersRepository) + .toConstantValue(usersRepo); + authContainer + .bind(AUTH_SYMBOLS.IAuthenticationService) + .toConstantValue(authService); + }); + + it("creates a new user and returns session + cookie + user", async () => { + const result = await signUpUseCase({ + username: "carol", + password: "secret_password", + }); + expect(result.user.username).toBe("carol"); + expect(result.session.userId).toBe(result.user.id); + expect(result.cookie.name).toBe("session"); + }); + + it("throws AuthenticationError when username taken", async () => { + await expect( + signUpUseCase({ username: "alice", password: "secret_password" }), + ).rejects.toBeInstanceOf(AuthenticationError); + }); +}); diff --git a/packages/auth/src/application/use-cases/sign-up.use-case.ts b/packages/auth/src/application/use-cases/sign-up.use-case.ts new file mode 100644 index 0000000..5c89f9a --- /dev/null +++ b/packages/auth/src/application/use-cases/sign-up.use-case.ts @@ -0,0 +1,46 @@ +import { AuthenticationError } from "../../entities/errors"; +import type { Cookie } from "../../entities/cookie"; +import type { Session } from "../../entities/session"; +import type { User } from "../../entities/user"; +import { authContainer } from "../../di/container"; +import { AUTH_SYMBOLS } from "../../di/symbols"; +import type { IUsersRepository } from "../repositories/users-repository.interface"; +import type { IAuthenticationService } from "../services/authentication-service.interface"; + +export async function signUpUseCase(input: { + username: string; + password: string; +}): Promise<{ + session: Session; + cookie: Cookie; + user: Pick; +}> { + const usersRepository = authContainer.get( + AUTH_SYMBOLS.IUsersRepository, + ); + const authService = authContainer.get( + AUTH_SYMBOLS.IAuthenticationService, + ); + + const existingUser = await usersRepository.getUserByUsername(input.username); + if (existingUser) { + throw new AuthenticationError("Username taken"); + } + + const passwordHash = await authService.hashPassword(input.password); + const userId = authService.generateUserId(); + + const newUser = await usersRepository.createUser({ + id: userId, + username: input.username, + passwordHash, + }); + + const { cookie, session } = await authService.createSession(newUser); + + return { + cookie, + session, + user: { id: newUser.id, username: newUser.username }, + }; +}