144 lines
4.7 KiB
TypeScript
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);
|
|
}
|
|
});
|
|
});
|