test: enhance change password and user profile tests with new scenarios and validations
This commit is contained in:
parent
54c2c79ccb
commit
c561cf9bcd
@ -10,9 +10,9 @@ import {
|
||||
} from '../models/error.model.js';
|
||||
import { LoggerService } from '../services/logger.service.js';
|
||||
import { RequestSessionService } from '../services/request-session.service.js';
|
||||
import { TokenService } from '../services/token.service.js';
|
||||
import { UserService } from '../services/user.service.js';
|
||||
import { getBaseUrl } from '../utils/url.utils.js';
|
||||
import { TokenService } from '../services/token.service.js';
|
||||
|
||||
export const createUser = async (req: Request, res: Response) => {
|
||||
const userOptions = req.body as MeetUserOptions;
|
||||
@ -76,47 +76,6 @@ export const getUser = async (req: Request, res: Response) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const deleteUser = async (req: Request, res: Response) => {
|
||||
const { userId } = req.params;
|
||||
|
||||
const logger = container.get(LoggerService);
|
||||
logger.verbose(`Deleting user with ID '${userId}'`);
|
||||
|
||||
try {
|
||||
const userService = container.get(UserService);
|
||||
await userService.deleteUser(userId);
|
||||
return res.status(200).json({ message: `User '${userId}' deleted successfully` });
|
||||
} catch (error) {
|
||||
handleError(res, error, 'deleting user');
|
||||
}
|
||||
};
|
||||
|
||||
export const bulkDeleteUsers = async (req: Request, res: Response) => {
|
||||
const { userIds } = req.query as { userIds: string[] };
|
||||
|
||||
const logger = container.get(LoggerService);
|
||||
logger.verbose(`Deleting users: ${userIds}`);
|
||||
|
||||
try {
|
||||
const userService = container.get(UserService);
|
||||
const { deleted, failed } = await userService.bulkDeleteUsers(userIds);
|
||||
|
||||
// All users were successfully deleted
|
||||
if (deleted.length > 0 && failed.length === 0) {
|
||||
return res.status(200).json({ message: 'All users deleted successfully', deleted });
|
||||
}
|
||||
|
||||
// Some or all users could not be deleted
|
||||
return res.status(400).json({
|
||||
message: `${failed.length} user(s) could not be deleted`,
|
||||
deleted,
|
||||
failed
|
||||
});
|
||||
} catch (error) {
|
||||
handleError(res, error, 'deleting users');
|
||||
}
|
||||
};
|
||||
|
||||
export const getMe = (_req: Request, res: Response) => {
|
||||
const requestSessionService = container.get(RequestSessionService);
|
||||
const user = requestSessionService.getAuthenticatedUser();
|
||||
@ -150,26 +109,6 @@ export const resetUserPassword = async (req: Request, res: Response) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const updateUserRole = async (req: Request, res: Response) => {
|
||||
const { userId } = req.params;
|
||||
const { role } = req.body as { role: MeetUserRole };
|
||||
|
||||
const logger = container.get(LoggerService);
|
||||
logger.verbose(`Admin updating role for user '${userId}' to '${role}'`);
|
||||
|
||||
try {
|
||||
const userService = container.get(UserService);
|
||||
const user = await userService.changeUserRole(userId, role);
|
||||
|
||||
return res.status(200).json({
|
||||
message: `Role for user '${userId}' updated successfully to '${role}'`,
|
||||
user: userService.convertToDTO(user)
|
||||
});
|
||||
} catch (error) {
|
||||
handleError(res, error, 'updating user role');
|
||||
}
|
||||
};
|
||||
|
||||
export const changePassword = async (req: Request, res: Response) => {
|
||||
const requestSessionService = container.get(RequestSessionService);
|
||||
const user = requestSessionService.getAuthenticatedUser();
|
||||
@ -210,3 +149,64 @@ export const changePassword = async (req: Request, res: Response) => {
|
||||
handleError(res, error, 'changing password');
|
||||
}
|
||||
};
|
||||
|
||||
export const updateUserRole = async (req: Request, res: Response) => {
|
||||
const { userId } = req.params;
|
||||
const { role } = req.body as { role: MeetUserRole };
|
||||
|
||||
const logger = container.get(LoggerService);
|
||||
logger.verbose(`Admin updating role for user '${userId}' to '${role}'`);
|
||||
|
||||
try {
|
||||
const userService = container.get(UserService);
|
||||
const user = await userService.changeUserRole(userId, role);
|
||||
|
||||
return res.status(200).json({
|
||||
message: `Role for user '${userId}' updated successfully to '${role}'`,
|
||||
user: userService.convertToDTO(user)
|
||||
});
|
||||
} catch (error) {
|
||||
handleError(res, error, 'updating user role');
|
||||
}
|
||||
};
|
||||
|
||||
export const deleteUser = async (req: Request, res: Response) => {
|
||||
const { userId } = req.params;
|
||||
|
||||
const logger = container.get(LoggerService);
|
||||
logger.verbose(`Deleting user with ID '${userId}'`);
|
||||
|
||||
try {
|
||||
const userService = container.get(UserService);
|
||||
await userService.deleteUser(userId);
|
||||
return res.status(200).json({ message: `User '${userId}' deleted successfully` });
|
||||
} catch (error) {
|
||||
handleError(res, error, 'deleting user');
|
||||
}
|
||||
};
|
||||
|
||||
export const bulkDeleteUsers = async (req: Request, res: Response) => {
|
||||
const { userIds } = req.query as { userIds: string[] };
|
||||
|
||||
const logger = container.get(LoggerService);
|
||||
logger.verbose(`Deleting users: ${userIds}`);
|
||||
|
||||
try {
|
||||
const userService = container.get(UserService);
|
||||
const { deleted, failed } = await userService.bulkDeleteUsers(userIds);
|
||||
|
||||
// All users were successfully deleted
|
||||
if (deleted.length > 0 && failed.length === 0) {
|
||||
return res.status(200).json({ message: 'All users deleted successfully', deleted });
|
||||
}
|
||||
|
||||
// Some or all users could not be deleted
|
||||
return res.status(400).json({
|
||||
message: `${failed.length} user(s) could not be deleted`,
|
||||
deleted,
|
||||
failed
|
||||
});
|
||||
} catch (error) {
|
||||
handleError(res, error, 'deleting users');
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,35 +1,175 @@
|
||||
import { beforeAll, describe, expect, it } from '@jest/globals';
|
||||
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 { changePassword, loginAdminUser, startTestServer } from '../../../helpers/request-helpers.js';
|
||||
import {
|
||||
changePassword,
|
||||
changePasswordReq,
|
||||
createRoom,
|
||||
createUser,
|
||||
deleteAllRooms,
|
||||
deleteAllUsers,
|
||||
loginReq,
|
||||
loginRootAdmin,
|
||||
loginUser,
|
||||
refreshTokenReq,
|
||||
startTestServer
|
||||
} from '../../../helpers/request-helpers.js';
|
||||
import { setupUser } from '../../../helpers/test-scenarios.js';
|
||||
|
||||
describe('Users API Tests', () => {
|
||||
let adminAccessToken: string;
|
||||
let rootAdminAccessToken: string;
|
||||
|
||||
beforeAll(async () => {
|
||||
await startTestServer();
|
||||
adminAccessToken = await loginAdminUser();
|
||||
({ accessToken: rootAdminAccessToken } = await loginRootAdmin());
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await deleteAllRooms();
|
||||
await deleteAllUsers();
|
||||
});
|
||||
|
||||
describe('Change Password Tests', () => {
|
||||
it('should successfully change password', async () => {
|
||||
it('should successfully change root admin password', async () => {
|
||||
const newPassword = 'newpassword123';
|
||||
const response = await changePassword(MEET_ENV.INITIAL_ADMIN_PASSWORD, newPassword, adminAccessToken);
|
||||
const response = await changePasswordReq(
|
||||
{ currentPassword: MEET_ENV.INITIAL_ADMIN_PASSWORD, newPassword },
|
||||
rootAdminAccessToken
|
||||
);
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body).toHaveProperty('message');
|
||||
expect(response.body.message).toContain('changed successfully');
|
||||
expect(response.body).not.toHaveProperty('accessToken');
|
||||
expect(response.body).not.toHaveProperty('refreshToken');
|
||||
|
||||
// Reset password
|
||||
await changePassword(newPassword, MEET_ENV.INITIAL_ADMIN_PASSWORD, adminAccessToken);
|
||||
// Reset password back
|
||||
await changePassword(newPassword, MEET_ENV.INITIAL_ADMIN_PASSWORD, rootAdminAccessToken);
|
||||
});
|
||||
|
||||
it('should successfully login with new password after change', async () => {
|
||||
const userId = MEET_ENV.INITIAL_ADMIN_USER;
|
||||
const initialPassword = MEET_ENV.INITIAL_ADMIN_PASSWORD;
|
||||
const newPassword = 'newpassword123';
|
||||
|
||||
// Change password
|
||||
const changeResponse = await changePasswordReq(
|
||||
{ currentPassword: initialPassword, newPassword },
|
||||
rootAdminAccessToken
|
||||
);
|
||||
expect(changeResponse.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);
|
||||
|
||||
// Reset password back
|
||||
await changePassword(newPassword, initialPassword, rootAdminAccessToken);
|
||||
});
|
||||
|
||||
it('should successfully change password and return new tokens when mustChangePassword is true', async () => {
|
||||
const userId = `user_${Date.now()}`;
|
||||
const initialPassword = 'password123';
|
||||
const newPassword = 'NewPassword123!';
|
||||
|
||||
// Create user (when created, this user is set to require password change)
|
||||
const createResponse = await createUser({
|
||||
userId,
|
||||
name: 'Test User',
|
||||
password: initialPassword,
|
||||
role: MeetUserRole.USER
|
||||
});
|
||||
expect(createResponse.status).toBe(201);
|
||||
|
||||
// Login to get temporary token
|
||||
const { accessToken: accessTokenTmp } = await loginUser(userId, initialPassword);
|
||||
|
||||
// Change password
|
||||
const response = await changePasswordReq({ currentPassword: initialPassword, newPassword }, accessTokenTmp);
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body).toHaveProperty('message');
|
||||
expect(response.body.message).toContain('changed successfully');
|
||||
expect(response.body).toHaveProperty('accessToken');
|
||||
expect(response.body).toHaveProperty('refreshToken');
|
||||
|
||||
const accessToken = response.body.accessToken;
|
||||
const refreshToken = response.body.refreshToken;
|
||||
|
||||
// Verify new access token work
|
||||
await createRoom({}, `Bearer ${accessToken}`);
|
||||
|
||||
// Verify new refresh token work
|
||||
const refreshResponse = await refreshTokenReq(`Bearer ${refreshToken}`);
|
||||
expect(refreshResponse.status).toBe(200);
|
||||
});
|
||||
|
||||
it('should successfully change password for regular user without returning tokens', async () => {
|
||||
const userData = await setupUser({
|
||||
userId: `user_${Date.now()}`,
|
||||
name: 'Regular User',
|
||||
password: 'password123',
|
||||
role: MeetUserRole.USER
|
||||
});
|
||||
|
||||
// Change password
|
||||
const response = await changePasswordReq(
|
||||
{ currentPassword: userData.password, newPassword: 'newpassword123' },
|
||||
userData.accessToken
|
||||
);
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body).toHaveProperty('message');
|
||||
expect(response.body.message).toContain('changed successfully');
|
||||
expect(response.body).not.toHaveProperty('accessToken');
|
||||
expect(response.body).not.toHaveProperty('refreshToken');
|
||||
});
|
||||
|
||||
it('should fail when current password is incorrect', async () => {
|
||||
const response = await changePassword('wrongpassword', 'newpassword123', adminAccessToken);
|
||||
const response = await changePasswordReq(
|
||||
{ currentPassword: 'wrongpassword', newPassword: 'newpassword123' },
|
||||
rootAdminAccessToken
|
||||
);
|
||||
expect(response.status).toBe(400);
|
||||
expect(response.body).toHaveProperty('message', 'Invalid current password');
|
||||
expect(response.body).toHaveProperty('message');
|
||||
expect(response.body.message).toContain('Invalid current password');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Change Password Validation Tests', () => {
|
||||
it('should fail when new password is too short', async () => {
|
||||
const response = await changePasswordReq(
|
||||
{ currentPassword: MEET_ENV.INITIAL_ADMIN_PASSWORD, newPassword: '1234' },
|
||||
rootAdminAccessToken
|
||||
);
|
||||
expectValidationError(response, 'newPassword', 'New password must be at least 5 characters long');
|
||||
});
|
||||
|
||||
it('should fail when new password is not 5 characters long', async () => {
|
||||
const response = await changePassword(MEET_ENV.INITIAL_ADMIN_PASSWORD, '1234', adminAccessToken);
|
||||
expectValidationError(response, 'newPassword', 'New password must be at least 5 characters long');
|
||||
it('should fail when currentPassword is missing', async () => {
|
||||
const response = await changePasswordReq(
|
||||
{ newPassword: 'newpassword123' } as { currentPassword: string; newPassword: string },
|
||||
rootAdminAccessToken
|
||||
);
|
||||
expectValidationError(response, 'currentPassword', 'Required');
|
||||
});
|
||||
|
||||
it('should fail when newPassword is missing', async () => {
|
||||
const response = await changePasswordReq(
|
||||
{ currentPassword: MEET_ENV.INITIAL_ADMIN_PASSWORD } as {
|
||||
currentPassword: string;
|
||||
newPassword: string;
|
||||
},
|
||||
rootAdminAccessToken
|
||||
);
|
||||
expectValidationError(response, 'newPassword', 'Required');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,22 +1,70 @@
|
||||
import { beforeAll, describe, expect, it } from '@jest/globals';
|
||||
import { getMe, loginAdminUser, startTestServer } from '../../../helpers/request-helpers.js';
|
||||
import { MeetUserRole } from '@openvidu-meet/typings';
|
||||
import { MEET_ENV } from '../../../../src/environment.js';
|
||||
import { deleteAllUsers, getMe, loginRootAdmin, startTestServer } from '../../../helpers/request-helpers.js';
|
||||
import { setupTestUsers } from '../../../helpers/test-scenarios.js';
|
||||
import { TestUsers } from '../../../interfaces/scenarios.js';
|
||||
|
||||
describe('Users API Tests', () => {
|
||||
let adminAccessToken: string;
|
||||
let rootAdminAccessToken: string;
|
||||
let testUsers: TestUsers;
|
||||
|
||||
beforeAll(async () => {
|
||||
await startTestServer();
|
||||
adminAccessToken = await loginAdminUser();
|
||||
({ accessToken: rootAdminAccessToken } = await loginRootAdmin());
|
||||
testUsers = await setupTestUsers();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await deleteAllUsers();
|
||||
});
|
||||
|
||||
describe('Profile Tests', () => {
|
||||
it('should return 200 and admin profile', async () => {
|
||||
const response = await getMe(adminAccessToken);
|
||||
it('should return root admin profile', async () => {
|
||||
const response = await getMe(rootAdminAccessToken);
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body).toHaveProperty('userId', 'admin');
|
||||
expect(response.body).toHaveProperty('userId', MEET_ENV.INITIAL_ADMIN_USER);
|
||||
expect(response.body).toHaveProperty('name', 'Admin');
|
||||
expect(response.body).toHaveProperty('role', 'admin');
|
||||
expect(response.body).toHaveProperty('role', MeetUserRole.ADMIN);
|
||||
expect(response.body).toHaveProperty('registrationDate');
|
||||
expect(response.body).not.toHaveProperty('passwordHash');
|
||||
expect(response.body).not.toHaveProperty('mustChangePassword');
|
||||
});
|
||||
|
||||
it('should return ADMIN user profile', async () => {
|
||||
const user = testUsers.admin;
|
||||
const response = await getMe(user.accessToken);
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body).toHaveProperty('userId', user.user.userId);
|
||||
expect(response.body).toHaveProperty('name', user.user.name);
|
||||
expect(response.body).toHaveProperty('role', user.user.role);
|
||||
expect(response.body).toHaveProperty('registrationDate', user.user.registrationDate);
|
||||
expect(response.body).not.toHaveProperty('passwordHash');
|
||||
expect(response.body).not.toHaveProperty('mustChangePassword');
|
||||
});
|
||||
|
||||
it('should return USER user profile', async () => {
|
||||
const user = testUsers.user;
|
||||
const response = await getMe(user.accessToken);
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body).toHaveProperty('userId', user.user.userId);
|
||||
expect(response.body).toHaveProperty('name', user.user.name);
|
||||
expect(response.body).toHaveProperty('role', user.user.role);
|
||||
expect(response.body).toHaveProperty('registrationDate', user.user.registrationDate);
|
||||
expect(response.body).not.toHaveProperty('passwordHash');
|
||||
expect(response.body).not.toHaveProperty('mustChangePassword');
|
||||
});
|
||||
|
||||
it('should return ROOM_MEMBER user profile', async () => {
|
||||
const user = testUsers.roomMember;
|
||||
const response = await getMe(user.accessToken);
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body).toHaveProperty('userId', user.user.userId);
|
||||
expect(response.body).toHaveProperty('name', user.user.name);
|
||||
expect(response.body).toHaveProperty('role', user.user.role);
|
||||
expect(response.body).toHaveProperty('registrationDate', user.user.registrationDate);
|
||||
expect(response.body).not.toHaveProperty('passwordHash');
|
||||
expect(response.body).not.toHaveProperty('mustChangePassword');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user