You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
123 lines
4.4 KiB
123 lines
4.4 KiB
import { Test, type TestingModule } from '@nestjs/testing'; |
|
import type { INestApplication } from '@nestjs/common'; |
|
import { ValidationPipe } from '@nestjs/common'; |
|
import request from 'supertest'; |
|
import cookieParser from 'cookie-parser'; |
|
import { AppModule } from '../src/app.module'; |
|
import { PrismaService } from '../src/prisma/prisma.service'; |
|
|
|
describe('Auth (e2e)', () => { |
|
let app: INestApplication; |
|
let prisma: PrismaService; |
|
|
|
const SENIOR_EMAIL = 'senior@local'; |
|
const SENIOR_PASSWORD = process.env.SEED_PASSWORD_SENIOR ?? 'senior123'; |
|
|
|
beforeAll(async () => { |
|
const module: TestingModule = await Test.createTestingModule({ |
|
imports: [AppModule], |
|
}).compile(); |
|
|
|
app = module.createNestApplication(); |
|
app.use(cookieParser()); |
|
app.useGlobalPipes(new ValidationPipe({ whitelist: true, transform: true })); |
|
await app.init(); |
|
prisma = app.get(PrismaService); |
|
}); |
|
|
|
afterAll(async () => { |
|
await app.close(); |
|
}); |
|
|
|
it('GET /health is public', async () => { |
|
const res = await request(app.getHttpServer()).get('/health'); |
|
expect(res.status).toBe(200); |
|
expect(res.body.status).toBe('ok'); |
|
}); |
|
|
|
it('GET /auth/me requires auth', async () => { |
|
const res = await request(app.getHttpServer()).get('/auth/me'); |
|
expect(res.status).toBe(401); |
|
}); |
|
|
|
it('POST /auth/login returns access cookie and userId', async () => { |
|
const res = await request(app.getHttpServer()) |
|
.post('/auth/login') |
|
.send({ email: SENIOR_EMAIL, password: SENIOR_PASSWORD }); |
|
|
|
expect(res.status).toBe(200); |
|
expect(res.body.userId).toBeDefined(); |
|
const cookies = (res.headers['set-cookie'] as unknown as string[]) ?? []; |
|
expect(cookies.some((c) => c.startsWith('access_token='))).toBe(true); |
|
expect(cookies.some((c) => c.startsWith('refresh_token='))).toBe(true); |
|
}); |
|
|
|
it('GET /auth/me with cookie returns user + role', async () => { |
|
const loginRes = await request(app.getHttpServer()) |
|
.post('/auth/login') |
|
.send({ email: SENIOR_EMAIL, password: SENIOR_PASSWORD }); |
|
const cookies = (loginRes.headers['set-cookie'] as unknown as string[]) ?? []; |
|
|
|
const meRes = await request(app.getHttpServer()) |
|
.get('/auth/me') |
|
.set('Cookie', cookies); |
|
|
|
expect(meRes.status).toBe(200); |
|
expect(meRes.body.email).toBe(SENIOR_EMAIL); |
|
expect(meRes.body.role).toBe('SENIOR_ADMIN'); |
|
}); |
|
|
|
it('POST /auth/login with bad password → 401', async () => { |
|
const res = await request(app.getHttpServer()) |
|
.post('/auth/login') |
|
.send({ email: SENIOR_EMAIL, password: 'wrong-password' }); |
|
expect(res.status).toBe(401); |
|
}); |
|
|
|
it('POST /auth/logout clears tokens', async () => { |
|
const loginRes = await request(app.getHttpServer()) |
|
.post('/auth/login') |
|
.send({ email: SENIOR_EMAIL, password: SENIOR_PASSWORD }); |
|
const cookies = (loginRes.headers['set-cookie'] as unknown as string[]) ?? []; |
|
|
|
const logoutRes = await request(app.getHttpServer()) |
|
.post('/auth/logout') |
|
.set('Cookie', cookies); |
|
|
|
expect(logoutRes.status).toBe(200); |
|
|
|
// Используем тот же refresh — должен быть отозван. |
|
const refreshCookie = cookies.find((c) => c.startsWith('refresh_token=')); |
|
expect(refreshCookie).toBeDefined(); |
|
|
|
const refreshRes = await request(app.getHttpServer()) |
|
.post('/auth/refresh') |
|
.set('Cookie', refreshCookie!); |
|
|
|
expect(refreshRes.status).toBe(401); |
|
}); |
|
|
|
describe('refresh token rotation', () => { |
|
it('issues new tokens with valid refresh', async () => { |
|
const loginRes = await request(app.getHttpServer()) |
|
.post('/auth/login') |
|
.send({ email: SENIOR_EMAIL, password: SENIOR_PASSWORD }); |
|
const cookies = (loginRes.headers['set-cookie'] as unknown as string[]) ?? []; |
|
const refreshCookie = cookies.find((c) => c.startsWith('refresh_token='))!; |
|
|
|
const refreshRes = await request(app.getHttpServer()) |
|
.post('/auth/refresh') |
|
.set('Cookie', refreshCookie); |
|
|
|
expect(refreshRes.status).toBe(200); |
|
const newCookies = (refreshRes.headers['set-cookie'] as unknown as string[]) ?? []; |
|
expect(newCookies.some((c) => c.startsWith('access_token='))).toBe(true); |
|
|
|
// Старый refresh теперь невалиден — должен быть отозван. |
|
const reuseRes = await request(app.getHttpServer()) |
|
.post('/auth/refresh') |
|
.set('Cookie', refreshCookie); |
|
expect(reuseRes.status).toBe(401); |
|
}); |
|
}); |
|
});
|
|
|