162 行
6.3 KiB
TypeScript
162 行
6.3 KiB
TypeScript
// 已读取AGENTS.md
|
|
import { NavCategoryService } from '../src/core/nav/nav-category.service';
|
|
import * as fs from 'fs';
|
|
import * as path from 'path';
|
|
|
|
describe('NavCategory Management System (TDD 首页金刚区导航品类后台配置与小程序联动)', () => {
|
|
let service: NavCategoryService;
|
|
const rootDir = path.resolve(__dirname, '../../');
|
|
const serverIndexPath = path.join(rootDir, 'server/src/index.ts');
|
|
const mysqlPath = path.join(rootDir, 'server/src/db/mysql.ts');
|
|
const repoPath = path.join(rootDir, 'server/src/db/repository.ts');
|
|
const mobileIndexPath = path.join(rootDir, 'pwuniapp/pages/index/index.vue');
|
|
|
|
beforeEach(() => {
|
|
service = new NavCategoryService();
|
|
});
|
|
|
|
describe('1. 核心服务 NavCategoryService 业务校验', () => {
|
|
it('默认加载 10 项经典品类种子且按 sortOrder 正序排序', () => {
|
|
const active = service.getActiveCategories();
|
|
expect(active.length).toBe(10);
|
|
expect(active[0].name).toBe('电子竞技');
|
|
expect(active[1].name).toBe('美食探店');
|
|
expect(active[2].name).toBe('KTV嗨唱');
|
|
expect(active[9].name).toBe('全部活动');
|
|
expect(active.every((c) => c.status === 1)).toBe(true);
|
|
});
|
|
|
|
it('成功新增金刚区品类导航并支持角标高亮', () => {
|
|
const item = service.createCategory({
|
|
code: 'camping',
|
|
name: '露营烧烤',
|
|
icon: 'warmFlame',
|
|
bgClass: 'bg-emerald',
|
|
cat: 'offline_play',
|
|
targetUrl: '/pages/activity/list?category=ALL',
|
|
badgeText: '新玩法',
|
|
sortOrder: 0, // 置顶
|
|
status: 1
|
|
});
|
|
|
|
expect(item.id).toBe(11);
|
|
expect(item.name).toBe('露营烧烤');
|
|
expect(item.badgeText).toBe('新玩法');
|
|
|
|
const active = service.getActiveCategories();
|
|
expect(active.length).toBe(11);
|
|
expect(active[0].id).toBe(11); // 置顶生效
|
|
});
|
|
|
|
it('创建品类时空名称或空图标应被拦截', () => {
|
|
expect(() => {
|
|
service.createCategory({
|
|
name: '',
|
|
icon: 'utensils'
|
|
});
|
|
}).toThrow('品类名称不能为空');
|
|
|
|
expect(() => {
|
|
service.createCategory({
|
|
name: '桌游体验',
|
|
icon: ' '
|
|
});
|
|
}).toThrow('品类图标不能为空');
|
|
});
|
|
|
|
it('品类名称或角标中含有违规敏感词应触发微信内容安全拦截', () => {
|
|
expect(() => {
|
|
service.createCategory({
|
|
name: '美女上门特殊服务招嫖',
|
|
icon: 'utensils'
|
|
});
|
|
}).toThrow();
|
|
|
|
expect(() => {
|
|
service.createCategory({
|
|
name: '私下转账免手续费活动',
|
|
icon: 'utensils'
|
|
});
|
|
}).toThrow();
|
|
});
|
|
|
|
it('支持快速上下线切换并在移动端列表中实时反映', () => {
|
|
const offline = service.toggleStatus(2, 0);
|
|
expect(offline.status).toBe(0);
|
|
|
|
// 移动端不再展示美食探店
|
|
const active = service.getActiveCategories();
|
|
expect(active.some((c) => c.id === 2)).toBe(false);
|
|
|
|
// 后台全量仍存在
|
|
expect(service.getAllCategories().some((c) => c.id === 2)).toBe(true);
|
|
|
|
// 重新上线
|
|
const online = service.toggleStatus(2, 1);
|
|
expect(online.status).toBe(1);
|
|
expect(service.getActiveCategories().some((c) => c.id === 2)).toBe(true);
|
|
});
|
|
|
|
it('更新品类配置与排序权重', () => {
|
|
const updated = service.updateCategory(3, {
|
|
name: '夜店派对',
|
|
badgeText: '超嗨',
|
|
sortOrder: 1
|
|
});
|
|
expect(updated.name).toBe('夜店派对');
|
|
expect(updated.badgeText).toBe('超嗨');
|
|
expect(updated.sortOrder).toBe(1);
|
|
});
|
|
|
|
it('删除品类导航项', () => {
|
|
const ok = service.deleteCategory(10);
|
|
expect(ok).toBe(true);
|
|
expect(service.getAllCategories().some((c) => c.id === 10)).toBe(false);
|
|
|
|
expect(() => {
|
|
service.deleteCategory(999);
|
|
}).toThrow('品类导航不存在或已被删除');
|
|
});
|
|
});
|
|
|
|
describe('2. 架构持久化与数据门禁校验', () => {
|
|
it('MySQL 数据库必须包含 nav_categories 表定义与 10 条种子数据', () => {
|
|
const mysqlContent = fs.readFileSync(mysqlPath, 'utf-8');
|
|
expect(mysqlContent).toContain('CREATE TABLE IF NOT EXISTS nav_categories');
|
|
expect(mysqlContent).toContain('sort_order INT DEFAULT 0');
|
|
expect(mysqlContent).toContain('status TINYINT DEFAULT 1');
|
|
expect(mysqlContent).toContain('INSERT INTO nav_categories');
|
|
});
|
|
|
|
it('Repository 数据仓储层必须实现完整的 nav_categories CRUD 方法', () => {
|
|
const repoContent = fs.readFileSync(repoPath, 'utf-8');
|
|
expect(repoContent).toContain('async getNavCategories(): Promise<any[]>');
|
|
expect(repoContent).toContain('async getAdminNavCategories(): Promise<any[]>');
|
|
expect(repoContent).toContain('async createNavCategory(');
|
|
expect(repoContent).toContain('async updateNavCategory(');
|
|
expect(repoContent).toContain('async updateNavCategoryStatus(');
|
|
expect(repoContent).toContain('async deleteNavCategory(');
|
|
expect(repoContent).toContain('WHERE status = 1');
|
|
expect(repoContent).toContain('ORDER BY sort_order ASC');
|
|
});
|
|
|
|
it('server/src/index.ts 必须注册完整的金刚区导航 API 路由与敏感词检查', () => {
|
|
const indexContent = fs.readFileSync(serverIndexPath, 'utf-8');
|
|
expect(indexContent).toContain("app.get('/api/nav-categories'");
|
|
expect(indexContent).toContain("app.get('/api/admin/nav-categories'");
|
|
expect(indexContent).toContain("app.post('/api/admin/nav-categories'");
|
|
expect(indexContent).toContain("app.put('/api/admin/nav-categories/:id'");
|
|
expect(indexContent).toContain("app.post('/api/admin/nav-categories/:id/status'");
|
|
expect(indexContent).toContain("app.delete('/api/admin/nav-categories/:id'");
|
|
});
|
|
|
|
it('服务文件与测试套件必须严格满足零系统 Emoji 规范', () => {
|
|
const emojiRegex = /[\u{1F300}-\u{1F9FF}\u{2600}-\u{26FF}\u{2700}-\u{27BF}]/u;
|
|
const serviceContent = fs.readFileSync(path.join(rootDir, 'server/src/core/nav/nav-category.service.ts'), 'utf-8');
|
|
const testContent = fs.readFileSync(__filename, 'utf-8');
|
|
expect(emojiRegex.test(serviceContent)).toBe(false);
|
|
expect(emojiRegex.test(testContent)).toBe(false);
|
|
});
|
|
});
|
|
});
|