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('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' }]); });