197 行
6.8 KiB
TypeScript
197 行
6.8 KiB
TypeScript
import { ActivityService, ActivityPayType, Activity, Participant } from '../src/core/activity/activity.service';
|
|
|
|
describe('ActivityService (TDD 测试套件: 活动项目与4位核销码)', () => {
|
|
let activityService: ActivityService;
|
|
|
|
beforeEach(() => {
|
|
activityService = new ActivityService();
|
|
});
|
|
|
|
describe('1. 发起活动 (AA制与项目制)', () => {
|
|
it('应成功创建AA制活动并正确初始化', () => {
|
|
const activity = activityService.createActivity({
|
|
creatorId: 1001,
|
|
title: '王者荣耀荣耀王者五排冲星车队',
|
|
gameCategory: '王者荣耀',
|
|
payType: ActivityPayType.AA_PAY,
|
|
aaFeeCents: 3000, // 每人30元
|
|
maxParticipants: 4, // 限制4人,0表示不限制
|
|
});
|
|
|
|
expect(activity.id).toBeDefined();
|
|
expect(activity.payType).toBe(ActivityPayType.AA_PAY);
|
|
expect(activity.aaFeeCents).toBe(3000);
|
|
expect(activity.maxParticipants).toBe(4);
|
|
expect(activity.currentParticipants).toBe(0);
|
|
});
|
|
|
|
it('应成功创建项目制活动(不限制报名人数)', () => {
|
|
const activity = activityService.createActivity({
|
|
creatorId: 1002,
|
|
title: '水友粉丝狂欢内战赛',
|
|
gameCategory: '英雄联盟',
|
|
payType: ActivityPayType.PROJECT_PAY,
|
|
totalFeeCents: 100000, // 发起人项目总包1000元
|
|
maxParticipants: 0, // 不限制报名人数
|
|
});
|
|
|
|
expect(activity.payType).toBe(ActivityPayType.PROJECT_PAY);
|
|
expect(activity.totalFeeCents).toBe(100000);
|
|
expect(activity.maxParticipants).toBe(0);
|
|
});
|
|
});
|
|
|
|
describe('2. 报名与生成 4 位纯数字核销码', () => {
|
|
let aaActivity: Activity;
|
|
|
|
beforeEach(() => {
|
|
aaActivity = activityService.createActivity({
|
|
creatorId: 1001,
|
|
title: '和平精英四排吃鸡车队',
|
|
gameCategory: '和平精英',
|
|
payType: ActivityPayType.AA_PAY,
|
|
aaFeeCents: 2500,
|
|
maxParticipants: 3,
|
|
});
|
|
});
|
|
|
|
it('报名成功后应生成合法的 4 位纯数字核销码 (1000-9999)', () => {
|
|
const participant = activityService.joinActivity(aaActivity.id, {
|
|
userId: 2001,
|
|
payAmountCents: 2500,
|
|
});
|
|
|
|
expect(participant.verifyCode).toMatch(/^\d{4}$/);
|
|
const codeNum = parseInt(participant.verifyCode, 10);
|
|
expect(codeNum).toBeGreaterThanOrEqual(1000);
|
|
expect(codeNum).toBeLessThanOrEqual(9999);
|
|
expect(participant.checkinStatus).toBe(false);
|
|
});
|
|
|
|
it('单活动内多个参与者的 4 位核销码不可重复碰撞', () => {
|
|
const p1 = activityService.joinActivity(aaActivity.id, { userId: 2001, payAmountCents: 2500 });
|
|
const p2 = activityService.joinActivity(aaActivity.id, { userId: 2002, payAmountCents: 2500 });
|
|
const p3 = activityService.joinActivity(aaActivity.id, { userId: 2003, payAmountCents: 2500 });
|
|
|
|
const codes = new Set([p1.verifyCode, p2.verifyCode, p3.verifyCode]);
|
|
expect(codes.size).toBe(3);
|
|
});
|
|
|
|
it('同一用户不可重复报名同一活动', () => {
|
|
activityService.joinActivity(aaActivity.id, { userId: 2001, payAmountCents: 2500 });
|
|
|
|
expect(() => {
|
|
activityService.joinActivity(aaActivity.id, { userId: 2001, payAmountCents: 2500 });
|
|
}).toThrow('用户已报名该活动');
|
|
});
|
|
|
|
it('超出人数限制时应阻止报名', () => {
|
|
activityService.joinActivity(aaActivity.id, { userId: 2001, payAmountCents: 2500 });
|
|
activityService.joinActivity(aaActivity.id, { userId: 2002, payAmountCents: 2500 });
|
|
activityService.joinActivity(aaActivity.id, { userId: 2003, payAmountCents: 2500 });
|
|
|
|
expect(() => {
|
|
activityService.joinActivity(aaActivity.id, { userId: 2004, payAmountCents: 2500 });
|
|
}).toThrow('活动报名人数已满');
|
|
});
|
|
});
|
|
|
|
describe('3. 4位核销码签到核销与防重控制', () => {
|
|
let testActivity: Activity;
|
|
let participant: Participant;
|
|
|
|
beforeEach(() => {
|
|
testActivity = activityService.createActivity({
|
|
creatorId: 1001,
|
|
title: '双排上分活动',
|
|
gameCategory: '王者荣耀',
|
|
payType: ActivityPayType.AA_PAY,
|
|
aaFeeCents: 2000,
|
|
maxParticipants: 1,
|
|
});
|
|
|
|
participant = activityService.joinActivity(testActivity.id, {
|
|
userId: 3001,
|
|
payAmountCents: 2000,
|
|
});
|
|
});
|
|
|
|
it('发起人使用正确的 4 位核销码应核销成功', () => {
|
|
const result = activityService.verifyCheckin({
|
|
activityId: testActivity.id,
|
|
operatorId: 1001, // 发起人操作
|
|
verifyCode: participant.verifyCode,
|
|
});
|
|
|
|
expect(result.success).toBe(true);
|
|
expect(result.participant.checkinStatus).toBe(true);
|
|
expect(result.participant.verifiedAt).toBeDefined();
|
|
});
|
|
|
|
it('同一 4 位核销码不可重复核销(防重签到)', () => {
|
|
activityService.verifyCheckin({
|
|
activityId: testActivity.id,
|
|
operatorId: 1001,
|
|
verifyCode: participant.verifyCode,
|
|
});
|
|
|
|
expect(() => {
|
|
activityService.verifyCheckin({
|
|
activityId: testActivity.id,
|
|
operatorId: 1001,
|
|
verifyCode: participant.verifyCode,
|
|
});
|
|
}).toThrow('该核销码已被核销,不可重复使用');
|
|
});
|
|
|
|
it('输入错误的 4 位核销码应报错拒绝', () => {
|
|
expect(() => {
|
|
activityService.verifyCheckin({
|
|
activityId: testActivity.id,
|
|
operatorId: 1001,
|
|
verifyCode: '0000', // 不存在的核销码
|
|
});
|
|
}).toThrow('无效或不存在的核销码');
|
|
});
|
|
|
|
it('非发起人或未授权人员核销应被拒绝', () => {
|
|
expect(() => {
|
|
activityService.verifyCheckin({
|
|
activityId: testActivity.id,
|
|
operatorId: 9999, // 非法第三方操作者
|
|
verifyCode: participant.verifyCode,
|
|
});
|
|
}).toThrow('无权核销该活动的核销码');
|
|
});
|
|
});
|
|
|
|
describe('4. 同城线下陪玩·陪吃·陪喝组局活动', () => {
|
|
it('应成功发起线下同城火锅探店AA组局活动并完成现场核销', () => {
|
|
const mealAct = activityService.createActivity({
|
|
creatorId: 1003,
|
|
title: '【周末同城】三里屯火锅探店饭搭子4人AA局',
|
|
gameCategory: '同城陪吃',
|
|
location: '北京市朝阳区三里屯',
|
|
payType: ActivityPayType.AA_PAY,
|
|
aaFeeCents: 5000,
|
|
maxParticipants: 4,
|
|
});
|
|
|
|
expect(mealAct.location).toBe('北京市朝阳区三里屯');
|
|
const p = activityService.joinActivity(mealAct.id, {
|
|
userId: 2005,
|
|
payAmountCents: 5000,
|
|
});
|
|
expect(p.verifyCode).toMatch(/^\d{4}$/);
|
|
|
|
const check = activityService.verifyCheckin({
|
|
activityId: mealAct.id,
|
|
operatorId: 1003,
|
|
verifyCode: p.verifyCode,
|
|
});
|
|
expect(check.success).toBe(true);
|
|
});
|
|
});
|
|
});
|
|
|