文件
shanghaipeiwan/server/tests/rbac-permission.spec.ts

726 行
27 KiB
TypeScript

import { AuthRoleManager, UserRoleFlag, WorkMode, CertStatus } from '../src/core/auth/auth-role.manager';
import { CompanionOrderService } from '../src/core/order/companion-order.service';
import { ActivityService, ActivityPayType } from '../src/core/activity/activity.service';
import { OrderStatus } from '../src/core/order/order-state-machine';
import { CompanionTrack, ExpenseBurdenType } from '../src/core/companion/service-category';
import * as fs from 'fs';
import * as path from 'path';
describe('RBAC Permission Architecture (TDD 全流程多角色权限与越权防御套件)', () => {
let roleManager: AuthRoleManager;
let orderService: CompanionOrderService;
let activityService: ActivityService;
beforeEach(() => {
roleManager = new AuthRoleManager();
orderService = new CompanionOrderService();
activityService = new ActivityService();
});
describe('1. 四大核心角色体系与权限位图验证', () => {
it('角色位图常量定义完整且互斥', () => {
expect(UserRoleFlag.PLAYER).toBe(1);
expect(UserRoleFlag.EXPERT).toBe(2);
expect(UserRoleFlag.HOST).toBe(4);
expect(UserRoleFlag.ADMIN).toBe(8);
});
it('普通注册用户默认仅拥有玩家身份 (PLAYER)', () => {
const user = roleManager.registerUser({ openid: 'wx_user_1', nickname: '玩家小明' });
expect(user.roleFlags).toBe(UserRoleFlag.PLAYER);
expect(roleManager.isPlayer(user.id)).toBe(true);
expect(roleManager.isExpert(user.id)).toBe(false);
expect(roleManager.isHost(user.id)).toBe(false);
expect(roleManager.isAdmin(user.id)).toBe(false);
expect(user.currentMode).toBe(WorkMode.PLAYER);
});
it('平台管理员账号具备 ADMIN 与 PLAYER 复合权限', () => {
const admin = roleManager.registerAdmin({ openid: 'wx_admin_1', nickname: '总控管理员' });
expect(roleManager.isAdmin(admin.id)).toBe(true);
expect(roleManager.isPlayer(admin.id)).toBe(true);
expect(admin.roleFlags & UserRoleFlag.ADMIN).toBeTruthy();
});
it('支持动态授予与撤销角色位图权限', () => {
const user = roleManager.registerUser({ openid: 'wx_user_2', nickname: '多面手' });
expect(roleManager.isHost(user.id)).toBe(false);
// 授予车队房主 HOST 权限
roleManager.grantRole(user.id, UserRoleFlag.HOST);
expect(roleManager.isHost(user.id)).toBe(true);
// 撤销 HOST 权限
roleManager.revokeRole(user.id, UserRoleFlag.HOST);
expect(roleManager.isHost(user.id)).toBe(false);
});
});
describe('2. 角色一:普通买家/玩家 (PLAYER) 业务与越权防御', () => {
let buyer: any;
let expert: any;
let stranger: any;
beforeEach(() => {
buyer = roleManager.registerUser({ openid: 'wx_buyer', nickname: '下单老板' });
expert = roleManager.registerUser({ openid: 'wx_expert', nickname: '认证大神' });
stranger = roleManager.registerUser({ openid: 'wx_stranger', nickname: '路人用户' });
// 认证大神
roleManager.submitExpertCert(expert.id, { realName: '李大神', idCardNo: '110101199001011234' });
roleManager.approveExpertCert(expert.id);
roleManager.switchMode(expert.id, WorkMode.EXPERT);
});
it('买家正常创建并支付线上订单', () => {
const order = orderService.createOrder({
buyerId: buyer.id,
expertId: expert.id,
track: CompanionTrack.ONLINE_GAME,
serviceTitle: '王者荣耀五排上分',
hours: 2,
totalAmountCents: 6000,
gameDetails: {
gameName: '王者荣耀',
gameId: '峡谷第一野王',
serverDistrict: '微信区',
},
});
expect(order.status).toBe(OrderStatus.PENDING_PAYMENT);
// 只有买家有权付款
orderService.payOrder(order.id, buyer.id);
expect(order.status).toBe(OrderStatus.PENDING_ACCEPT);
});
it('越权拦截:非下单买家尝试支付订单应被拒绝', () => {
const order = orderService.createOrder({
buyerId: buyer.id,
expertId: expert.id,
track: CompanionTrack.ONLINE_GAME,
serviceTitle: '王者双排',
hours: 1,
totalAmountCents: 3000,
});
expect(() => {
orderService.payOrder(order.id, stranger.id);
}).toThrow('非下单买家无权支付该订单');
});
it('待付款状态下买家可取消订单,非买家取消应被拒绝', () => {
const order = orderService.createOrder({
buyerId: buyer.id,
expertId: expert.id,
track: CompanionTrack.ONLINE_GAME,
serviceTitle: '王者双排',
hours: 1,
totalAmountCents: 3000,
});
// 非买家尝试取消
expect(() => {
orderService.buyerCancel(order.id, stranger.id);
}).toThrow('只有下单买家有权取消订单');
// 买家正常取消
orderService.buyerCancel(order.id, buyer.id);
expect(order.status).toBe(OrderStatus.CANCELLED);
});
it('待确认状态下买家正常确认完成验收,非买家验收应被拒绝', () => {
const order = orderService.createOrder({
buyerId: buyer.id,
expertId: expert.id,
track: CompanionTrack.ONLINE_GAME,
serviceTitle: '排位服务',
hours: 1,
totalAmountCents: 3000,
});
orderService.payOrder(order.id, buyer.id);
orderService.expertAccept(order.id, expert.id, roleManager);
orderService.expertApplyFinish(order.id, expert.id);
expect(order.status).toBe(OrderStatus.PENDING_CONFIRM);
// 非买家尝试确认验收
expect(() => {
orderService.buyerConfirm(order.id, stranger.id);
}).toThrow('只有下单买家有权确认完成验收');
// 买家确认完成
orderService.buyerConfirm(order.id, buyer.id);
expect(order.status).toBe(OrderStatus.COMPLETED);
});
it('待确认状态下买家发起争议仲裁,非买家发起仲裁应被拒绝', () => {
const order = orderService.createOrder({
buyerId: buyer.id,
expertId: expert.id,
track: CompanionTrack.ONLINE_GAME,
serviceTitle: '排位服务',
hours: 1,
totalAmountCents: 3000,
});
orderService.payOrder(order.id, buyer.id);
orderService.expertAccept(order.id, expert.id, roleManager);
orderService.expertApplyFinish(order.id, expert.id);
// 非买家尝试发起争议
expect(() => {
orderService.buyerDispute(order.id, stranger.id, '大神挂机');
}).toThrow('只有下单买家有权发起争议仲裁');
// 买家发起争议
orderService.buyerDispute(order.id, buyer.id, '大神迟到并且对局中挂机');
expect(order.status).toBe(OrderStatus.DISPUTED);
expect(order.disputeReason).toBe('大神迟到并且对局中挂机');
});
it('越权拦截:买家不可越权执行平台管理员争议仲裁', () => {
const order = orderService.createOrder({
buyerId: buyer.id,
expertId: expert.id,
track: CompanionTrack.ONLINE_GAME,
serviceTitle: '排位服务',
hours: 1,
totalAmountCents: 3000,
});
orderService.payOrder(order.id, buyer.id);
orderService.expertAccept(order.id, expert.id, roleManager);
orderService.expertApplyFinish(order.id, expert.id);
orderService.buyerDispute(order.id, buyer.id, '要求全额退款');
// 买家尝试越权自行仲裁退款
expect(() => {
orderService.arbitrate({
orderId: order.id,
operatorAdminId: buyer.id,
decision: 'REFUND',
roleManager,
});
}).toThrow();
});
});
describe('3. 角色二:陪玩大神/陪伴师 (EXPERT) 认证全流程与越权防御', () => {
let applicant: any;
let normalBuyer: any;
let admin: any;
beforeEach(() => {
applicant = roleManager.registerUser({ openid: 'wx_app_1', nickname: '申请大神选手' });
normalBuyer = roleManager.registerUser({ openid: 'wx_buy_2', nickname: '买家阿强' });
admin = roleManager.registerAdmin({ openid: 'wx_admin_2', nickname: '平台运营审核员' });
});
it('申请实名认证成功并生成 PENDING 记录', () => {
const cert = roleManager.submitExpertCert(applicant.id, {
realName: '王小萌',
idCardNo: '110101200001011111',
});
expect(cert.status).toBe(CertStatus.PENDING);
expect(roleManager.getCertStatus(applicant.id)).toBe(CertStatus.PENDING);
expect(roleManager.isExpert(applicant.id)).toBe(false);
});
it('越权拦截:普通买家尝试审核大神认证申请应被抛出权限异常拒绝', () => {
roleManager.submitExpertCert(applicant.id, {
realName: '王小萌',
idCardNo: '110101200001011111',
});
expect(() => {
roleManager.approveExpertCert(applicant.id, normalBuyer.id);
}).toThrow('只有平台管理员有权审核大神认证申请');
expect(() => {
roleManager.rejectExpertCert(applicant.id, '资料不合格', normalBuyer.id);
}).toThrow('只有平台管理员有权审核大神认证申请');
});
it('平台管理员成功审核通过实名认证,角色位图升级并支持接单', () => {
roleManager.submitExpertCert(applicant.id, {
realName: '王小萌',
idCardNo: '110101200001011111',
});
const cert = roleManager.approveExpertCert(applicant.id, admin.id);
expect(cert.status).toBe(CertStatus.APPROVED);
expect(cert.auditedBy).toBe(admin.id);
expect(roleManager.isExpert(applicant.id)).toBe(true);
// 允许切换到接单模式
roleManager.switchMode(applicant.id, WorkMode.EXPERT);
expect(roleManager.getUser(applicant.id).currentMode).toBe(WorkMode.EXPERT);
});
it('平台管理员驳回实名认证并记录驳回理由', () => {
roleManager.submitExpertCert(applicant.id, {
realName: '王小萌',
idCardNo: '110101200001011111',
});
const cert = roleManager.rejectExpertCert(applicant.id, '身份证照片模糊不清晰', admin.id);
expect(cert.status).toBe(CertStatus.REJECTED);
expect(cert.rejectReason).toBe('身份证照片模糊不清晰');
expect(roleManager.isExpert(applicant.id)).toBe(false);
});
it('越权拦截:未认证用户尝试接单应被严格拦截拒绝', () => {
const order = orderService.createOrder({
buyerId: normalBuyer.id,
expertId: applicant.id, // 尚未认证
track: CompanionTrack.ONLINE_GAME,
serviceTitle: '游戏陪玩',
hours: 1,
totalAmountCents: 3500,
});
orderService.payOrder(order.id, normalBuyer.id);
expect(() => {
orderService.expertAccept(order.id, applicant.id, roleManager);
}).toThrow('未认证大神无法接单');
});
it('同城线下订单生成 4 位现场核销码,非接单大神核销被拒绝,接单大神现场核销闭环', () => {
// 认证大神
roleManager.submitExpertCert(applicant.id, { realName: '王小萌', idCardNo: '110101200001011111' });
roleManager.approveExpertCert(applicant.id, admin.id);
const order = orderService.createOrder({
buyerId: normalBuyer.id,
expertId: applicant.id,
track: CompanionTrack.OFFLINE_MEAL,
serviceTitle: '三里屯火锅探店',
hours: 2,
totalAmountCents: 12000,
offlineDetails: {
city: '北京市',
venueName: '朝阳大悦城海底捞',
meetTime: '今晚 19:30',
expenseBurden: ExpenseBurdenType.AA_SHARE,
},
});
orderService.payOrder(order.id, normalBuyer.id);
orderService.expertAccept(order.id, applicant.id, roleManager);
expect(order.status).toBe(OrderStatus.PENDING_CONFIRM);
expect(order.offlineVerifyCode).toMatch(/^\d{4}$/);
// 非接单大神越权核销
expect(() => {
orderService.verifyOfflineCheckin({
orderId: order.id,
operatorExpertId: 9999, // 外部非法大神
verifyCode: order.offlineVerifyCode!,
});
}).toThrow('无权核销该订单');
// 接单大神凭正确 4 位码核销成功进入服务中
const checkinRes = orderService.verifyOfflineCheckin({
orderId: order.id,
operatorExpertId: applicant.id,
verifyCode: order.offlineVerifyCode!,
});
expect(checkinRes.success).toBe(true);
expect(order.status).toBe(OrderStatus.IN_SERVICE);
// 防重校验:不可重复核销
expect(() => {
orderService.verifyOfflineCheckin({
orderId: order.id,
operatorExpertId: applicant.id,
verifyCode: order.offlineVerifyCode!,
});
}).toThrow('该订单已完成现场核销签到');
});
it('接单大神拒单流转为已退款,非指定接单大神拒单应被拦截', () => {
roleManager.submitExpertCert(applicant.id, { realName: '王小萌', idCardNo: '110101200001011111' });
roleManager.approveExpertCert(applicant.id, admin.id);
const order = orderService.createOrder({
buyerId: normalBuyer.id,
expertId: applicant.id,
track: CompanionTrack.ONLINE_GAME,
serviceTitle: '游戏陪练',
hours: 1,
totalAmountCents: 3500,
});
orderService.payOrder(order.id, normalBuyer.id);
// 非指定接单大神尝试拒单
expect(() => {
orderService.expertReject(order.id, 9999);
}).toThrow('只有指定接单大神有权拒单');
// 指定接单大神拒单
orderService.expertReject(order.id, applicant.id, '时间冲突');
expect(order.status).toBe(OrderStatus.REFUNDED);
});
it('越权拦截:大神不可越权执行平台管理员争议仲裁', () => {
roleManager.submitExpertCert(applicant.id, { realName: '王小萌', idCardNo: '110101200001011111' });
roleManager.approveExpertCert(applicant.id, admin.id);
const order = orderService.createOrder({
buyerId: normalBuyer.id,
expertId: applicant.id,
track: CompanionTrack.ONLINE_GAME,
serviceTitle: '游戏陪练',
hours: 1,
totalAmountCents: 3500,
});
orderService.payOrder(order.id, normalBuyer.id);
orderService.expertAccept(order.id, applicant.id, roleManager);
orderService.expertApplyFinish(order.id, applicant.id);
orderService.buyerDispute(order.id, normalBuyer.id, '未达段位');
// 大神尝试越权自行结单打款
expect(() => {
orderService.arbitrate({
orderId: order.id,
operatorAdminId: applicant.id,
decision: 'RELEASE',
roleManager,
});
}).toThrow();
});
});
describe('4. 角色三:车队房主/局长 (HOST) 权限闭环与越权防御', () => {
let host: any;
let participant1: any;
let participant2: any;
let outsider: any;
let admin: any;
beforeEach(() => {
host = roleManager.registerUser({ openid: 'wx_host', nickname: '局长房主' });
participant1 = roleManager.registerUser({ openid: 'wx_p1', nickname: '搭子甲' });
participant2 = roleManager.registerUser({ openid: 'wx_p2', nickname: '搭子乙' });
outsider = roleManager.registerUser({ openid: 'wx_out', nickname: '无关人员' });
admin = roleManager.registerAdmin({ openid: 'wx_admin_3', nickname: '监管运营' });
});
it('房主发起组局自动获得 HOST 权限', () => {
const act = activityService.createActivity({
creatorId: host.id,
title: '【周末同城】火锅探店4人局',
gameCategory: '同城陪吃',
payType: ActivityPayType.AA_PAY,
aaFeeCents: 5000,
maxParticipants: 4,
roleManager,
});
expect(act.creatorId).toBe(host.id);
expect(roleManager.isHost(host.id)).toBe(true);
});
it('房主动态调价加赏差额全员补齐,非房主调价抛出权限异常', () => {
const act = activityService.createActivity({
creatorId: host.id,
title: '【加赏急招】电竞开黑车队',
gameCategory: '热门电竞',
payType: ActivityPayType.PROJECT_PAY,
perPersonFeeCents: 5000,
maxParticipants: 3,
roleManager,
});
const p1 = activityService.joinActivity(act.id, { userId: participant1.id, payAmountCents: 5000 });
// 非房主尝试调价
expect(() => {
activityService.adjustActivityPrice({
activityId: act.id,
operatorId: outsider.id,
newPriceCents: 8000,
});
}).toThrow('只有发起人有权调整活动出价');
// 房主成功调价至80元/人
const res = activityService.adjustActivityPrice({
activityId: act.id,
operatorId: host.id,
newPriceCents: 8000,
});
expect(res.priceDiffCents).toBe(3000);
expect(p1.payAmountCents).toBe(8000);
});
it('房主使用 4 位核销码核销到场搭子,非房主核销被拦截拒绝', () => {
const act = activityService.createActivity({
creatorId: host.id,
title: '同城剧本杀局',
gameCategory: '同城陪玩',
payType: ActivityPayType.AA_PAY,
aaFeeCents: 6000,
maxParticipants: 4,
roleManager,
});
const p = activityService.joinActivity(act.id, { userId: participant1.id, payAmountCents: 6000 });
// 非房主越权核销搭子门票
expect(() => {
activityService.verifyCheckin({
activityId: act.id,
operatorId: outsider.id,
verifyCode: p.verifyCode,
});
}).toThrow('无权核销该活动的核销码');
// 房主核销成功
const checkRes = activityService.verifyCheckin({
activityId: act.id,
operatorId: host.id,
verifyCode: p.verifyCode,
});
expect(checkRes.success).toBe(true);
expect(checkRes.participant.checkinStatus).toBe(true);
});
it('房主解散组局并全额原路退款已报名搭子,非房主解散被拦截', () => {
const act = activityService.createActivity({
creatorId: host.id,
title: '同城KTV狂欢局',
gameCategory: '同城陪唱',
payType: ActivityPayType.AA_PAY,
aaFeeCents: 7500,
maxParticipants: 4,
roleManager,
});
activityService.joinActivity(act.id, { userId: participant1.id, payAmountCents: 7500 });
activityService.joinActivity(act.id, { userId: participant2.id, payAmountCents: 7500 });
// 非房主尝试解散
expect(() => {
activityService.cancelActivity({
activityId: act.id,
operatorId: outsider.id,
reason: '恶意解散',
});
}).toThrow('只有发起人有权解散此组局');
// 房主正常解散
const cancelRes = activityService.cancelActivity({
activityId: act.id,
operatorId: host.id,
reason: '突发暴雨改期',
});
expect(cancelRes.success).toBe(true);
expect(cancelRes.status).toBe('CANCELLED');
expect(cancelRes.refundedCount).toBe(2);
expect(cancelRes.totalRefundCents).toBe(15000);
});
it('平台管理员具备全平台活动监管强制解散权限', () => {
const act = activityService.createActivity({
creatorId: host.id,
title: '涉嫌违规商圈组局',
gameCategory: '同城陪喝',
payType: ActivityPayType.AA_PAY,
aaFeeCents: 5000,
maxParticipants: 2,
roleManager,
});
activityService.joinActivity(act.id, { userId: participant1.id, payAmountCents: 5000 });
// 非管理员调用强制解散被拒绝
expect(() => {
activityService.adminForceCancelActivity({
activityId: act.id,
operatorAdminId: outsider.id,
reason: '违规封禁',
roleManager,
});
}).toThrow('只有平台管理员有权执行平台强制解散');
// 平台管理员执行强制解散并全额退款
const adminCancelRes = activityService.adminForceCancelActivity({
activityId: act.id,
operatorAdminId: admin.id,
reason: '违规场所管控下架',
roleManager,
});
expect(adminCancelRes.success).toBe(true);
expect(adminCancelRes.refundedCount).toBe(1);
});
});
describe('5. 角色四:平台管理员/运营端 (ADMIN) 仲裁与全流程监管', () => {
let buyer: any;
let expert: any;
let admin: any;
let order: any;
beforeEach(() => {
buyer = roleManager.registerUser({ openid: 'wx_b_arb', nickname: '申诉买家' });
expert = roleManager.registerUser({ openid: 'wx_e_arb', nickname: '涉事大神' });
admin = roleManager.registerAdmin({ openid: 'wx_a_arb', nickname: '总台仲裁法官' });
roleManager.submitExpertCert(expert.id, { realName: '赵选手', idCardNo: '110101199501018888' });
roleManager.approveExpertCert(expert.id, admin.id);
order = orderService.createOrder({
buyerId: buyer.id,
expertId: expert.id,
track: CompanionTrack.ONLINE_GAME,
serviceTitle: '王者双排冲国服',
hours: 3,
totalAmountCents: 10500,
});
orderService.payOrder(order.id, buyer.id);
orderService.expertAccept(order.id, expert.id, roleManager);
orderService.expertApplyFinish(order.id, expert.id);
orderService.buyerDispute(order.id, buyer.id, '大神接单后擅自挂机且言语态度恶劣,申请全额退款');
});
it('管理员判定买家胜诉全额退款 (REFUND),订单流转为 REFUNDED', () => {
expect(order.status).toBe(OrderStatus.DISPUTED);
const arbRes = orderService.arbitrate({
orderId: order.id,
operatorAdminId: admin.id,
decision: 'REFUND',
reason: '查证大神确实严重违规挂机,判定全额退款买家并记录信用扣分',
roleManager,
});
expect(arbRes.status).toBe(OrderStatus.REFUNDED);
expect(arbRes.arbitrationDecision).toBe('REFUND');
expect(arbRes.arbitratedBy).toBe(admin.id);
expect(arbRes.arbitratedAt).toBeDefined();
});
it('管理员判定申诉不成立驳回 (RELEASE),订单流转为 COMPLETED', () => {
expect(order.status).toBe(OrderStatus.DISPUTED);
const arbRes = orderService.arbitrate({
orderId: order.id,
operatorAdminId: admin.id,
decision: 'RELEASE',
reason: '核实对局战绩完整履约且达成胜率指标,驳回申诉并释放服务报酬',
roleManager,
});
expect(arbRes.status).toBe(OrderStatus.COMPLETED);
expect(arbRes.arbitrationDecision).toBe('RELEASE');
expect(arbRes.arbitratedBy).toBe(admin.id);
});
it('越权拦截:非平台管理员尝试执行仲裁判定被抛出异常拒绝', () => {
const impostor = roleManager.registerUser({ openid: 'wx_impostor', nickname: '假冒管理员' });
expect(() => {
orderService.arbitrate({
orderId: order.id,
operatorAdminId: impostor.id,
decision: 'REFUND',
roleManager,
});
}).toThrow('只有平台管理员有权执行争议仲裁');
});
it('非 DISPUTED 状态的订单不可执行仲裁判定', () => {
const normalOrder = orderService.createOrder({
buyerId: buyer.id,
expertId: expert.id,
track: CompanionTrack.ONLINE_GAME,
serviceTitle: '普通对局',
hours: 1,
totalAmountCents: 3500,
});
expect(() => {
orderService.arbitrate({
orderId: normalOrder.id,
operatorAdminId: admin.id,
decision: 'REFUND',
roleManager,
});
}).toThrow('非争议仲裁中,不可执行仲裁');
});
});
describe('6. 前端 UniApp 双身份与角色守卫 (Role Guard) 规则门禁验证', () => {
const roleJsPath = path.resolve(__dirname, '../../pwuniapp/common/role.js');
const roleJsContent = fs.readFileSync(roleJsPath, 'utf-8');
it('role.js 必须定义四大核心角色 (PLAYER, EXPERT, HOST, ADMIN)', () => {
expect(roleJsContent).toContain("PLAYER: 'PLAYER'");
expect(roleJsContent).toContain("EXPERT: 'EXPERT'");
expect(roleJsContent).toContain("HOST: 'HOST'");
expect(roleJsContent).toContain("ADMIN: 'ADMIN'");
});
it('role.js 必须包含完整的认证状态机定义 (CERT_STATUS)', () => {
expect(roleJsContent).toContain('NONE: 0');
expect(roleJsContent).toContain('PENDING: 1');
expect(roleJsContent).toContain('APPROVED: 2');
expect(roleJsContent).toContain('REJECTED: 3');
});
it('role.js 必须配置非大神禁止访问的受保护页面清单与 checkRoleGuard 守卫拦截', () => {
expect(roleJsContent).toContain("'/pages/expert/workspace'");
expect(roleJsContent).toContain("'/pages/expert/radar'");
expect(roleJsContent).toContain("'/pages/expert/hall'");
expect(roleJsContent).toContain('function checkRoleGuard');
expect(roleJsContent).toContain('function isExpertCertified');
expect(roleJsContent).toContain('function installRoleGuard');
});
it('App.vue 必须在 onLaunch 中注册 installRoleGuard 全局路由守卫', () => {
const appVuePath = path.resolve(__dirname, '../../pwuniapp/App.vue');
const appVueContent = fs.readFileSync(appVuePath, 'utf-8');
expect(appVueContent).toContain('installRoleGuard');
expect(appVueContent).toContain('installRoleGuard()');
});
it('CustomTabBar 底部导航栏必须对未认证非大神访问接单端进行权限拦截', () => {
const tabbarPath = path.resolve(__dirname, '../../pwuniapp/components/CustomTabBar.vue');
const tabbarContent = fs.readFileSync(tabbarPath, 'utf-8');
expect(tabbarContent).toContain('isExpertCertified');
});
it('三大专家页面必须在 onShow 生命周期中配置兜底权限重定向防穿透', () => {
const expertPages = [
path.resolve(__dirname, '../../pwuniapp/pages/expert/workspace.vue'),
path.resolve(__dirname, '../../pwuniapp/pages/expert/radar.vue'),
path.resolve(__dirname, '../../pwuniapp/pages/expert/hall.vue'),
];
for (const p of expertPages) {
const content = fs.readFileSync(p, 'utf-8');
expect(content).toContain('isExpertCertified');
expect(content).toContain('/pages/user/index');
}
});
it('大神端冷启动或刷新进入小程序首页时,首页与 App.vue 必须自动路由至大神接单大厅', () => {
const indexPath = path.resolve(__dirname, '../../pwuniapp/pages/index/index.vue');
const indexContent = fs.readFileSync(indexPath, 'utf-8');
expect(indexContent).toContain('checkExpertRedirect');
expect(indexContent).toContain('/pages/expert/hall');
expect(indexContent).toContain('ROLE_TYPES.EXPERT');
const appPath = path.resolve(__dirname, '../../pwuniapp/App.vue');
const appContent = fs.readFileSync(appPath, 'utf-8');
expect(appContent).toContain('/pages/expert/hall');
expect(appContent).toContain('ROLE_TYPES.EXPERT');
});
});
});