Update paths in sync.sh, master/main.go, and CLAUDE.md to reflect the directory rename. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
214 lines
7.8 KiB
TypeScript
214 lines
7.8 KiB
TypeScript
// Tests for user.ts
|
|
// These are pure unit tests - no database needed
|
|
|
|
import assert from "node:assert/strict";
|
|
import { describe, it } from "node:test";
|
|
import {
|
|
AnonymousUser,
|
|
AuthenticatedUser,
|
|
anonymousUser,
|
|
type Permission,
|
|
type Role,
|
|
} from "./user";
|
|
|
|
describe("User", () => {
|
|
describe("AuthenticatedUser.create", () => {
|
|
it("creates a user with default values", () => {
|
|
const user = AuthenticatedUser.create("test@example.com");
|
|
|
|
assert.equal(user.email, "test@example.com");
|
|
assert.equal(user.status, "active");
|
|
assert.equal(user.isAnonymous(), false);
|
|
assert.deepEqual([...user.roles], []);
|
|
assert.deepEqual([...user.permissions], []);
|
|
});
|
|
|
|
it("creates a user with custom values", () => {
|
|
const user = AuthenticatedUser.create("test@example.com", {
|
|
id: "custom-id",
|
|
displayName: "Test User",
|
|
status: "pending",
|
|
roles: ["admin"],
|
|
permissions: ["posts:create"],
|
|
});
|
|
|
|
assert.equal(user.id, "custom-id");
|
|
assert.equal(user.displayName, "Test User");
|
|
assert.equal(user.status, "pending");
|
|
assert.deepEqual([...user.roles], ["admin"]);
|
|
assert.deepEqual([...user.permissions], ["posts:create"]);
|
|
});
|
|
});
|
|
|
|
describe("status checks", () => {
|
|
it("isActive returns true for active users", () => {
|
|
const user = AuthenticatedUser.create("test@example.com", {
|
|
status: "active",
|
|
});
|
|
assert.equal(user.isActive(), true);
|
|
});
|
|
|
|
it("isActive returns false for suspended users", () => {
|
|
const user = AuthenticatedUser.create("test@example.com", {
|
|
status: "suspended",
|
|
});
|
|
assert.equal(user.isActive(), false);
|
|
});
|
|
|
|
it("isActive returns false for pending users", () => {
|
|
const user = AuthenticatedUser.create("test@example.com", {
|
|
status: "pending",
|
|
});
|
|
assert.equal(user.isActive(), false);
|
|
});
|
|
});
|
|
|
|
describe("role checks", () => {
|
|
it("hasRole returns true when user has the role", () => {
|
|
const user = AuthenticatedUser.create("test@example.com", {
|
|
roles: ["admin", "editor"],
|
|
});
|
|
assert.equal(user.hasRole("admin"), true);
|
|
assert.equal(user.hasRole("editor"), true);
|
|
});
|
|
|
|
it("hasRole returns false when user does not have the role", () => {
|
|
const user = AuthenticatedUser.create("test@example.com", {
|
|
roles: ["user"],
|
|
});
|
|
assert.equal(user.hasRole("admin"), false);
|
|
});
|
|
|
|
it("hasAnyRole returns true when user has at least one role", () => {
|
|
const user = AuthenticatedUser.create("test@example.com", {
|
|
roles: ["editor"],
|
|
});
|
|
assert.equal(user.hasAnyRole(["admin", "editor"]), true);
|
|
});
|
|
|
|
it("hasAnyRole returns false when user has none of the roles", () => {
|
|
const user = AuthenticatedUser.create("test@example.com", {
|
|
roles: ["user"],
|
|
});
|
|
assert.equal(user.hasAnyRole(["admin", "editor"]), false);
|
|
});
|
|
|
|
it("hasAllRoles returns true when user has all roles", () => {
|
|
const user = AuthenticatedUser.create("test@example.com", {
|
|
roles: ["admin", "editor", "user"],
|
|
});
|
|
assert.equal(user.hasAllRoles(["admin", "editor"]), true);
|
|
});
|
|
|
|
it("hasAllRoles returns false when user is missing a role", () => {
|
|
const user = AuthenticatedUser.create("test@example.com", {
|
|
roles: ["admin"],
|
|
});
|
|
assert.equal(user.hasAllRoles(["admin", "editor"]), false);
|
|
});
|
|
});
|
|
|
|
describe("permission checks", () => {
|
|
it("hasPermission returns true for direct permissions", () => {
|
|
const user = AuthenticatedUser.create("test@example.com", {
|
|
permissions: ["posts:create" as Permission],
|
|
});
|
|
assert.equal(
|
|
user.hasPermission("posts:create" as Permission),
|
|
true,
|
|
);
|
|
});
|
|
|
|
it("hasPermission returns true for role-derived permissions", () => {
|
|
const user = AuthenticatedUser.create("test@example.com", {
|
|
roles: ["admin" as Role],
|
|
});
|
|
// admin role has users:read, users:create, users:update, users:delete
|
|
assert.equal(user.hasPermission("users:read" as Permission), true);
|
|
assert.equal(
|
|
user.hasPermission("users:delete" as Permission),
|
|
true,
|
|
);
|
|
});
|
|
|
|
it("hasPermission returns false when permission not granted", () => {
|
|
const user = AuthenticatedUser.create("test@example.com", {
|
|
roles: ["user" as Role],
|
|
});
|
|
// user role only has users:read
|
|
assert.equal(
|
|
user.hasPermission("users:delete" as Permission),
|
|
false,
|
|
);
|
|
});
|
|
|
|
it("can() is a convenience method for hasPermission", () => {
|
|
const user = AuthenticatedUser.create("test@example.com", {
|
|
roles: ["admin" as Role],
|
|
});
|
|
assert.equal(user.can("read", "users"), true);
|
|
assert.equal(user.can("delete", "users"), true);
|
|
assert.equal(user.can("create", "posts"), false);
|
|
});
|
|
});
|
|
|
|
describe("effectivePermissions", () => {
|
|
it("returns combined direct and role-derived permissions", () => {
|
|
const user = AuthenticatedUser.create("test@example.com", {
|
|
roles: ["user" as Role],
|
|
permissions: ["posts:create" as Permission],
|
|
});
|
|
|
|
const perms = user.effectivePermissions();
|
|
assert.equal(perms.has("posts:create" as Permission), true);
|
|
assert.equal(perms.has("users:read" as Permission), true); // from user role
|
|
});
|
|
|
|
it("returns empty set for user with no roles or permissions", () => {
|
|
const user = AuthenticatedUser.create("test@example.com");
|
|
const perms = user.effectivePermissions();
|
|
assert.equal(perms.size, 0);
|
|
});
|
|
});
|
|
|
|
describe("serialization", () => {
|
|
it("toJSON returns plain object", () => {
|
|
const user = AuthenticatedUser.create("test@example.com", {
|
|
id: "test-id",
|
|
displayName: "Test",
|
|
status: "active",
|
|
roles: ["admin"],
|
|
permissions: ["posts:create"],
|
|
});
|
|
|
|
const json = user.toJSON();
|
|
assert.equal(json.id, "test-id");
|
|
assert.equal(json.email, "test@example.com");
|
|
assert.equal(json.displayName, "Test");
|
|
assert.equal(json.status, "active");
|
|
assert.deepEqual(json.roles, ["admin"]);
|
|
assert.deepEqual(json.permissions, ["posts:create"]);
|
|
});
|
|
|
|
it("toString returns readable string", () => {
|
|
const user = AuthenticatedUser.create("test@example.com", {
|
|
id: "test-id",
|
|
});
|
|
assert.equal(user.toString(), "User(id test-id)");
|
|
});
|
|
});
|
|
|
|
describe("AnonymousUser", () => {
|
|
it("isAnonymous returns true", () => {
|
|
const user = AnonymousUser.create("anon@example.com");
|
|
assert.equal(user.isAnonymous(), true);
|
|
});
|
|
|
|
it("anonymousUser singleton is anonymous", () => {
|
|
assert.equal(anonymousUser.isAnonymous(), true);
|
|
assert.equal(anonymousUser.id, "-1");
|
|
assert.equal(anonymousUser.email, "anonymous@example.com");
|
|
});
|
|
});
|
|
});
|