Files
Web_BLS_RCUAction_Server/bls-rcu-action-backend/tests/processor.test.js
XuJiacheng 339db6f95f feat(processor): 添加循环名称自动生成的配置开关
新增环境变量 ENABLE_LOOP_NAME_AUTO_GENERATION 用于控制当缓存未命中时是否自动生成循环名称。当设置为 false 时,系统将不再生成 [类型-地址-回路] 格式的备用名称,而是直接返回 null。更新了配置文件、处理器逻辑并添加了相应的单元测试。
2026-02-03 09:26:01 +08:00

230 lines
6.4 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 { describe, it, expect } from 'vitest';
import { buildRowsFromPayload } from '../src/processor/index.js';
import projectMetadata from '../src/cache/projectMetadata.js';
describe('Processor Logic', () => {
const basePayload = {
ts_ms: 1700000000000,
hotel_id: 1001,
room_id: '8001',
device_id: 'dev_001',
direction: '上报',
cmd_word: '0x36',
frame_id: 1,
udp_raw: 'AA552000543353413610CD63088151000000000000000001180003000114005ECB',
sys_lock_status: 0,
report_count: 0,
fault_count: 0
};
it('should validate required fields', () => {
expect(() => buildRowsFromPayload({})).toThrow();
expect(() => buildRowsFromPayload({ ...basePayload, ts_ms: undefined })).toThrow();
});
it('should handle 0x36 Status Report with device list', () => {
const payload = {
...basePayload,
direction: '上报',
cmd_word: '0x36',
report_count: 2,
device_list: [
{ dev_type: 1, dev_addr: 10, dev_loop: 1, dev_data: 100 },
{ dev_type: 1, dev_addr: 11, dev_loop: 2, dev_data: 0 }
]
};
const rows = buildRowsFromPayload(payload);
expect(rows).toHaveLength(2);
expect(rows[0].action_type).toBe('设备回路状态');
expect(rows[0].dev_addr).toBe(10);
expect(rows[1].dev_addr).toBe(11);
expect(rows[0].details.device_list).toHaveLength(2);
});
it('should handle 0x36 Fault Report', () => {
const payload = {
...basePayload,
direction: '上报',
cmd_word: '0x36',
fault_count: 1,
fault_list: [
{ dev_type: 1, dev_addr: 10, dev_loop: 1, error_type: 2, error_data: 5 }
]
};
const rows = buildRowsFromPayload(payload);
expect(rows).toHaveLength(1);
expect(rows[0].action_type).toBe('设备回路状态');
expect(rows[0].error_type).toBe(2);
});
it('should handle 0x36 Mixed Report (Status + Fault)', () => {
const payload = {
...basePayload,
direction: '上报',
cmd_word: '0x36',
report_count: 1,
fault_count: 1,
device_list: [{ dev_type: 1, dev_addr: 10, dev_loop: 1, dev_data: 100 }],
fault_list: [{ dev_type: 1, dev_addr: 10, dev_loop: 1, error_type: 2, error_data: 5 }]
};
const rows = buildRowsFromPayload(payload);
expect(rows).toHaveLength(2); // 1 status + 1 fault
});
it('should handle 0x0F Control Command', () => {
const payload = {
...basePayload,
direction: '下发',
cmd_word: '0x0F',
control_list: [
{ dev_type: 1, dev_addr: 10, dev_loop: 1, type_l: 1, type_h: 2 }
]
};
const rows = buildRowsFromPayload(payload);
expect(rows).toHaveLength(1);
expect(rows[0].action_type).toBe('下发控制');
expect(rows[0].type_l).toBe(1);
expect(rows[0].type_h).toBe(2);
expect(rows[0].dev_loop).toBe(1);
});
it('should handle 0x0F ACK', () => {
const payload = {
...basePayload,
direction: '上报',
cmd_word: '0x0F',
control_list: [
{ dev_type: 1, dev_addr: 1, dev_loop: 1, dev_data: 1, type_h: 0 }
]
};
const rows = buildRowsFromPayload(payload);
expect(rows).toHaveLength(1);
expect(rows[0].action_type).toBe('设备回路状态');
});
it('should fallback when lists are empty for 0x36', () => {
const payload = {
...basePayload,
direction: '上报',
cmd_word: '0x36',
device_list: [],
fault_list: []
};
const rows = buildRowsFromPayload(payload);
expect(rows).toHaveLength(1);
expect(rows[0].action_type).toBe('设备回路状态');
expect(rows[0].dev_type).toBeNull();
});
it('should classify 0x36 as 用户操作 when dev_type is user-operated', () => {
const payload = {
...basePayload,
direction: '上报',
cmd_word: '0x36',
device_list: [
{ dev_type: 2, dev_addr: 10, dev_loop: 1, dev_data: 100 }
]
};
const rows = buildRowsFromPayload(payload);
expect(rows).toHaveLength(1);
expect(rows[0].action_type).toBe('用户操作');
});
it('should store udp_raw as base64 when input is hex string', () => {
const payload = {
...basePayload,
direction: '上报',
cmd_word: '0x36',
device_list: [],
fault_list: []
};
const expectedBase64 = Buffer.from(payload.udp_raw.replace(/[\s:]/g, ''), 'hex').toString('base64');
const rows = buildRowsFromPayload(payload);
expect(rows).toHaveLength(1);
expect(rows[0].udp_raw).toBe(expectedBase64);
});
it('should keep udp_raw unchanged when input is not hex string', () => {
const payload = {
...basePayload,
udp_raw: 'YWJjMTIz',
direction: '上报',
cmd_word: '0x36',
device_list: [
{ dev_type: 1, dev_addr: 10, dev_loop: 1, dev_data: 100 }
]
};
const rows = buildRowsFromPayload(payload);
expect(rows[0].udp_raw).toBe('YWJjMTIz');
});
it('should default extra to empty object when not provided', () => {
const payload = {
...basePayload,
direction: '上报',
cmd_word: '0x36',
device_list: [],
fault_list: []
};
const rows = buildRowsFromPayload(payload);
expect(rows[0].extra).toEqual({});
});
it('should preserve extra when provided by upstream', () => {
const payload = {
...basePayload,
direction: '上报',
cmd_word: '0x36',
device_list: [],
fault_list: [],
extra: {
source: 'upstream',
trace_id: 'trace-123'
}
};
const rows = buildRowsFromPayload(payload);
expect(rows[0].extra).toEqual({
source: 'upstream',
trace_id: 'trace-123'
});
});
it('should enrich rows with loop_name from metadata', () => {
// Mock metadata
projectMetadata.roomMap.set('dev_001', 101);
// Key format: roomTypeId:00Type00Addr00Loop
// type=1, addr=10, loop=1 -> 001010001
projectMetadata.loopMap.set('101:001010001', 'Main Chandelier');
const payload = {
...basePayload,
direction: '上报',
cmd_word: '0x36',
device_list: [
{ dev_type: 1, dev_addr: 10, dev_loop: 1, dev_data: 100 }, // Should match 001010001
{ dev_type: 1, dev_addr: 10, dev_loop: 2, dev_data: 0 } // Should not match (001010002) -> Fallback
]
};
const rows = buildRowsFromPayload(payload);
expect(rows[0].loop_name).toBe('Main Chandelier');
// dev_type 1 is '强电继电器(输出状态)'
expect(rows[1].loop_name).toBe('[1强电继电器输出状态-10-2]');
});
});