// The Lucia project. // Copyright 2026-2026 DSP, inc. All rights reserved. // Authors: // imacat.yang@dsp.im (imacat), 2026/03/05 import { describe, it, expect, beforeEach, vi } from "vitest"; import { setActivePinia, createPinia } from "pinia"; import { http, HttpResponse } from "msw"; import { server } from "@/mocks/node.js"; import { findRequest, captureRequest } from "@/mocks/request-log.js"; vi.mock("@/module/apiError.js", () => ({ default: vi.fn(), })); // Mock login store to avoid its side effects vi.mock("@/stores/login", async () => { const { defineStore } = await import("pinia"); return { useLoginStore: defineStore("loginStore", { state: () => ({ userData: { username: "currentUser", name: "Current" }, }), actions: { getUserData: vi.fn(), }, }), }; }); import { useAcctMgmtStore } from "@/stores/acctMgmt"; describe("acctMgmtStore", () => { let store; beforeEach(() => { setActivePinia(createPinia()); store = useAcctMgmtStore(); document.cookie = "luciaToken=fake-test-token"; }); it("has correct default state", () => { expect(store.allUserAccountList).toEqual([]); expect(store.isAcctMenuOpen).toBe(false); }); describe("menu actions", () => { it("openAcctMenu sets true", () => { store.openAcctMenu(); expect(store.isAcctMenuOpen).toBe(true); }); it("closeAcctMenu sets false", () => { store.openAcctMenu(); store.closeAcctMenu(); expect(store.isAcctMenuOpen).toBe(false); }); it("toggleIsAcctMenuOpen toggles", () => { store.toggleIsAcctMenuOpen(); expect(store.isAcctMenuOpen).toBe(true); store.toggleIsAcctMenuOpen(); expect(store.isAcctMenuOpen).toBe(false); }); }); describe("setCurrentViewingUser", () => { it("finds user by username", () => { store.allUserAccountList = [ { username: "alice", name: "Alice", detail: {} }, { username: "bob", name: "Bob", detail: {} }, ]; store.setCurrentViewingUser("bob"); expect(store.currentViewingUser.name).toBe("Bob"); }); }); describe("clearCurrentViewingUser", () => { it("resets to empty user", () => { store.currentViewingUser = { username: "test", detail: {} }; store.clearCurrentViewingUser(); expect(store.currentViewingUser.username).toBe(""); }); }); describe("createNewAccount", () => { it("posts to /api/users and sets flag on success", async () => { const randomPassword = crypto.randomUUID(); const user = { username: "newuser", password: randomPassword }; server.use( http.post("/api/users", async ({ request }) => { const body = await request.json(); captureRequest("POST", new URL(request.url).pathname, body); return new HttpResponse(null, { status: 200 }); }), ); await store.createNewAccount(user); const reqs = findRequest("POST", "/api/users"); expect(reqs).toHaveLength(1); expect(reqs[0].body).toEqual(user); expect(store.isOneAccountJustCreate).toBe(true); expect(store.justCreateUsername).toBe("newuser"); }); }); describe("deleteAccount", () => { it("returns true on success", async () => { server.use( http.delete("/api/users/:username", ({ request }) => { captureRequest("DELETE", new URL(request.url).pathname); return new HttpResponse(null, { status: 200 }); }), ); const result = await store.deleteAccount("alice"); const reqs = findRequest("DELETE", "/api/users/alice"); expect(reqs).toHaveLength(1); expect(result).toBe(true); }); it("encodes special characters in username", async () => { server.use( http.delete("/api/users/:username", ({ request }) => { captureRequest("DELETE", new URL(request.url).pathname); return new HttpResponse(null, { status: 200 }); }), ); await store.deleteAccount("user@domain/name"); const reqs = findRequest("DELETE", "/api/users/user%40domain%2Fname"); expect(reqs).toHaveLength(1); }); it("returns false on error", async () => { server.use( http.delete("/api/users/:username", () => new HttpResponse(null, { status: 500 }), ), ); const result = await store.deleteAccount("alice"); expect(result).toBe(false); }); }); describe("editAccount", () => { it("puts edited data", async () => { const randomPassword = crypto.randomUUID(); const detail = { username: "alice", password: randomPassword, name: "Alice", is_active: true, }; server.use( http.put("/api/users/:username", async ({ request }) => { const body = await request.json(); captureRequest("PUT", new URL(request.url).pathname, body); return new HttpResponse(null, { status: 200 }); }), ); const result = await store.editAccount("alice", detail); const reqs = findRequest("PUT", "/api/users/alice"); expect(reqs).toHaveLength(1); expect(reqs[0].body.password).toBe(randomPassword); expect(result).toBe(true); }); }); describe("addRoleToUser", () => { it("puts role assignment", async () => { server.use( http.put("/api/users/:username/roles/:role", ({ request }) => { captureRequest("PUT", new URL(request.url).pathname); return new HttpResponse(null, { status: 200 }); }), ); const result = await store.addRoleToUser("alice", "admin"); const reqs = findRequest("PUT", "/api/users/alice/roles/admin"); expect(reqs).toHaveLength(1); expect(result).toBe(true); }); it("encodes special characters in username and role", async () => { server.use( http.put("/api/users/:username/roles/:role", ({ request }) => { captureRequest("PUT", new URL(request.url).pathname); return new HttpResponse(null, { status: 200 }); }), ); await store.addRoleToUser("user@org", "role/special"); const reqs = findRequest("PUT", "/api/users/user%40org/roles/role%2Fspecial"); expect(reqs).toHaveLength(1); }); }); describe("deleteRoleToUser", () => { it("deletes role", async () => { server.use( http.delete("/api/users/:username/roles/:role", ({ request }) => { captureRequest("DELETE", new URL(request.url).pathname); return new HttpResponse(null, { status: 200 }); }), ); const result = await store.deleteRoleToUser("alice", "admin"); const reqs = findRequest("DELETE", "/api/users/alice/roles/admin"); expect(reqs).toHaveLength(1); expect(result).toBe(true); }); }); describe("getUserDetail", () => { it("fetches user and sets admin flag", async () => { server.use( http.get("/api/users/:username", () => HttpResponse.json({ username: "alice", roles: [{ code: "admin" }], }), ), ); const result = await store.getUserDetail("alice"); expect(result).toBe(true); expect(store.currentViewingUser.is_admin).toBe(true); }); it("returns false on error", async () => { server.use( http.get("/api/users/:username", () => new HttpResponse(null, { status: 500 }), ), ); const result = await store.getUserDetail("ghost"); expect(result).toBe(false); }); }); describe("hover state actions", () => { beforeEach(() => { store.allUserAccountList = [ { username: "alice", isDeleteHovered: false, isRowHovered: false, isEditHovered: false, isDetailHovered: false, }, ]; }); it("changeIsDeleteHoveredByUser", () => { store.changeIsDeleteHoveredByUser("alice", true); expect(store.allUserAccountList[0].isDeleteHovered).toBe(true); }); it("changeIsRowHoveredByUser", () => { store.changeIsRowHoveredByUser("alice", true); expect(store.allUserAccountList[0].isRowHovered).toBe(true); }); it("changeIsEditHoveredByUser", () => { store.changeIsEditHoveredByUser("alice", true); expect(store.allUserAccountList[0].isEditHovered).toBe(true); }); it("changeIsDetailHoveredByUser", () => { store.changeIsDetailHoveredByUser("alice", true); expect(store.allUserAccountList[0].isDetailHovered).toBe(true); }); }); describe("moveCurrentLoginUserToFirstRow", () => { it("moves logged-in user to first position", async () => { const list = [ { username: "alice", name: "Alice" }, { username: "currentUser", name: "Current" }, { username: "bob", name: "Bob" }, ]; const result = await store.moveCurrentLoginUserToFirstRow(list); expect(result.map((u) => u.username)).toEqual([ "currentUser", "alice", "bob", ]); }); it("preserves list when logged-in user is not found", async () => { const list = [ { username: "alice", name: "Alice" }, { username: "bob", name: "Bob" }, { username: "charlie", name: "Charlie" }, ]; const result = await store.moveCurrentLoginUserToFirstRow(list); expect(result.map((u) => u.username)).toEqual([ "alice", "bob", "charlie", ]); }); }); it("resetJustCreateFlag resets flag", () => { store.isOneAccountJustCreate = true; store.resetJustCreateFlag(); expect(store.isOneAccountJustCreate).toBe(false); }); it("setShouldUpdateList sets boolean", () => { store.setShouldUpdateList(true); expect(store.shouldUpdateList).toBe(true); }); it("updateSingleAccountPiniaState updates user", () => { store.allUserAccountList = [{ username: "alice", name: "Old" }]; store.updateSingleAccountPiniaState({ username: "alice", name: "New", }); expect(store.allUserAccountList[0].name).toBe("New"); }); });