// 已读取AGENTS.md import { MarketingService } from '../src/core/marketing/marketing.service'; import * as fs from 'fs'; import * as path from 'path'; describe('Marketing Management System (TDD 充值赠送阶梯与通用优惠券管理套件)', () => { let service: MarketingService; 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'); beforeEach(() => { service = new MarketingService(); }); describe('1. 充值阶梯与赠送业务逻辑校验', () => { it('默认初始加载 5 个经典充值阶梯且按 sortOrder 正序排序', () => { const tiers = service.getActiveTiers(); expect(tiers.length).toBe(5); expect(tiers[0].amount).toBe(50); expect(tiers[0].bonusAmount).toBe(0); expect(tiers[1].amount).toBe(100); expect(tiers[1].bonusAmount).toBe(5); expect(tiers[3].amount).toBe(500); expect(tiers[3].bonusAmount).toBe(50); }); it('成功创建新充值赠送阶梯', () => { const tier = service.createTier({ amount: 2000, bonusAmount: 300, tag: '超级VIP', sortOrder: 6, status: 1 }); expect(tier.id).toBe(6); expect(tier.amount).toBe(2000); expect(tier.bonusAmount).toBe(300); }); it('充值金额小于等于 0 时应被拒绝', () => { expect(() => { service.createTier({ amount: 0, bonusAmount: 10 }); }).toThrow('充值金额必须大于0'); }); }); describe('2. 平台营销优惠券业务校验', () => { it('默认加载优惠券种子且支持按品类过滤', () => { const allCoupons = service.getActiveCoupons(); expect(allCoupons.length).toBe(3); const mealCoupons = service.getActiveCoupons('MEAL'); // 包含 ALL 和 MEAL 专属 expect(mealCoupons.every((c) => c.categoryLimit === 'ALL' || c.categoryLimit === 'MEAL')).toBe(true); }); it('成功创建合规优惠券', () => { const coupon = service.createCoupon({ name: '国庆特惠同城微醺立减券', discountAmount: 25, minSpend: 60, categoryLimit: 'DRINK', description: '望京跳海酒馆微醺可用' }); expect(coupon.id).toBe(4); expect(coupon.discountAmount).toBe(25); }); it('优惠券名称为空或减免金额无效时应被拒绝', () => { expect(() => { service.createCoupon({ name: '', discountAmount: 20 }); }).toThrow('优惠券名称不能为空'); expect(() => { service.createCoupon({ name: '测试券', discountAmount: -5 }); }).toThrow('减免面额必须大于0'); }); it('优惠券名称或描述涉嫌违规敏感词应触发微信内容安全拦截', () => { expect(() => { service.createCoupon({ name: '私下转账免手续费优惠大促', discountAmount: 50 }); }).toThrow(); }); }); describe('3. 数据库与 API 架构持久化门禁校验', () => { it('MySQL 数据库必须包含 recharge_tiers 与 coupons 表定义及种子数据', () => { const mysqlContent = fs.readFileSync(mysqlPath, 'utf-8'); expect(mysqlContent).toContain('CREATE TABLE IF NOT EXISTS recharge_tiers'); expect(mysqlContent).toContain('CREATE TABLE IF NOT EXISTS coupons'); expect(mysqlContent).toContain('INSERT INTO recharge_tiers'); expect(mysqlContent).toContain('INSERT INTO coupons'); }); it('Repository 数据仓储层必须实现完整的充值阶梯与优惠券 CRUD 方法', () => { const repoContent = fs.readFileSync(repoPath, 'utf-8'); expect(repoContent).toContain('async getRechargeTiers(): Promise'); expect(repoContent).toContain('async createRechargeTier('); expect(repoContent).toContain('async updateRechargeTier('); expect(repoContent).toContain('async deleteRechargeTier('); expect(repoContent).toContain('async getCoupons('); expect(repoContent).toContain('async createCoupon('); expect(repoContent).toContain('async updateCoupon('); expect(repoContent).toContain('async deleteCoupon('); }); it('server/src/index.ts 必须注册完整的营销 API 路由', () => { const indexContent = fs.readFileSync(serverIndexPath, 'utf-8'); expect(indexContent).toContain("app.get('/api/marketing/recharge-tiers'"); expect(indexContent).toContain("app.get('/api/admin/recharge-tiers'"); expect(indexContent).toContain("app.post('/api/admin/recharge-tiers'"); expect(indexContent).toContain("app.get('/api/marketing/coupons'"); expect(indexContent).toContain("app.get('/api/admin/coupons'"); expect(indexContent).toContain("app.post('/api/admin/coupons'"); }); 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/marketing/marketing.service.ts'), 'utf-8'); const testContent = fs.readFileSync(__filename, 'utf-8'); expect(emojiRegex.test(serviceContent)).toBe(false); expect(emojiRegex.test(testContent)).toBe(false); }); }); });