Files
overlord/Overlord-Server/src/users.build-permission.test.ts
T
2026-05-19 00:46:30 -05:00

125 lines
3.8 KiB
TypeScript

import { describe, expect, test } from "bun:test";
import {
canBuildClients,
createUser,
deleteUser,
getUserByUsername,
setUserCanBuild,
} from "./users";
import { hasPermission } from "./rbac";
const createdUserIds: number[] = [];
async function createTempUser(role: "viewer" | "operator" | "admin") {
const username = `bp_${role.slice(0, 3)}_${Date.now().toString(36)}${Math.floor(Math.random() * 1000).toString(36)}`;
const result = await createUser(username, "Aa1!VeryLongTestPassword_2026", role, "test");
expect(result.success).toBe(true);
const user = getUserByUsername(username);
expect(user).not.toBeNull();
createdUserIds.push(user!.id);
return user!;
}
function cleanupCreatedUsers() {
while (createdUserIds.length > 0) {
const id = createdUserIds.pop();
if (typeof id === "number") {
deleteUser(id);
}
}
}
describe("build permission (can_build)", () => {
test("admin always has build permission regardless of can_build flag", async () => {
try {
const admin = await createTempUser("admin");
expect(canBuildClients(admin.id, admin.role)).toBe(true);
setUserCanBuild(admin.id, false);
// Admin role always returns true
expect(canBuildClients(admin.id, "admin")).toBe(true);
} finally {
cleanupCreatedUsers();
}
});
test("operator defaults to can_build = 1", async () => {
try {
const operator = await createTempUser("operator");
expect(canBuildClients(operator.id, operator.role)).toBe(true);
} finally {
cleanupCreatedUsers();
}
});
test("viewer defaults to can_build = 0", async () => {
try {
const viewer = await createTempUser("viewer");
expect(canBuildClients(viewer.id, viewer.role)).toBe(false);
} finally {
cleanupCreatedUsers();
}
});
test("revoking build permission denies building for non-admin", async () => {
try {
const operator = await createTempUser("operator");
expect(canBuildClients(operator.id, operator.role)).toBe(true);
const result = setUserCanBuild(operator.id, false);
expect(result.success).toBe(true);
expect(canBuildClients(operator.id, operator.role)).toBe(false);
} finally {
cleanupCreatedUsers();
}
});
test("granting build permission re-enables building", async () => {
try {
const viewer = await createTempUser("viewer");
expect(canBuildClients(viewer.id, viewer.role)).toBe(false);
setUserCanBuild(viewer.id, true);
expect(canBuildClients(viewer.id, viewer.role)).toBe(true);
setUserCanBuild(viewer.id, false);
expect(canBuildClients(viewer.id, viewer.role)).toBe(false);
} finally {
cleanupCreatedUsers();
}
});
test("hasPermission for clients:build uses userId when provided", async () => {
try {
const operator = await createTempUser("operator");
expect(hasPermission(operator.role, "clients:build", operator.id)).toBe(true);
setUserCanBuild(operator.id, false);
expect(hasPermission(operator.role, "clients:build", operator.id)).toBe(false);
} finally {
cleanupCreatedUsers();
}
});
test("hasPermission for clients:build without userId falls back to role check", () => {
expect(hasPermission("admin", "clients:build")).toBe(true);
expect(hasPermission("operator", "clients:build")).toBe(true);
expect(hasPermission("viewer", "clients:build")).toBe(false);
});
test("other permissions are unaffected by can_build", async () => {
try {
const operator = await createTempUser("operator");
setUserCanBuild(operator.id, false);
expect(hasPermission(operator.role, "clients:control", operator.id)).toBe(true);
expect(hasPermission(operator.role, "users:manage", operator.id)).toBe(false);
} finally {
cleanupCreatedUsers();
}
});
});