- Integrated BetterAuth library for modern authentication - Added Session, Account, and Verification database tables - Created complete auth module with service, controller, guards, and decorators - Implemented shared authentication types in @mosaic/shared package - Added comprehensive test coverage (26 tests passing) - Documented type sharing strategy for monorepo - Updated environment configuration with OIDC and JWT settings Key architectural decisions: - BetterAuth over Passport.js for better TypeScript support - Separation of User (DB entity) vs AuthUser (client-safe subset) - Shared types package to prevent FE/BE drift - Factory pattern for auth config to use shared Prisma instance Ready for frontend integration (Issue #6). Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> Fixes #4
142 lines
3.7 KiB
TypeScript
142 lines
3.7 KiB
TypeScript
import { describe, it, expect, beforeEach, vi } from "vitest";
|
|
import { Test, TestingModule } from "@nestjs/testing";
|
|
import { AuthService } from "./auth.service";
|
|
import { PrismaService } from "../prisma/prisma.service";
|
|
|
|
describe("AuthService", () => {
|
|
let service: AuthService;
|
|
let prisma: PrismaService;
|
|
|
|
const mockPrismaService = {
|
|
user: {
|
|
findUnique: vi.fn(),
|
|
},
|
|
};
|
|
|
|
beforeEach(async () => {
|
|
const module: TestingModule = await Test.createTestingModule({
|
|
providers: [
|
|
AuthService,
|
|
{
|
|
provide: PrismaService,
|
|
useValue: mockPrismaService,
|
|
},
|
|
],
|
|
}).compile();
|
|
|
|
service = module.get<AuthService>(AuthService);
|
|
prisma = module.get<PrismaService>(PrismaService);
|
|
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
describe("getAuth", () => {
|
|
it("should return BetterAuth instance", () => {
|
|
const auth = service.getAuth();
|
|
expect(auth).toBeDefined();
|
|
expect(auth.handler).toBeDefined();
|
|
});
|
|
});
|
|
|
|
describe("getUserById", () => {
|
|
const mockUser = {
|
|
id: "user-123",
|
|
email: "test@example.com",
|
|
name: "Test User",
|
|
authProviderId: "auth-123",
|
|
};
|
|
|
|
it("should get user by ID", async () => {
|
|
mockPrismaService.user.findUnique.mockResolvedValue(mockUser);
|
|
|
|
const result = await service.getUserById("user-123");
|
|
|
|
expect(result).toEqual(mockUser);
|
|
expect(mockPrismaService.user.findUnique).toHaveBeenCalledWith({
|
|
where: { id: "user-123" },
|
|
select: {
|
|
id: true,
|
|
email: true,
|
|
name: true,
|
|
authProviderId: true,
|
|
},
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("getUserByEmail", () => {
|
|
const mockUser = {
|
|
id: "user-123",
|
|
email: "test@example.com",
|
|
name: "Test User",
|
|
authProviderId: "auth-123",
|
|
};
|
|
|
|
it("should get user by email", async () => {
|
|
mockPrismaService.user.findUnique.mockResolvedValue(mockUser);
|
|
|
|
const result = await service.getUserByEmail("test@example.com");
|
|
|
|
expect(result).toEqual(mockUser);
|
|
expect(mockPrismaService.user.findUnique).toHaveBeenCalledWith({
|
|
where: { email: "test@example.com" },
|
|
select: {
|
|
id: true,
|
|
email: true,
|
|
name: true,
|
|
authProviderId: true,
|
|
},
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("verifySession", () => {
|
|
const mockSessionData = {
|
|
user: {
|
|
id: "user-123",
|
|
email: "test@example.com",
|
|
name: "Test User",
|
|
},
|
|
session: {
|
|
id: "session-123",
|
|
token: "test-token",
|
|
},
|
|
};
|
|
|
|
it("should return session data for valid token", async () => {
|
|
const auth = service.getAuth();
|
|
const mockGetSession = vi.fn().mockResolvedValue(mockSessionData);
|
|
auth.api = { getSession: mockGetSession } as any;
|
|
|
|
const result = await service.verifySession("valid-token");
|
|
|
|
expect(result).toEqual(mockSessionData);
|
|
expect(mockGetSession).toHaveBeenCalledWith({
|
|
headers: {
|
|
authorization: "Bearer valid-token",
|
|
},
|
|
});
|
|
});
|
|
|
|
it("should return null for invalid session", async () => {
|
|
const auth = service.getAuth();
|
|
const mockGetSession = vi.fn().mockResolvedValue(null);
|
|
auth.api = { getSession: mockGetSession } as any;
|
|
|
|
const result = await service.verifySession("invalid-token");
|
|
|
|
expect(result).toBeNull();
|
|
});
|
|
|
|
it("should return null and log error on verification failure", async () => {
|
|
const auth = service.getAuth();
|
|
const mockGetSession = vi.fn().mockRejectedValue(new Error("Verification failed"));
|
|
auth.api = { getSession: mockGetSession } as any;
|
|
|
|
const result = await service.verifySession("error-token");
|
|
|
|
expect(result).toBeNull();
|
|
});
|
|
});
|
|
});
|