alepha
Version:
Easy-to-use modern TypeScript framework for building many kind of applications.
527 lines (453 loc) • 13.6 kB
text/typescript
import { Alepha } from "alepha";
import { DbEntityNotFoundError } from "alepha/orm";
import { AlephaOrmPostgres } from "alepha/orm/postgres";
import { AlephaSecurity, type UserAccountToken } from "alepha/security";
import { BadRequestError } from "alepha/server";
import { describe, it } from "vitest";
import {
AdminUserController,
AlephaApiUsers,
SessionService,
UserService,
} from "../index.ts";
const adminUser: UserAccountToken = {
id: "00000000-0000-0000-0000-000000000001",
name: "Test Admin",
roles: ["admin"],
};
const asAdmin = { user: adminUser };
const setup = async () => {
const alepha = Alepha.create({
env: { LOG_LEVEL: "error" },
});
alepha.with(AlephaOrmPostgres);
alepha.with(AlephaApiUsers);
alepha.with(AlephaSecurity);
await alepha.start();
return {
alepha,
userService: alepha.inject(UserService),
controller: alepha.inject(AdminUserController),
};
};
describe("alepha/api/users - AdminUserController CRUD", () => {
it("should create a new user", async ({ expect }) => {
const { controller } = await setup();
const result = await controller.createUser(
{
body: {
username: "newuser",
email: "newuser@example.com",
phoneNumber: "+1234567890",
firstName: "New",
lastName: "User",
enabled: true,
},
},
asAdmin,
);
expect(result.username).toBe("newuser");
expect(result.email).toBe("newuser@example.com");
expect(result.phoneNumber).toBe("+1234567890");
expect(result.firstName).toBe("New");
expect(result.lastName).toBe("User");
expect(result.enabled).toBe(true);
expect(result.emailVerified).toBe(false);
expect(result.roles).toEqual(["user"]);
expect(result.id).toBeDefined();
});
it("should create a user with custom roles", async ({ expect }) => {
const { controller } = await setup();
const result = await controller.createUser(
{
body: {
username: "admin",
email: "admin@example.com",
roles: ["admin", "user"],
},
},
{ user: adminUser },
);
expect(result.username).toBe("admin");
expect(result.email).toBe("admin@example.com");
expect(result.roles).toEqual(["admin", "user"]);
});
it("should reject duplicate username", async ({ expect }) => {
const { controller } = await setup();
await controller.createUser(
{
body: {
username: "duplicateuser",
email: "duplicate@example.com",
},
},
asAdmin,
);
await expect(
controller.createUser(
{
body: {
username: "duplicateuser",
email: "duplicate2@example.com",
},
},
asAdmin,
),
).rejects.toThrowError(BadRequestError);
});
it("should get a user by ID", async ({ expect }) => {
const { controller } = await setup();
const created = await controller.createUser(
{
body: {
username: "getuser",
email: "getuser@example.com",
firstName: "Get",
lastName: "User",
},
},
asAdmin,
);
const result = await controller.getUser(
{ params: { id: created.id } },
asAdmin,
);
expect(result.id).toBe(created.id);
expect(result.username).toBe("getuser");
expect(result.email).toBe("getuser@example.com");
expect(result.firstName).toBe("Get");
expect(result.lastName).toBe("User");
});
it("should throw error for non-existent user", async ({ expect }) => {
const { controller } = await setup();
await expect(
controller.getUser(
{ params: { id: "550e8400-e29b-41d4-a716-446655440000" } },
asAdmin,
),
).rejects.toThrowError(DbEntityNotFoundError);
});
it("should update a user", async ({ expect }) => {
const { controller } = await setup();
const created = await controller.createUser(
{
body: {
username: "updateuser",
email: "updateuser@example.com",
firstName: "Original",
lastName: "Name",
},
},
asAdmin,
);
const result = await controller.updateUser(
{
params: { id: created.id },
body: {
firstName: "Updated",
lastName: "User",
},
},
asAdmin,
);
expect(result.id).toBe(created.id);
expect(result.username).toBe("updateuser");
expect(result.email).toBe("updateuser@example.com");
expect(result.firstName).toBe("Updated");
expect(result.lastName).toBe("User");
});
it("should update user roles", async ({ expect }) => {
const { controller } = await setup();
const created = await controller.createUser(
{
body: {
username: "roleupdate",
email: "roleupdate@example.com",
},
},
asAdmin,
);
expect(created.roles).toEqual(["user"]);
const result = await controller.updateUser(
{
params: { id: created.id },
body: {
roles: ["admin", "moderator"],
},
},
asAdmin,
);
expect(result.roles).toEqual(["admin", "moderator"]);
});
it("should delete a user", async ({ expect }) => {
const { controller } = await setup();
const created = await controller.createUser(
{
body: {
username: "deleteuser",
email: "deleteuser@example.com",
},
},
asAdmin,
);
const result = await controller.deleteUser(
{ params: { id: created.id } },
asAdmin,
);
expect(result.ok).toBe(true);
expect(result.id).toBe(created.id);
// Verify user is deleted
await expect(
controller.getUser({ params: { id: created.id } }, asAdmin),
).rejects.toThrowError(DbEntityNotFoundError);
});
it("should find users with pagination", async ({ expect }) => {
const { controller } = await setup();
// Create multiple users
const user1 = await controller.createUser(
{
body: {
username: "user1",
email: "user1@example.com",
firstName: "User",
lastName: "One",
},
},
asAdmin,
);
const user2 = await controller.createUser(
{
body: {
username: "user2",
email: "user2@example.com",
firstName: "User",
lastName: "Two",
},
},
asAdmin,
);
const user3 = await controller.createUser(
{
body: {
username: "user3",
email: "user3@example.com",
firstName: "User",
lastName: "Three",
},
},
asAdmin,
);
const result = await controller.findUsers({ query: {} }, asAdmin);
expect(result.content.length).toBeGreaterThanOrEqual(3);
// Verify all created users are in the results
const userIds = result.content.map((u) => u.id);
expect(userIds).toContain(user1.id);
expect(userIds).toContain(user2.id);
expect(userIds).toContain(user3.id);
});
it("should filter users by email", async ({ expect }) => {
const { controller } = await setup();
await controller.createUser(
{ body: { username: "filter1", email: "filter1@example.com" } },
asAdmin,
);
await controller.createUser(
{ body: { username: "filter2", email: "filter2@test.com" } },
asAdmin,
);
const result = await controller.findUsers(
{ query: { email: "%example.com%" } },
asAdmin,
);
expect(result.content.length).toBeGreaterThanOrEqual(1);
expect(result.content.every((u) => u.email?.includes("example.com"))).toBe(
true,
);
});
it("should filter users by enabled status", async ({ expect }) => {
const { controller } = await setup();
await controller.createUser(
{
body: {
username: "enableduser",
email: "enabled@example.com",
enabled: true,
},
},
asAdmin,
);
await controller.createUser(
{
body: {
username: "disableduser",
email: "disabled@example.com",
enabled: false,
},
},
asAdmin,
);
const enabledResult = await controller.findUsers(
{ query: { enabled: true } },
asAdmin,
);
expect(enabledResult.content.every((u) => u.enabled === true)).toBe(true);
const disabledResult = await controller.findUsers(
{ query: { enabled: false } },
asAdmin,
);
expect(disabledResult.content.every((u) => u.enabled === false)).toBe(true);
});
it("should filter users by email verification status", async ({ expect }) => {
const { controller } = await setup();
await controller.createUser(
{
body: {
username: "verifieduser",
email: "verified@example.com",
emailVerified: true,
},
},
asAdmin,
);
await controller.createUser(
{
body: {
username: "unverifieduser",
email: "unverified@example.com",
emailVerified: false,
},
},
asAdmin,
);
const verifiedResult = await controller.findUsers(
{ query: { emailVerified: true } },
asAdmin,
);
expect(verifiedResult.content.every((u) => u.emailVerified === true)).toBe(
true,
);
const unverifiedResult = await controller.findUsers(
{ query: { emailVerified: false } },
asAdmin,
);
expect(
unverifiedResult.content.every((u) => u.emailVerified === false),
).toBe(true);
});
it("should filter users by roles", async ({ expect }) => {
const { controller } = await setup();
await controller.createUser.run(
{
body: {
username: "adminuser",
email: "admin@example.com",
roles: ["admin", "user"],
},
},
asAdmin,
);
await controller.createUser(
{
body: {
username: "regularuser",
email: "regular@example.com",
roles: ["user"],
},
},
asAdmin,
);
const adminResult = await controller.findUsers(
{ query: { roles: ["admin"] } },
asAdmin,
);
expect(adminResult.content.every((u) => u.roles.includes("admin"))).toBe(
true,
);
});
it("should sort users by creation date (newest first)", async ({
expect,
}) => {
const { controller } = await setup();
const user1 = await controller.createUser(
{ body: { username: "firstuser", email: "first@example.com" } },
asAdmin,
);
const user2 = await controller.createUser(
{ body: { username: "seconduser", email: "second@example.com" } },
asAdmin,
);
const user3 = await controller.createUser(
{ body: { username: "thirduser", email: "third@example.com" } },
asAdmin,
);
const result = await controller.findUsers({ query: {} }, asAdmin);
const userIds = result.content.map((u) => u.id);
expect(userIds.indexOf(user3.id)).toBeLessThan(userIds.indexOf(user2.id));
expect(userIds.indexOf(user2.id)).toBeLessThan(userIds.indexOf(user1.id));
});
});
describe("alepha/api/users - AdminUserController delete cleanup", () => {
it("should clean up sessions and identities when deleting a user", async ({
expect,
}) => {
const { alepha, controller } = await setup();
const sessionService = alepha.inject(SessionService);
// Arrange: create a user
const user = await controller.createUser(
{
body: {
username: "cleanupuser",
email: "cleanupuser@example.com",
},
},
asAdmin,
);
// Arrange: create identities for the user (credentials + google)
await sessionService.identities().create({
userId: user.id,
provider: "credentials",
providerUserId: user.id,
});
await sessionService.identities().create({
userId: user.id,
provider: "google",
providerUserId: "google-123",
});
// Arrange: create sessions for the user
await sessionService.sessions().create({
userId: user.id,
refreshToken: "00000000-0000-0000-0000-000000000010",
expiresAt: new Date(Date.now() + 3_600_000).toISOString(),
});
await sessionService.sessions().create({
userId: user.id,
refreshToken: "00000000-0000-0000-0000-000000000011",
expiresAt: new Date(Date.now() + 3_600_000).toISOString(),
});
// Sanity check: sessions and identities exist before deletion
const sessionsBefore = await sessionService
.sessions()
.findMany({ where: { userId: { eq: user.id } } });
expect(sessionsBefore.length).toBe(2);
const identitiesBefore = await sessionService
.identities()
.findMany({ where: { userId: { eq: user.id } } });
expect(identitiesBefore.length).toBe(2);
// Act: delete the user
const result = await controller.deleteUser(
{ params: { id: user.id } },
asAdmin,
);
expect(result.ok).toBe(true);
// Assert: sessions for that user should be gone
const sessionsAfter = await sessionService
.sessions()
.findMany({ where: { userId: { eq: user.id } } });
expect(sessionsAfter.length).toBe(0);
// Assert: identities for that user should be gone
const identitiesAfter = await sessionService
.identities()
.findMany({ where: { userId: { eq: user.id } } });
expect(identitiesAfter.length).toBe(0);
});
});