import { Test, TestingModule } from '@nestjs/testing'; import { INestApplication } from '@nestjs/common'; import request from 'supertest'; import { AppModule } from '../src/app.module'; describe('M1 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 || ''; } // ══════════════════════════════════════════════ // M1-01: AI Gateway 深化 // ══════════════════════════════════════════════ describe('M1-01 AI Gateway Deepening', () => { let token: string; beforeAll(async () => { token = await loginAdmin(); }); it('GET /admin-api/ai-gateway/status → 200 with routes info', async () => { if (!token) return; const res = await request(app.getHttpServer()) .get('/admin-api/ai-gateway/status') .set('Authorization', `Bearer ${token}`) .expect(200); expect(res.body.data).toHaveProperty('providers'); expect(res.body.data).toHaveProperty('routes'); expect(res.body.data).toHaveProperty('activeRoutes'); }); // ── Model Routes CRUD ── it('GET /admin-api/ai-gateway/routes → 200 with route list', async () => { if (!token) return; const res = await request(app.getHttpServer()) .get('/admin-api/ai-gateway/routes') .set('Authorization', `Bearer ${token}`) .expect(200); expect(Array.isArray(res.body.data)).toBe(true); }); it('GET /admin-api/ai-gateway/routes → 401 without token', async () => { await request(app.getHttpServer()) .get('/admin-api/ai-gateway/routes') .expect(401); }); it('POST /admin-api/ai-gateway/routes → creates route and reloads cache', async () => { if (!token) return; const res = await request(app.getHttpServer()) .post('/admin-api/ai-gateway/routes') .set('Authorization', `Bearer ${token}`) .send({ tier: 'cheap', taskType: 'test-e2e', preferredProvider: 'deepseek', preferredModel: 'deepseek-v4-flash', fallbackProvider: 'deepseek', fallbackModel: 'deepseek-v4-flash', maxRetries: 1, }) .expect([200, 201]); if (res.body?.data?.id) { await request(app.getHttpServer()) .delete(`/admin-api/ai-gateway/routes/${res.body.data.id}`) .set('Authorization', `Bearer ${token}`); } }); it('PUT /admin-api/ai-gateway/routes/:id → updates route', async () => { if (!token) return; const create = await request(app.getHttpServer()) .post('/admin-api/ai-gateway/routes') .set('Authorization', `Bearer ${token}`) .send({ tier: 'strong', taskType: 'test-update', preferredProvider: 'deepseek', preferredModel: 'deepseek-v4-pro', fallbackProvider: 'minimax', fallbackModel: 'minimax-m2.7', maxRetries: 2, }); const id = create.body?.data?.id; if (!id) return; await request(app.getHttpServer()) .put(`/admin-api/ai-gateway/routes/${id}`) .set('Authorization', `Bearer ${token}`) .send({ maxRetries: 4 }) .expect(200); await request(app.getHttpServer()) .delete(`/admin-api/ai-gateway/routes/${id}`) .set('Authorization', `Bearer ${token}`); }); // ── Provider management ── it('GET /admin-api/ai-gateway/providers → 200 with provider list', async () => { if (!token) return; const res = await request(app.getHttpServer()) .get('/admin-api/ai-gateway/providers') .set('Authorization', `Bearer ${token}`) .expect(200); expect(Array.isArray(res.body.data)).toBe(true); }); it('PUT /admin-api/ai-gateway/providers/:name → enables/disables provider', async () => { if (!token) return; await request(app.getHttpServer()) .put('/admin-api/ai-gateway/providers/deepseek') .set('Authorization', `Bearer ${token}`) .send({ enabled: true }) .expect(200); }); // ── Fallback events ── it('GET /admin-api/ai-gateway/fallback-events → 200 with events list', async () => { if (!token) return; const res = await request(app.getHttpServer()) .get('/admin-api/ai-gateway/fallback-events') .set('Authorization', `Bearer ${token}`) .expect(200); expect(Array.isArray(res.body.data)).toBe(true); }); }); // ══════════════════════════════════════════════ // M1-02: Vector & Retrieval Module // ══════════════════════════════════════════════ describe('M1-02 Vector & Retrieval', () => { let token: string; beforeAll(async () => { token = await loginAdmin(); }); it('GET /admin-api/vector/collection → 200 with collection info', 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'); expect(res.body.data).toHaveProperty('pointsCount'); }); it('GET /admin-api/vector/collection → 401 without token', async () => { await request(app.getHttpServer()) .get('/admin-api/vector/collection') .expect(401); }); it('GET /admin-api/vector/count → 200 with vector count', 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('collection'); expect(res.body.data).toHaveProperty('count'); }); it('POST /admin-api/vector/reindex → 200 (reserved)', async () => { if (!token) return; const res = await request(app.getHttpServer()) .post('/admin-api/vector/reindex') .set('Authorization', `Bearer ${token}`) .expect([200, 201]); expect(res.body.data).toHaveProperty('message'); }); }); // ══════════════════════════════════════════════ // M1-03: Task Queue 深化 // ══════════════════════════════════════════════ describe('M1-03 Task Queue Deepening', () => { let token: string; beforeAll(async () => { token = await loginAdmin(); }); it('GET /admin-api/events/stats → 200 with task type configs', async () => { if (!token) return; const res = await request(app.getHttpServer()) .get('/admin-api/events/stats') .set('Authorization', `Bearer ${token}`) .expect(200); expect(res.body.data).toHaveProperty('taskStats'); expect(res.body.data).toHaveProperty('totalTaskTypes'); }); it('GET /admin-api/events/stats → 401 without token', async () => { await request(app.getHttpServer()) .get('/admin-api/events/stats') .expect(401); }); it('GET /admin-api/events/workers → 200 with worker status', async () => { if (!token) return; const res = await request(app.getHttpServer()) .get('/admin-api/events/workers') .set('Authorization', `Bearer ${token}`) .expect(200); expect(res.body.data).toHaveProperty('workers'); expect(res.body.data).toHaveProperty('count'); }); it('POST /admin-api/events/:queue/jobs/batch-retry → 200', async () => { if (!token) return; await request(app.getHttpServer()) .post('/admin-api/events/ai-analysis/jobs/batch-retry') .set('Authorization', `Bearer ${token}`) .send({ count: 10 }) .expect([200, 201]); }); }); });