import { Test, TestingModule } from '@nestjs/testing'; import { INestApplication } from '@nestjs/common'; import request from 'supertest'; import { AppModule } from '../src/app.module'; describe('M2 E2E Tests', () => { let app: INestApplication; beforeAll(async () => { const moduleFixture: TestingModule = await Test.createTestingModule({ imports: [AppModule], }).compile(); app = moduleFixture.createNestApplication(); app.setGlobalPrefix('api', { exclude: ['admin-api/(.*)', 'internal/(.*)'] }); await app.init(); }); afterAll(async () => { await app.close(); }); async function loginAdmin(): Promise { const res = await request(app.getHttpServer()) .post('/admin-api/auth/login') .send({ email: 'admin@zhixi.app', password: 'admin123' }); return res.body?.data?.accessToken || ''; } // ══════════════════════════════════════════════ // M2-01: User & Account 深化 // ══════════════════════════════════════════════ describe('M2-01 User & Account Deepening', () => { let token: string; beforeAll(async () => { token = await loginAdmin(); }); // ── Admin membership management ── it('GET /admin-api/users/memberships → 200 member list', async () => { if (!token) return; const res = await request(app.getHttpServer()) .get('/admin-api/users/memberships') .set('Authorization', `Bearer ${token}`) .expect(200); expect(Array.isArray(res.body.data)).toBe(true); }); it('POST /admin-api/users/memberships → 200 assign membership', async () => { if (!token) return; const res = await request(app.getHttpServer()) .post('/admin-api/users/memberships') .set('Authorization', `Bearer ${token}`) .send({ userId: 'user1', planId: 'plan-free' }) .expect([200, 201]); expect(res.body.data).toHaveProperty('id'); }); // ── Admin deletion requests ── it('GET /admin-api/users/deletion-requests → 200 list', async () => { if (!token) return; const res = await request(app.getHttpServer()) .get('/admin-api/users/deletion-requests') .set('Authorization', `Bearer ${token}`) .expect(200); expect(Array.isArray(res.body.data)).toBe(true); }); it('POST /admin-api/users/deletion-requests/:id/approve → approve', async () => { if (!token) return; const res = await request(app.getHttpServer()) .post('/admin-api/users/deletion-requests/test-id/approve') .set('Authorization', `Bearer ${token}`) .expect([200, 201]); expect(res.body.data).toHaveProperty('status', 'completed'); }); it('POST /admin-api/users/deletion-requests/:id/reject → reject', async () => { if (!token) return; const res = await request(app.getHttpServer()) .post('/admin-api/users/deletion-requests/test-id2/reject') .set('Authorization', `Bearer ${token}`) .expect([200, 201]); expect(res.body.data).toHaveProperty('status', 'cancelled'); }); // ── Admin device view ── it('GET /admin-api/users/:userId/devices → 200 device list', async () => { if (!token) return; const res = await request(app.getHttpServer()) .get('/admin-api/users/user1/devices') .set('Authorization', `Bearer ${token}`) .expect(200); expect(Array.isArray(res.body.data)).toBe(true); }); }); // ══════════════════════════════════════════════ // M2-02: Workspace & KnowledgeBase // ══════════════════════════════════════════════ describe('M2-02 Workspace & KnowledgeBase', () => { let token: string; beforeAll(async () => { token = await loginAdmin(); }); it('POST /api/knowledge-bases → 201 create KB', async () => { const res = await request(app.getHttpServer()) .post('/api/knowledge-bases') .send({ title: 'E2E Test KB', description: 'test' }) .expect([200, 201]); expect(res.body.data).toHaveProperty('id'); }); it('GET /api/knowledge-bases/:id/folders → 200 folder list', async () => { const kb = await request(app.getHttpServer()) .post('/api/knowledge-bases') .send({ title: 'Folder Test', description: 'test' }); const kbId = kb.body?.data?.id; if (!kbId) return; const res = await request(app.getHttpServer()) .get(`/api/knowledge-bases/${kbId}/folders`) .expect(200); expect(Array.isArray(res.body.data)).toBe(true); }); it('POST /api/knowledge-bases/:id/folders → 201 create folder', async () => { const kb = await request(app.getHttpServer()) .post('/api/knowledge-bases') .send({ title: 'Folder Create Test', description: 'test' }); const kbId = kb.body?.data?.id; if (!kbId) return; const res = await request(app.getHttpServer()) .post(`/api/knowledge-bases/${kbId}/folders`) .send({ name: 'Chapter 1' }) .expect([200, 201]); expect(res.body.data).toHaveProperty('id'); }); it('GET /admin-api/knowledge-bases → 200 admin KB list', async () => { if (!token) return; const res = await request(app.getHttpServer()) .get('/admin-api/knowledge-bases') .set('Authorization', `Bearer ${token}`) .expect(200); expect(res.body.data).toHaveProperty('items'); expect(res.body.data).toHaveProperty('total'); }); }); // ══════════════════════════════════════════════ // M2-03: Material & Source // ══════════════════════════════════════════════ describe('M2-03 Material & Source', () => { let token: string; beforeAll(async () => { token = await loginAdmin(); }); it('POST /api/knowledge-bases/:kbId/sources → 201 add source', async () => { const kb = await request(app.getHttpServer()) .post('/api/knowledge-bases') .send({ title: 'Source Test KB', description: 'test' }); const kbId = kb.body?.data?.id; if (!kbId) return; const res = await request(app.getHttpServer()) .post(`/api/knowledge-bases/${kbId}/sources`) .send({ type: 'file', title: 'Test PDF', originalFilename: 'test.pdf', mimeType: 'application/pdf' }) .expect([200, 201]); expect(res.body.data).toHaveProperty('id'); }); it('GET /admin-api/knowledge-bases/:id/sources → 200 source list', async () => { if (!token) return; const res = await request(app.getHttpServer()) .get('/admin-api/knowledge-bases/kb-test/sources') .set('Authorization', `Bearer ${token}`) .expect(200); expect(Array.isArray(res.body.data)).toBe(true); }); it('GET /admin-api/knowledge-bases/sources/:sourceId/references → 200', async () => { if (!token) return; const res = await request(app.getHttpServer()) .get('/admin-api/knowledge-bases/sources/src-test/references') .set('Authorization', `Bearer ${token}`) .expect(200); expect(Array.isArray(res.body.data)).toBe(true); }); }); // ══════════════════════════════════════════════ // M2-04: Ingestion & Indexing // ══════════════════════════════════════════════ describe('M2-04 Ingestion & Indexing', () => { let token: string; beforeAll(async () => { token = await loginAdmin(); }); it('POST /api/imports → 201 create import', async () => { const res = await request(app.getHttpServer()) .post('/api/imports') .send({ sourceType: 'file', sourceName: 'test.pdf', knowledgeBaseId: 'kb1', userId: 'user1' }) .expect([200, 201]); expect(res.body.data).toHaveProperty('id'); }); it('GET /admin-api/imports → 200 import list', async () => { if (!token) return; const res = await request(app.getHttpServer()) .get('/admin-api/imports') .set('Authorization', `Bearer ${token}`) .expect(200); expect(res.body.data).toHaveProperty('items'); expect(res.body.data).toHaveProperty('total'); }); it('GET /admin-api/imports → 401 without token', async () => { await request(app.getHttpServer()).get('/admin-api/imports').expect(401); }); it('GET /admin-api/imports/:id → 200 import detail', async () => { if (!token) return; const res = await request(app.getHttpServer()) .get('/admin-api/imports/test-id') .set('Authorization', `Bearer ${token}`) .expect(200); expect(res.body.data).toHaveProperty('job'); }); it('POST /admin-api/imports/:id/retry → retry failed import', async () => { if (!token) return; const res = await request(app.getHttpServer()) .post('/admin-api/imports/test-id/retry') .set('Authorization', `Bearer ${token}`) .expect([200, 201]); expect(res.body.success).toBe(true); }); }); // ══════════════════════════════════════════════ // M2-05: Vector & Retrieval 对接 // ══════════════════════════════════════════════ describe('M2-05 Vector Integration', () => { let token: string; beforeAll(async () => { token = await loginAdmin(); }); it('VectorService collection accessible', async () => { if (!token) return; const res = await request(app.getHttpServer()) .get('/admin-api/vector/collection') .set('Authorization', `Bearer ${token}`) .expect(200); expect(res.body.data).toHaveProperty('name'); }); it('VectorService count accessible', async () => { if (!token) return; const res = await request(app.getHttpServer()) .get('/admin-api/vector/count') .set('Authorization', `Bearer ${token}`) .expect(200); expect(res.body.data).toHaveProperty('count'); }); }); // ══════════════════════════════════════════════ // M2-07: RAG Chat // ══════════════════════════════════════════════ describe('M2-07 RAG Chat', () => { let token: string; beforeAll(async () => { token = await loginAdmin(); }); it('POST /api/rag-chat/sessions → 201 create session', async () => { const res = await request(app.getHttpServer()) .post('/api/rag-chat/sessions') .send({ knowledgeBaseId: 'kb1', title: 'Test Chat' }) .expect([200, 201]); expect(res.body.data).toHaveProperty('id'); }); it('GET /api/rag-chat/sessions → 200 list sessions', async () => { const res = await request(app.getHttpServer()) .get('/api/rag-chat/sessions') .expect(200); expect(Array.isArray(res.body.data)).toBe(true); }); it('POST /api/rag-chat/sessions/:id/messages → send message', async () => { const session = await request(app.getHttpServer()) .post('/api/rag-chat/sessions') .send({ knowledgeBaseId: 'kb1' }); const sId = session.body?.data?.id; if (!sId) return; const res = await request(app.getHttpServer()) .post(`/api/rag-chat/sessions/${sId}/messages`) .send({ content: '这个知识库的主要内容是什么?' }) .expect([200, 201]); expect(res.body.data).toHaveProperty('message'); }); it('GET /admin-api/rag-chat/sessions → 200 admin sessions', async () => { if (!token) return; const res = await request(app.getHttpServer()) .get('/admin-api/rag-chat/sessions') .set('Authorization', `Bearer ${token}`) .expect(200); expect(Array.isArray(res.body.data)).toBe(true); }); }); });