235 lines
8.5 KiB
TypeScript
235 lines
8.5 KiB
TypeScript
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 || '', /原资源/);
|
||
}
|
||
});
|