test: add integration tests for user management API

This commit is contained in:
juancarmore 2026-02-02 12:41:30 +01:00
parent c561cf9bcd
commit 21f4563202
9 changed files with 1515 additions and 13 deletions

View File

@ -15,7 +15,7 @@ export const UserOptionsSchema: z.ZodType<MeetUserOptions> = z.object({
export const UserFiltersSchema: z.ZodType<MeetUserFilters> = z.object({
userId: z.string().optional(),
name: z.string().optional(),
fields: z.string().optional(),
role: z.nativeEnum(MeetUserRole).optional(),
maxItems: z.coerce
.number()
.positive('maxItems must be a positive number')

View File

@ -292,43 +292,43 @@ export const changePassword = async (
};
};
export const resetUserPassword = async (userId: string, newPassword: string) => {
export const resetUserPassword = async (userId: string, newPassword: string, accessToken?: string) => {
checkAppIsRunning();
const { accessToken } = await loginRootAdmin();
const { accessToken: rootAdminAccessToken } = await loginRootAdmin();
return await request(app)
.put(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/users/${userId}/password`)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, accessToken)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, accessToken ?? rootAdminAccessToken)
.send({ newPassword });
};
export const updateUserRole = async (userId: string, role: string) => {
export const updateUserRole = async (userId: string, role: string, accessToken?: string) => {
checkAppIsRunning();
const { accessToken } = await loginRootAdmin();
const { accessToken: rootAdminAccessToken } = await loginRootAdmin();
return await request(app)
.put(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/users/${userId}/role`)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, accessToken)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, accessToken ?? rootAdminAccessToken)
.send({ role });
};
export const deleteUser = async (userId: string) => {
export const deleteUser = async (userId: string, accessToken?: string) => {
checkAppIsRunning();
const { accessToken } = await loginRootAdmin();
const { accessToken: rootAdminAccessToken } = await loginRootAdmin();
return await request(app)
.delete(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/users/${userId}`)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, accessToken)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, accessToken ?? rootAdminAccessToken)
.send();
};
export const bulkDeleteUsers = async (userIds: string[]) => {
export const bulkDeleteUsers = async (userIds: string[], accessToken?: string) => {
checkAppIsRunning();
const { accessToken } = await loginRootAdmin();
const { accessToken: rootAdminAccessToken } = await loginRootAdmin();
return await request(app)
.delete(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/users`)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, accessToken)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, accessToken ?? rootAdminAccessToken)
.query({ userIds: userIds.join(',') });
};

View File

@ -0,0 +1,285 @@
import { afterAll, beforeAll, describe, expect, it } from '@jest/globals';
import { MeetRoomMemberRole, MeetUserRole } from '@openvidu-meet/typings';
import { MEET_ENV } from '../../../../src/environment.js';
import { expectValidationError } from '../../../helpers/assertion-helpers.js';
import {
bulkDeleteUsers,
createRoom,
createRoomMember,
deleteAllRooms,
deleteAllUsers,
getRoom,
getRoomMember,
getUser,
startTestServer
} from '../../../helpers/request-helpers.js';
import { setupTestUsers, setupUser } from '../../../helpers/test-scenarios.js';
import { TestUsers, UserData } from '../../../interfaces/scenarios.js';
describe('Users API Tests', () => {
let testUsers: TestUsers;
beforeAll(async () => {
await startTestServer();
testUsers = await setupTestUsers();
});
afterAll(async () => {
await deleteAllRooms();
await deleteAllUsers();
});
const createUserWithRole = async (role: MeetUserRole): Promise<UserData> => {
const userId = `user_${Date.now()}`;
const userData = await setupUser({
userId,
name: 'Test User',
password: 'password123',
role
});
return userData;
};
describe('Bulk Delete Users Tests', () => {
it('should successfully delete multiple users with different roles', async () => {
const { user: user1 } = await createUserWithRole(MeetUserRole.USER);
const { user: user2 } = await createUserWithRole(MeetUserRole.ADMIN);
const { user: user3 } = await createUserWithRole(MeetUserRole.ROOM_MEMBER);
const response = await bulkDeleteUsers([user1.userId, user2.userId, user3.userId]);
expect(response.status).toBe(200);
expect(response.body).toHaveProperty('message', 'All users deleted successfully');
expect(response.body).toHaveProperty('deleted');
expect(response.body.deleted).toHaveLength(3);
expect(response.body.deleted).toContain(user1.userId);
expect(response.body.deleted).toContain(user2.userId);
expect(response.body.deleted).toContain(user3.userId);
// Verify users no longer exist
const getUser1Response = await getUser(user1.userId);
expect(getUser1Response.status).toBe(404);
const getUser2Response = await getUser(user2.userId);
expect(getUser2Response.status).toBe(404);
const getUser3Response = await getUser(user3.userId);
expect(getUser3Response.status).toBe(404);
});
it('should fail to delete root admin and return it in failed list', async () => {
const response = await bulkDeleteUsers([MEET_ENV.INITIAL_ADMIN_USER], testUsers.admin.accessToken);
expect(response.status).toBe(400);
expect(response.body).toHaveProperty('message');
expect(response.body.message).toContain('1 user(s) could not be deleted');
expect(response.body).toHaveProperty('deleted');
expect(response.body).toHaveProperty('failed');
expect(response.body.deleted).toHaveLength(0);
expect(response.body.failed).toHaveLength(1);
expect(response.body.failed[0]).toHaveProperty('userId', MEET_ENV.INITIAL_ADMIN_USER);
expect(response.body.failed[0]).toHaveProperty('error', 'Cannot delete the root admin user');
});
it('should fail to delete own account and return it in failed list', async () => {
const response = await bulkDeleteUsers([testUsers.admin.user.userId], testUsers.admin.accessToken);
expect(response.status).toBe(400);
expect(response.body).toHaveProperty('message');
expect(response.body.message).toContain('1 user(s) could not be deleted');
expect(response.body).toHaveProperty('deleted');
expect(response.body).toHaveProperty('failed');
expect(response.body.deleted).toHaveLength(0);
expect(response.body.failed).toHaveLength(1);
expect(response.body.failed[0]).toHaveProperty('userId', testUsers.admin.user.userId);
expect(response.body.failed[0]).toHaveProperty('error', 'Cannot delete your own account');
});
it('should return nonexistent users in failed list', async () => {
const nonexistentId = 'nonexistent_user_123';
const response = await bulkDeleteUsers([nonexistentId]);
expect(response.status).toBe(400);
expect(response.body).toHaveProperty('message');
expect(response.body.message).toContain('1 user(s) could not be deleted');
expect(response.body).toHaveProperty('deleted');
expect(response.body).toHaveProperty('failed');
expect(response.body.deleted).toHaveLength(0);
expect(response.body.failed).toHaveLength(1);
expect(response.body.failed[0]).toHaveProperty('userId', nonexistentId);
expect(response.body.failed[0]).toHaveProperty('error', 'User not found');
});
it('should handle mixed success and failure results', async () => {
const { user: user1 } = await createUserWithRole(MeetUserRole.USER);
const { user: user2 } = await createUserWithRole(MeetUserRole.USER);
const nonexistentId = 'nonexistent_user_123';
const response = await bulkDeleteUsers([
user1.userId,
user2.userId,
MEET_ENV.INITIAL_ADMIN_USER,
nonexistentId
]);
expect(response.status).toBe(400);
expect(response.body).toHaveProperty('message');
expect(response.body.message).toContain('2 user(s) could not be deleted');
expect(response.body).toHaveProperty('deleted');
expect(response.body).toHaveProperty('failed');
expect(response.body.deleted).toHaveLength(2);
expect(response.body.deleted).toContain(user1.userId);
expect(response.body.deleted).toContain(user2.userId);
expect(response.body.failed).toHaveLength(2);
});
it('should fail when no users can be deleted', async () => {
const response = await bulkDeleteUsers([MEET_ENV.INITIAL_ADMIN_USER, 'nonexistent_user_123']);
expect(response.status).toBe(400);
expect(response.body).toHaveProperty('message');
expect(response.body.message).toContain('2 user(s) could not be deleted');
expect(response.body).toHaveProperty('deleted');
expect(response.body).toHaveProperty('failed');
expect(response.body.deleted).toHaveLength(0);
expect(response.body.failed).toHaveLength(2);
});
it('should transfer ownership of multiple rooms from multiple users', async () => {
const user1Data = await createUserWithRole(MeetUserRole.ADMIN);
const user2Data = await createUserWithRole(MeetUserRole.USER);
// Create rooms owned by each user
const room1 = await createRoom(undefined, user1Data.accessToken);
const room2 = await createRoom(undefined, user1Data.accessToken);
const room3 = await createRoom(undefined, user2Data.accessToken);
// Delete both users
const deleteResponse = await bulkDeleteUsers([user1Data.user.userId, user2Data.user.userId]);
expect(deleteResponse.status).toBe(200);
expect(deleteResponse.body.deleted).toHaveLength(2);
// Verify all rooms have ownership transferred to root admin
const getRoom1Response = await getRoom(room1.roomId);
expect(getRoom1Response.body).toHaveProperty('owner', MEET_ENV.INITIAL_ADMIN_USER);
const getRoom2Response = await getRoom(room2.roomId);
expect(getRoom2Response.body).toHaveProperty('owner', MEET_ENV.INITIAL_ADMIN_USER);
const getRoom3Response = await getRoom(room3.roomId);
expect(getRoom3Response.body).toHaveProperty('owner', MEET_ENV.INITIAL_ADMIN_USER);
});
it('should remove memberships from multiple rooms for multiple users', async () => {
const user1Data = await createUserWithRole(MeetUserRole.USER);
const user2Data = await createUserWithRole(MeetUserRole.ROOM_MEMBER);
// Create rooms
const room1 = await createRoom();
const room2 = await createRoom();
// Add both users as members to both rooms
await createRoomMember(room1.roomId, {
userId: user1Data.user.userId,
baseRole: MeetRoomMemberRole.MODERATOR
});
await createRoomMember(room1.roomId, {
userId: user2Data.user.userId,
baseRole: MeetRoomMemberRole.SPEAKER
});
await createRoomMember(room2.roomId, {
userId: user1Data.user.userId,
baseRole: MeetRoomMemberRole.SPEAKER
});
await createRoomMember(room2.roomId, {
userId: user2Data.user.userId,
baseRole: MeetRoomMemberRole.MODERATOR
});
// Delete both users
const deleteResponse = await bulkDeleteUsers([user1Data.user.userId, user2Data.user.userId]);
expect(deleteResponse.status).toBe(200);
expect(deleteResponse.body.deleted).toHaveLength(2);
// Verify all memberships are removed
const getMember1Room1Response = await getRoomMember(room1.roomId, user1Data.user.userId);
expect(getMember1Room1Response.status).toBe(404);
const getMember2Room1Response = await getRoomMember(room1.roomId, user2Data.user.userId);
expect(getMember2Room1Response.status).toBe(404);
const getMember1Room2Response = await getRoomMember(room2.roomId, user1Data.user.userId);
expect(getMember1Room2Response.status).toBe(404);
const getMember2Room2Response = await getRoomMember(room2.roomId, user2Data.user.userId);
expect(getMember2Room2Response.status).toBe(404);
});
it('should handle complex cleanup with multiple users having mixed roles', async () => {
// Create users with different room relationships
const owner1Data = await createUserWithRole(MeetUserRole.ADMIN);
const owner2Data = await createUserWithRole(MeetUserRole.USER);
const memberData = await createUserWithRole(MeetUserRole.ROOM_MEMBER);
// Create rooms with various ownership
const room1 = await createRoom(undefined, owner1Data.accessToken);
const room2 = await createRoom(undefined, owner2Data.accessToken);
const room3 = await createRoom();
// Add members to rooms
await createRoomMember(room1.roomId, {
userId: memberData.user.userId,
baseRole: MeetRoomMemberRole.SPEAKER
});
await createRoomMember(room2.roomId, {
userId: memberData.user.userId,
baseRole: MeetRoomMemberRole.SPEAKER
});
await createRoomMember(room3.roomId, {
userId: owner2Data.user.userId,
baseRole: MeetRoomMemberRole.MODERATOR
});
await createRoomMember(room3.roomId, {
userId: memberData.user.userId,
baseRole: MeetRoomMemberRole.SPEAKER
});
// Delete all three users
const deleteResponse = await bulkDeleteUsers([
owner1Data.user.userId,
owner2Data.user.userId,
memberData.user.userId
]);
expect(deleteResponse.status).toBe(200);
expect(deleteResponse.body.deleted).toHaveLength(3);
// Verify owned rooms transferred to root admin
const getRoom1Response = await getRoom(room1.roomId);
expect(getRoom1Response.body).toHaveProperty('owner', MEET_ENV.INITIAL_ADMIN_USER);
const getRoom2Response = await getRoom(room2.roomId);
expect(getRoom2Response.body).toHaveProperty('owner', MEET_ENV.INITIAL_ADMIN_USER);
// Verify all memberships removed
const getMember1Response = await getRoomMember(room1.roomId, memberData.user.userId);
expect(getMember1Response.status).toBe(404);
const getMember2Response = await getRoomMember(room2.roomId, memberData.user.userId);
expect(getMember2Response.status).toBe(404);
const getMember3Response = await getRoomMember(room3.roomId, owner2Data.user.userId);
expect(getMember3Response.status).toBe(404);
const getMember4Response = await getRoomMember(room3.roomId, memberData.user.userId);
expect(getMember4Response.status).toBe(404);
});
});
describe('Bulk Delete Users Validation Tests', () => {
it('should fail when userIds parameter is empty', async () => {
const response = await bulkDeleteUsers([]);
expectValidationError(response, 'userIds', 'At least one userId is required');
});
});
});

View File

@ -0,0 +1,267 @@
import { afterAll, beforeAll, describe, expect, it } from '@jest/globals';
import { MeetUserOptions, MeetUserRole } from '@openvidu-meet/typings';
import { expectValidationError } from '../../../helpers/assertion-helpers.js';
import { createUser, deleteAllUsers, getUser, loginReq, startTestServer } from '../../../helpers/request-helpers.js';
describe('Users API Tests', () => {
beforeAll(async () => {
await startTestServer();
});
afterAll(async () => {
await deleteAllUsers();
});
describe('Create User Tests', () => {
it('should successfully create a USER with all required fields', async () => {
const userId = `user_${Date.now()}`;
const userOptions = {
userId,
name: 'Test User',
password: 'password123',
role: MeetUserRole.USER
};
const response = await createUser(userOptions);
expect(response.status).toBe(201);
expect(response.body).toHaveProperty('userId', userId);
expect(response.body).toHaveProperty('name', 'Test User');
expect(response.body).toHaveProperty('role', MeetUserRole.USER);
expect(response.body).toHaveProperty('registrationDate');
expect(response.body).not.toHaveProperty('passwordHash');
expect(response.body).not.toHaveProperty('mustChangePassword');
// Verify Location header is set correctly
expect(response.headers).toHaveProperty('location');
expect(response.headers.location).toContain(`/users/${userId}`);
});
it('should successfully create an ADMIN user', async () => {
const userId = `admin_${Date.now()}`;
const userOptions = {
userId,
name: 'Test Admin',
password: 'admin_pass',
role: MeetUserRole.ADMIN
};
const response = await createUser(userOptions);
expect(response.status).toBe(201);
expect(response.body).toHaveProperty('userId', userId);
expect(response.body).toHaveProperty('role', MeetUserRole.ADMIN);
});
it('should successfully create a ROOM_MEMBER user', async () => {
const userId = `rm_${Date.now()}`;
const userOptions = {
userId,
name: 'Test Room Member',
password: 'member_pass',
role: MeetUserRole.ROOM_MEMBER
};
const response = await createUser(userOptions);
expect(response.status).toBe(201);
expect(response.body).toHaveProperty('userId', userId);
expect(response.body).toHaveProperty('role', MeetUserRole.ROOM_MEMBER);
});
it('should allow newly created user to login with the provided password', async () => {
const userId = `user_${Date.now()}`;
const password = 'password123';
const userOptions = {
userId,
name: 'Test User',
password,
role: MeetUserRole.USER
};
const createResponse = await createUser(userOptions);
expect(createResponse.status).toBe(201);
// Try to login with the created user
const loginResponse = await loginReq({ userId, password });
expect(loginResponse.status).toBe(200);
});
it('should create user with mustChangePassword flag set to true', async () => {
const userId = `user_${Date.now()}`;
const password = 'password123';
const userOptions = {
userId,
name: 'Test User',
password,
role: MeetUserRole.USER
};
const createResponse = await createUser(userOptions);
expect(createResponse.status).toBe(201);
// Login to verify mustChangePassword is true
const loginResponse = await loginReq({ userId, password });
expect(loginResponse.status).toBe(200);
expect(loginResponse.body).toHaveProperty('mustChangePassword', true);
});
it('should retrieve created user with correct information', async () => {
const userId = `user_${Date.now()}`;
const userOptions = {
userId,
name: 'Test User',
password: 'password123',
role: MeetUserRole.USER
};
const createResponse = await createUser(userOptions);
expect(createResponse.status).toBe(201);
// Get user to verify it was created correctly
const getResponse = await getUser(userId);
expect(getResponse.status).toBe(200);
expect(getResponse.body).toHaveProperty('userId', userId);
expect(getResponse.body).toHaveProperty('name', 'Test User');
expect(getResponse.body).toHaveProperty('role', MeetUserRole.USER);
});
it('should fail when trying to create a user with duplicate userId', async () => {
const userId = `user_${Date.now()}`;
const userOptions = {
userId,
name: 'Test User',
password: 'password123',
role: MeetUserRole.USER
};
// Create user first time
const firstResponse = await createUser(userOptions);
expect(firstResponse.status).toBe(201);
// Try to create the same user again
const secondResponse = await createUser(userOptions);
expect(secondResponse.status).toBe(409);
expect(secondResponse.body).toHaveProperty('message');
expect(secondResponse.body.message).toContain('already exists');
});
});
describe('Create User Validation Tests', () => {
it('should fail when userId is missing', async () => {
const response = await createUser({
name: 'Test User',
password: 'password123',
role: MeetUserRole.USER
} as MeetUserOptions);
expectValidationError(response, 'userId', 'Required');
});
it('should fail when name is missing', async () => {
const response = await createUser({
userId: `user_${Date.now()}`,
password: 'password123',
role: MeetUserRole.USER
} as MeetUserOptions);
expectValidationError(response, 'name', 'Required');
});
it('should fail when name is empty', async () => {
const response = await createUser({
userId: `user_${Date.now()}`,
name: '',
password: 'password123',
role: MeetUserRole.USER
});
expectValidationError(response, 'name', 'cannot be empty');
});
it('should fail when password is missing', async () => {
const response = await createUser({
userId: `user_${Date.now()}`,
name: 'Test User',
role: MeetUserRole.USER
} as MeetUserOptions);
expectValidationError(response, 'password', 'Required');
});
it('should fail when role is missing', async () => {
const response = await createUser({
userId: `user_${Date.now()}`,
name: 'Test User',
password: 'password123'
} as MeetUserOptions);
expectValidationError(response, 'role', 'Required');
});
it('should fail when userId exceeds maximum length', async () => {
const response = await createUser({
userId: 'a'.repeat(21), // Max is 20 characters
name: 'Test User',
password: 'password123',
role: MeetUserRole.USER
});
expectValidationError(response, 'userId', 'cannot exceed 20 characters');
});
it('should fail when userId is too short', async () => {
const response = await createUser({
userId: 'abcd', // Min is 5 characters
name: 'Test User',
password: 'password123',
role: MeetUserRole.USER
});
expectValidationError(response, 'userId', 'at least 5 characters');
});
it('should fail when userId contains uppercase letters', async () => {
const response = await createUser({
userId: 'User123',
name: 'Test User',
password: 'password123',
role: MeetUserRole.USER
});
expectValidationError(response, 'userId', 'lowercase letters, numbers, and underscores');
});
it('should fail when userId contains invalid special characters', async () => {
const response = await createUser({
userId: 'user-123',
name: 'Test User',
password: 'password123',
role: MeetUserRole.USER
});
expectValidationError(response, 'userId', 'lowercase letters, numbers, and underscores');
});
it('should fail when name exceeds maximum length', async () => {
const response = await createUser({
userId: `user_${Date.now()}`,
name: 'a'.repeat(51), // Max is 50 characters
password: 'password123',
role: MeetUserRole.USER
});
expectValidationError(response, 'name', 'cannot exceed 50 characters');
});
it('should fail when password is too short', async () => {
const response = await createUser({
userId: `user_${Date.now()}`,
name: 'Test User',
password: '1234', // Min is 5 characters
role: MeetUserRole.USER
});
expectValidationError(response, 'password', 'at least 5 characters');
});
it('should fail when role is invalid', async () => {
const response = await createUser({
userId: `user_${Date.now()}`,
name: 'Test User',
password: 'password123',
role: 'invalid' as MeetUserRole
});
expectValidationError(response, 'role', 'Invalid enum value');
});
});
});

View File

@ -0,0 +1,299 @@
import { afterAll, beforeAll, describe, expect, it } from '@jest/globals';
import { MeetRoomMemberRole, MeetUserRole } from '@openvidu-meet/typings';
import { MEET_ENV } from '../../../../src/environment.js';
import {
createRoom,
createRoomMember,
deleteAllRooms,
deleteAllUsers,
deleteUser,
getRoom,
getRoomMember,
getUser,
startTestServer
} from '../../../helpers/request-helpers.js';
import { setupTestUsers, setupUser } from '../../../helpers/test-scenarios.js';
import { TestUsers, UserData } from '../../../interfaces/scenarios.js';
describe('Users API Tests', () => {
let testUsers: TestUsers;
beforeAll(async () => {
await startTestServer();
testUsers = await setupTestUsers();
});
afterAll(async () => {
await deleteAllRooms();
await deleteAllUsers();
});
const createUserWithRole = async (role: MeetUserRole): Promise<UserData> => {
const userId = `user_${Date.now()}`;
const userData = await setupUser({
userId,
name: 'Test User',
password: 'password123',
role
});
return userData;
};
describe('Delete User Tests', () => {
it('should successfully delete USER', async () => {
const { user } = await createUserWithRole(MeetUserRole.USER);
const response = await deleteUser(user.userId);
expect(response.status).toBe(200);
expect(response.body).toHaveProperty('message');
expect(response.body.message).toContain('deleted successfully');
expect(response.body.message).toContain(user.userId);
});
it('should successfully delete ADMIN user', async () => {
const { user } = await createUserWithRole(MeetUserRole.ADMIN);
const response = await deleteUser(user.userId);
expect(response.status).toBe(200);
expect(response.body).toHaveProperty('message');
expect(response.body.message).toContain('deleted successfully');
});
it('should successfully delete ROOM_MEMBER user', async () => {
const { user } = await createUserWithRole(MeetUserRole.ROOM_MEMBER);
const response = await deleteUser(user.userId);
expect(response.status).toBe(200);
expect(response.body).toHaveProperty('message');
expect(response.body.message).toContain('deleted successfully');
});
it('should verify user is actually deleted', async () => {
const { user } = await createUserWithRole(MeetUserRole.USER);
// Delete user
const deleteResponse = await deleteUser(user.userId);
expect(deleteResponse.status).toBe(200);
// Verify user no longer exists
const getUserResponse = await getUser(user.userId);
expect(getUserResponse.status).toBe(404);
expect(getUserResponse.body).toHaveProperty('message');
expect(getUserResponse.body.message).toContain('not found');
});
it('should fail when trying to delete root admin user', async () => {
const response = await deleteUser(MEET_ENV.INITIAL_ADMIN_USER);
expect(response.status).toBe(403);
expect(response.body).toHaveProperty('message');
expect(response.body.message).toContain('Cannot delete the root admin user');
});
it('should fail when admin tries to delete own account', async () => {
const response = await deleteUser(testUsers.admin.user.userId, testUsers.admin.accessToken);
expect(response.status).toBe(403);
expect(response.body).toHaveProperty('message');
expect(response.body.message).toContain('Cannot delete your own account');
});
it('should fail when root admin tries to delete own account', async () => {
const response = await deleteUser(MEET_ENV.INITIAL_ADMIN_USER);
expect(response.status).toBe(403);
expect(response.body).toHaveProperty('message');
expect(response.body.message).toContain('Cannot delete the root admin user');
});
it('should fail when user does not exist', async () => {
const response = await deleteUser('nonexistent_user_123');
expect(response.status).toBe(404);
expect(response.body).toHaveProperty('message');
expect(response.body.message).toContain('not found');
});
it('should transfer room ownership to root admin when deleting room owner', async () => {
// Create user who will own a room
const userData = await createUserWithRole(MeetUserRole.ADMIN);
// Create room owned by this user
const room = await createRoom(undefined, userData.accessToken);
// Verify initial ownership
const getRoomResponse = await getRoom(room.roomId);
expect(getRoomResponse.status).toBe(200);
expect(getRoomResponse.body).toHaveProperty('owner', userData.user.userId);
// Delete the user
const deleteResponse = await deleteUser(userData.user.userId);
expect(deleteResponse.status).toBe(200);
// Verify room ownership transferred to root admin
const getRoomAfterResponse = await getRoom(room.roomId);
expect(getRoomAfterResponse.status).toBe(200);
expect(getRoomAfterResponse.body).toHaveProperty('owner', MEET_ENV.INITIAL_ADMIN_USER);
});
it('should transfer ownership of multiple rooms when deleting room owner', async () => {
// Create user who will own multiple rooms
const userData = await createUserWithRole(MeetUserRole.USER);
// Create 3 rooms owned by this user
const room1 = await createRoom(undefined, userData.accessToken);
const room2 = await createRoom(undefined, userData.accessToken);
const room3 = await createRoom(undefined, userData.accessToken);
// Delete the user
const deleteResponse = await deleteUser(userData.user.userId);
expect(deleteResponse.status).toBe(200);
// Verify all rooms have ownership transferred to root admin
const getRoom1Response = await getRoom(room1.roomId);
expect(getRoom1Response.body).toHaveProperty('owner', MEET_ENV.INITIAL_ADMIN_USER);
const getRoom2Response = await getRoom(room2.roomId);
expect(getRoom2Response.body).toHaveProperty('owner', MEET_ENV.INITIAL_ADMIN_USER);
const getRoom3Response = await getRoom(room3.roomId);
expect(getRoom3Response.body).toHaveProperty('owner', MEET_ENV.INITIAL_ADMIN_USER);
});
it('should remove user memberships when deleting a room member', async () => {
// Create user who will be a room member
const userData = await createUserWithRole(MeetUserRole.USER);
const userId = userData.user.userId;
// Create a room
const room = await createRoom();
// Add user as member to the room
await createRoomMember(room.roomId, { userId, baseRole: MeetRoomMemberRole.MODERATOR });
// Verify membership exists
const getMemberResponse = await getRoomMember(room.roomId, userId);
expect(getMemberResponse.status).toBe(200);
expect(getMemberResponse.body).toHaveProperty('memberId', userId);
// Delete the user
const deleteResponse = await deleteUser(userId);
expect(deleteResponse.status).toBe(200);
// Verify membership no longer exists
const getMemberAfterResponse = await getRoomMember(room.roomId, userId);
expect(getMemberAfterResponse.status).toBe(404);
expect(getMemberAfterResponse.body).toHaveProperty('message');
expect(getMemberAfterResponse.body.message).toContain('not found');
});
it('should remove memberships from multiple rooms when deleting a room member', async () => {
// Create user who will be a member of multiple rooms
const userData = await createUserWithRole(MeetUserRole.USER);
const userId = userData.user.userId;
// Create 3 rooms and add user as member to each
const room1 = await createRoom();
const room2 = await createRoom();
const room3 = await createRoom();
await createRoomMember(room1.roomId, { userId, baseRole: MeetRoomMemberRole.MODERATOR });
await createRoomMember(room2.roomId, { userId, baseRole: MeetRoomMemberRole.MODERATOR });
await createRoomMember(room3.roomId, { userId, baseRole: MeetRoomMemberRole.SPEAKER });
// Verify memberships exist
const getMember1Response = await getRoomMember(room1.roomId, userId);
expect(getMember1Response.status).toBe(200);
expect(getMember1Response.body).toHaveProperty('memberId', userId);
const getMember2Response = await getRoomMember(room2.roomId, userId);
expect(getMember2Response.status).toBe(200);
expect(getMember2Response.body).toHaveProperty('memberId', userId);
const getMember3Response = await getRoomMember(room3.roomId, userId);
expect(getMember3Response.status).toBe(200);
expect(getMember3Response.body).toHaveProperty('memberId', userId);
// Delete the user
const deleteResponse = await deleteUser(userId);
expect(deleteResponse.status).toBe(200);
// Verify all memberships are removed
const getMember1AfterResponse = await getRoomMember(room1.roomId, userId);
expect(getMember1AfterResponse.status).toBe(404);
const getMember2AfterResponse = await getRoomMember(room2.roomId, userId);
expect(getMember2AfterResponse.status).toBe(404);
const getMember3AfterResponse = await getRoomMember(room3.roomId, userId);
expect(getMember3AfterResponse.status).toBe(404);
});
it('should handle both room ownership transfer and membership removal when deleting user', async () => {
// Create user who will own some rooms and be member of others
const userData = await createUserWithRole(MeetUserRole.USER);
const userId = userData.user.userId;
// Create rooms owned by this user
const ownedRoom1 = await createRoom(undefined, userData.accessToken);
const ownedRoom2 = await createRoom(undefined, userData.accessToken);
// Create rooms where user is just a member
const memberRoom1 = await createRoom();
const memberRoom2 = await createRoom();
await createRoomMember(memberRoom1.roomId, { userId, baseRole: MeetRoomMemberRole.SPEAKER });
await createRoomMember(memberRoom2.roomId, { userId, baseRole: MeetRoomMemberRole.MODERATOR });
// Delete the user
const deleteResponse = await deleteUser(userId);
expect(deleteResponse.status).toBe(200);
// Verify owned rooms transferred to root admin
const getOwnedRoom1Response = await getRoom(ownedRoom1.roomId);
expect(getOwnedRoom1Response.body).toHaveProperty('owner', MEET_ENV.INITIAL_ADMIN_USER);
const getOwnedRoom2Response = await getRoom(ownedRoom2.roomId);
expect(getOwnedRoom2Response.body).toHaveProperty('owner', MEET_ENV.INITIAL_ADMIN_USER);
// Verify memberships removed
const getMember1Response = await getRoomMember(memberRoom1.roomId, userId);
expect(getMember1Response.status).toBe(404);
const getMember2Response = await getRoomMember(memberRoom2.roomId, userId);
expect(getMember2Response.status).toBe(404);
});
it('should not affect other room members when deleting a room owner', async () => {
// Create owner user
const ownerData = await createUserWithRole(MeetUserRole.USER);
const ownerId = ownerData.user.userId;
// Create another user who will be a member
const memberData = await createUserWithRole(MeetUserRole.USER);
const memberId = memberData.user.userId;
// Create room owned by owner
const room = await createRoom(undefined, ownerData.accessToken);
// Add member to the room
await createRoomMember(room.roomId, { userId: memberId, baseRole: MeetRoomMemberRole.SPEAKER });
// Verify both owner and member before deletion
const getRoomBeforeResponse = await getRoom(room.roomId);
expect(getRoomBeforeResponse.body).toHaveProperty('owner', ownerId);
const getMemberBeforeResponse = await getRoomMember(room.roomId, memberId);
expect(getMemberBeforeResponse.status).toBe(200);
// Delete the owner
const deleteResponse = await deleteUser(ownerId);
expect(deleteResponse.status).toBe(200);
// Verify room ownership transferred
const getRoomAfterResponse = await getRoom(room.roomId);
expect(getRoomAfterResponse.body).toHaveProperty('owner', MEET_ENV.INITIAL_ADMIN_USER);
// Verify member still exists
const getMemberAfterResponse = await getRoomMember(room.roomId, memberId);
expect(getMemberAfterResponse.status).toBe(200);
expect(getMemberAfterResponse.body).toHaveProperty('memberId', memberId);
});
});
});

View File

@ -0,0 +1,66 @@
import { afterAll, beforeAll, describe, expect, it } from '@jest/globals';
import { MeetUserRole } from '@openvidu-meet/typings';
import { MEET_ENV } from '../../../../src/environment.js';
import { deleteAllUsers, getUser, startTestServer } from '../../../helpers/request-helpers.js';
import { setupTestUsers } from '../../../helpers/test-scenarios.js';
import { TestUsers } from '../../../interfaces/scenarios.js';
describe('Users API Tests', () => {
let testUsers: TestUsers;
beforeAll(async () => {
await startTestServer();
testUsers = await setupTestUsers();
});
afterAll(async () => {
await deleteAllUsers();
});
describe('Get User Tests', () => {
it('should successfully get a user by userId', async () => {
const response = await getUser(testUsers.admin.user.userId);
expect(response.status).toBe(200);
expect(response.body).toHaveProperty('userId', testUsers.admin.user.userId);
expect(response.body).toHaveProperty('name', testUsers.admin.user.name);
expect(response.body).toHaveProperty('role', MeetUserRole.ADMIN);
expect(response.body).toHaveProperty('registrationDate');
});
it('should get root admin user', async () => {
const response = await getUser(MEET_ENV.INITIAL_ADMIN_USER);
expect(response.status).toBe(200);
expect(response.body).toHaveProperty('userId', MEET_ENV.INITIAL_ADMIN_USER);
expect(response.body).toHaveProperty('name', 'Admin');
expect(response.body).toHaveProperty('role', MeetUserRole.ADMIN);
});
it('should get user with different roles', async () => {
const userResponse = await getUser(testUsers.user.user.userId);
expect(userResponse.status).toBe(200);
expect(userResponse.body).toHaveProperty('role', MeetUserRole.USER);
const roomMemberResponse = await getUser(testUsers.roomMember.user.userId);
expect(roomMemberResponse.status).toBe(200);
expect(roomMemberResponse.body).toHaveProperty('role', MeetUserRole.ROOM_MEMBER);
});
it('should not expose sensitive fields', async () => {
const response = await getUser(testUsers.admin.user.userId);
expect(response.status).toBe(200);
expect(response.body).not.toHaveProperty('passwordHash');
expect(response.body).not.toHaveProperty('mustChangePassword');
expect(response.body).toHaveProperty('userId');
expect(response.body).toHaveProperty('name');
expect(response.body).toHaveProperty('role');
expect(response.body).toHaveProperty('registrationDate');
});
it('should return 404 when user does not exist', async () => {
const response = await getUser('nonexistent_user_123');
expect(response.status).toBe(404);
expect(response.body).toHaveProperty('message');
expect(response.body.message).toContain('not found');
});
});
});

View File

@ -0,0 +1,260 @@
import { afterAll, beforeAll, describe, expect, it } from '@jest/globals';
import { MeetUser, MeetUserRole } from '@openvidu-meet/typings';
import { MEET_ENV } from '../../../../src/environment.js';
import { expectValidationError } from '../../../helpers/assertion-helpers.js';
import { createUser, deleteAllUsers, getUsers, startTestServer } from '../../../helpers/request-helpers.js';
describe('Users API Tests', () => {
beforeAll(async () => {
await startTestServer();
// Create a timestamp to ensure unique IDs (max 6 digits to fit in 20 char limit)
const ts = String(Date.now()).slice(-6);
// Create users sequentially to have predictable registration order
await createUser({
userId: `alice_${ts}`,
name: 'Alice Anderson',
password: 'password123',
role: MeetUserRole.ADMIN
});
await createUser({
userId: `bob_${ts}`,
name: 'Bob Brown',
password: 'password123',
role: MeetUserRole.USER
});
await createUser({
userId: `charlie_${ts}`,
name: 'Charlie Clark',
password: 'password123',
role: MeetUserRole.ROOM_MEMBER
});
await createUser({
userId: `diana_${ts}`,
name: 'Diana Davis',
password: 'password123',
role: MeetUserRole.ADMIN
});
await createUser({
userId: `eve_${ts}`,
name: 'Eve Evans',
password: 'password123',
role: MeetUserRole.USER
});
});
afterAll(async () => {
await deleteAllUsers();
});
describe('Get Users Tests', () => {
it('should successfully get all users without filters', async () => {
const response = await getUsers();
expect(response.status).toBe(200);
expect(response.body).toHaveProperty('users');
expect(Array.isArray(response.body.users)).toBe(true);
// Root admin + 5 test users = 6 total
expect(response.body.users.length).toBe(6);
expect(response.body).toHaveProperty('pagination');
expect(response.body.pagination).toHaveProperty('isTruncated', false);
});
it('should filter users by userId using partial match', async () => {
const response = await getUsers({ userId: 'alice' });
expect(response.status).toBe(200);
expect(response.body.users).toHaveLength(1);
expect(response.body.users[0].userId).toContain('alice');
});
it('should filter users by userId case-insensitive', async () => {
const response = await getUsers({ userId: 'ALICE' });
expect(response.status).toBe(200);
expect(response.body.users).toHaveLength(1);
expect(response.body.users[0].userId).toContain('alice');
});
it('should filter users by name using partial match', async () => {
const response = await getUsers({ name: 'Anderson' });
expect(response.status).toBe(200);
expect(response.body.users).toHaveLength(1);
expect(response.body.users[0].name).toContain('Anderson');
});
it('should filter users by name case-insensitive', async () => {
const response = await getUsers({ name: 'brown' });
expect(response.status).toBe(200);
expect(response.body.users).toHaveLength(1);
expect(response.body.users[0].name).toContain('Brown');
});
it('should filter root admin by userId', async () => {
const response = await getUsers({ userId: MEET_ENV.INITIAL_ADMIN_USER });
expect(response.status).toBe(200);
expect(response.body.users).toHaveLength(1);
expect(response.body.users[0]).toHaveProperty('userId', MEET_ENV.INITIAL_ADMIN_USER);
expect(response.body.users[0]).toHaveProperty('role', MeetUserRole.ADMIN);
});
it('should filter users by role', async () => {
const response = await getUsers({ role: MeetUserRole.ADMIN });
expect(response.status).toBe(200);
// We created 2 admins + 1 root admin = 3 total
expect(response.body.users).toHaveLength(3);
response.body.users.forEach((user: MeetUser) => {
expect(user).toHaveProperty('role', MeetUserRole.ADMIN);
});
});
it('should return empty array when no users match filter', async () => {
const response = await getUsers({ userId: 'nonexistent123xyz' });
expect(response.status).toBe(200);
expect(response.body.users).toHaveLength(0);
});
it('should respect maxItems parameter', async () => {
const response = await getUsers({ maxItems: 2 });
expect(response.status).toBe(200);
expect(response.body.users.length).toBe(2);
expect(response.body.pagination).toHaveProperty('maxItems', 2);
});
it('should limit maxItems to 100', async () => {
const response = await getUsers({ maxItems: 150 });
expect(response.status).toBe(200);
expect(response.body.pagination).toHaveProperty('maxItems', 100);
});
it('should use default maxItems of 10 when not specified', async () => {
const response = await getUsers();
expect(response.status).toBe(200);
expect(response.body.pagination).toHaveProperty('maxItems', 10);
});
it('should handle pagination with isTruncated flag', async () => {
// Request only 3 users when we have 6 total
const response = await getUsers({ maxItems: 3 });
expect(response.status).toBe(200);
expect(response.body.users).toHaveLength(3);
expect(response.body.pagination).toHaveProperty('isTruncated', true);
expect(response.body.pagination).toHaveProperty('nextPageToken');
});
it('should support pagination with nextPageToken', async () => {
// First page: 3 users
const firstResponse = await getUsers({ maxItems: 3 });
expect(firstResponse.status).toBe(200);
expect(firstResponse.body.users).toHaveLength(3);
expect(firstResponse.body.pagination.isTruncated).toBe(true);
// Second page: next 3 users
const secondResponse = await getUsers({
maxItems: 3,
nextPageToken: firstResponse.body.pagination.nextPageToken
});
expect(secondResponse.status).toBe(200);
expect(secondResponse.body.users).toHaveLength(3);
expect(secondResponse.body.pagination.isTruncated).toBe(false);
});
it('should sort users by registrationDate in descending order by default', async () => {
const response = await getUsers();
expect(response.status).toBe(200);
expect(response.body.users.length).toBe(6);
// Eve was created last, should be first (most recent)
expect(response.body.users[0].userId).toContain('eve');
// Root admin was created first, should be last (oldest)
expect(response.body.users[5].userId).toBe('admin');
// Verify all dates are in descending order
for (let i = 0; i < response.body.users.length - 1; i++) {
expect(response.body.users[i].registrationDate).toBeGreaterThanOrEqual(
response.body.users[i + 1].registrationDate
);
}
});
it('should sort users by registrationDate in ascending order', async () => {
const response = await getUsers({ sortField: 'registrationDate', sortOrder: 'asc' });
expect(response.status).toBe(200);
expect(response.body.users.length).toBe(6);
// Root admin was created first, should be first (oldest)
expect(response.body.users[0].userId).toBe('admin');
// Eve was created last, should be last (most recent)
expect(response.body.users[5].userId).toContain('eve');
// Verify all dates are in ascending order
for (let i = 0; i < response.body.users.length - 1; i++) {
expect(response.body.users[i].registrationDate).toBeLessThanOrEqual(
response.body.users[i + 1].registrationDate
);
}
});
it('should sort users by name in descending order', async () => {
const response = await getUsers({ sortField: 'name', sortOrder: 'desc' });
expect(response.status).toBe(200);
expect(response.body.users.length).toBe(6);
// Verify names are in reverse alphabetical order (case-insensitive)
for (let i = 0; i < response.body.users.length - 1; i++) {
const currentName = response.body.users[i].name.toLowerCase();
const nextName = response.body.users[i + 1].name.toLowerCase();
expect(currentName.localeCompare(nextName)).toBeGreaterThanOrEqual(0);
}
});
it('should sort users by name in ascending order', async () => {
const response = await getUsers({ sortField: 'name', sortOrder: 'asc' });
expect(response.status).toBe(200);
expect(response.body.users.length).toBe(6);
// Verify names are in alphabetical order (case-insensitive)
for (let i = 0; i < response.body.users.length - 1; i++) {
const currentName = response.body.users[i].name.toLowerCase();
const nextName = response.body.users[i + 1].name.toLowerCase();
expect(currentName.localeCompare(nextName)).toBeLessThanOrEqual(0);
}
});
it('should not expose sensitive fields in user objects', async () => {
const response = await getUsers({ maxItems: 3 });
expect(response.status).toBe(200);
response.body.users.forEach((user: MeetUser) => {
expect(user).not.toHaveProperty('passwordHash');
expect(user).not.toHaveProperty('mustChangePassword');
expect(user).toHaveProperty('userId');
expect(user).toHaveProperty('name');
expect(user).toHaveProperty('role');
expect(user).toHaveProperty('registrationDate');
});
});
});
describe('Get Users Validation Tests', () => {
it('should fail when maxItems is zero', async () => {
const response = await getUsers({ maxItems: 0 });
expectValidationError(response, 'maxItems', 'must be a positive number');
});
it('should fail when maxItems is negative', async () => {
const response = await getUsers({ maxItems: -5 });
expectValidationError(response, 'maxItems', 'must be a positive number');
});
it('should fail when sortField is invalid', async () => {
const response = await getUsers({ sortField: 'userId' });
expectValidationError(response, 'sortField', 'Invalid enum value');
});
it('should fail when sortOrder is invalid', async () => {
const response = await getUsers({ sortOrder: 'invalid' });
expectValidationError(response, 'sortOrder', 'Invalid enum value');
});
});
});

View File

@ -0,0 +1,171 @@
import { afterAll, beforeAll, describe, expect, it } from '@jest/globals';
import { MeetUserRole } from '@openvidu-meet/typings';
import { MEET_ENV } from '../../../../src/environment.js';
import { expectValidationError } from '../../../helpers/assertion-helpers.js';
import {
createUser,
deleteAllUsers,
loginReq,
resetUserPassword,
startTestServer
} from '../../../helpers/request-helpers.js';
import { setupTestUsers } from '../../../helpers/test-scenarios.js';
import { TestUsers } from '../../../interfaces/scenarios.js';
describe('Users API Tests', () => {
let testUsers: TestUsers;
beforeAll(async () => {
await startTestServer();
testUsers = await setupTestUsers();
});
afterAll(async () => {
await deleteAllUsers();
});
describe('Reset User Password Tests', () => {
it('should successfully reset user password by admin', async () => {
const newPassword = 'newpassword123';
const response = await resetUserPassword(
testUsers.user.user.userId,
newPassword,
testUsers.admin.accessToken
);
expect(response.status).toBe(200);
expect(response.body).toHaveProperty('message');
expect(response.body.message).toContain('reset successfully');
expect(response.body.message).toContain('must change password on next login');
});
it('should allow user to login with new password after reset', async () => {
const userId = `user_${Date.now()}`;
const initialPassword = 'password123';
const newPassword = 'resetpassword456';
// Create user
await createUser({
userId,
name: 'Test User',
password: initialPassword,
role: MeetUserRole.USER
});
// Reset password
const response = await resetUserPassword(userId, newPassword);
expect(response.status).toBe(200);
// Verify old password no longer works
const loginOldResponse = await loginReq({ userId, password: initialPassword });
expect(loginOldResponse.status).toBe(404);
// Verify new password works
const loginResponse = await loginReq({ userId, password: newPassword });
expect(loginResponse.status).toBe(200);
expect(loginResponse.body).toHaveProperty('accessToken');
expect(loginResponse.body).toHaveProperty('mustChangePassword', true);
});
it('should set mustChangePassword flag to true after password reset', async () => {
const newPassword = 'resetpassword456';
const response = await resetUserPassword(testUsers.user.user.userId, newPassword);
expect(response.status).toBe(200);
// Login and verify mustChangePassword is true
const loginResponse = await loginReq({
userId: testUsers.user.user.userId,
password: newPassword
});
expect(loginResponse.status).toBe(200);
expect(loginResponse.body).toHaveProperty('mustChangePassword', true);
});
it('should reset password for ADMIN users', async () => {
const userId = `admin_${Date.now()}`;
const newPassword = 'newadminpass123';
// Create ADMIN user
await createUser({
userId,
name: 'Test User',
password: 'password123',
role: MeetUserRole.ADMIN
});
// Reset password
const response = await resetUserPassword(userId, newPassword, testUsers.admin.accessToken);
expect(response.status).toBe(200);
// Verify admin can login with new password
const loginResponse = await loginReq({
userId,
password: newPassword
});
expect(loginResponse.status).toBe(200);
expect(loginResponse.body).toHaveProperty('mustChangePassword', true);
});
it('should reset password for ROOM_MEMBER users', async () => {
const newPassword = 'newroompass123';
const response = await resetUserPassword(testUsers.roomMember.user.userId, newPassword);
expect(response.status).toBe(200);
// Verify room member can login with new password
const loginResponse = await loginReq({
userId: testUsers.roomMember.user.userId,
password: newPassword
});
expect(loginResponse.status).toBe(200);
expect(loginResponse.body).toHaveProperty('mustChangePassword', true);
});
it.skip('should fail when trying to reset own password', async () => {
const response = await resetUserPassword(
testUsers.admin.user.userId,
'newpassword123',
testUsers.admin.accessToken
);
expect(response.status).toBe(403);
expect(response.body).toHaveProperty('message');
expect(response.body.message).toContain('Cannot reset your own password');
});
it('should fail when root admin tries to reset own password', async () => {
const response = await resetUserPassword(MEET_ENV.INITIAL_ADMIN_USER, 'newpassword123');
expect(response.status).toBe(403);
expect(response.body).toHaveProperty('message');
expect(response.body.message).toContain('Cannot reset your own password');
});
it('should fail when trying to reset root admin password', async () => {
const response = await resetUserPassword(
MEET_ENV.INITIAL_ADMIN_USER,
'newpassword123',
testUsers.admin.accessToken
);
expect(response.status).toBe(403);
expect(response.body).toHaveProperty('message');
expect(response.body.message).toContain('Cannot reset password for the root admin user');
});
it('should fail when user does not exist', async () => {
const response = await resetUserPassword('nonexistent_user_123', 'newpassword123');
expect(response.status).toBe(404);
expect(response.body).toHaveProperty('message');
expect(response.body.message).toContain('not found');
});
});
describe('Reset User Password Validation Tests', () => {
it('should fail when newPassword is missing', async () => {
const response = await resetUserPassword(testUsers.user.user.userId, undefined as unknown as string);
expectValidationError(response, 'newPassword', 'Required');
});
it('should fail when newPassword is too short', async () => {
const response = await resetUserPassword(testUsers.user.user.userId, '1234');
expectValidationError(response, 'newPassword', 'at least 5 characters');
});
});
});

View File

@ -0,0 +1,154 @@
import { afterAll, beforeAll, describe, expect, it } from '@jest/globals';
import { MeetUserRole } from '@openvidu-meet/typings';
import { MEET_ENV } from '../../../../src/environment.js';
import { expectValidationError } from '../../../helpers/assertion-helpers.js';
import {
createUser,
deleteAllUsers,
getUser,
startTestServer,
updateUserRole
} from '../../../helpers/request-helpers.js';
import { setupTestUsers } from '../../../helpers/test-scenarios.js';
import { TestUsers } from '../../../interfaces/scenarios.js';
describe('Users API Tests', () => {
let testUsers: TestUsers;
beforeAll(async () => {
await startTestServer();
testUsers = await setupTestUsers();
});
afterAll(async () => {
await deleteAllUsers();
});
describe('Update User Role Tests', () => {
it('should successfully update user role to ADMIN', async () => {
const response = await updateUserRole(testUsers.user.user.userId, MeetUserRole.ADMIN);
expect(response.status).toBe(200);
expect(response.body).toHaveProperty('message');
expect(response.body.message).toContain('updated successfully');
expect(response.body.message).toContain(MeetUserRole.ADMIN);
expect(response.body).toHaveProperty('user');
expect(response.body.user).toHaveProperty('userId', testUsers.user.user.userId);
expect(response.body.user).toHaveProperty('role', MeetUserRole.ADMIN);
});
it('should successfully update user role to USER', async () => {
// Create user with ADMIN role first
const userId = `user_${Date.now()}`;
await createUser({
userId,
name: 'Test User',
password: 'password123',
role: MeetUserRole.ADMIN
});
// Update role to USER
const response = await updateUserRole(userId, MeetUserRole.USER);
expect(response.status).toBe(200);
expect(response.body).toHaveProperty('user');
expect(response.body.user).toHaveProperty('role', MeetUserRole.USER);
});
it('should successfully update user role to ROOM_MEMBER', async () => {
// Create user with USER role first
const userId = `user_${Date.now()}`;
await createUser({
userId,
name: 'Test User',
password: 'password123',
role: MeetUserRole.USER
});
// Update role to ROOM_MEMBER
const response = await updateUserRole(userId, MeetUserRole.ROOM_MEMBER);
expect(response.status).toBe(200);
expect(response.body).toHaveProperty('user');
expect(response.body.user).toHaveProperty('role', MeetUserRole.ROOM_MEMBER);
});
it('should persist role change after update', async () => {
// Create user with USER role first
const userId = `user_${Date.now()}`;
await createUser({
userId,
name: 'Test User',
password: 'password123',
role: MeetUserRole.USER
});
// Update role to ADMIN
const updateResponse = await updateUserRole(userId, MeetUserRole.ADMIN);
expect(updateResponse.status).toBe(200);
// Verify role persisted by getting user
const getUserResponse = await getUser(userId);
expect(getUserResponse.status).toBe(200);
expect(getUserResponse.body).toHaveProperty('role', MeetUserRole.ADMIN);
});
it('should not expose sensitive fields in response', async () => {
const response = await updateUserRole(testUsers.user.user.userId, MeetUserRole.ADMIN);
expect(response.status).toBe(200);
expect(response.body.user).not.toHaveProperty('passwordHash');
expect(response.body.user).not.toHaveProperty('mustChangePassword');
expect(response.body.user).toHaveProperty('userId');
expect(response.body.user).toHaveProperty('name');
expect(response.body.user).toHaveProperty('role');
expect(response.body.user).toHaveProperty('registrationDate');
});
it('should fail when trying to update own role', async () => {
const response = await updateUserRole(
testUsers.admin.user.userId,
MeetUserRole.USER,
testUsers.admin.accessToken
);
expect(response.status).toBe(403);
expect(response.body).toHaveProperty('message');
expect(response.body.message).toContain('Cannot change your own role');
});
it('should fail when root admin tries to update own role', async () => {
const response = await updateUserRole(MEET_ENV.INITIAL_ADMIN_USER, MeetUserRole.USER);
expect(response.status).toBe(403);
expect(response.body).toHaveProperty('message');
expect(response.body.message).toContain('Cannot change the role of the root admin user');
});
it('should fail when trying to update root admin role', async () => {
const response = await updateUserRole(
MEET_ENV.INITIAL_ADMIN_USER,
MeetUserRole.USER,
testUsers.admin.accessToken
);
expect(response.status).toBe(403);
expect(response.body).toHaveProperty('message');
expect(response.body.message).toContain('Cannot change the role of the root admin user');
});
it('should fail when user does not exist', async () => {
const response = await updateUserRole('nonexistent_user_123', MeetUserRole.ADMIN);
expect(response.status).toBe(404);
expect(response.body).toHaveProperty('message');
expect(response.body.message).toContain('not found');
});
});
describe('Update User Role Validation Tests', () => {
it('should fail when role is missing', async () => {
const response = await updateUserRole(testUsers.user.user.userId, undefined as unknown as MeetUserRole);
expectValidationError(response, 'role', 'Required');
});
it('should fail when role is invalid', async () => {
const response = await updateUserRole(testUsers.user.user.userId, 'INVALID_ROLE' as MeetUserRole);
expectValidationError(response, 'role', 'Invalid enum value');
});
});
});