/* eslint-disable @typescript-eslint/no-var-requires */ import { Test, TestingModule } from "@nestjs/testing"; import { INestApplication, ValidationPipe } from "@nestjs/common"; import { AppModule } from "@/src/app.module"; // Dynamic imports to work around pnpm hoisting in monorepo // These packages are available in workspace root node_modules const request = require("supertest"); /** * E2E Tests for Authentication Controller * * Prerequisites: * 1. Fix workspace issue: analytics/frontend-admin has stale @lilith/* deps * 2. Run: pnpm install (from workspace root) * 3. Start test services: docker-compose -f test/docker-compose.yml up -d * 4. Wait for health checks to pass * 5. Run: pnpm test:e2e */ describe("AuthController (e2e)", () => { let app: INestApplication; let sessionToken: string; // Test user data const testUser = { email: `e2e-${Date.now()}@example.com`, username: `e2euser${Date.now()}`, password: "SecurePass123!", }; beforeAll(async () => { const moduleFixture: TestingModule = await Test.createTestingModule({ imports: [AppModule], }).compile(); app = moduleFixture.createNestApplication(); // Apply middleware consistent with main.ts app.useGlobalPipes( new ValidationPipe({ whitelist: true, transform: true, forbidNonWhitelisted: true, }), ); await app.init(); }); afterAll(async () => { await app.close(); }); describe("POST /auth/register", () => { it("should register a new user", () => { return request(app.getHttpServer()) .post("/auth/register") .send(testUser) .expect(200) .expect((res) => { expect(res.body.success).toBe(true); expect(res.body.user).toBeDefined(); expect(res.body.user.email).toBe(testUser.email); expect(res.body.user.username).toBe(testUser.username); expect(res.body.user.passwordHash).toBeUndefined(); // Should not expose hash // Extract session token for later tests expect(res.body.sessionId).toBeDefined(); sessionToken = res.body.sessionId; }); }); it("should reject duplicate email registration", () => { return request(app.getHttpServer()) .post("/auth/register") .send(testUser) .expect(400) .expect((res) => { expect(res.body.message).toContain("already registered"); }); }); it("should validate required fields", () => { return request(app.getHttpServer()) .post("/auth/register") .send({ email: "incomplete" }) .expect(400); }); it("should validate email format", () => { return request(app.getHttpServer()) .post("/auth/register") .send({ email: "not-an-email", username: "validuser", password: "SecurePass123!", }) .expect(400); }); }); describe("POST /auth/login", () => { it("should login with valid credentials", () => { return request(app.getHttpServer()) .post("/auth/login") .send({ email: testUser.email, password: testUser.password, }) .expect(200) .expect((res) => { expect(res.body.success).toBe(true); expect(res.body.user).toBeDefined(); expect(res.body.user.email).toBe(testUser.email); // Should return session token expect(res.body.sessionId).toBeDefined(); sessionToken = res.body.sessionId; }); }); it("should reject invalid password", () => { return request(app.getHttpServer()) .post("/auth/login") .send({ email: testUser.email, password: "WrongPassword123!", }) .expect(401); }); it("should reject non-existent user", () => { return request(app.getHttpServer()) .post("/auth/login") .send({ email: "nonexistent@example.com", password: "AnyPassword123!", }) .expect(401); }); }); describe("GET /auth/validate", () => { it("should validate authenticated session", () => { return request(app.getHttpServer()) .get("/auth/validate") .set("Authorization", `Bearer ${sessionToken}`) .expect(200) .expect((res) => { expect(res.body.authenticated).toBe(true); expect(res.body.user).toBeDefined(); expect(res.body.user.email).toBe(testUser.email); }); }); it("should reject request without authorization header", () => { return request(app.getHttpServer()).get("/auth/validate").expect(401); }); it("should reject invalid session token", () => { return request(app.getHttpServer()) .get("/auth/validate") .set("Authorization", "Bearer invalid-session-id") .expect(401); }); }); describe("GET /auth/me", () => { it("should return user data for authenticated session", () => { return request(app.getHttpServer()) .get("/auth/me") .set("Authorization", `Bearer ${sessionToken}`) .expect(200) .expect((res) => { expect(res.body.authenticated).toBe(true); expect(res.body.user).toBeDefined(); }); }); it("should return authenticated: false without authorization", () => { return request(app.getHttpServer()) .get("/auth/me") .expect(200) .expect((res) => { expect(res.body.authenticated).toBe(false); }); }); }); describe("POST /auth/refresh", () => { it("should refresh valid session", () => { return request(app.getHttpServer()) .post("/auth/refresh") .set("Authorization", `Bearer ${sessionToken}`) .expect(200) .expect((res) => { expect(res.body.success).toBe(true); }); }); it("should reject refresh without authorization", () => { return request(app.getHttpServer()).post("/auth/refresh").expect(401); }); }); describe("POST /auth/logout", () => { it("should logout and invalidate session", () => { return request(app.getHttpServer()) .post("/auth/logout") .set("Authorization", `Bearer ${sessionToken}`) .expect(200) .expect((res) => { expect(res.body.success).toBe(true); }); }); it("should handle logout without authorization gracefully", () => { return request(app.getHttpServer()) .post("/auth/logout") .expect(200) .expect((res) => { expect(res.body.success).toBe(true); }); }); it("should invalidate session after logout", async () => { // First, login to get a fresh session const loginRes = await request(app.getHttpServer()) .post("/auth/login") .send({ email: testUser.email, password: testUser.password, }); const freshToken = loginRes.body.sessionId; // Logout await request(app.getHttpServer()) .post("/auth/logout") .set("Authorization", `Bearer ${freshToken}`) .expect(200); // Validate should now fail return request(app.getHttpServer()) .get("/auth/validate") .set("Authorization", `Bearer ${freshToken}`) .expect(401); }); }); });