const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const test = require('node:test');
const RelativePages = require('../public/js/relative-pages.js');
test('亲友记录页只从 URL 读取安全的真实家谱和记录编号', () => {
assert.equal(
RelativePages.getCurrentGenealogyId('?genealogyId=2060000000000000001&relativeId=2060000000000000002'),
'2060000000000000001'
);
assert.equal(
RelativePages.getCurrentRelativeId('?genealogyId=2060000000000000001&relativeId=2060000000000000002'),
'2060000000000000002'
);
assert.equal(RelativePages.getCurrentGenealogyId(''), '');
assert.equal(RelativePages.getCurrentRelativeId('?relativeId=unsafe'), '');
});
test('亲友记录写入只构造 YAML RelativeRecordBody 并转换后端时间格式', () => {
assert.deepEqual(RelativePages.buildRelativeRecordBody({
relativeName: ' 王叔 ',
relationName: ' 长辈 ',
eventName: ' 寿宴 ',
eventTime: '2026-07-24T09:30',
giftAmount: '500.5',
recordContent: ' 出席 ',
mediaOssIds: '2060000000000000003,2060000000000000004',
sortOrder: '3',
status: '0',
relativeId: 'should-not-send',
appUserId: 'should-not-send'
}), {
relativeName: '王叔',
relationName: '长辈',
eventName: '寿宴',
eventTime: '2026-07-24 09:30:00',
giftAmount: 500.5,
recordContent: '出席',
mediaOssIds: '2060000000000000003,2060000000000000004',
sortOrder: 3,
status: '0'
});
});
test('亲友记录校验姓名、金额、时间、附件、排序和可重读状态', () => {
assert.equal(RelativePages.validateRelativeRecordBody({ relativeName: '' }), '请填写亲友姓名');
assert.equal(
RelativePages.validateRelativeRecordBody(RelativePages.buildRelativeRecordBody({ relativeName: '王叔', giftAmount: '一百' })),
'礼金金额必须是有限数字'
);
assert.equal(
RelativePages.validateRelativeRecordBody(RelativePages.buildRelativeRecordBody({
relativeName: '王叔',
giftAmount: '9007199254740993.01'
})),
'礼金金额超出浏览器可安全提交的精度'
);
assert.equal(
RelativePages.validateRelativeRecordBody(RelativePages.buildRelativeRecordBody({ relativeName: '王叔', giftAmount: '500.50' })),
''
);
assert.equal(
RelativePages.validateRelativeRecordBody({ relativeName: '王叔', eventTime: '2026-07-24T09:30:00+08:00' }),
'事件时间格式无效'
);
assert.equal(
RelativePages.validateRelativeRecordBody({ relativeName: '王叔', eventTime: '2026-99-24 09:30:00' }),
'事件时间格式无效'
);
assert.equal(
RelativePages.validateRelativeRecordBody({ relativeName: '王叔', mediaOssIds: '101, 102' }),
'附件上传结果无效'
);
assert.equal(
RelativePages.validateRelativeRecordBody(RelativePages.buildRelativeRecordBody({ relativeName: '王叔', sortOrder: '1.5' })),
'排序值必须是安全整数'
);
assert.equal(
RelativePages.validateRelativeRecordBody({ relativeName: '王叔', status: '1' }),
'当前 PC 无法重新读取停用记录,暂不开放停用'
);
assert.equal(
RelativePages.validateRelativeRecordBody({ relativeName: '王叔', giftAmount: -500.25, status: '0' }),
''
);
});
test('亲友记录响应使用 PC RelativeRecordVo 并保留 BigDecimal 字符串', () => {
assert.deepEqual(RelativePages.normalizeRelativeRecord({
relativeId: '2060000000000000001',
genealogyId: '2060000000000000002',
genealogyNo: 'G20260729001',
genealogyName: '叶氏家谱',
surname: '叶',
appUserId: '2060000000000000003',
appUserNickName: '叶子',
appUserPhone: '19100000000',
relativeName: '王叔',
relationName: '长辈',
eventName: '寿宴',
eventTime: '2026-07-24 09:30:00',
giftAmount: '500.50',
recordContent: '出席',
sortOrder: 1,
status: '0',
remark: '往来记录'
}), {
relativeId: '2060000000000000001',
genealogyId: '2060000000000000002',
genealogyNo: 'G20260729001',
genealogyName: '叶氏家谱',
surname: '叶',
appUserId: '2060000000000000003',
appUserNickName: '叶子',
appUserPhone: '19100000000',
relativeName: '王叔',
relationName: '长辈',
eventName: '寿宴',
eventTime: '2026-07-24 09:30:00',
giftAmount: '500.50',
recordContent: '出席',
sortOrder: 1,
status: '0',
remark: '往来记录'
});
assert.equal(RelativePages.normalizeRelativeRecord({
relativeId: Number('2060000000000000001'),
genealogyId: '2',
relativeName: '王叔',
status: '0'
}), null);
});
test('亲友记录渲染业务字段但不暴露手机号、OSS ID 或未转义内容', () => {
const record = RelativePages.normalizeRelativeRecord({
relativeId: '1',
genealogyId: '2',
appUserId: '3',
appUserNickName: '叶子',
appUserPhone: '19100000000',
relativeName: '',
relationName: '长辈',
eventName: '寿宴',
eventTime: '2026-07-24 09:30:00',
giftAmount: '500.50',
recordContent: '
出席',
sortOrder: 1,
status: '0'
});
const html = RelativePages.renderRelativeDetail(record);
assert.doesNotMatch(html, /