forked from wangziqi/gongxue-base
fix(correctness): 并发/事务/实体/时区/状态一致性修复
由 OCR(open-codereview.ai,deepseek-v4-flash)审查驱动修复: - wallets 原子扣款防 double-spend;refund 条件更新幂等;findTransactions 分页 - financial/imports/occupancies/attendance 事务与 advisory lock;重复生成/提交幂等 - 矛盾校验器、日期区间、实体双映射/DECIMAL/nullable、时区统一(china-time) - rbac-seed 防重激活、exam 权限恢复、状态一致性、路由顺序、N+1/IN 分块等性能项 Reviewed-by: OCR (open-codereview.ai)
This commit is contained in:
@@ -28,6 +28,7 @@ describe('RbacService seedData', () => {
|
||||
remove: jest.fn(async (value: any) => value),
|
||||
};
|
||||
const roleRepo = {
|
||||
count: jest.fn(async () => 0),
|
||||
findOne: jest.fn(async ({ where }: any) =>
|
||||
where.code === 'system_admin' || where.name === '系统管理员' ? systemAdminRole : null,
|
||||
),
|
||||
@@ -99,6 +100,7 @@ describe('RbacService seedData', () => {
|
||||
remove: jest.fn(async (value) => value),
|
||||
};
|
||||
const roleRepo = {
|
||||
count: jest.fn(async () => 0),
|
||||
findOne: jest.fn(async ({ where }: any) =>
|
||||
where.code === 'teacher' || where.name === '老师' ? teacherRole : null,
|
||||
),
|
||||
@@ -187,6 +189,7 @@ describe('RbacService legacy role consolidation', () => {
|
||||
remove: jest.fn(async (value) => value),
|
||||
};
|
||||
const roleRepo = {
|
||||
count: jest.fn(async () => 0),
|
||||
findOne: jest.fn(async () => targetRole),
|
||||
create: jest.fn((value) => ({ ...value, permissions: [] })),
|
||||
save: jest.fn(async (value) => value),
|
||||
@@ -217,3 +220,71 @@ describe('RbacService legacy role consolidation', () => {
|
||||
expect(roleRepo.remove).toHaveBeenCalledWith(legacyRole);
|
||||
});
|
||||
});
|
||||
|
||||
describe('RbacSeedService startup gating', () => {
|
||||
const makeService = (roleCount: number) => {
|
||||
const permRepo = {
|
||||
findOne: jest.fn(async () => null),
|
||||
create: jest.fn(),
|
||||
save: jest.fn(),
|
||||
find: jest.fn(async () => []),
|
||||
remove: jest.fn(),
|
||||
};
|
||||
const roleRepo = {
|
||||
count: jest.fn(async () => roleCount),
|
||||
findOne: jest.fn(async () => null),
|
||||
create: jest.fn(),
|
||||
save: jest.fn(),
|
||||
find: jest.fn(async () => []),
|
||||
remove: jest.fn(),
|
||||
};
|
||||
const userRepo = {
|
||||
update: jest.fn(async () => ({ affected: 0 })),
|
||||
count: jest.fn(),
|
||||
create: jest.fn(),
|
||||
save: jest.fn(),
|
||||
findOne: jest.fn(),
|
||||
};
|
||||
const service = new RbacService(
|
||||
permRepo as never,
|
||||
roleRepo as never,
|
||||
userRepo as never,
|
||||
{} as never,
|
||||
{} as never,
|
||||
{} as never,
|
||||
{} as never,
|
||||
{} as never,
|
||||
);
|
||||
return { service, permRepo, roleRepo, userRepo };
|
||||
};
|
||||
|
||||
it('skips re-seeding (no reactivation) when roles exist and SEED_ROLES is not set', async () => {
|
||||
const original = process.env.SEED_ROLES;
|
||||
delete process.env.SEED_ROLES;
|
||||
try {
|
||||
const { service, permRepo, roleRepo, userRepo } = makeService(3);
|
||||
await service.seedData();
|
||||
expect(roleRepo.count).toHaveBeenCalled();
|
||||
expect(permRepo.findOne).not.toHaveBeenCalled();
|
||||
expect(roleRepo.save).not.toHaveBeenCalled();
|
||||
expect(userRepo.update).not.toHaveBeenCalled();
|
||||
} finally {
|
||||
if (original === undefined) delete process.env.SEED_ROLES;
|
||||
else process.env.SEED_ROLES = original;
|
||||
}
|
||||
});
|
||||
|
||||
it('seeds when SEED_ROLES=true even if roles already exist', async () => {
|
||||
const original = process.env.SEED_ROLES;
|
||||
process.env.SEED_ROLES = 'true';
|
||||
try {
|
||||
const { service, permRepo, roleRepo } = makeService(3);
|
||||
await service.seedData();
|
||||
expect(permRepo.findOne).toHaveBeenCalled();
|
||||
expect(roleRepo.save).toHaveBeenCalled();
|
||||
} finally {
|
||||
if (original === undefined) delete process.env.SEED_ROLES;
|
||||
else process.env.SEED_ROLES = original;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user