141 lines
6.3 KiB
JavaScript
141 lines
6.3 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import test from "node:test";
|
|
import { groupUsers, useUserManagement } from "./UserManagement.js";
|
|
import { useUserGroupEditor } from "./UserGroupEditor.js";
|
|
import { mountComposable } from "../../test-support/mount.js";
|
|
|
|
async function setup(t) {
|
|
const calls = [];
|
|
const mounted = await mountComposable(t, useUserManagement, async (path, options) => {
|
|
calls.push({ path, options });
|
|
return Response.json({ items: [], current_role: "super_admin" });
|
|
});
|
|
calls.length = 0;
|
|
return { ...mounted, calls };
|
|
}
|
|
|
|
test("主超级管理员不开放用户管理修改操作", async (t) => {
|
|
const { state, calls } = await setup(t);
|
|
const item = { id: "primary-super-admin", role: "super_admin", status: "active" };
|
|
assert.equal(state.isPrimaryAccount(item), true);
|
|
state.openEdit(item);
|
|
assert.equal(state.showForm.value, false);
|
|
await state.toggleStatus(item);
|
|
await state.deleteUser(item);
|
|
assert.equal(calls.length, 0);
|
|
});
|
|
|
|
test("禁止新增或提升为超级管理员,包括非标准大小写", async (t) => {
|
|
const { state, calls } = await setup(t);
|
|
for (const editing of [false, true]) {
|
|
for (const role of ["super_admin", " SUPER_ADMIN "]) {
|
|
if (editing) state.openEdit({ id: "operator", username: "operator", role: "admin" });
|
|
else state.openCreate();
|
|
Object.assign(state.form.value, { username: "operator", password: "OperPass123", role });
|
|
await state.saveUser();
|
|
assert.equal(state.formError.value, "不能新增或提升为超级管理员");
|
|
}
|
|
}
|
|
assert.equal(calls.length, 0);
|
|
});
|
|
|
|
test("管理员和普通用户仍能正常创建及编辑", async (t) => {
|
|
const { state, calls } = await setup(t);
|
|
for (const role of ["admin", "user"]) {
|
|
state.openCreate();
|
|
Object.assign(state.form.value, { username: "operator", password: "OperPass123", role });
|
|
await state.saveUser();
|
|
state.openEdit({ id: "operator", username: "operator", role, status: "active" });
|
|
await state.saveUser();
|
|
}
|
|
const writes = calls.filter(({ options }) => options.method);
|
|
assert.deepEqual(writes.map(({ options }) => options.method), ["POST", "PUT", "POST", "PUT"]);
|
|
assert.equal(state.formError.value, "");
|
|
});
|
|
|
|
test("默认分组置顶,多组管理员同时出现在所属分组且不重复", () => {
|
|
const members = [
|
|
{ id: "multi", groups: ["乙组", "甲组", "甲组"] },
|
|
{ id: "single", groups: ["甲组"] },
|
|
{ id: "default" },
|
|
];
|
|
const result = groupUsers(members);
|
|
assert.equal(result[0].isDefault, true);
|
|
assert.deepEqual(result[0].users.map((user) => user.id), ["default"]);
|
|
assert.equal(result.find((group) => group.name === "甲组").users.length, 2);
|
|
assert.equal(result.find((group) => group.name === "乙组").users.length, 1);
|
|
});
|
|
|
|
test("管理员只能读取成员,普通用户不显示分组,未知权限默认关闭写入", async (t) => {
|
|
for (const role of ["admin", "user", ""]) {
|
|
const writes = [];
|
|
const { state } = await mountComposable(t, useUserManagement, async (path, options) => {
|
|
if (options.method) writes.push(path);
|
|
return Response.json({ items: [], current_role: role });
|
|
});
|
|
assert.equal(state.canManage.value, false);
|
|
assert.equal(state.showGroups.value, role === "admin");
|
|
state.openCreate();
|
|
state.openEdit({ id: "member", role: "user" });
|
|
assert.equal(state.showForm.value, false);
|
|
await state.saveUser();
|
|
await state.toggleStatus({ id: "member", status: "active" });
|
|
await state.deleteUser({ id: "member" });
|
|
assert.deepEqual(writes, []);
|
|
}
|
|
});
|
|
|
|
test("普通用户不允许多组,降级时保留选择供用户修正", async (t) => {
|
|
const { state, calls } = await setup(t);
|
|
state.openEdit({ id: "leader", username: "leader", role: "admin", groups: ["甲组", "乙组"] });
|
|
state.form.value.role = "user";
|
|
await state.saveUser();
|
|
assert.match(state.formError.value, /只能属于一个分组/);
|
|
assert.deepEqual(state.form.value.groups, ["甲组", "乙组"]);
|
|
assert.equal(calls.length, 0);
|
|
state.form.value.groups = ["乙组"];
|
|
await state.saveUser();
|
|
assert.deepEqual(JSON.parse(calls.find((call) => call.options.method === "PUT").options.body).groups, ["乙组"]);
|
|
});
|
|
|
|
test("分组编辑器支持管理员多组并拒绝普通用户多组及保留名称", () => {
|
|
const props = { modelValue: [], role: "admin" };
|
|
const editor = useUserGroupEditor(props, (_, value) => { props.modelValue = value; });
|
|
for (const name of [" 甲组 ", "乙组", "甲组"]) {
|
|
editor.groupName.value = name;
|
|
editor.addGroup();
|
|
}
|
|
assert.deepEqual(props.modelValue, ["甲组", "乙组"]);
|
|
props.role = "user";
|
|
editor.groupName.value = "丙组";
|
|
editor.addGroup();
|
|
assert.match(editor.groupError.value, /只能属于一个分组/);
|
|
editor.removeGroup("乙组");
|
|
assert.deepEqual(props.modelValue, ["甲组"]);
|
|
editor.groupName.value = "默认分组";
|
|
editor.addGroup();
|
|
assert.match(editor.groupError.value, /默认分组无需添加/);
|
|
});
|
|
|
|
test("分组页面结构可编译,默认组不折叠,命名组默认关闭,密码入口仅在用户管理", async () => {
|
|
const { readFile } = await import("node:fs/promises");
|
|
const { parse, compileScript, compileTemplate } = await import("@vue/compiler-sfc");
|
|
for (const file of ["UserManagement.vue", "UserTable.vue", "UserGroupEditor.vue", "UserWorkspace.vue"]) {
|
|
const source = await readFile(new URL(file, import.meta.url), "utf8");
|
|
const { descriptor, errors } = parse(source);
|
|
assert.deepEqual(errors, []);
|
|
assert.ok(compileScript(descriptor, { id: file }));
|
|
assert.deepEqual(compileTemplate({ source: descriptor.template.content, filename: file, id: file }).errors, []);
|
|
}
|
|
const source = await readFile(new URL("UserManagement.vue", import.meta.url), "utf8");
|
|
assert.match(source, /section v-if="group.isDefault"/);
|
|
assert.match(source, /<details v-else class="user-group">/);
|
|
assert.match(source, /v-else-if="showGroups"/);
|
|
assert.match(source, /<ChangePassword/);
|
|
const dashboard = await readFile(new URL("../Dashboard/Dashboard.vue", import.meta.url), "utf8");
|
|
const navigation = await readFile(new URL("../Dashboard/DashboardNavigation.vue", import.meta.url), "utf8");
|
|
assert.doesNotMatch(dashboard, /<ChangePassword/);
|
|
assert.doesNotMatch(dashboard, /@click="openSettings"/);
|
|
assert.match(navigation, /\$emit\('settings'\)/);
|
|
});
|