237 lines
8.2 KiB
JavaScript
237 lines
8.2 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { createRequire } from 'node:module';
|
|
|
|
const require = createRequire(import.meta.url);
|
|
const plans = require('../chrome-extension/ltjt-order-assistant/operation-plans.js');
|
|
|
|
const baseCreate = {
|
|
action: 'team_order_create',
|
|
order_nature: 'test',
|
|
submit_mode: 'dry_run',
|
|
data: {
|
|
test_marker: 'PLAN-TEST',
|
|
product: { name: '遇见老挝' },
|
|
departure_dates: ['2026-08-11'],
|
|
passenger_counts: { adult: 2, expected_total: 2 },
|
|
op_user: { name: '测试OP' },
|
|
sales_user: { name: '测试销售' },
|
|
},
|
|
};
|
|
|
|
test('team-single remains the only live browser execution path', () => {
|
|
const result = plans.validateOperation(baseCreate);
|
|
assert.equal(result.ok, true);
|
|
assert.equal(result.action, 'team_order_create');
|
|
assert.equal(result.execution, 'browser_team_single');
|
|
assert.equal(result.noErpWrite, false);
|
|
});
|
|
|
|
test('team-batch absorbs the handoff fallback rule without enabling native batch submit', () => {
|
|
const operation = {
|
|
...baseCreate,
|
|
action: 'team_order_batch_create',
|
|
data: {
|
|
...baseCreate.data,
|
|
departure_dates: ['2026-08-11', '2026-08-18'],
|
|
},
|
|
};
|
|
const result = plans.validateOperation(operation);
|
|
assert.equal(result.ok, true);
|
|
assert.equal(result.execution, 'browser_team_single_fallback');
|
|
assert.equal(result.noErpWrite, false);
|
|
assert.match(result.warnings.join('\n'), /DoInfoJHs/);
|
|
|
|
const expanded = plans.expandTeamBatch(operation);
|
|
assert.equal(expanded.length, 2);
|
|
assert.deepEqual(expanded.map((item) => item.data.departure_dates[0]), ['2026-8-11', '2026-8-18']);
|
|
assert.deepEqual(expanded.map((item) => item.data.test_marker), [
|
|
'PLAN-TEST-D20260811',
|
|
'PLAN-TEST-D20260818',
|
|
]);
|
|
});
|
|
|
|
test('order updates require an identifier and only run a no-write browser preview', () => {
|
|
const result = plans.validateOperation({
|
|
action: 'order_update',
|
|
identifier: 'LW-270520A-A',
|
|
data: {
|
|
updates: {
|
|
actions: [{ target: 'rooms.TWN', operation: 'set', value: 10 }],
|
|
},
|
|
},
|
|
});
|
|
assert.equal(result.ok, true);
|
|
assert.equal(result.execution, 'browser_dry_run');
|
|
assert.equal(result.noErpWrite, true);
|
|
assert.equal(result.identifier, 'LW-270520A-A');
|
|
});
|
|
|
|
test('parent confirmation export is blocked before any browser operation', () => {
|
|
const result = plans.validateOperation({
|
|
action: 'confirmation_export',
|
|
route: 'split_parent',
|
|
identifier: 'PARENT-EXAMPLE',
|
|
data: { existing_refs: { parent_group_no: 'PARENT-EXAMPLE' } },
|
|
});
|
|
assert.equal(result.ok, false);
|
|
assert.match(result.blockers.join('\n'), /母团/);
|
|
assert.equal(result.execution, 'blocked');
|
|
});
|
|
|
|
test('confirmation export normalizes supported aliases and blocks unknown artifacts', () => {
|
|
const valid = plans.validateOperation({
|
|
action: 'confirmation_export',
|
|
identifier: 'LW-270520A-A',
|
|
exportTypes: ['confirmation', 'job'],
|
|
data: { existing_refs: { identifier: 'LW-270520A-A' } },
|
|
});
|
|
assert.equal(valid.ok, true);
|
|
assert.deepEqual(valid.exportTypes, ['xingyou-confirm', 'job-order']);
|
|
|
|
const invalid = plans.validateOperation({
|
|
action: 'confirmation_export',
|
|
identifier: 'LW-270520A-A',
|
|
exportTypes: ['visitor-detail'],
|
|
data: { existing_refs: { identifier: 'LW-270520A-A' } },
|
|
});
|
|
assert.equal(invalid.ok, false);
|
|
assert.match(invalid.blockers.join('\n'), /不支持/);
|
|
});
|
|
|
|
test('unknown operations fail closed', () => {
|
|
const result = plans.validateOperation({ action: 'delete_order', data: {} });
|
|
assert.equal(result.ok, false);
|
|
assert.equal(result.execution, 'blocked');
|
|
assert.match(result.blockers.join('\n'), /不支持/);
|
|
});
|
|
|
|
test('normalizes handoff create tasks without inventing missing ERP values', () => {
|
|
const operation = plans.normalizeOperation({
|
|
operation: 'create_order',
|
|
route: 'team_single',
|
|
fields: {
|
|
productName: '遇见老挝',
|
|
departureDate: '2026-08-11',
|
|
pax: 4,
|
|
op: '测试OP',
|
|
salesperson: '测试销售',
|
|
},
|
|
originalText: '交接包格式任务',
|
|
});
|
|
assert.equal(operation.action, 'team_order_create');
|
|
assert.equal(operation.data.product.name, '遇见老挝');
|
|
assert.equal(operation.data.passenger_counts.adult, 4);
|
|
assert.equal(operation.data.customer.name, '');
|
|
assert.equal(operation.data.test_marker, '');
|
|
});
|
|
|
|
test('routes a test split-parent task into the guarded live browser adapter', () => {
|
|
const operation = plans.normalizeOperation({
|
|
operation: 'create_order',
|
|
route: 'split_parent',
|
|
fields: {
|
|
productName: '示例线路',
|
|
departureDates: ['2026-08-15'],
|
|
cycle: 'weekly',
|
|
plannedPax: 20,
|
|
groupsPerDate: 1,
|
|
bookingCustomer: '测试渠道',
|
|
followOp: '测试OP',
|
|
salesperson: '测试销售',
|
|
orderNature: 'test',
|
|
testMarker: 'SPLIT-PARENT-TEST',
|
|
},
|
|
});
|
|
const result = plans.validateOperation(operation);
|
|
assert.equal(operation.action, 'shared_plan_create');
|
|
assert.equal(result.ok, true);
|
|
assert.equal(result.execution, 'browser_live_split_parent');
|
|
assert.equal(result.definition.liveSubmit, 'test-only');
|
|
assert.equal(operation.data.planned_capacity, 20);
|
|
assert.equal(operation.data.op_user.name, '测试OP');
|
|
});
|
|
|
|
test('absorbs split-child parent reference without fabricating a product or parent id', () => {
|
|
const operation = plans.normalizeOperation({
|
|
operation: 'create_order',
|
|
route: 'split_child',
|
|
fields: {
|
|
parentGroupNo: 'PARENT-EXAMPLE',
|
|
departureDate: '2026-08-15',
|
|
bookingCustomer: '测试渠道',
|
|
pax: 8,
|
|
prices: { adult: 100 },
|
|
op: '测试OP',
|
|
salesperson: '测试销售',
|
|
orderNature: 'test',
|
|
testMarker: 'SPLIT-CHILD-TEST',
|
|
},
|
|
});
|
|
const result = plans.validateOperation(operation);
|
|
assert.equal(operation.action, 'shared_child_order_create');
|
|
assert.equal(result.ok, true);
|
|
assert.equal(operation.data.parent_group_no, 'PARENT-EXAMPLE');
|
|
assert.equal(operation.data.product.name, '');
|
|
assert.equal(result.execution, 'browser_live_split_child');
|
|
});
|
|
|
|
test('routes a traveler-only handoff update into the traveler import business action', () => {
|
|
const operation = plans.normalizeOperation({
|
|
operation: 'update_order',
|
|
identifier: 'LW-270520A-A',
|
|
updatePlan: {
|
|
actions: [{ target: 'travelerList', operation: 'traveler_upsert', value: { mode: 'append_and_verify' } }],
|
|
},
|
|
attachments: ['inputs/traveler-list-example.xlsx'],
|
|
});
|
|
const result = plans.validateOperation(operation);
|
|
assert.equal(operation.action, 'passenger_list_import');
|
|
assert.equal(result.ok, true);
|
|
assert.equal(result.execution, 'browser_dry_run');
|
|
assert.equal(result.definition.dryRunAdapter, 'inspect_existing_order_traveler');
|
|
});
|
|
|
|
test('applies room and remark updates to a browser-read snapshot without writing ERP', () => {
|
|
const preview = plans.buildUpdatePreview({
|
|
action: 'order_update',
|
|
identifier: 'LW-270520A-A',
|
|
data: {
|
|
updates: {
|
|
actions: [
|
|
{ target: 'rooms.TWN', operation: 'delta', value: 2 },
|
|
{ target: 'remark', operation: 'append', value: '客户补充备注' },
|
|
],
|
|
},
|
|
},
|
|
}, {
|
|
rooms: { TWN: 3 },
|
|
pax: { adult: 4 },
|
|
prices: { adult: 100 },
|
|
remark: '原备注',
|
|
});
|
|
assert.equal(preview.status, 'ready');
|
|
assert.equal(preview.snapshot.rooms.TWN, 5);
|
|
assert.match(preview.snapshot.remark, /原备注/);
|
|
assert.match(preview.snapshot.remark, /客户补充备注/);
|
|
assert.equal(preview.no_erp_write, true);
|
|
});
|
|
|
|
test('builds export-only source paths and never emits a save action', () => {
|
|
const plan = plans.buildConfirmationExportPlan({
|
|
action: 'confirmation_export',
|
|
identifier: 'LW-270520A-A',
|
|
exportTypes: ['confirmation', 'liantai', 'job'],
|
|
data: { existing_refs: { identifier: 'LW-270520A-A' } },
|
|
}, { ddid: '123', tid: '456' });
|
|
assert.equal(plan.export_only, true);
|
|
assert.equal(plan.never_resave, true);
|
|
assert.deepEqual(plan.types, ['xingyou-confirm', 'liantai-confirm', 'job-order']);
|
|
assert.deepEqual(plan.artifacts.map((item) => item.path), [
|
|
'/System/Business/orders_confirm_news.asp',
|
|
'/System/Business/orders_confirm_new.asp',
|
|
'/System/Business/teams_beian1.asp',
|
|
]);
|
|
assert.equal(plan.no_erp_write, true);
|
|
});
|