const employeesRoutes = require("../../../src/routes/employees"); const { buildEmployeeOrganization, buildEmployeeProfile } = employeesRoutes.__testables; function buildEmployeeRow(overrides = {}) { return { id: "11111111-1111-4111-8111-111111111111", employee_no: "E001", name: "Alice", title: "员工", status: "active", direct_manager_id: null, department: { id: "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa", name: "行政部", }, ...overrides, }; } describe("employee profile organization", () => { it("builds reporting chain from top to current employee and keeps direct reports scoped to one hop", async () => { const currentEmployee = buildEmployeeRow({ id: "33333333-3333-4333-8333-333333333333", employee_no: "E003", name: "Carol", title: "专员", direct_manager_id: "22222222-2222-4222-8222-222222222222", department: { id: "dept-3", name: "采购部" }, }); const manager = buildEmployeeRow({ id: "22222222-2222-4222-8222-222222222222", employee_no: "E002", name: "Bob", title: "经理", direct_manager_id: "11111111-1111-4111-8111-111111111111", department: { id: "dept-2", name: "财务部" }, }); const director = buildEmployeeRow({ id: "11111111-1111-4111-8111-111111111111", employee_no: "E001", name: "Alice", title: "总监", department: { id: "dept-1", name: "管理层" }, }); const prisma = { employee: { findUnique: vi.fn(async ({ where }) => { if (where.id === manager.id) return manager; if (where.id === director.id) return director; return null; }), findMany: vi.fn().mockResolvedValue([ buildEmployeeRow({ id: "44444444-4444-4444-8444-444444444444", employee_no: "E004", name: "David", title: "专员", status: "active", department: { id: "dept-3", name: "采购部" }, }), buildEmployeeRow({ id: "55555555-5555-4555-8555-555555555555", employee_no: "E005", name: "Eve", title: "专员", status: "archived", department: { id: "dept-3", name: "采购部" }, }), ]), }, }; const organization = await buildEmployeeOrganization(prisma, currentEmployee); expect(organization.reportingChain.map((item) => item.id)).toEqual([ director.id, manager.id, currentEmployee.id, ]); expect(organization.reportingChain.at(-1)).toMatchObject({ id: currentEmployee.id, department: { id: "dept-3", name: "采购部" }, }); expect(organization.directReports.map((item) => item.id)).toEqual([ "44444444-4444-4444-8444-444444444444", "55555555-5555-4555-8555-555555555555", ]); expect(prisma.employee.findMany).toHaveBeenCalledWith({ where: { direct_manager_id: currentEmployee.id }, orderBy: [{ status: "asc" }, { name: "asc" }], select: { id: true, employee_no: true, name: true, title: true, status: true, avatar_path: true, department: { select: { id: true, name: true } }, }, }); }); it("stops on manager cycle and returns the partial reporting chain", async () => { const currentEmployee = buildEmployeeRow({ id: "66666666-6666-4666-8666-666666666666", employee_no: "E006", name: "Frank", title: "经理", direct_manager_id: "77777777-7777-4777-8777-777777777777", }); const cycleManager = buildEmployeeRow({ id: "77777777-7777-4777-8777-777777777777", employee_no: "E007", name: "Grace", title: "总监", direct_manager_id: currentEmployee.id, }); const prisma = { employee: { findUnique: vi.fn(async ({ where }) => (where.id === cycleManager.id ? cycleManager : null)), findMany: vi.fn().mockResolvedValue([]), }, }; const organization = await buildEmployeeOrganization(prisma, currentEmployee); expect(organization.reportingChain.map((item) => item.id)).toEqual([cycleManager.id, currentEmployee.id]); expect(prisma.employee.findUnique).toHaveBeenCalledTimes(1); }); it("embeds stable organization arrays into employee profile responses", async () => { const currentEmployee = { ...buildEmployeeRow({ id: "88888888-8888-4888-8888-888888888888", employee_no: "E008", name: "Helen", title: "主管", }), phone: "13800000000", email: "helen@example.com", employment_status: "active", department_id: "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa", hire_date: new Date("2026-01-10T00:00:00.000Z"), probation_end_date: null, last_working_date: null, work_location: "上海", emergency_contact_name: "联系人", emergency_contact_phone: "13900000000", notes: "none", archived_at: null, roles: [{ role_code: "employee" }], direct_manager: null, }; const prisma = { employee: { findUnique: vi.fn(async ({ where }) => (where.id === currentEmployee.id ? currentEmployee : null)), findMany: vi.fn().mockResolvedValue([]), }, employeeChecklistTask: { findMany: vi.fn().mockResolvedValue([]), }, employeeLifecycleEvent: { findMany: vi.fn().mockResolvedValue([]), }, assetCard: { findMany: vi.fn().mockResolvedValue([]), count: vi.fn().mockResolvedValue(0), }, workflowTask: { count: vi.fn().mockResolvedValue(0), }, }; const profile = await buildEmployeeProfile(prisma, currentEmployee.id); expect(profile.organization.reportingChain).toEqual([ { id: currentEmployee.id, employeeNo: "E008", name: "Helen", title: "主管", status: "active", avatarUrl: null, department: { id: "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa", name: "行政部", }, }, ]); expect(profile.organization.directReports).toEqual([]); expect(profile.assignedAssets).toEqual([]); }); });