import { Test, TestingModule } from '@nestjs/testing'; import { INestApplication, ValidationPipe } from '@nestjs/common'; import cookieParser from 'cookie-parser'; import request from 'supertest'; import { App } from 'supertest/types'; import { AppModule } from './../src/app.module'; import { HttpExceptionFilter } from './../src/common/filters/http-exception.filter'; import { PrismaService } from './../src/prisma/prisma.service'; type Authentication = { user: { email: string }; accessToken: string }; type Problem = { code: string }; describe('Authentication (e2e)', () => { let app: INestApplication; beforeEach(async () => { const moduleFixture: TestingModule = await Test.createTestingModule({ imports: [AppModule], }).compile(); app = moduleFixture.createNestApplication(); app.setGlobalPrefix('api/v1'); app.use(cookieParser()); app.useGlobalPipes( new ValidationPipe({ transform: true, whitelist: true }), ); app.useGlobalFilters(new HttpExceptionFilter()); await app.init(); await app.get(PrismaService).session.deleteMany(); await app.get(PrismaService).tbankConnection.deleteMany(); await app.get(PrismaService).user.deleteMany(); }); afterEach(() => app.close()); it('registers, guards profile, rotates refresh and logs out', async () => { const agent = request.agent(app.getHttpServer()); const registration = await agent .post('/api/v1/auth/register') .send({ email: ' User@Example.com ', password: 'a secure password' }) .expect(201); const authentication = registration.body as Authentication; expect(authentication.user.email).toBe('user@example.com'); const accessToken = authentication.accessToken; await request(app.getHttpServer()).get('/api/v1/auth/me').expect(401); await agent .get('/api/v1/auth/me') .set('Authorization', `Bearer ${accessToken}`) .expect(200) .expect(({ body }: { body: unknown }) => expect((body as { email: string }).email).toBe('user@example.com'), ); const firstCookie = registration.headers['set-cookie'][0]; await agent.post('/api/v1/auth/refresh').expect(201); await request(app.getHttpServer()) .post('/api/v1/auth/refresh') .set('Cookie', firstCookie) .expect(401) .expect('Content-Type', /application\/problem\+json/); await agent.post('/api/v1/auth/logout').expect(204); await agent.post('/api/v1/auth/logout').expect(204); }); it('rejects duplicate registration and invalid credentials', async () => { await request(app.getHttpServer()) .post('/api/v1/auth/register') .send({ email: 'user@example.com', password: 'a secure password' }) .expect(201); await request(app.getHttpServer()) .post('/api/v1/auth/register') .send({ email: 'USER@example.com', password: 'a secure password' }) .expect(409) .expect(({ body }: { body: unknown }) => expect((body as Problem).code).toBe('EMAIL_ALREADY_REGISTERED'), ); await request(app.getHttpServer()) .post('/api/v1/auth/login') .send({ email: 'user@example.com', password: 'wrong password' }) .expect(401) .expect(({ body }: { body: unknown }) => expect((body as Problem).code).toBe('INVALID_CREDENTIALS'), ); }); it('guards T-Bank connection and never returns a token', async () => { const registration = await request(app.getHttpServer()) .post('/api/v1/auth/register') .send({ email: 'connection@example.com', password: 'a secure password' }) .expect(201); const accessToken = (registration.body as Authentication).accessToken; await request(app.getHttpServer()) .get('/api/v1/tbank-connection') .expect(401); await request(app.getHttpServer()) .get('/api/v1/tbank-connection') .set('Authorization', `Bearer ${accessToken}`) .expect(200) .expect(({ body }: { body: unknown }) => expect(body).toEqual({ connected: false, connectedAt: null }), ); }); it('guards brokerage accounts and reports a missing connection', async () => { const registration = await request(app.getHttpServer()) .post('/api/v1/auth/register') .send({ email: 'accounts@example.com', password: 'a secure password' }) .expect(201); const accessToken = (registration.body as Authentication).accessToken; await request(app.getHttpServer()) .get('/api/v1/brokerage-accounts') .expect(401); await request(app.getHttpServer()) .get('/api/v1/brokerage-accounts') .set('Authorization', `Bearer ${accessToken}`) .expect(409) .expect(({ body }: { body: unknown }) => expect((body as Problem).code).toBe('TBANK_NOT_CONNECTED'), ); }); it('guards portfolio and reports a missing connection', async () => { const registration = await request(app.getHttpServer()) .post('/api/v1/auth/register') .send({ email: 'portfolio@example.com', password: 'a secure password' }) .expect(201); const accessToken = (registration.body as Authentication).accessToken; await request(app.getHttpServer()) .get('/api/v1/brokerage-accounts/account/portfolio') .expect(401); await request(app.getHttpServer()) .get('/api/v1/brokerage-accounts/account/portfolio') .set('Authorization', `Bearer ${accessToken}`) .expect(409) .expect(({ body }: { body: unknown }) => expect((body as Problem).code).toBe('TBANK_NOT_CONNECTED'), ); }); });