fix: return AgentBus attachment rejections
This commit is contained in:
1 parent
d7a821da16
commit
693aed54aa
3 files changed
+291
No files matched your search
@@ -779,6 +779,73 @@ test('WeChat attachment placeholder without file metadata fails closed before ta
|
||||
assert.ok(logs.some((entry) => entry.metadata.agentbus_event === 'outbound_result_sent'));
|
||||
});
|
||||
|
||||
test('attachment validation rejection returns an immediate failed result without scheduling parsing', async (t) => {
|
||||
const socket = new FakeSocket();
|
||||
const events = new EventEmitter();
|
||||
const rejectionMessage = '名单附件校验未通过【roster_workbook_conversion_failed】,请按模板修正后重新发送。';
|
||||
let parseQueueCalls = 0;
|
||||
const tasks: AgentBusTaskGateway = {
|
||||
events,
|
||||
async ingestMessage() {
|
||||
return {
|
||||
task: makeTask('awaiting_attachment', {
|
||||
task_id: 'TASK-ATTACHMENT-REJECTED',
|
||||
source: 'agentbus',
|
||||
message: rejectionMessage
|
||||
}),
|
||||
attached: true,
|
||||
created: false,
|
||||
input_attachment: {
|
||||
status: 'rejected',
|
||||
error_code: 'roster_workbook_conversion_failed',
|
||||
message: rejectionMessage
|
||||
}
|
||||
};
|
||||
},
|
||||
async getTask() {
|
||||
return makeTask('awaiting_attachment');
|
||||
}
|
||||
};
|
||||
const listener = new AgentBusListener({
|
||||
config: testConfig(),
|
||||
tasks,
|
||||
organizationId: 'org-1',
|
||||
scheduleParseQueue: async () => {
|
||||
parseQueueCalls += 1;
|
||||
},
|
||||
socketFactory: () => socket as unknown as AgentBusSocket
|
||||
});
|
||||
t.after(() => listener.stop());
|
||||
|
||||
listener.start();
|
||||
socket.readyState = 1;
|
||||
socket.emit('open');
|
||||
socket.emit('message', JSON.stringify({
|
||||
id: 'ready-attachment-rejected',
|
||||
type: 'event',
|
||||
session_id: 'session-attachment-rejected',
|
||||
epoch: 1,
|
||||
to: 'bot:test:listener',
|
||||
payload: { event: 'session.ready' }
|
||||
}));
|
||||
socket.emit('message', JSON.stringify({
|
||||
id: 'wechat-attachment-rejected',
|
||||
type: 'event',
|
||||
from: 'channel:wechat:user-1',
|
||||
conversation_id: 'conversation-attachment-rejected',
|
||||
payload: { text: '名单附件补充消息' }
|
||||
}));
|
||||
|
||||
await waitFor(() => socket.sent.length === 2);
|
||||
assert.equal(parseQueueCalls, 0);
|
||||
assert.equal((socket.sent[0].payload as Record<string, unknown>).event, 'task.progress');
|
||||
assert.equal((socket.sent[1].payload as Record<string, unknown>).event, 'task.result');
|
||||
assert.equal((socket.sent[1].payload as Record<string, unknown>).status, 'failed');
|
||||
assert.equal((socket.sent[1].payload as Record<string, unknown>).text, rejectionMessage);
|
||||
assert.equal(socket.sent[1].reply_to, 'wechat-attachment-rejected');
|
||||
assert.equal(socket.sent[1].conversation_id, 'conversation-attachment-rejected');
|
||||
});
|
||||
|
||||
test('listener reload stop does not overwrite the channel status as disabled', () => {
|
||||
const statuses: string[] = [];
|
||||
const listener = new AgentBusListener({
|
||||
@@ -1049,3 +1116,148 @@ test('durable channel listener persists route and resends accepted/result delive
|
||||
assert.deepEqual(deliveryState.failures, []);
|
||||
listener.stop();
|
||||
});
|
||||
|
||||
test('durable channel persists and sends an attachment rejection result while the task keeps waiting', async (t) => {
|
||||
const config = testConfig();
|
||||
const socket = new FakeSocket();
|
||||
const events = new EventEmitter();
|
||||
const rejectionMessage = '名单附件校验未通过【roster_workbook_conversion_failed】,请按模板修正后重新发送。';
|
||||
let acceptedCreated = false;
|
||||
let resultEnqueued = false;
|
||||
let acceptedDelivered = false;
|
||||
let resultDelivered = false;
|
||||
let parseQueueCalls = 0;
|
||||
let resultPayload: Record<string, unknown> = {};
|
||||
const accepted: AgentBusDelivery = {
|
||||
id: 'delivery-attachment-rejected-accepted',
|
||||
channel_id: 'channel-1',
|
||||
task_id: 'task-row-attachment-rejected',
|
||||
inbound_frame_id: 'channel-attachment-rejected',
|
||||
inbound_from: 'channel:wechat:user-1',
|
||||
conversation_id: 'conversation-attachment-rejected',
|
||||
delivery_kind: 'accepted',
|
||||
payload: { event: 'task.progress', status: 'accepted', text: '已受理,正在处理。' },
|
||||
attempt_count: 1
|
||||
};
|
||||
const result: AgentBusDelivery = {
|
||||
...accepted,
|
||||
id: 'delivery-attachment-rejected-result',
|
||||
delivery_kind: 'result',
|
||||
payload: resultPayload
|
||||
};
|
||||
const logs: Array<{ level: string; metadata: Record<string, unknown>; message?: string }> = [];
|
||||
const tasks: AgentBusTaskGateway = {
|
||||
events,
|
||||
async ingestMessage() {
|
||||
acceptedCreated = true;
|
||||
return {
|
||||
task: makeTask('awaiting_attachment', {
|
||||
task_id: 'TASK-ATTACHMENT-REJECTED',
|
||||
source: 'agentbus',
|
||||
channel_id: 'channel-1',
|
||||
channel_name: '外部用户 A',
|
||||
message: rejectionMessage
|
||||
}),
|
||||
attached: true,
|
||||
created: false,
|
||||
input_attachment: {
|
||||
status: 'rejected',
|
||||
error_code: 'roster_workbook_conversion_failed',
|
||||
message: rejectionMessage
|
||||
}
|
||||
};
|
||||
},
|
||||
async getTask() {
|
||||
return makeTask('awaiting_attachment');
|
||||
},
|
||||
async listAgentBusFinalizationCandidates() {
|
||||
return [];
|
||||
},
|
||||
async enqueueAgentBusResult(channelId, inboundFrameId, payload) {
|
||||
assert.equal(channelId, 'channel-1');
|
||||
assert.equal(inboundFrameId, 'channel-attachment-rejected');
|
||||
resultPayload = payload;
|
||||
result.payload = resultPayload;
|
||||
resultEnqueued = true;
|
||||
},
|
||||
async claimAgentBusDeliveries() {
|
||||
const deliveries: AgentBusDelivery[] = [];
|
||||
if (acceptedCreated && !acceptedDelivered) deliveries.push(accepted);
|
||||
if (resultEnqueued && !resultDelivered) deliveries.push(result);
|
||||
return deliveries;
|
||||
},
|
||||
async markAgentBusDeliveryDelivered(deliveryId) {
|
||||
if (deliveryId === accepted.id) acceptedDelivered = true;
|
||||
if (deliveryId === result.id) resultDelivered = true;
|
||||
},
|
||||
async markAgentBusDeliveryFailed() {},
|
||||
async releaseAgentBusDeliveries() {}
|
||||
};
|
||||
const listener = new AgentBusListener({
|
||||
config,
|
||||
tasks,
|
||||
organizationId: 'org-1',
|
||||
scheduleParseQueue: async () => {
|
||||
parseQueueCalls += 1;
|
||||
},
|
||||
socketFactory: () => socket as unknown as AgentBusSocket,
|
||||
channel: {
|
||||
id: 'channel-1',
|
||||
displayName: '外部用户 A',
|
||||
wsUrl: 'wss://mesh.nianxx.cn/ws',
|
||||
wsToken: 'channel-ws-token',
|
||||
botAddress: 'bot:channel-a:listener'
|
||||
},
|
||||
logger: {
|
||||
info(metadata, message) {
|
||||
logs.push({ level: 'info', metadata, message });
|
||||
},
|
||||
warn(metadata, message) {
|
||||
logs.push({ level: 'warn', metadata, message });
|
||||
},
|
||||
error(metadata, message) {
|
||||
logs.push({ level: 'error', metadata, message });
|
||||
}
|
||||
}
|
||||
});
|
||||
t.after(() => listener.stop());
|
||||
|
||||
listener.start();
|
||||
socket.readyState = 1;
|
||||
socket.emit('open');
|
||||
socket.emit('message', JSON.stringify({
|
||||
id: 'ready-durable-attachment-rejected',
|
||||
type: 'event',
|
||||
session_id: 'session-durable-attachment-rejected',
|
||||
epoch: 4,
|
||||
to: 'bot:channel-a:listener',
|
||||
payload: { event: 'session.ready' }
|
||||
}));
|
||||
socket.emit('message', JSON.stringify({
|
||||
id: 'channel-attachment-rejected',
|
||||
type: 'event',
|
||||
from: 'channel:wechat:user-1',
|
||||
conversation_id: 'conversation-attachment-rejected',
|
||||
payload: { text: '名单附件补充消息' }
|
||||
}));
|
||||
|
||||
await waitFor(() => resultDelivered, 3_000);
|
||||
assert.equal(acceptedDelivered, true);
|
||||
assert.equal(resultDelivered, true);
|
||||
assert.equal(parseQueueCalls, 0);
|
||||
assert.deepEqual(resultPayload, {
|
||||
event: 'task.result',
|
||||
status: 'failed',
|
||||
task_id: 'TASK-ATTACHMENT-REJECTED',
|
||||
text: rejectionMessage
|
||||
});
|
||||
assert.equal(socket.sent.length, 2);
|
||||
assert.equal((socket.sent[0].payload as Record<string, unknown>).event, 'task.progress');
|
||||
assert.equal((socket.sent[1].payload as Record<string, unknown>).event, 'task.result');
|
||||
assert.equal((socket.sent[1].payload as Record<string, unknown>).status, 'failed');
|
||||
assert.equal((socket.sent[1].payload as Record<string, unknown>).text, rejectionMessage);
|
||||
assert.equal(socket.sent[1].reply_to, 'channel-attachment-rejected');
|
||||
assert.equal(socket.sent[1].conversation_id, 'conversation-attachment-rejected');
|
||||
assert.ok(logs.some((entry) => entry.metadata.agentbus_event === 'attachment_rejection_reply_requested'
|
||||
&& entry.metadata.error_code === 'roster_workbook_conversion_failed'));
|
||||
});
|
||||
Reference in new issue
Block a user