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 || '', /原资源/);
}
});