test: add integration tests for user management API
This commit is contained in:
parent
c561cf9bcd
commit
21f4563202
@ -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')
|
||||
|
||||
@ -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(',') });
|
||||
};
|
||||
|
||||
|
||||
@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
267
meet-ce/backend/tests/integration/api/users/create-user.test.ts
Normal file
267
meet-ce/backend/tests/integration/api/users/create-user.test.ts
Normal 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');
|
||||
});
|
||||
});
|
||||
});
|
||||
299
meet-ce/backend/tests/integration/api/users/delete-user.test.ts
Normal file
299
meet-ce/backend/tests/integration/api/users/delete-user.test.ts
Normal 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);
|
||||
});
|
||||
});
|
||||
});
|
||||
66
meet-ce/backend/tests/integration/api/users/get-user.test.ts
Normal file
66
meet-ce/backend/tests/integration/api/users/get-user.test.ts
Normal 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');
|
||||
});
|
||||
});
|
||||
});
|
||||
260
meet-ce/backend/tests/integration/api/users/get-users.test.ts
Normal file
260
meet-ce/backend/tests/integration/api/users/get-users.test.ts
Normal 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');
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
Loading…
x
Reference in New Issue
Block a user