group-generation-recovery-smoke-test.js 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. 'use strict';
  2. const assert = require('assert/strict');
  3. const fs = require('fs');
  4. const os = require('os');
  5. const path = require('path');
  6. const { GroupAgentService } = require('../mcp/src/dashboard/group-agent-service');
  7. function output() {
  8. return {
  9. content: '已收到,我先为您整理后续安排。',
  10. confidence: 0.95,
  11. intent: '需求确认',
  12. reason: '群消息恢复测试固定输出。',
  13. requiresHuman: true,
  14. citations: [],
  15. toolTrace: [],
  16. };
  17. }
  18. async function main() {
  19. const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'qiwei-group-recovery-'));
  20. const roomId = 'group-recovery-room';
  21. const sourceMessageId = 'group-recovery-inbound-1';
  22. const messages = [{
  23. msgUniqueIdentifier: sourceMessageId,
  24. msgServerId: sourceMessageId,
  25. fromRoomId: roomId,
  26. msgType: 1,
  27. senderId: 'customer-1',
  28. senderName: '测试客户',
  29. content: '请发一下服务介绍',
  30. timestamp: Date.now(),
  31. }];
  32. const groups = { [roomId]: { roomName: '恢复测试群' } };
  33. const statePath = path.join(dir, 'group-agent-replies.json');
  34. const runtime = {
  35. db: { globalState: () => ({ paused: false }) },
  36. config: { qiwei: { groupGenerationRecoveryRetryCooldownMs: 1000 } },
  37. agent: { async run() { return output(); } },
  38. qiwei: { async sendText() { return { isSendSuccess: true }; } },
  39. };
  40. const options = {
  41. projectRoot: dir,
  42. statePath,
  43. getRuntime: () => runtime,
  44. getAccount: () => ({ uid: 'fixture', userId: 'self' }),
  45. loadGroups: () => groups,
  46. loadMessages: () => messages,
  47. appendMessage: (_roomId, message) => messages.push(message),
  48. };
  49. try {
  50. // Model a runtime exit after the inbound message is archived and the relay
  51. // has ACKed it, but before the in-memory generation promise is started.
  52. const beforeRestart = new GroupAgentService(options);
  53. const job = beforeRestart.queueGenerationJob(roomId, sourceMessageId, 'relay_callback');
  54. assert.equal(job.status, 'queued');
  55. const afterRestart = new GroupAgentService(options);
  56. const recovered = await afterRestart.recoverPendingGenerations('smoke:restart');
  57. assert.equal(recovered.recovered, 1);
  58. const state = afterRestart.loadState();
  59. const stored = afterRestart.roomState(state, roomId);
  60. const persisted = afterRestart.findGenerationJob(stored, sourceMessageId);
  61. assert.equal(persisted.status, 'draft_ready');
  62. assert.equal(afterRestart.publicState(roomId).pendingReply.sourceMessageId, sourceMessageId);
  63. assert.equal(stored.audit.some(item => item.action === 'group_generation_started'), true);
  64. // A second scan must not regenerate or create a duplicate draft.
  65. const second = await afterRestart.recoverPendingGenerations('smoke:dedupe');
  66. assert.equal(second.recovered, 0);
  67. assert.equal(stored.drafts.length, 1);
  68. process.stdout.write(JSON.stringify({ status: 'passed', checks: [
  69. '已归档群消息拥有持久化 generation job',
  70. '运行时重启后仅恢复一次并生成待审核草稿',
  71. '终态任务不会被重复回放',
  72. ] }, null, 2) + '\n');
  73. } finally {
  74. fs.rmSync(dir, { recursive: true, force: true });
  75. }
  76. }
  77. main().catch(error => {
  78. process.stderr.write(JSON.stringify({ status: 'failed', message: error.message, stack: error.stack }, null, 2) + '\n');
  79. process.exitCode = 1;
  80. });