import { beforeEach, describe, expect, it, vi } from 'vitest'; import { useAuthStore } from '@/stores/auth-store'; import { createDepartment, disableDepartment, enableDepartment, fetchDepartments, updateDepartment, } from './identity'; const USER_ID = '01HZZZZZZZZZZZZZZZZZZZZZZ1'; const ROOT_ID = '01HZZZZZZZZZZZZZZZZZZZZZZ2'; const CHILD_ID = '01HZZZZZZZZZZZZZZZZZZZZZZ3'; const jsonResponse = (body: unknown, status = 200): Response => new Response(JSON.stringify(body), { status, headers: { 'Content-Type': 'application/json' }, }); const root = { id: ROOT_ID, name: '内容生产中心', parentId: null, leader: { id: USER_ID, name: '陈默' }, status: 'active', sortOrder: 1, memberCount: 19, directMemberCount: 4, createdAt: '2026-11-01T00:00:00.000Z', children: [ { id: CHILD_ID, name: '素材一组', parentId: ROOT_ID, leader: null, status: 'disabled', sortOrder: 2, memberCount: 9, directMemberCount: 9, createdAt: '2026-11-01T00:00:00.000Z', children: [], }, ], }; function calls(): { url: string; init: RequestInit | undefined }[] { return vi.mocked(globalThis.fetch).mock.calls.map((call) => { const input = call[0]; const url = typeof input === 'string' ? input : input instanceof URL ? input.href : input.url; return { url, init: call[1] }; }); } function lastBody(): unknown { const body = calls().at(-1)?.init?.body; return typeof body === 'string' ? (JSON.parse(body) as unknown) : undefined; } describe('部门接口', () => { beforeEach(() => { vi.restoreAllMocks(); window.localStorage.clear(); useAuthStore.getState().clearSession(); useAuthStore.getState().setSession({ accessToken: 'token', user: { id: USER_ID, name: '陈默', account: 'chen.mo' }, permissions: ['user:view', 'user:manage'], }); }); it('部门树默认只取启用部门,includeDisabled 时带 query', async () => { const fetchMock = vi .spyOn(globalThis, 'fetch') .mockResolvedValueOnce(jsonResponse({ items: [root] })) .mockResolvedValueOnce(jsonResponse({ items: [root] })); const activeOnly = await fetchDepartments(); expect(String(calls()[0]?.url)).toContain('/departments/tree?include_disabled=false'); expect(activeOnly.map((node) => node.name)).toEqual(['内容生产中心', '素材一组']); expect(activeOnly[1]?.status).toBe('disabled'); await fetchDepartments(true); expect(String(calls()[1]?.url)).toContain('/departments/tree?include_disabled=true'); expect(fetchMock).toHaveBeenCalledTimes(2); }); it('新建返回部门 id,请求走 POST /departments', async () => { vi.spyOn(globalThis, 'fetch').mockResolvedValueOnce(jsonResponse({ id: CHILD_ID })); await expect( createDepartment({ name: '设计组', parentId: ROOT_ID, leaderUserId: null, sortOrder: 3, }), ).resolves.toBe(CHILD_ID); expect(calls()[0]?.init?.method).toBe('POST'); expect(String(calls()[0]?.url)).toContain('/departments'); expect(lastBody()).toEqual({ name: '设计组', parentId: ROOT_ID, leaderUserId: null, sortOrder: 3, }); }); it('更新 / 停用 / 启用使用约定方法与路径', async () => { vi.spyOn(globalThis, 'fetch') .mockResolvedValueOnce(new Response(null, { status: 204 })) .mockResolvedValueOnce(new Response(null, { status: 204 })) .mockResolvedValueOnce(new Response(null, { status: 204 })); await updateDepartment(CHILD_ID, { name: '设计组', leaderUserId: null }); await disableDepartment(CHILD_ID); await enableDepartment(CHILD_ID); const requestLines = calls().map((call) => { const url = new URL(call.url); return `${call.init?.method ?? 'GET'} ${url.pathname.replace('/api/v1', '')}`; }); expect(requestLines).toEqual([ `PATCH /departments/${CHILD_ID}`, `POST /departments/${CHILD_ID}/disable`, `POST /departments/${CHILD_ID}/enable`, ]); expect(lastBody()).toBeUndefined(); }); });