| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136 |
- '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 { recordDeviceGuid } = require('../mcp/src/core/device-broker-mapping');
- const { getGroupOperationsStore } = require('../mcp/src/core/group-operations-store');
- const {
- writeConfirmedMapping,
- readRoomMessages,
- } = require('../mcp/src/tools/qiwei-group-management-run');
- const { runPollOnce } = require('./start-relay-client');
- async function main() {
- recordDeviceGuid('device-1', { wecomUserId: 'staff-1' });
- 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(`staff-1:${messageId}`), true);
- const roomId = 'room-callback-test';
- const groupMessageId = `callback-group-${Date.now()}`;
- writeConfirmedMapping({
- [roomId]: {
- roomId,
- roomName: '回调测试客户群',
- status: 'ACTIVE',
- reviewStatus: 'CONFIRMED',
- },
- });
- const groupEnvelope = {
- code: 0,
- source: 'relay',
- data: [{
- guid: 'device-1', cmd: 15000, msgType: 2, msgUniqueIdentifier: groupMessageId,
- fromRoomId: roomId, senderId: 'customer-2', senderName: '群客户',
- timestamp: Math.floor(Date.now() / 1000), seq: 2,
- msgData: { content: '客户群回调新消息' },
- }],
- };
- const groupResult = await processWebhookEvents(groupEnvelope);
- assert.strictEqual(groupResult.processed, 1);
- assert.strictEqual(groupResult.errors, 0);
- assert.strictEqual(readRoomMessages(roomId).length, 1);
- assert.strictEqual(readRoomMessages(roomId)[0].content, '客户群回调新消息');
- assert.strictEqual(bridged.length, 2, '已确认群消息应进入客服 Agent 生成链路');
- const duplicateResult = await processWebhookEvents(groupEnvelope);
- assert.strictEqual(duplicateResult.ignored, 1);
- assert.strictEqual(readRoomMessages(roomId).length, 1, '重复群回调不得重复入库');
- assert.strictEqual(bridged.length, 2, '重复群回调不得重复进入 Agent');
- const unknownRoomId = 'room-callback-unknown';
- const unknownResult = await processWebhookEvents({
- code: 0,
- source: 'relay',
- data: [{
- guid: 'device-1', cmd: 15000, msgType: 2,
- msgUniqueIdentifier: `callback-unknown-${Date.now()}`,
- fromRoomId: unknownRoomId, senderId: 'customer-3', senderName: '未知群客户',
- timestamp: Math.floor(Date.now() / 1000), seq: 3,
- msgData: { content: '未知群消息' },
- }],
- });
- assert.strictEqual(unknownResult.ignored, 1);
- assert.strictEqual(readRoomMessages(unknownRoomId).length, 0, '未确认群不得写入消息库');
- assert.strictEqual(bridged.length, 2, '未确认群不得进入 Agent');
- 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] private callbacks bridge to Agent; confirmed group callbacks archive, deduplicate, and queue generation; unknown groups stay isolated; failed Relay events remain unacked');
- }
- main().finally(() => {
- delete require.cache[agentServicePath];
- try { getGroupOperationsStore().close(); } catch {}
- fs.rmSync(root, { recursive: true, force: true });
- }).catch(error => {
- console.error(error);
- process.exitCode = 1;
- });
|