Files
jiapu/tests/album-pages.test.js
T
fizzleaf fb1743aa2a feat(api): 完善家谱系统API客户端契约
- 实现家谱管理相关方法,包括创建、详情、概览、我的家谱和选项查询
- 添加家谱加入申请功能,支持申请、审核、取消和待审核列表操作
- 集成通知详情获取方法和通知ID安全验证机制
- 完善功德记录、谱文、相册、视频、祭祀活动的完整CRUD操作契约
- 实现家谱成员管理功能,包含成员列表、更新、移除和转让所有者操作
- 优化路径ID验证逻辑,拒绝不安全的数值ID并提供明确错误提示
- 更新测试用例以验证所有新增API方法的路径和请求体白名单机制
2026-07-29 16:57:27 +08:00

196 lines
7.0 KiB
JavaScript

const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const test = require('node:test');
const modulePath = path.join(__dirname, '..', 'public', 'js', 'album-pages.js');
const AlbumPages = fs.existsSync(modulePath) ? require(modulePath) : {};
function requireFunction(name) {
assert.equal(typeof AlbumPages[name], 'function', `缺少 AlbumPages.${name}`);
return AlbumPages[name];
}
test('相册页面只从 URL 读取安全的家谱、相册和照片编号', () => {
const getCurrentGenealogyId = requireFunction('getCurrentGenealogyId');
const getCurrentAlbumId = requireFunction('getCurrentAlbumId');
assert.equal(getCurrentGenealogyId('?genealogyId=2060000000000000001'), '2060000000000000001');
assert.equal(getCurrentAlbumId('?albumId=2060000000000000002'), '2060000000000000002');
assert.equal(getCurrentAlbumId('?albumId=unsafe'), '');
});
test('相册写入只构造 AlbumBody 并固定正常状态', () => {
const buildAlbumBody = requireFunction('buildAlbumBody');
assert.deepEqual(buildAlbumBody({
albumName: ' 祠堂旧影 ',
albumDesc: ' 历史照片 ',
coverOssId: '2060000000000000003',
sortOrder: '2',
status: '1',
albumId: 'must-drop',
photoCount: 99
}), {
albumName: '祠堂旧影',
albumDesc: '历史照片',
coverOssId: '2060000000000000003',
sortOrder: 2,
status: '0'
});
});
test('照片写入只构造 AlbumPhotoBody 并转换拍摄时间', () => {
const buildAlbumPhotoBody = requireFunction('buildAlbumPhotoBody');
assert.deepEqual(buildAlbumPhotoBody({
ossId: '2060000000000000004',
photoTitle: ' 合影 ',
photoDesc: ' 清明祭祖 ',
photographer: ' 宗亲 ',
shootTime: '2026-07-29T10:30',
sortOrder: '3',
status: '1',
albumId: 'must-drop'
}), {
ossId: '2060000000000000004',
photoTitle: '合影',
photoDesc: '清明祭祖',
photographer: '宗亲',
shootTime: '2026-07-29 10:30:00',
sortOrder: 3,
status: '0'
});
});
test('相册和照片校验上传派生 ID、日期、排序和停用状态', () => {
const buildAlbumBody = requireFunction('buildAlbumBody');
const buildAlbumPhotoBody = requireFunction('buildAlbumPhotoBody');
const validateAlbumBody = requireFunction('validateAlbumBody');
const validateAlbumPhotoBody = requireFunction('validateAlbumPhotoBody');
assert.equal(validateAlbumBody({ albumName: '', status: '0' }), '请填写相册名称');
assert.equal(
validateAlbumBody(buildAlbumBody({ albumName: '相册', coverOssId: Number.MAX_SAFE_INTEGER + 1 })),
'封面文件编号无效,请重新选择文件'
);
assert.equal(validateAlbumBody({ albumName: '相册', status: '1' }), '当前 PC 无法重新读取停用相册,暂不开放停用');
assert.equal(validateAlbumPhotoBody({ ossId: '', status: '0' }), '请选择并上传照片');
assert.equal(
validateAlbumPhotoBody(buildAlbumPhotoBody({ ossId: '1', shootTime: '2026-02-30T10:00' })),
'拍摄时间格式无效'
);
assert.equal(
validateAlbumPhotoBody(buildAlbumPhotoBody({ ossId: '1', sortOrder: '1.5' })),
'排序值必须是安全整数'
);
});
test('相册和照片响应使用完整 PC Vo 并拒绝不安全 ID', () => {
const normalizeAlbum = requireFunction('normalizeAlbum');
const normalizeAlbumPhoto = requireFunction('normalizeAlbumPhoto');
const album = normalizeAlbum({
albumId: '2060000000000000001',
genealogyId: '2060000000000000002',
genealogyName: '叶氏家谱',
albumName: '祠堂旧影',
albumDesc: '历史照片',
coverOssId: '2060000000000000003',
photoCount: 2,
sortOrder: 1,
status: '0',
remark: '旧影'
});
const photo = normalizeAlbumPhoto({
photoId: '2060000000000000004',
genealogyId: '2060000000000000002',
albumId: '2060000000000000001',
albumName: '祠堂旧影',
ossId: '2060000000000000005',
photoTitle: '合影',
photoDesc: '清明祭祖',
photographer: '宗亲',
shootTime: '2026-07-29 10:30:00',
sortOrder: 1,
status: '0',
remark: ''
});
assert.equal(album.albumId, '2060000000000000001');
assert.equal(album.photoCount, 2);
assert.equal(photo.photoId, '2060000000000000004');
assert.equal(photo.ossId, '2060000000000000005');
assert.equal(normalizeAlbum({
albumId: Number('2060000000000000001'),
genealogyId: '2',
albumName: '相册',
status: '0'
}), null);
});
test('相册详情通过列表中的稳定 albumId 重读并匹配', () => {
const findAlbumById = requireFunction('findAlbumById');
const albums = [{
albumId: '1',
genealogyId: '2',
albumName: '旧影',
photoCount: 0,
status: '0'
}];
assert.equal(findAlbumById(albums, '1').albumName, '旧影');
assert.equal(findAlbumById(albums, '9'), null);
assert.equal(findAlbumById({ rows: albums }, '1'), null);
});
test('相册和照片展示转义内容并隐藏 OSS ID 与内部 ID', () => {
const normalizeAlbum = requireFunction('normalizeAlbum');
const normalizeAlbumPhoto = requireFunction('normalizeAlbumPhoto');
const renderAlbumDetail = requireFunction('renderAlbumDetail');
const renderPhotoList = requireFunction('renderPhotoList');
const albumHtml = renderAlbumDetail(normalizeAlbum({
albumId: '1',
genealogyId: '2',
albumName: '<img src=x>旧影',
albumDesc: '<script>alert(1)</script>历史',
coverOssId: '3',
photoCount: 1,
status: '0'
}));
const photoHtml = renderPhotoList([normalizeAlbumPhoto({
photoId: '4',
genealogyId: '2',
albumId: '1',
ossId: '5',
photoTitle: '<img src=x>合影',
photoDesc: '祭祖',
status: '0'
})]);
assert.doesNotMatch(albumHtml + photoHtml, /<script|<img/);
assert.doesNotMatch(albumHtml + photoHtml, />2<|>3<|>4<|>5</);
assert.match(albumHtml + photoHtml, /旧影|历史|合影|祭祖/);
});
test('相册页面拆分列表、编辑和照片详情且不允许手填 OSS ID 或状态', () => {
const projectRoot = path.join(__dirname, '..');
const listPage = fs.readFileSync(path.join(projectRoot, 'profile-album.html'), 'utf8');
const editPage = fs.readFileSync(path.join(projectRoot, 'profile-album-edit.html'), 'utf8');
const detailPage = fs.readFileSync(path.join(projectRoot, 'profile-album-detail.html'), 'utf8');
[listPage, editPage, detailPage].forEach((source) => {
assert.doesNotMatch(source, /data-feature-status="pending"|pending-pages\.js/);
assert.match(source, /public\/js\/album-pages\.js/);
});
assert.match(listPage, /data-album-list/);
assert.match(editPage, /name="albumName"/);
assert.match(editPage, /name="coverOssId" type="hidden"/);
assert.match(editPage, /name="status" type="hidden" value="0"/);
assert.match(detailPage, /data-album-detail/);
assert.match(detailPage, /data-album-photo-list/);
assert.match(detailPage, /name="ossId" type="hidden"/);
assert.match(detailPage, /name="shootTime" type="datetime-local"/);
assert.match(detailPage, /name="status" type="hidden" value="0"/);
assert.doesNotMatch(editPage + detailPage, /type="text"[^>]+(?:coverOssId|ossId)|name="albumId"|name="photoId"/);
});