process.env.NODE_ENV = 'test'; delete process.env.DATABASE_URL; const { db } = await import('../api/db.js'); const { database, closeDatabase } = await import('../api/database.js'); const { decideCandidateInTransaction, ReviewDecisionError } = await import('../api/services/reviewService.js'); function assert(condition: unknown, message: string): asserts condition { if (!condition) throw new Error(message); } db.exec('BEGIN IMMEDIATE'); try { const project = await database.one<{ id: number }>('SELECT id FROM projects ORDER BY id LIMIT 1'); assert(project, 'SQLite 测试需要至少一个项目'); const collectionId = await database.insertId("INSERT INTO collections (project_id, name, status) VALUES (?, ?, 'reviewing')", [project.id, `候选稿测试 ${Date.now()}`]); const noteId = await database.insertId("INSERT INTO notes (collection_id, title, review_status) VALUES (?, ?, 'pending')", [collectionId, '候选稿 A']); const roundId = await database.insertId("INSERT INTO review_rounds (note_id, round_number, status) VALUES (?, 1, 'reviewing')", [noteId]); await database.execute('UPDATE notes SET active_round_id = ? WHERE id = ?', [roundId, noteId]); await database.execute("INSERT INTO work_versions (note_id, version_number, title, review_status, review_round_id, candidate_name, candidate_status) VALUES (?, 1, ?, 'pending', ?, ?, 'pending')", [noteId, '候选稿 A', roundId, '方案 A']); await database.execute("INSERT INTO work_versions (note_id, version_number, title, review_status, review_round_id, candidate_name, candidate_status) VALUES (?, 2, ?, 'pending', ?, ?, 'pending')", [noteId, '候选稿 B', roundId, '方案 B']); await decideCandidateInTransaction(database, { noteId, versionNumber: 1, projectId: project.id, decision: 'changes_requested', reason: 'A 需调整', actorName: '测试客户', actorRole: 'client' }); assert((await database.one<{ review_status: string }>('SELECT review_status FROM notes WHERE id = ?', [noteId]))?.review_status === 'pending', '仍有待验收候选稿时作品应保持待验收'); await decideCandidateInTransaction(database, { noteId, versionNumber: 2, projectId: project.id, decision: 'approved', reason: '', actorName: '测试客户', actorRole: 'client' }); const note = await database.one<{ version_number: number; approved_version_number: number; review_status: string }>('SELECT version_number, approved_version_number, review_status FROM notes WHERE id = ?', [noteId]); assert(note?.version_number === 2 && note.approved_version_number === 2 && note.review_status === 'approved', '作品未指向选中候选稿'); const candidates = await database.all[number]>('SELECT version_number, candidate_status FROM work_versions WHERE review_round_id = ? ORDER BY version_number', [roundId]); assert(candidates[0]?.candidate_status === 'not_selected' && candidates[1]?.candidate_status === 'selected', '同轮候选稿结果未正确收口'); assert((await database.one<{ status: string }>('SELECT status FROM collections WHERE id = ?', [collectionId]))?.status === 'completed', '作品通过后作品交付集未自动完成'); let historicalRejected = false; try { await decideCandidateInTransaction(database, { noteId, versionNumber: 1, projectId: project.id, decision: 'approved', reason: '', actorName: '测试客户', actorRole: 'client' }); } catch (error) { historicalRejected = error instanceof ReviewDecisionError && error.statusCode === 409; } assert(historicalRejected, '已完成轮次的候选稿不应被重复验收'); process.stdout.write('SQLite 多候选稿验证通过:单稿退修、其他候选继续验收、选中收口与历史只读\n'); } finally { db.exec('ROLLBACK'); await closeDatabase(); }