flnxi-Wu/lib/__tests__/license-route-coverage.test.ts

144 lines
4.7 KiB
TypeScript

import fs from 'fs';
import path from 'path';
const rootDir = path.resolve(__dirname, '..', '..');
function readProjectFile(relativePath: string): string {
return fs.readFileSync(path.join(rootDir, relativePath), 'utf8');
}
describe('许可证业务入口覆盖', () => {
const requiredChecks: Array<{
file: string;
helper: string;
reason: string;
}> = [
{
file: 'app/api/auth/register/route.ts',
helper: 'canCreateUser',
reason: '免费版用户数限制必须在注册入口服务端执行',
},
{
file: 'app/api/materials/upload/route.ts',
helper: 'canUploadMaterial',
reason: '免费版素材数限制必须在上传入口服务端执行',
},
{
file: 'app/api/admin/upload-templates/route.ts',
helper: 'canUseCustomTemplates',
reason: '自定义模板新增必须走许可证功能开关',
},
{
file: 'app/api/admin/upload-templates/[id]/route.ts',
helper: 'canUseCustomTemplates',
reason: '自定义模板编辑和删除必须走许可证功能开关',
},
{
file: 'app/api/admin/upload-templates/[id]/migrate/route.ts',
helper: 'canUseCustomTemplates',
reason: '模板迁移必须走许可证功能开关',
},
{
file: 'app/api/templates/route.ts',
helper: 'canUseCustomTemplates',
reason: '首页模板筛选不能在降级后继续暴露历史自定义模板',
},
{
file: 'app/api/upload-templates/route.ts',
helper: 'canUseCustomTemplates',
reason: '上传页模板选择不能在降级后继续使用历史自定义模板',
},
{
file: 'app/api/material-types/route.ts',
helper: 'canUseCustomTemplates',
reason: '素材类型筛选不能在降级后继续暴露历史自定义模板',
},
{
file: 'app/api/departments/route.ts',
helper: 'canUseDepartmentIsolation',
reason: '部门隔离相关写操作必须走许可证功能开关',
},
{
file: 'app/api/system-settings/route.ts',
helper: 'canUseSystemSettings',
reason: '系统设置写操作必须走许可证功能开关',
},
{
file: 'app/api/system-settings/upload-logo/route.ts',
helper: 'canUseSystemSettings',
reason: '系统 LOGO 上传属于系统设置写操作,必须走许可证功能开关',
},
{
file: 'app/api/admin/cleanup-database/route.ts',
helper: 'canUseSystemSettings',
reason: '数据库清理属于高风险维护操作,必须走许可证功能开关',
},
{
file: 'app/api/admin/reset-material-rankings/route.ts',
helper: 'canUseSystemSettings',
reason: '排行榜重置属于高风险维护操作,必须走许可证功能开关',
},
{
file: 'app/api/admin/migrate-material-fields/route.ts',
helper: 'canUseSystemSettings',
reason: '素材字段迁移属于高风险维护操作,必须走许可证功能开关',
},
{
file: 'app/api/tunnel/start/route.ts',
helper: 'canUseIntranetPenetration',
reason: '内网穿透启动必须走专业版功能开关',
},
{
file: 'app/api/tunnel/status/route.ts',
helper: 'canUseIntranetPenetration',
reason: '内网穿透状态查询不能绕过专业版功能开关',
},
{
file: 'app/api/tunnel/stop/route.ts',
helper: 'canUseIntranetPenetration',
reason: '内网穿透停止入口也必须维持同一许可证边界',
},
];
it.each(requiredChecks)('$file 应调用 $helper', ({ file, helper, reason }) => {
const source = readProjectFile(file);
expect(source.includes(helper)).toBe(true);
if (!source.includes(helper)) {
throw new Error(reason);
}
});
it('高价值付费能力必须同时检查签名模块凭证', () => {
const source = readProjectFile('lib/license/check-limits.ts');
expect(source).toContain('hasPremiumModule');
expect(source).toContain('PREMIUM_MODULE_IDS.CUSTOM_TEMPLATES_CORE');
expect(source).toContain('PREMIUM_MODULE_IDS.INTRANET_PENETRATION_CORE');
});
it('高风险后台维护入口必须要求确认短语', () => {
const destructiveRoutes = [
{
file: 'app/api/admin/cleanup-database/route.ts',
phrase: '清理全部素材',
},
{
file: 'app/api/admin/reset-material-rankings/route.ts',
phrase: '重置排行榜数据',
},
{
file: 'app/api/admin/migrate-material-fields/route.ts',
phrase: '执行字段迁移',
},
];
for (const { file, phrase } of destructiveRoutes) {
const source = readProjectFile(file);
expect(source).toContain('confirmPhrase');
expect(source).toContain(phrase);
}
});
});