import { describe, expect, it } from 'vitest'; import request from 'supertest'; import { createApp } from '../app.js'; import { createFakeRedis } from '../test/fakeRedis.js'; describe('projects API', () => { it('GET /api/projects returns projects from unified list', async () => { const now = Date.now(); const redis = createFakeRedis({ 项目心跳: JSON.stringify([ { projectName: 'Demo', apiBaseUrl: 'http://localhost:8080', lastActiveAt: now }, ]), }); const app = createApp({ redis }); const resp = await request(app).get('/api/projects'); expect(resp.status).toBe(200); expect(resp.body.success).toBe(true); expect(resp.body.count).toBe(1); expect(resp.body.projects[0]).toMatchObject({ id: 'Demo', name: 'Demo', apiBaseUrl: 'http://localhost:8080', }); expect([ 'online', 'offline', 'unknown' ]).toContain( resp.body.projects[0].status, ); }); it('POST /api/projects/migrate migrates old *_项目心跳 keys into 项目心跳 list', async () => { const now = Date.now(); const redis = createFakeRedis({ 'A_项目心跳': JSON.stringify({ apiBaseUrl: 'http://a', lastActiveAt: now, }), }); const app = createApp({ redis }); const resp = await request(app) .post('/api/projects/migrate') .send({ dryRun: false, deleteOldKeys: true }); expect(resp.status).toBe(200); expect(resp.body.success).toBe(true); expect(resp.body.migrated).toBe(1); const listRaw = await redis.get('项目心跳'); expect(typeof listRaw).toBe('string'); const list = JSON.parse(listRaw); expect(Array.isArray(list)).toBe(true); expect(list[0]).toMatchObject({ projectName: 'A', apiBaseUrl: 'http://a', }); const old = await redis.get('A_项目心跳'); expect(old).toBeNull(); }); });