callback-relay-smoke-test.js 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. 'use strict';
  2. const assert = require('assert');
  3. const fs = require('fs');
  4. const os = require('os');
  5. const path = require('path');
  6. const root = fs.mkdtempSync(path.join(os.tmpdir(), 'qiwei-callback-relay-smoke-'));
  7. process.env.QIWEI_OUTPUTS_DIR = root;
  8. const agentServicePath = require.resolve('../mcp/src/dashboard/agent-service');
  9. const bridged = [];
  10. require.cache[agentServicePath] = {
  11. id: agentServicePath,
  12. filename: agentServicePath,
  13. loaded: true,
  14. exports: {
  15. ingestWebhookMessage: async message => {
  16. bridged.push(message);
  17. return { status: 'pending_review' };
  18. },
  19. },
  20. };
  21. const { processWebhookEvents } = require('../mcp/src/core/webhook-processor');
  22. const { readProcessedEventIds } = require('../mcp/src/core/webhook-store');
  23. const { runPollOnce } = require('./start-relay-client');
  24. async function main() {
  25. const messageId = `callback-private-${Date.now()}`;
  26. const result = await processWebhookEvents({
  27. code: 0,
  28. msg: 'test',
  29. source: 'relay',
  30. data: [{
  31. guid: 'device-1', cmd: 15000, msgType: 1, msgUniqueIdentifier: messageId,
  32. senderId: 'customer-1', senderName: '测试客户', receiverId: 'staff-1',
  33. timestamp: Math.floor(Date.now() / 1000), seq: 1,
  34. msgData: { content: '回调消息测试' },
  35. }],
  36. });
  37. assert.strictEqual(result.processed, 1);
  38. assert.strictEqual(result.errors, 0);
  39. assert.strictEqual(bridged.length, 1);
  40. assert.strictEqual(bridged[0].msgData.content, '回调消息测试');
  41. assert.strictEqual(readProcessedEventIds().has(messageId), true);
  42. const originalFetch = global.fetch;
  43. let ackRequests = 0;
  44. global.fetch = async url => {
  45. if (String(url).endsWith('/api/relay/poll')) {
  46. return new Response(JSON.stringify({ success: true, events: [
  47. { eventId: 'broken-event', encryptedPayload: 'not-valid-base64' },
  48. ] }), { status: 200, headers: { 'Content-Type': 'application/json' } });
  49. }
  50. if (String(url).endsWith('/api/relay/ack')) {
  51. ackRequests += 1;
  52. return new Response(JSON.stringify({ success: true, ackedCount: 1 }), { status: 200 });
  53. }
  54. throw new Error(`unexpected URL: ${url}`);
  55. };
  56. try {
  57. const poll = await runPollOnce('https://relay.example.test', 'secret', 'device-1', 'invalid-private-key');
  58. assert.strictEqual(poll.failed, 1);
  59. assert.strictEqual(poll.acked, 0);
  60. assert.strictEqual(ackRequests, 0);
  61. } finally {
  62. global.fetch = originalFetch;
  63. }
  64. console.log('[ok] callback messages bridge to Agent and failed Relay events remain unacked');
  65. }
  66. main().finally(() => {
  67. delete require.cache[agentServicePath];
  68. fs.rmSync(root, { recursive: true, force: true });
  69. }).catch(error => {
  70. console.error(error);
  71. process.exitCode = 1;
  72. });