ec-nest/src/staff/staff.service.spec.ts
liuyun 0be521bd72 fix(c0): staff 绑定 userId 前校验账号在本租户存在
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-10-14 13:01:32 +08:00

94 lines
3.4 KiB
TypeScript

import { describe, expect, it, vi } from 'vitest';
import { StaffService } from './staff.service.js';
import type { PrismaService } from '../prisma/prisma.service.js';
import { ErrorCodes } from '../common/error-codes.js';
import { runWithRequestContext } from '../common/context/request-context.js';
import type { RequestContext } from '../common/context/request-context.js';
const ctx: RequestContext = {
tenantCode: 'T001',
userId: 'u1',
username: 'admin',
super: false,
dataScope: 'self',
orgCode: null,
orgCodes: [],
};
const baseDto = {
name: '张三',
idCardNo: '530102199001011234',
phone: '13800000000',
jobType: 'nurse',
workType: 'full',
employStatus: 'on',
};
function makeService(
overrides: {
staffFindFirst?: ReturnType<typeof vi.fn>;
userFindFirst?: ReturnType<typeof vi.fn>;
create?: ReturnType<typeof vi.fn>;
} = {},
) {
const staffFindFirst = overrides.staffFindFirst ?? vi.fn().mockResolvedValue(null);
const userFindFirst = overrides.userFindFirst ?? vi.fn().mockResolvedValue({ id: 'u9', tenantCode: 'T001' });
const create = overrides.create ?? vi.fn().mockResolvedValue({ id: 's1', ...baseDto, tenantCode: 'T001' });
const prisma = {
client: { staff: { findFirst: staffFindFirst, create }, user: { findFirst: userFindFirst } },
};
return { service: new StaffService(prisma as unknown as PrismaService), staffFindFirst, userFindFirst, create };
}
describe('StaffService', () => {
it('create 自动注入 tenantCode/createdBy', async () => {
const { service, create } = makeService();
await runWithRequestContext(ctx, () => service.createStaff(baseDto));
expect(create).toHaveBeenCalledWith({
data: { ...baseDto, tenantCode: 'T001', createdBy: 'u1' },
});
});
it('userId 指向的账号不存在时 create 抛 USER_NOT_FOUND', async () => {
const userFindFirst = vi.fn().mockResolvedValue(null);
const staffFindFirst = vi.fn();
const create = vi.fn();
const { service } = makeService({ userFindFirst, staffFindFirst, create });
await expect(
runWithRequestContext(ctx, () => service.createStaff({ ...baseDto, userId: 'u9' })),
).rejects.toMatchObject({ response: { code: ErrorCodes.USER_NOT_FOUND } });
expect(userFindFirst).toHaveBeenCalledWith({ where: { id: 'u9', tenantCode: 'T001' } });
expect(staffFindFirst).not.toHaveBeenCalled();
expect(create).not.toHaveBeenCalled();
});
it('userId 已被占用时 create 抛 STAFF_USER_LINKED', async () => {
const staffFindFirst = vi.fn().mockResolvedValue({ id: 's-other', userId: 'u9' });
const create = vi.fn();
const { service } = makeService({ staffFindFirst, create });
await expect(
runWithRequestContext(ctx, () => service.createStaff({ ...baseDto, userId: 'u9' })),
).rejects.toMatchObject({ response: { code: ErrorCodes.STAFF_USER_LINKED } });
expect(staffFindFirst).toHaveBeenCalledWith({ where: { userId: 'u9' } });
expect(create).not.toHaveBeenCalled();
});
it('userId 为空时不校验、直接创建', async () => {
const userFindFirst = vi.fn();
const staffFindFirst = vi.fn();
const { service, create } = makeService({ userFindFirst, staffFindFirst });
await runWithRequestContext(ctx, () => service.createStaff(baseDto));
expect(userFindFirst).not.toHaveBeenCalled();
expect(staffFindFirst).not.toHaveBeenCalled();
expect(create).toHaveBeenCalled();
});
});