'use strict'; const assert = require('assert'); const fs = require('fs'); const os = require('os'); const path = require('path'); const root = fs.mkdtempSync(path.join(os.tmpdir(), 'qiwei-callback-relay-smoke-')); process.env.QIWEI_OUTPUTS_DIR = root; const agentServicePath = require.resolve('../mcp/src/dashboard/agent-service'); const bridged = []; require.cache[agentServicePath] = { id: agentServicePath, filename: agentServicePath, loaded: true, exports: { ingestWebhookMessage: async message => { bridged.push(message); return { status: 'pending_review' }; }, }, }; const { processWebhookEvents } = require('../mcp/src/core/webhook-processor'); const { readProcessedEventIds } = require('../mcp/src/core/webhook-store'); const { runPollOnce } = require('./start-relay-client'); async function main() { const messageId = `callback-private-${Date.now()}`; const result = await processWebhookEvents({ code: 0, msg: 'test', source: 'relay', data: [{ guid: 'device-1', cmd: 15000, msgType: 1, msgUniqueIdentifier: messageId, senderId: 'customer-1', senderName: '测试客户', receiverId: 'staff-1', timestamp: Math.floor(Date.now() / 1000), seq: 1, msgData: { content: '回调消息测试' }, }], }); assert.strictEqual(result.processed, 1); assert.strictEqual(result.errors, 0); assert.strictEqual(bridged.length, 1); assert.strictEqual(bridged[0].msgData.content, '回调消息测试'); assert.strictEqual(readProcessedEventIds().has(messageId), true); const originalFetch = global.fetch; let ackRequests = 0; global.fetch = async url => { if (String(url).endsWith('/api/relay/poll')) { return new Response(JSON.stringify({ success: true, events: [ { eventId: 'broken-event', encryptedPayload: 'not-valid-base64' }, ] }), { status: 200, headers: { 'Content-Type': 'application/json' } }); } if (String(url).endsWith('/api/relay/ack')) { ackRequests += 1; return new Response(JSON.stringify({ success: true, ackedCount: 1 }), { status: 200 }); } throw new Error(`unexpected URL: ${url}`); }; try { const poll = await runPollOnce('https://relay.example.test', 'secret', 'device-1', 'invalid-private-key'); assert.strictEqual(poll.failed, 1); assert.strictEqual(poll.acked, 0); assert.strictEqual(ackRequests, 0); } finally { global.fetch = originalFetch; } console.log('[ok] callback messages bridge to Agent and failed Relay events remain unacked'); } main().finally(() => { delete require.cache[agentServicePath]; fs.rmSync(root, { recursive: true, force: true }); }).catch(error => { console.error(error); process.exitCode = 1; });