Files
lucia-frontend/tests/stores/acctMgmt.test.js
2026-03-09 13:52:08 +08:00

296 lines
8.2 KiB
JavaScript

// 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";
vi.mock("@/module/apiError.js", () => ({
default: vi.fn(),
}));
const { mockGet, mockPost, mockPut, mockDelete } = vi.hoisted(() => ({
mockGet: vi.fn(),
mockPost: vi.fn(),
mockPut: vi.fn(),
mockDelete: vi.fn(),
}));
vi.mock("@/api/client.js", () => ({
default: { get: mockGet, post: mockPost, put: mockPut, delete: mockDelete },
}));
// 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();
vi.clearAllMocks();
});
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 () => {
mockPost.mockResolvedValue({ status: 200 });
const user = { username: "newuser", password: "pass" };
await store.createNewAccount(user);
expect(mockPost).toHaveBeenCalledWith("/api/users", user);
expect(store.isOneAccountJustCreate).toBe(true);
expect(store.justCreateUsername).toBe("newuser");
});
});
describe("deleteAccount", () => {
it("returns true on success", async () => {
mockDelete.mockResolvedValue({ status: 200 });
const result = await store.deleteAccount("alice");
expect(mockDelete).toHaveBeenCalledWith("/api/users/alice");
expect(result).toBe(true);
});
it("encodes special characters in username", async () => {
mockDelete.mockResolvedValue({ status: 200 });
await store.deleteAccount("user@domain/name");
expect(mockDelete).toHaveBeenCalledWith(
"/api/users/user%40domain%2Fname",
);
});
it("returns false on error", async () => {
mockDelete.mockRejectedValue(new Error("fail"));
const result = await store.deleteAccount("alice");
expect(result).toBe(false);
});
});
describe("editAccount", () => {
it("puts edited data", async () => {
mockPut.mockResolvedValue({ status: 200 });
const detail = {
username: "alice",
password: "newpw",
name: "Alice",
is_active: true,
};
const result = await store.editAccount("alice", detail);
expect(mockPut).toHaveBeenCalledWith(
"/api/users/alice",
expect.objectContaining({ password: "newpw" }),
);
expect(result).toBe(true);
});
});
describe("addRoleToUser", () => {
it("puts role assignment", async () => {
mockPut.mockResolvedValue({ status: 200 });
const result = await store.addRoleToUser("alice", "admin");
expect(mockPut).toHaveBeenCalledWith("/api/users/alice/roles/admin");
expect(result).toBe(true);
});
it("encodes special characters in username and role", async () => {
mockPut.mockResolvedValue({ status: 200 });
await store.addRoleToUser("user@org", "role/special");
expect(mockPut).toHaveBeenCalledWith(
"/api/users/user%40org/roles/role%2Fspecial",
);
});
});
describe("deleteRoleToUser", () => {
it("deletes role", async () => {
mockDelete.mockResolvedValue({ status: 200 });
const result = await store.deleteRoleToUser("alice", "admin");
expect(mockDelete).toHaveBeenCalledWith("/api/users/alice/roles/admin");
expect(result).toBe(true);
});
});
describe("getUserDetail", () => {
it("fetches user and sets admin flag", async () => {
mockGet.mockResolvedValue({
status: 200,
data: {
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 () => {
mockGet.mockRejectedValue(new Error("not found"));
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");
});
});