import { Test, TestingModule } from '@nestjs/testing'; import request from 'supertest'; import { AppModule } from '../../app.module'; import { INestApplication } from '@nestjs/common'; describe('OAuth2 + Session (e2e)', () => { let app: INestApplication; let sessionCookie: string | undefined; beforeAll(async () => { const moduleFixture: TestingModule = await Test.createTestingModule({ imports: [AppModule], }).compile(); app = moduleFixture.createNestApplication(); await app.init(); }); afterAll(async () => { await app.close(); }); it('/login (POST) - success', async () => { const response = await request(app.getHttpServer()) .post('/login') .send({ emailOrUsername: 'admin@example.com', password: 'password123' }) .expect(200); expect(response.body).toEqual({ ok: true, user: expect.any(Object) }); const setCookie = response.headers['set-cookie'] as string[]; if (setCookie && setCookie.length > 0) { sessionCookie = setCookie[0].split(';')[0]; } expect(sessionCookie).toBeDefined(); }); it('/me (GET) - with session', async () => { expect(sessionCookie).toBeDefined(); await request(app.getHttpServer()) .get('/me') .set('Cookie', sessionCookie!) .expect(200); }); it('/oauth2/authorize - not logged in, redirect to login', async () => { const query = new URLSearchParams({ response_type: 'code', client_id: 'web-client', redirect_uri: 'http://localhost:3000/auth/callback', scope: 'read write', state: 'teststate', }).toString(); const response = await request(app.getHttpServer()) .get(`/oauth2/authorize?${query}`) .expect(302); expect(response.headers.location).toContain('/login?next='); }); it('/oauth2/authorize - after login, redirect with code', async () => { // 假设 sessionCookie 已存在 expect(sessionCookie).toBeDefined(); const query = new URLSearchParams({ response_type: 'code', client_id: 'web-client', redirect_uri: 'http://localhost:3000/auth/callback', scope: 'read write', state: 'teststate', }).toString(); const response = await request(app.getHttpServer()) .get(`/oauth2/authorize?${query}`) .set('Cookie', sessionCookie!) .expect(302); const location = response.headers.location; expect(location).toContain('http://localhost:3000/auth/callback?code='); expect(location).toContain('state=teststate'); }); it('/oauth2/token - authorization_code grant', async () => { // 先从 authorize 获取 code const query = new URLSearchParams({ response_type: 'code', client_id: 'web-client', redirect_uri: 'http://localhost:3000/auth/callback', scope: 'read write', state: 'teststate', }).toString(); const authResponse = await request(app.getHttpServer()) .get(`/oauth2/authorize?${query}`) .set('Cookie', sessionCookie!); const location = authResponse.headers.location; const code = new URL(location).searchParams.get('code'); expect(code).toBeDefined(); // 用 code 换 token const tokenResponse = await request(app.getHttpServer()) .post('/oauth2/token') .type('form') .send({ grant_type: 'authorization_code', code, redirect_uri: 'http://localhost:3000/auth/callback', client_id: 'web-client', client_secret: 'change_me', }) .expect(200); expect(tokenResponse.body).toEqual({ access_token: expect.any(String), token_type: 'Bearer', expires_in: expect.any(Number), refresh_token: expect.any(String), scope: 'read write', }); }); it('/oauth2/token - client_credentials grant', async () => { const response = await request(app.getHttpServer()) .post('/oauth2/token') .type('form') .send({ grant_type: 'client_credentials', client_id: 'web-client', client_secret: 'change_me', }) .expect(200); expect(response.body).toEqual({ access_token: expect.any(String), token_type: 'Bearer', expires_in: expect.any(Number), scope: 'read write', }); }); });