import { Test, TestingModule } from '@nestjs/testing'; import { INestApplication, ValidationPipe } from '@nestjs/common'; import * as request from 'supertest'; import * as cookieParser from 'cookie-parser'; import { getRepositoryToken } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { AppModule } from '../src/app.module'; import { User } from '../src/users/entities/user.entity'; describe('Auth (e2e)', () => { let app: INestApplication; let userRepo: Repository; beforeAll(async () => { const moduleFixture: TestingModule = await Test.createTestingModule({ imports: [AppModule], }).compile(); app = moduleFixture.createNestApplication(); app.use(cookieParser()); app.useGlobalPipes(new ValidationPipe({ whitelist: true, transform: true })); await app.init(); userRepo = moduleFixture.get>(getRepositoryToken(User)); }); afterAll(async () => { await app.close(); }); const testEmail = `test-${Date.now()}@example.com`; const testPassword = 'Test1234!'; describe('POST /auth/register', () => { it('should register a new user and return tokens', async () => { const res = await request(app.getHttpServer()) .post('/auth/register') .send({ email: testEmail, password: testPassword, name: 'Test User' }) .expect(201); expect(res.body.success).toBe(true); expect(res.body.accessToken).toBeDefined(); expect(res.body.user.email).toBe(testEmail); expect(res.body.user.passwordHash).toBeUndefined(); }); it('should reject duplicate email', async () => { await request(app.getHttpServer()) .post('/auth/register') .send({ email: testEmail, password: testPassword }) .expect(409); }); it('should reject short password', async () => { await request(app.getHttpServer()) .post('/auth/register') .send({ email: 'new@example.com', password: '123' }) .expect(400); }); }); describe('POST /auth/login', () => { it('should login with correct credentials', async () => { const res = await request(app.getHttpServer()) .post('/auth/login') .send({ email: testEmail, password: testPassword }) .expect(201); expect(res.body.success).toBe(true); expect(res.body.accessToken).toBeDefined(); // Cookies should be set const cookies = res.headers['set-cookie'] as string[]; expect(cookies).toBeDefined(); const hasAccessCookie = cookies.some((c) => c.startsWith('accessToken=')); expect(hasAccessCookie).toBe(true); }); it('should reject wrong password', async () => { await request(app.getHttpServer()) .post('/auth/login') .send({ email: testEmail, password: 'wrongpassword' }) .expect(401); }); it('should reject non-existent email', async () => { await request(app.getHttpServer()) .post('/auth/login') .send({ email: 'nonexistent@example.com', password: testPassword }) .expect(401); }); }); describe('POST /auth/refresh', () => { it('should rotate refresh token', async () => { // First login to get refresh token const loginRes = await request(app.getHttpServer()) .post('/auth/login') .send({ email: testEmail, password: testPassword }); const cookies = loginRes.headers['set-cookie'] as string[]; const refreshCookie = cookies.find((c) => c.startsWith('refreshToken=')); expect(refreshCookie).toBeDefined(); // Use refresh token const refreshRes = await request(app.getHttpServer()) .post('/auth/refresh') .set('Cookie', cookies) .expect(201); expect(refreshRes.body.success).toBe(true); expect(refreshRes.body.accessToken).toBeDefined(); }); it('should reject missing refresh token', async () => { await request(app.getHttpServer()) .post('/auth/refresh') .expect(401); }); }); describe('POST /auth/magic-link', () => { it('should accept magic link request for existing email', async () => { const res = await request(app.getHttpServer()) .post('/auth/magic-link') .send({ email: testEmail }) .expect(201); expect(res.body.success).toBe(true); }); it('should accept magic link request for non-existing email (no enumeration)', async () => { const res = await request(app.getHttpServer()) .post('/auth/magic-link') .send({ email: 'nobody@nowhere.com' }) .expect(201); expect(res.body.success).toBe(true); }); }); });