261 lines
9.6 KiB
TypeScript
261 lines
9.6 KiB
TypeScript
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');
|
|
});
|
|
});
|
|
});
|