161 行
6.3 KiB
TypeScript
161 行
6.3 KiB
TypeScript
// 已读取AGENTS.md
|
||
import { ContentSecurityService } from '../src/core/security/content-security.service';
|
||
import * as fs from 'fs';
|
||
import * as path from 'path';
|
||
|
||
describe('ContentSecurityService & Compliance Architecture (TDD 微信内容安全与合规体验套件)', () => {
|
||
let secService: ContentSecurityService;
|
||
const rootDir = path.resolve(__dirname, '../../');
|
||
|
||
beforeEach(() => {
|
||
secService = new ContentSecurityService();
|
||
});
|
||
|
||
describe('1. 文本内容安全过滤规则 (security.msgSecCheck)', () => {
|
||
it('正常合规文本应安全放行', () => {
|
||
const texts = [
|
||
'今晚三里屯吃铜锅涮肉,有一起的搭子吗?人均AA',
|
||
'工体纯K量贩KTV麦霸欢唱局,准时到场',
|
||
'王者荣耀荣耀王者五排车队,辅助位缺一'
|
||
];
|
||
for (const t of texts) {
|
||
const res = secService.checkText(t);
|
||
expect(res.pass).toBe(true);
|
||
expect(res.code).toBe(0);
|
||
expect(res.suggest).toBe('pass');
|
||
}
|
||
});
|
||
|
||
it('涉黄低俗敏感词应被严格拦截 (code 87014)', () => {
|
||
const badTexts = [
|
||
'同城美女上门特殊服务,随时可约',
|
||
'招嫖包夜加微联系',
|
||
'同城一夜情约炮'
|
||
];
|
||
for (const t of badTexts) {
|
||
const res = secService.checkText(t);
|
||
expect(res.pass).toBe(false);
|
||
expect(res.code).toBe(87014);
|
||
expect(res.suggest).toBe('block');
|
||
expect(res.message).toContain('涉嫌违规');
|
||
}
|
||
});
|
||
|
||
it('黑产欺诈与引流私下交易词汇应被阻断', () => {
|
||
const scamTexts = [
|
||
'加微信私下转账免手续费直接打款',
|
||
'刷单兼职日赚千元'
|
||
];
|
||
for (const t of scamTexts) {
|
||
const res = secService.checkText(t);
|
||
expect(res.pass).toBe(false);
|
||
expect(res.code).toBe(87014);
|
||
}
|
||
});
|
||
|
||
it('maskText 能够正确对违规敏感词执行脱敏掩码', () => {
|
||
const masked = secService.maskText('严禁上门特殊服务违规行为');
|
||
expect(masked).toContain('******');
|
||
expect(masked).not.toContain('上门特殊服务');
|
||
});
|
||
});
|
||
|
||
describe('2. 多媒体图片内容安全校验 (security.mediaCheckAsync)', () => {
|
||
it('合规格式与尺寸的图片应顺利通过校验', () => {
|
||
const check = secService.checkImage('avatar_user_1.png', 'image/png', 500 * 1024);
|
||
expect(check.pass).toBe(true);
|
||
expect(check.code).toBe(0);
|
||
});
|
||
|
||
it('非图片或伪装可执行脚本应被即时拦截', () => {
|
||
const badCases = [
|
||
{ file: 'virus.exe', mime: 'application/octet-stream', size: 1024 },
|
||
{ file: 'shell.php', mime: 'text/plain', size: 1024 },
|
||
{ file: 'pic.jpg.sh', mime: 'image/jpeg', size: 1024 }
|
||
];
|
||
for (const c of badCases) {
|
||
const res = secService.checkImage(c.file, c.mime, c.size);
|
||
expect(res.pass).toBe(false);
|
||
expect(res.code).toBe(87014);
|
||
}
|
||
});
|
||
|
||
it('超大图片体积(超过10MB)应被安全拒绝', () => {
|
||
const res = secService.checkImage('huge.png', 'image/png', 12 * 1024 * 1024);
|
||
expect(res.pass).toBe(false);
|
||
expect(res.message).toContain('10MB');
|
||
});
|
||
});
|
||
|
||
describe('3. 前端微信隐私保护指引弹窗组件规范性验证 (PrivacyModal.vue)', () => {
|
||
const modalPath = path.join(rootDir, 'pwuniapp', 'components', 'PrivacyModal.vue');
|
||
|
||
it('PrivacyModal.vue 文件必须存在且格式有效', () => {
|
||
expect(fs.existsSync(modalPath)).toBe(true);
|
||
const content = fs.readFileSync(modalPath, 'utf-8');
|
||
expect(content).toContain('privacy-dialog');
|
||
expect(content).toContain('用户隐私保护指引');
|
||
});
|
||
|
||
it('必须支持微信小程序官方 agreePrivacyAuthorization 原生授权按钮', () => {
|
||
const content = fs.readFileSync(modalPath, 'utf-8');
|
||
expect(content).toContain('open-type="agreePrivacyAuthorization"');
|
||
expect(content).toContain('wx.onNeedPrivacyAuthorization');
|
||
});
|
||
|
||
it('必须支持用户服务协议与隐私指引原生调用或弹窗', () => {
|
||
const content = fs.readFileSync(modalPath, 'utf-8');
|
||
expect(content).toContain('openPrivacyContract');
|
||
});
|
||
|
||
it('必须适配冷暖双主题与无 Emoji 门禁', () => {
|
||
const content = fs.readFileSync(modalPath, 'utf-8');
|
||
expect(content).toContain('theme-warm');
|
||
const emojiRegex = /(?:\p{Extended_Pictographic}|\p{Emoji_Presentation}|[\u2600-\u27BF])/u;
|
||
expect(emojiRegex.test(content)).toBe(false);
|
||
});
|
||
|
||
it('首页与个人中心必须集成并暴露用户隐私指引入口', () => {
|
||
const indexContent = fs.readFileSync(path.join(rootDir, 'pwuniapp', 'pages', 'index', 'index.vue'), 'utf-8');
|
||
const userContent = fs.readFileSync(path.join(rootDir, 'pwuniapp', 'pages', 'user', 'index.vue'), 'utf-8');
|
||
expect(indexContent).toContain('privacy-modal');
|
||
expect(userContent).toContain('privacy-modal');
|
||
});
|
||
});
|
||
|
||
describe('4. 小程序主包与分包架构配置规范性验证 (pages.json)', () => {
|
||
const pagesJsonPath = path.join(rootDir, 'pwuniapp', 'pages.json');
|
||
|
||
it('pages.json 必须显式配置 subPackages 分包结构', () => {
|
||
const raw = fs.readFileSync(pagesJsonPath, 'utf-8');
|
||
const config = JSON.parse(raw);
|
||
expect(Array.isArray(config.subPackages)).toBe(true);
|
||
expect(config.subPackages.length).toBeGreaterThanOrEqual(2);
|
||
|
||
const roots = config.subPackages.map((s: any) => s.root);
|
||
expect(roots).toContain('subpackages/expert');
|
||
expect(roots).toContain('subpackages/activity');
|
||
});
|
||
|
||
it('必须配置主包向分包的智能预加载规则 preloadRule', () => {
|
||
const raw = fs.readFileSync(pagesJsonPath, 'utf-8');
|
||
const config = JSON.parse(raw);
|
||
expect(config.preloadRule).toBeDefined();
|
||
expect(config.preloadRule['pages/index/index']).toBeDefined();
|
||
expect(config.preloadRule['pages/user/index']).toBeDefined();
|
||
});
|
||
|
||
it('主包核心 TabBar 页面保持精炼', () => {
|
||
const raw = fs.readFileSync(pagesJsonPath, 'utf-8');
|
||
const config = JSON.parse(raw);
|
||
const tabPaths = config.tabBar.list.map((t: any) => t.pagePath);
|
||
expect(tabPaths).toEqual([
|
||
'pages/index/index',
|
||
'pages/activity/list',
|
||
'pages/order/list',
|
||
'pages/user/index'
|
||
]);
|
||
});
|
||
});
|
||
});
|