AutoRpa/state_center_web/src/stateMachine.test.mjs

268 lines
10 KiB
JavaScript
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 {
actionLabel,
allowedTaskActions,
buildBusinessOverview,
buildAcceptanceChecklist,
buildJobReferenceChain,
buildNodeCallbackRequestBody,
buildNodeDetails,
buildNodeCallbackTemplate,
jobStatusBucketLabel,
jobStatusBucketMatches,
nodeCallbackActionLabel,
reasonLabel,
roleLabel,
taskCandidates,
taskStatusLabel,
} from './stateMachine.ts';
test('待办相关编码全部显示为中文', () => {
assert.equal(roleLabel('PLANNER'), '计划员');
assert.equal(taskStatusLabel('OPEN'), '待处理');
assert.equal(actionLabel('SELECT_CANDIDATE'), '选择候选货品');
assert.equal(reasonLabel('MATCH_MULTI'), '存在多个候选货品');
assert.equal(reasonLabel('SOURCE_ADD'), '需要新增货源');
});
test('候选货品兼容数据库返回的数组和 JSON 文本', () => {
const task = {
task_id: 'TASK-1',
job_id: 'JOB-1',
node_code: 'PRODUCT_MATCH',
role_code: 'PLANNER',
status: 'OPEN',
title: '对码待确认',
reason_type: 'MATCH_MULTI',
};
assert.equal(taskCandidates({ ...task, candidates_json: '[{"goods_id":"G-1"}]' })[0]?.goods_id, 'G-1');
assert.equal(taskCandidates({ ...task, candidates_json: [{ goods_id: 'G-2' }] })[0]?.goods_id, 'G-2');
});
test('对码待办只提供后端支持的业务动作', () => {
assert.deepEqual(
allowedTaskActions({
task_id: 'TASK-1',
job_id: 'JOB-1',
node_code: 'PRODUCT_MATCH',
role_code: 'PLANNER',
status: 'OPEN',
title: '对码待确认',
reason_type: 'MATCH_MULTI',
}),
['SELECT_CANDIDATE', 'TRANSFER_MANUAL', 'TERMINATE_JOB'],
);
});
test('合同建单成功回调模板带必填合同编号', () => {
const template = buildNodeCallbackTemplate('CONTRACT_CREATE', 'complete');
assert.equal(template.attemptNo, 1);
assert.equal(template.executorPlatform, 'iS-RPA');
assert.equal(template.businessRefs.contractSystemNo, 'HT-联调-001');
assert.equal(template.artifacts[0].artifactType, 'CONTRACT_PDF');
});
test('节点失败回调模板带后端必填错误字段', () => {
const template = buildNodeCallbackTemplate('REBATE_IMPORT', 'fail');
assert.equal(nodeCallbackActionLabel('fail'), '失败');
assert.equal(template.errorCode, 'SYS_PAGE_TIMEOUT');
assert.equal(template.retryable, true);
assert.equal(template.routeRole, 'OPS');
});
test('成功回调请求自动补幂等键并保留手工覆盖字段', () => {
const body = buildNodeCallbackRequestBody(
'complete',
'CONTRACT_CREATE',
{
summary: { contractAmount: 1200 },
businessRefs: { contractSystemNo: 'HT-20260811-001' },
},
'idem-001',
);
assert.equal(body.idempotencyKey, 'idem-001');
assert.equal(body.businessRefs.contractSystemNo, 'HT-20260811-001');
assert.equal(body.summary.contractAmount, 1200);
});
test('单据状态筛选按验收口径归类', () => {
assert.equal(jobStatusBucketLabel('ACTIVE'), '进行中');
assert.equal(jobStatusBucketMatches('ALL', 'SUCCESS'), true);
assert.equal(jobStatusBucketMatches('ACTIVE', 'RUNNING'), true);
assert.equal(jobStatusBucketMatches('ACTIVE', 'WAITING_EXTERNAL'), true);
assert.equal(jobStatusBucketMatches('WAITING', 'WAITING_USER'), true);
assert.equal(jobStatusBucketMatches('WAITING', 'RUNNING'), false);
assert.equal(jobStatusBucketMatches('FAILED', 'FAILED_MANUAL'), true);
assert.equal(jobStatusBucketMatches('SUCCESS', 'SUCCESS'), true);
});
test('节点详情按节点关联执行记录、证据和待办', () => {
const details = buildNodeDetails(
[
{ node_code: 'ORDER_FETCH', display_status: 'SUCCESS', attempt_no: 1, status: 'SUCCESS' },
{ node_code: 'SOURCE_ADD', display_status: 'WAITING_USER', attempt_no: 1, status: 'WAITING_USER' },
],
[
{
node_code: 'ORDER_FETCH',
attempt_no: 1,
status: 'SUCCESS',
started_at: '2026-08-11T09:00:00',
ended_at: '2026-08-11T09:00:12',
output_json: '{"summary":{"orderCount":2}}',
},
],
[
{
node_code: 'ORDER_FETCH',
attempt_no: 1,
artifact_type: 'SOURCE_EXCEL',
storage_path: '/data/jobs/JOB-1/orders.xlsx',
},
],
[
{
task_id: 'TASK-1',
job_id: 'JOB-1',
node_code: 'SOURCE_ADD',
role_code: 'PURCHASER',
status: 'OPEN',
title: '新增货源',
reason_type: 'SOURCE_ADD',
},
],
);
assert.equal(details[0].summaryText, 'orderCount:2');
assert.equal(details[0].artifacts[0].storage_path, '/data/jobs/JOB-1/orders.xlsx');
assert.equal(details[1].openTask?.task_id, 'TASK-1');
});
test('编号链能标出已回填和待回填项', () => {
const chain = buildJobReferenceChain({
job_id: 'JOB-1',
supplier_name: '重庆亿昊药业',
order_date: '2026-08-04',
source_type: 'YYC',
status: 'RUNNING',
current_node_code: 'CONTRACT_CREATE',
order_no_list_json: '["ZQD-1","ZQD-2"]',
contract_system_no: 'HT-1',
rebate_order_no: null,
m5_order_no: '',
receipt_notice_no: 'SH-1',
});
assert.deepEqual(chain.map((item) => item.status), ['DONE', 'DONE', 'MISSING', 'MISSING', 'DONE']);
assert.equal(chain[0].value, 'ZQD-1、ZQD-2');
});
test('业务概览只保留首屏必须信息', () => {
const overview = buildBusinessOverview(
{
job_id: 'JOB-1',
supplier_name: '重庆亿昊药业',
order_date: '2026-08-04',
source_type: 'YYC',
status: 'SUCCESS',
current_node_code: 'ARCHIVE_JOB',
current_role: 'OPS',
contract_system_no: 'HT-1',
rebate_order_no: 'FL-1',
m5_order_no: 'M5-1',
receipt_notice_no: 'SH-1',
order_no_list_json: '["ZQD-1","ZQD-2"]',
},
[
{ node_code: 'ORDER_FETCH', display_status: 'SUCCESS', attempt_no: 1, status: 'SUCCESS' },
{ node_code: 'PRODUCT_MATCH', display_status: 'SUCCESS', attempt_no: 1, status: 'SUCCESS' },
{ node_code: 'SOURCE_ADD', display_status: 'WAITING_USER', attempt_no: 1, status: 'WAITING_USER' },
{ node_code: 'CONTRACT_CREATE', display_status: 'PENDING' },
{ node_code: 'ARCHIVE_JOB', display_status: 'SUCCESS', attempt_no: 1, status: 'SUCCESS' },
],
[
{ task_id: 'TASK-1', job_id: 'JOB-1', node_code: 'SOURCE_ADD', role_code: 'PURCHASER', status: 'OPEN', title: '新增货源:重庆亿昊药业', reason_type: 'SOURCE_ADD' },
],
[
{ node_code: 'ORDER_FETCH', attempt_no: 1, artifact_type: 'SOURCE_EXCEL', storage_path: '/data/jobs/JOB-1/orders.xlsx' },
],
{
node_order: ['ORDER_FETCH', 'PRODUCT_MATCH', 'SOURCE_ADD', 'CONTRACT_CREATE', 'ARCHIVE_JOB'],
transitions: {},
conditional_nodes: [],
human_node_roles: {},
node_executor: {},
node_labels: {
ORDER_FETCH: '订单获取',
PRODUCT_MATCH: '对码',
SOURCE_ADD: '新增货源',
CONTRACT_CREATE: '合同建单',
ARCHIVE_JOB: '归档',
},
executor_labels: {},
status_labels: {
SUCCESS: '已完成',
WAITING_USER: '等待人工',
PENDING: '待处理',
},
},
);
assert.equal(overview.header.currentStatusText, '已完成');
assert.equal(overview.header.currentNodeText, '归档');
assert.equal(overview.referenceChain.length, 5);
assert.equal(overview.primaryTask?.title, '新增货源:重庆亿昊药业');
assert.equal(overview.flow.visibleNodes.length, 5);
assert.equal(overview.summary.evidenceCount, 1);
});
test('验收收口能识别真库一单的完成和缺口', () => {
const checklist = buildAcceptanceChecklist(
{
job_id: 'JOB-1',
supplier_name: '重庆亿昊药业',
order_date: '2026-08-04',
source_type: 'YYC',
status: 'SUCCESS',
current_node_code: 'ARCHIVE_JOB',
contract_system_no: 'HT-1',
rebate_order_no: 'FL-1',
m5_order_no: 'M5-1',
receipt_notice_no: 'SH-1',
order_no_list_json: '["ZQD-1"]',
},
[
{ node_code: 'ORDER_FETCH', display_status: 'SUCCESS', attempt_no: 1, status: 'SUCCESS' },
{ node_code: 'OCR_EXTRACT', display_status: 'SKIPPED' },
{ node_code: 'PRODUCT_MATCH', display_status: 'SUCCESS', attempt_no: 1, status: 'SUCCESS' },
{ node_code: 'SOURCE_ADD', display_status: 'SUCCESS', attempt_no: 1, status: 'SUCCESS' },
{ node_code: 'CONTRACT_CREATE', display_status: 'SUCCESS', attempt_no: 1, status: 'SUCCESS' },
{ node_code: 'CONTRACT_APPROVE_WAIT', display_status: 'SUCCESS', attempt_no: 1, status: 'SUCCESS' },
{ node_code: 'REBATE_HEADER', display_status: 'SUCCESS', attempt_no: 1, status: 'SUCCESS' },
{ node_code: 'REBATE_IMPORT', display_status: 'SUCCESS', attempt_no: 1, status: 'SUCCESS' },
{ node_code: 'REBATE_SUBMIT', display_status: 'SUCCESS', attempt_no: 1, status: 'SUCCESS' },
{ node_code: 'M5_CREATE_ORDER', display_status: 'SUCCESS', attempt_no: 1, status: 'SUCCESS' },
{ node_code: 'M5_MANUAL_CONFIRM', display_status: 'SUCCESS', attempt_no: 1, status: 'SUCCESS' },
{ node_code: 'M5_RECEIPT_NOTICE', display_status: 'SUCCESS', attempt_no: 1, status: 'SUCCESS' },
{ node_code: 'ARCHIVE_JOB', display_status: 'SUCCESS', attempt_no: 1, status: 'SUCCESS' },
],
[
{ node_code: 'ORDER_FETCH', attempt_no: 1, status: 'SUCCESS', started_at: '2026-08-11T09:00:00', ended_at: '2026-08-11T09:00:12', output_json: '{"summary":{"orderCount":1}}' },
{ node_code: 'CONTRACT_CREATE', attempt_no: 1, status: 'SUCCESS', started_at: '2026-08-11T09:02:00', ended_at: '2026-08-11T09:02:12', output_json: '{"summary":{"saved":true}}' },
],
[
{ node_code: 'ORDER_FETCH', attempt_no: 1, artifact_type: 'SOURCE_EXCEL', storage_path: '/data/jobs/JOB-1/orders.xlsx' },
],
[
{ task_id: 'TASK-1', job_id: 'JOB-1', node_code: 'M5_MANUAL_CONFIRM', role_code: 'PURCHASER', status: 'SUBMITTED', title: 'M5人工确认', reason_type: 'SOURCE_ADD' },
],
);
assert.equal(checklist.find((item) => item.key === 'timeline_complete')?.status, 'DONE');
assert.equal(checklist.find((item) => item.key === 'node_detail_coverage')?.status, 'PARTIAL');
assert.equal(checklist.find((item) => item.key === 'reference_chain')?.status, 'DONE');
});