Files
LWLT-AIBOT/control-plane/test/reply-contract.test.ts
2026-09-11 21:22:14 +08:00

235 lines
8.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import assert from 'node:assert/strict';
import test from 'node:test';
import {
buildCustomerSuccessReply,
buildCustomerSuccessReceipt,
replyAttachmentsFromReceipt,
replyTextFromReceipt
} from '../src/reply-contract.js';
test('hotel arrangement uses the customer bilingual booking template', () => {
const operation = {
action: 'arrangement_hotel',
data: {
customer: { name: '老挝联泰' },
existing_refs: { kind: 'independent_order', identifier: 'LW-260603Z-A' },
arrangement: {
mode: 'create',
start_date: '2026-06-03',
end_date: '2026-06-04',
room_count: 6
}
}
};
const result = {
report: {
requery: {
subpage: {
arrangement_target: {
item_value: 'TWN',
quantity_value: '6'
}
}
}
}
};
const receipt = { success: true, group_number: 'LW-260603Z-A' };
const reply = buildCustomerSuccessReply(receipt, result, operation);
assert.ok(reply);
assert.equal(reply.profile, 'hotel-arrangement-lwlt-v1');
assert.equal(reply.text, [
'①NEW BOOKING',
'GROUP CODE: LW-260603Z-A',
'IN. 03/06/2026',
'OUT 04/06/2026',
'1Night. 6Twin',
'②新订单',
'团号:LW-260603Z-A 老挝联泰',
'入住日期:2026.06.03',
'退房日期:2026.06.04',
'几 晚:1晚',
'房型/房数:标间 6间'
].join('\n'));
});
test('hotel clear and update replies stay business-readable when the booking template is not applicable', () => {
const clearReply = buildCustomerSuccessReply(
{ success: true, group_number: 'LW-260603Z-A' },
{},
{
action: 'arrangement_hotel',
data: {
existing_refs: { identifier: 'LW-260603Z-A' },
arrangement: { mode: 'clear' }
}
}
);
assert.ok(clearReply);
assert.equal(clearReply.text, '酒店安排已清除\n团号:LW-260603Z-A');
const updateReply = buildCustomerSuccessReply(
{ success: true, group_number: 'LW-260603Z-A' },
{ report: { changes: [{ target: 'room_count', after: 9 }] } },
{
action: 'arrangement_hotel',
data: {
existing_refs: { identifier: 'LW-260603Z-A' },
arrangement: { mode: 'update', end_date: '2026-06-05', room_count: 9 }
}
}
);
assert.ok(updateReply);
assert.equal(updateReply.text, '酒店安排成功\n团号:LW-260603Z-A\n退房日期:2026-06-05\n房数:9间');
});
test('hotel replacement reply describes the new hotel and count while retaining the original dates', () => {
const reply = buildCustomerSuccessReply(
{ success: true, group_number: 'LW-TEST-A' },
{},
{
action: 'arrangement_hotel',
data: {
arrangement: {
mode: 'update',
resource: { id: '100', name: '原酒店' },
start_date: '2026-09-15',
end_date: '2026-09-16',
room_count: 8,
changes: { resource: { id: '270', name: '测试新酒店' }, room_count: 9 }
}
}
}
);
assert.equal(reply?.text, '酒店安排成功\n团号:LW-TEST-A\n资源:测试新酒店\n入住日期:2026-09-15\n退房日期:2026-09-16\n房数:9间');
assert.equal(reply?.fields.resource, '测试新酒店');
assert.equal(reply?.fields.quantity, 9);
});
test('batch newbooking receipt preserves every group number', () => {
const reply = buildCustomerSuccessReply(
{ success: true, group_number: 'LW-260603A-A', group_numbers: ['LW-260603A-A', 'LW-260605A-A'] },
{},
{ action: 'team_order_batch_create', data: {} }
);
assert.ok(reply);
assert.equal(reply.text, '批量下单成功\n团号:LW-260603A-A、LW-260605A-A');
});
test('update and file replies expose business fields without technical evidence', () => {
const updateReply = buildCustomerSuccessReply(
{ success: true, group_number: 'LW-260603A-A' },
{ report: { changes: [{ target: 'planned_capacity', after: 45 }] } },
{
action: 'order_update_shared_plan',
data: {
existing_refs: { identifier: 'LW-260603A-A' },
updates: { actions: [{ target: 'planned_capacity', operation: 'set', value: 45 }] }
}
}
);
assert.ok(updateReply);
assert.equal(updateReply.text, '散拼母团信息修改成功\n团号:LW-260603A-A\n计划收客数:45');
assert.doesNotMatch(updateReply.text, /sha256|verification|blocker|execution/iu);
const baseReceipt = buildCustomerSuccessReceipt(
{ success: true, group_number: 'LW-260603A-A' },
{
report: {
artifacts: [{
file_name: 'liantai-confirm.docx',
content_type: 'application/msword',
bytes: 28892,
download_url: '/api/tasks/TASK-1/artifacts/ART-1',
sha256: 'secret-hash'
}]
}
},
{ action: 'confirmation_export', data: { existing_refs: { identifier: 'LW-260603A-A' } } }
);
assert.equal(replyTextFromReceipt(baseReceipt), '团队文件已准备\n团号:LW-260603A-A\n文件:liantai-confirm.docx');
assert.deepEqual(replyAttachmentsFromReceipt(baseReceipt), [{
name: 'liantai-confirm.docx',
content_type: 'application/msword',
size: 28892,
download_url: '/api/tasks/TASK-1/artifacts/ART-1'
}]);
assert.doesNotMatch(JSON.stringify(baseReceipt), /secret-hash/);
});
test('visitor export reply exposes only the mobile XLSX delivery artifact', () => {
const receipt = buildCustomerSuccessReceipt(
{ success: true, group_number: 'LW-260816Z-A' },
{
report: {
artifacts: [
{
file_name: 'LW-260816Z-A Visitor.xls',
content_type: 'application/vnd.ms-excel',
bytes: 9377,
agentbus_visible: false,
download_url: '/api/tasks/TASK-VISITOR/artifacts/ART-XLS'
},
{
file_name: 'LW-260816Z-A Visitor.xlsx',
content_type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
bytes: 5211,
agentbus_visible: true,
download_url: '/api/tasks/TASK-VISITOR/artifacts/ART-XLSX'
}
]
}
},
{ action: 'confirmation_export', data: { existing_refs: { identifier: 'LW-260816Z-A' } } }
);
assert.equal(replyTextFromReceipt(receipt!), '团队文件已准备\n团号:LW-260816Z-A\n文件:LW-260816Z-A Visitor.xlsx');
assert.deepEqual(replyAttachmentsFromReceipt(receipt!), [{
name: 'LW-260816Z-A Visitor.xlsx',
content_type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
size: 5211,
download_url: '/api/tasks/TASK-VISITOR/artifacts/ART-XLSX'
}]);
assert.doesNotMatch(JSON.stringify(receipt), /Visitor\.xls(?!x)/u);
});
test('legacy ERP mojibake filenames are repaired when a stored receipt is read', () => {
const receipt = buildCustomerSuccessReceipt(
{ success: true, group_number: 'LW-261202A-A' },
{
report: {
artifacts: [{
file_name: 'LLW-261202A-A 客户确认书.doc',
content_type: 'application/msword',
bytes: 30649,
download_url: '/api/tasks/TASK-LEGACY/artifacts/ART-LEGACY'
}]
}
},
{ action: 'confirmation_export', data: { existing_refs: { identifier: 'LW-261202A-A' } } }
);
assert.equal(replyTextFromReceipt(receipt), '团队文件已准备\n团号:LW-261202A-A\n文件:LLW-261202A-A 客户确认书.doc');
assert.deepEqual(replyAttachmentsFromReceipt(receipt), [{
name: 'LLW-261202A-A 客户确认书.doc',
content_type: 'application/msword',
size: 30649,
download_url: '/api/tasks/TASK-LEGACY/artifacts/ART-LEGACY'
}]);
});
test('arrangement replacement receipts report the new resource and quantity with original dates', () => {
for (const action of ['arrangement_guide', 'arrangement_vehicle', 'arrangement_transport', 'arrangement_other']) {
const guide = action === 'arrangement_guide';
const reply = buildCustomerSuccessReply({ success: true, group_number: 'LW-TEST-UPDATE' }, {}, {
action,
data: { arrangement: {
mode: 'update', [guide ? 'resource' : 'supplier']: { name: '原资源' }, quantity: 3,
date: '2026-09-15',
changes: { [guide ? 'resource' : 'supplier']: { name: '新资源' }, ...(!guide ? { quantity: 8, item: '新项目' } : {}) }
} }
});
assert.equal(reply?.fields.resource, '新资源');
assert.equal(reply?.fields.date, '2026-09-15');
if (!guide) { assert.equal(reply?.fields.quantity, 8); assert.equal(reply?.fields.item, '新项目'); }
assert.doesNotMatch(reply?.text || '', /原资源/);
}
});