91ef044e9c
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Validate Code Quality And Tests (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI Build & Deploy Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been skipped
CI Build & Deploy Pipeline / Build Staging API Image (push) Successful in 8m2s
CI Build & Deploy Pipeline / Build Staging Web Image (push) Failing after 8m14s
CI Build & Deploy Pipeline / Build Staging Worker Image (push) Successful in 11m2s
CI Build & Deploy Pipeline / Build Production API Image (push) Has been skipped
CI Build & Deploy Pipeline / Build Production Web Image (push) Has been skipped
CI Build & Deploy Pipeline / Build Production Worker Image (push) Has been skipped
CI Build & Deploy Pipeline / Deploy Production (push) Has been skipped
CI Build & Deploy Pipeline / Production Browser E2E (push) Has been skipped
CI Build & Deploy Pipeline / Staging E2E Tests (push) Has been skipped
CI Build & Deploy Pipeline / Staging API Integration Tests (push) Has been skipped
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com> Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
267 lines
7.7 KiB
TypeScript
267 lines
7.7 KiB
TypeScript
import { describe, expect, it, vi, beforeEach } from "vitest"
|
|
import {
|
|
normalizeUser,
|
|
login,
|
|
register,
|
|
logout,
|
|
getCurrentUser,
|
|
refreshAccessToken,
|
|
requestPasswordReset,
|
|
resetPassword,
|
|
verifyEmail,
|
|
} from "@/api/auth"
|
|
|
|
const mockPost = vi.fn()
|
|
const mockGet = vi.fn()
|
|
const mockAxiosPost = vi.fn()
|
|
|
|
vi.mock("@/api/client", () => ({
|
|
default: {
|
|
post: (...args: unknown[]) => mockPost(...args),
|
|
get: (...args: unknown[]) => mockGet(...args),
|
|
defaults: { baseURL: "/api/v1" },
|
|
},
|
|
}))
|
|
|
|
vi.mock("axios", () => ({
|
|
default: {
|
|
post: (...args: unknown[]) => mockAxiosPost(...args),
|
|
},
|
|
post: (...args: unknown[]) => mockAxiosPost(...args),
|
|
}))
|
|
|
|
vi.mock("antd", () => ({ message: { error: vi.fn(), success: vi.fn() } }))
|
|
|
|
describe("normalizeUser", () => {
|
|
it("normalizes canonical API current-user fields", () => {
|
|
expect(
|
|
normalizeUser({
|
|
user_id: "user-1",
|
|
email: "user@example.com",
|
|
username: "user",
|
|
display_name: "User",
|
|
email_verified: true,
|
|
}),
|
|
).toEqual({
|
|
id: "user-1",
|
|
user_id: "user-1",
|
|
email: "user@example.com",
|
|
username: "user",
|
|
display_name: "User",
|
|
is_email_verified: true,
|
|
email_verified: true,
|
|
created_at: undefined,
|
|
})
|
|
})
|
|
|
|
it("keeps compatibility with legacy UI-shaped user fields", () => {
|
|
expect(
|
|
normalizeUser({
|
|
id: "user-2",
|
|
email: "legacy@example.com",
|
|
username: "legacy",
|
|
display_name: "Legacy",
|
|
is_email_verified: false,
|
|
created_at: "2026-06-22T00:00:00Z",
|
|
}),
|
|
).toEqual({
|
|
id: "user-2",
|
|
user_id: "user-2",
|
|
email: "legacy@example.com",
|
|
username: "legacy",
|
|
display_name: "Legacy",
|
|
is_email_verified: false,
|
|
email_verified: false,
|
|
created_at: "2026-06-22T00:00:00Z",
|
|
})
|
|
})
|
|
|
|
it("prefers id over user_id when both present", () => {
|
|
const result = normalizeUser({
|
|
id: "id-first",
|
|
user_id: "userid-second",
|
|
email: "test@test.com",
|
|
username: "test",
|
|
display_name: "Test",
|
|
})
|
|
expect(result.id).toBe("id-first")
|
|
expect(result.user_id).toBe("id-first")
|
|
})
|
|
|
|
it("prefers is_email_verified over email_verified", () => {
|
|
const result = normalizeUser({
|
|
email: "test@test.com",
|
|
username: "test",
|
|
display_name: "Test",
|
|
is_email_verified: true,
|
|
email_verified: false,
|
|
})
|
|
expect(result.is_email_verified).toBe(true)
|
|
expect(result.email_verified).toBe(true)
|
|
})
|
|
|
|
it("defaults email verified to false when both missing", () => {
|
|
const result = normalizeUser({
|
|
email: "test@test.com",
|
|
username: "test",
|
|
display_name: "Test",
|
|
})
|
|
expect(result.is_email_verified).toBe(false)
|
|
expect(result.email_verified).toBe(false)
|
|
})
|
|
})
|
|
|
|
describe("auth API functions", () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks()
|
|
mockPost.mockResolvedValue({ data: { success: true } })
|
|
mockGet.mockResolvedValue({ data: {} })
|
|
mockAxiosPost.mockResolvedValue({ data: { access_token: "tok" } })
|
|
})
|
|
|
|
describe("login", () => {
|
|
it("calls login API with correct params", async () => {
|
|
mockPost.mockResolvedValue({
|
|
data: { access_token: "acc", refresh_token: "ref", user_id: "1" },
|
|
})
|
|
const result = await login({ email: "test@test.com", password: "pass" })
|
|
expect(mockPost).toHaveBeenCalledWith("/auth/login", {
|
|
email: "test@test.com",
|
|
password: "pass",
|
|
})
|
|
expect(result.access_token).toBe("acc")
|
|
})
|
|
|
|
it("rejects on error", async () => {
|
|
mockPost.mockRejectedValue(new Error("login failed"))
|
|
await expect(login({ email: "t", password: "p" })).rejects.toThrow()
|
|
})
|
|
})
|
|
|
|
describe("register", () => {
|
|
it("calls register API", async () => {
|
|
mockPost.mockResolvedValue({ data: { message: "ok" } })
|
|
const result = await register({
|
|
email: "test@test.com",
|
|
password: "pass",
|
|
username: "testuser",
|
|
})
|
|
expect(mockPost).toHaveBeenCalledWith("/auth/register", {
|
|
email: "test@test.com",
|
|
password: "pass",
|
|
username: "testuser",
|
|
})
|
|
expect(result.message).toBe("ok")
|
|
})
|
|
|
|
it("rejects on error", async () => {
|
|
mockPost.mockRejectedValue(new Error("register failed"))
|
|
await expect(register({ email: "t", password: "p", username: "u" })).rejects.toThrow()
|
|
})
|
|
})
|
|
|
|
describe("logout", () => {
|
|
it("calls logout API", async () => {
|
|
mockPost.mockResolvedValue({ data: {} })
|
|
await logout()
|
|
expect(mockPost).toHaveBeenCalledWith("/auth/logout")
|
|
})
|
|
|
|
it("rejects on error", async () => {
|
|
mockPost.mockRejectedValue(new Error("logout failed"))
|
|
await expect(logout()).rejects.toThrow()
|
|
})
|
|
})
|
|
|
|
describe("getCurrentUser", () => {
|
|
it("fetches and normalizes user", async () => {
|
|
mockGet.mockResolvedValue({
|
|
data: {
|
|
user_id: "u1",
|
|
email: "user@test.com",
|
|
username: "user1",
|
|
display_name: "User One",
|
|
email_verified: true,
|
|
},
|
|
})
|
|
const result = await getCurrentUser()
|
|
expect(mockGet).toHaveBeenCalledWith("/auth/me")
|
|
expect(result.id).toBe("u1")
|
|
expect(result.email).toBe("user@test.com")
|
|
expect(result.is_email_verified).toBe(true)
|
|
})
|
|
|
|
it("rejects on error", async () => {
|
|
mockGet.mockRejectedValue(new Error("fetch failed"))
|
|
await expect(getCurrentUser()).rejects.toThrow()
|
|
})
|
|
})
|
|
|
|
describe("refreshAccessToken", () => {
|
|
it("calls refresh endpoint with raw axios", async () => {
|
|
mockAxiosPost.mockResolvedValue({
|
|
data: { access_token: "new-acc", refresh_token: "new-ref" },
|
|
})
|
|
const result = await refreshAccessToken("old-refresh")
|
|
expect(mockAxiosPost).toHaveBeenCalledWith("/api/v1/auth/refresh", {
|
|
refresh_token: "old-refresh",
|
|
})
|
|
expect(result.access_token).toBe("new-acc")
|
|
})
|
|
|
|
it("rejects on error", async () => {
|
|
mockAxiosPost.mockRejectedValue(new Error("refresh failed"))
|
|
await expect(refreshAccessToken("tok")).rejects.toThrow()
|
|
})
|
|
})
|
|
|
|
describe("requestPasswordReset", () => {
|
|
it("calls forgot-password API", async () => {
|
|
mockPost.mockResolvedValue({ data: { message: "sent" } })
|
|
const result = await requestPasswordReset("test@test.com")
|
|
expect(mockPost).toHaveBeenCalledWith("/auth/forgot-password", {
|
|
email: "test@test.com",
|
|
})
|
|
expect(result.message).toBe("sent")
|
|
})
|
|
|
|
it("rejects on error", async () => {
|
|
mockPost.mockRejectedValue(new Error("failed"))
|
|
await expect(requestPasswordReset("e")).rejects.toThrow()
|
|
})
|
|
})
|
|
|
|
describe("resetPassword", () => {
|
|
it("calls reset-password API", async () => {
|
|
mockPost.mockResolvedValue({ data: { message: "reset ok" } })
|
|
const result = await resetPassword("token123", "newpass")
|
|
expect(mockPost).toHaveBeenCalledWith("/auth/reset-password", {
|
|
token: "token123",
|
|
new_password: "newpass",
|
|
})
|
|
expect(result.message).toBe("reset ok")
|
|
})
|
|
|
|
it("rejects on error", async () => {
|
|
mockPost.mockRejectedValue(new Error("failed"))
|
|
await expect(resetPassword("t", "p")).rejects.toThrow()
|
|
})
|
|
})
|
|
|
|
describe("verifyEmail", () => {
|
|
it("calls verify-email API", async () => {
|
|
mockPost.mockResolvedValue({ data: { message: "verified" } })
|
|
const result = await verifyEmail("verify-token")
|
|
expect(mockPost).toHaveBeenCalledWith("/auth/verify-email", {
|
|
token: "verify-token",
|
|
})
|
|
expect(result.message).toBe("verified")
|
|
})
|
|
|
|
it("rejects on error", async () => {
|
|
mockPost.mockRejectedValue(new Error("verify failed"))
|
|
await expect(verifyEmail("t")).rejects.toThrow()
|
|
})
|
|
})
|
|
})
|