auth.test.ts 4.77 KB
import { describe, it, expect, vi, beforeEach } from "vitest";
import { useAuthStore } from "@/stores/auth";

// mock API 模块,不发真实请求
vi.mock("@/api/user", () => ({
  apiLogin: vi.fn(),
  apiRegister: vi.fn(),
  apiForgotPassword: vi.fn(),
  apiResetPassword: vi.fn(),
  apiResendActivation: vi.fn(),
  apiProfile: vi.fn(),
  apiVerify: vi.fn(),
  apiRefreshToken: vi.fn(),
  apiLogout: vi.fn(),
}));

vi.mock("@/utils/request.ts", () => ({
  service: { defaults: { headers: { common: {} } } },
}));

vi.mock("@/utils/newbieTask", () => ({
  triggerNewbieTask: vi.fn(),
}));

import * as userApi from "@/api/user";

const mockApiLogin = vi.mocked(userApi.apiLogin);
const mockApiLogout = vi.mocked(userApi.apiLogout);
const mockApiRefreshToken = vi.mocked(userApi.apiRefreshToken);

beforeEach(() => {
  vi.clearAllMocks();
});

describe("useAuthStore - login", () => {
  it("格式1(标准格式):正确解析 token 并写入 localStorage", async () => {
    mockApiLogin.mockResolvedValue({
      data: { token: "abc123", refreshToken: "refresh456" },
    } as any);

    const store = useAuthStore();
    const result = await store.login({ email: "a@b.com", password: "123" });

    expect(result.success).toBe(true);
    expect(store.token).toBe("abc123");
    expect(localStorage.getItem("auth_token")).toBe("abc123");
    expect(store.isAuthenticated).toBe(true);
  });

  it("格式3(嵌套格式):正确解析 token", async () => {
    mockApiLogin.mockResolvedValue({
      data: { data: { token: "nested123" } },
    } as any);

    const store = useAuthStore();
    const result = await store.login({ email: "a@b.com", password: "123" });

    expect(result.success).toBe(true);
    expect(store.token).toBe("nested123");
  });

  it("登录失败:error 有值,loading 恢复 false,isAuthenticated 为 false", async () => {
    mockApiLogin.mockRejectedValue({
      response: { data: { message: "密码错误" } },
    });

    const store = useAuthStore();
    const result = await store.login({ email: "a@b.com", password: "wrong" });

    expect(result.success).toBe(false);
    expect(store.error).toBe("密码错误");
    expect(store.loading).toBe(false);
    expect(store.isAuthenticated).toBe(false);
  });

  it("登录过程中 loading 为 true,完成后恢复 false", async () => {
    let resolveLogin!: (v: any) => void;
    mockApiLogin.mockReturnValue(
      new Promise((res) => {
        resolveLogin = res;
      }),
    );

    const store = useAuthStore();
    const loginPromise = store.login({ email: "a@b.com", password: "123" });

    expect(store.loading).toBe(true);

    resolveLogin({ data: { token: "abc" } });
    await loginPromise;

    expect(store.loading).toBe(false);
  });
});

describe("useAuthStore - logout", () => {
  it("logout 后 token、localStorage、user 全部清空", async () => {
    mockApiLogin.mockResolvedValue({
      data: { token: "abc123", refreshToken: "refresh456" },
    } as any);
    mockApiLogout.mockResolvedValue({} as any);

    const store = useAuthStore();
    await store.login({ email: "a@b.com", password: "123" });

    store.logout();

    expect(store.token).toBe("");
    expect(store.isAuthenticated).toBe(false);
    expect(store.user).toBeNull();
    expect(localStorage.getItem("auth_token")).toBeNull();
  });

  it("logout 后再初始化 store,LOGOUT_FLAG 阻止读取残留 token", () => {
    localStorage.setItem("auth_token", "stale-token");
    localStorage.setItem("user_logged_out", "true");

    const store = useAuthStore();

    expect(store.token).toBe("");
    expect(store.isAuthenticated).toBe(false);
  });
});

describe("useAuthStore - isAuthenticated", () => {
  it("有 token 时为 true", async () => {
    mockApiLogin.mockResolvedValue({ data: { token: "abc" } } as any);

    const store = useAuthStore();
    await store.login({ email: "a@b.com", password: "123" });

    expect(store.isAuthenticated).toBe(true);
  });

  it("无 token 时为 false", () => {
    const store = useAuthStore();
    expect(store.isAuthenticated).toBe(false);
  });
});

describe("useAuthStore - refreshAccessToken", () => {
  it("无 refreshToken 时直接返回失败", async () => {
    const store = useAuthStore();
    const result = await store.refreshAccessToken();

    expect(result.success).toBe(false);
    expect(result.message).toBe("No refresh token");
  });

  it("刷新成功后更新 token", async () => {
    mockApiRefreshToken.mockResolvedValue({
      data: { token: "new-token", refreshToken: "new-refresh" },
    } as any);

    const store = useAuthStore();
    store.refreshToken = "old-refresh" as any;

    const result = await store.refreshAccessToken();

    expect(result.success).toBe(true);
    expect(store.token).toBe("new-token");
    expect(localStorage.getItem("auth_token")).toBe("new-token");
  });
});