hexabot/api/src/user/controllers/role.controller.spec.ts
2024-10-16 18:54:55 +01:00

275 lines
9.4 KiB
TypeScript

/*
* Copyright © 2024 Hexastack. All rights reserved.
*
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
*/
import { CACHE_MANAGER } from '@nestjs/cache-manager';
import { ForbiddenException, NotFoundException } from '@nestjs/common';
import { EventEmitter2 } from '@nestjs/event-emitter';
import { MongooseModule } from '@nestjs/mongoose';
import { Test, TestingModule } from '@nestjs/testing';
import { Request } from 'express';
import { AttachmentRepository } from '@/attachment/repositories/attachment.repository';
import { AttachmentModel } from '@/attachment/schemas/attachment.schema';
import { AttachmentService } from '@/attachment/services/attachment.service';
import { LoggerService } from '@/logger/logger.service';
import { installPermissionFixtures } from '@/utils/test/fixtures/permission';
import { roleFixtures } from '@/utils/test/fixtures/role';
import { getPageQuery } from '@/utils/test/pagination';
import {
closeInMongodConnection,
rootMongooseTestModule,
} from '@/utils/test/test';
import { RoleCreateDto, RoleUpdateDto } from '../dto/role.dto';
import { PermissionRepository } from '../repositories/permission.repository';
import { RoleRepository } from '../repositories/role.repository';
import { UserRepository } from '../repositories/user.repository';
import { PermissionModel } from '../schemas/permission.schema';
import { RoleModel, Role } from '../schemas/role.schema';
import { UserModel } from '../schemas/user.schema';
import { PermissionService } from '../services/permission.service';
import { RoleService } from '../services/role.service';
import { UserService } from '../services/user.service';
import { RoleController } from './role.controller';
describe('RoleController', () => {
let roleController: RoleController;
let roleService: RoleService;
let permissionService: PermissionService;
let userService: UserService;
let roleAdmin: Role;
let rolePublic: Role;
beforeAll(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [RoleController],
imports: [
rootMongooseTestModule(installPermissionFixtures),
MongooseModule.forFeature([
RoleModel,
PermissionModel,
UserModel,
AttachmentModel,
]),
],
providers: [
LoggerService,
PermissionService,
UserService,
UserRepository,
RoleService,
RoleRepository,
PermissionRepository,
EventEmitter2,
AttachmentService,
AttachmentRepository,
{
provide: CACHE_MANAGER,
useValue: {
del: jest.fn(),
get: jest.fn(),
set: jest.fn(),
},
},
],
}).compile();
roleController = module.get<RoleController>(RoleController);
roleService = module.get<RoleService>(RoleService);
permissionService = module.get<PermissionService>(PermissionService);
userService = module.get<UserService>(UserService);
roleAdmin = await roleService.findOne({ name: 'admin' });
rolePublic = await roleService.findOne({ name: 'public' });
});
afterAll(async () => {
await closeInMongodConnection();
});
afterEach(jest.clearAllMocks);
describe('findPage', () => {
const pageQuery = getPageQuery<Role>({ sort: ['_id', 'asc'] });
it('should find roles', async () => {
jest.spyOn(roleService, 'findPage');
const result = await roleController.findPage(pageQuery, [], {});
expect(roleService.findPage).toHaveBeenCalledWith({}, pageQuery);
expect(result).toEqualPayload(roleFixtures);
});
it('should find roles, and for each role populate the corresponding users and permissions', async () => {
jest.spyOn(roleService, 'findPageAndPopulate');
const allRoles = await roleService.findAll();
const allPermissions = await permissionService.findAll();
const allUsers = await userService.findAll();
const result = await roleController.findPage(
pageQuery,
['users', 'permissions'],
{},
);
const rolesWithPermissionsAndUsers = allRoles.reduce((acc, currRole) => {
const roleWithPermissionsAndUsers = {
...currRole,
permissions: allPermissions.filter((currPermission) => {
return currPermission.role === currRole.id;
}),
users: allUsers.filter((currUser) => {
return currUser.roles.includes(currRole.id);
}),
};
acc.push(roleWithPermissionsAndUsers);
return acc;
}, []);
expect(roleService.findPageAndPopulate).toHaveBeenCalledWith(
{},
pageQuery,
);
expect(result).toEqualPayload(rolesWithPermissionsAndUsers);
});
});
describe('findOne', () => {
it('should find one role', async () => {
jest.spyOn(roleService, 'findOne');
const result = await roleController.findOne(roleAdmin.id, []);
expect(roleService.findOne).toHaveBeenCalledWith(roleAdmin.id);
expect(result).toEqualPayload(
roleFixtures.find((role) => role.name === 'admin'),
);
});
it('should find one role and populate its permissions and users ', async () => {
jest.spyOn(roleService, 'findOneAndPopulate');
const users = (await userService.findAll()).filter((user) =>
user.roles.includes(roleAdmin.id),
);
const permissions = await permissionService.find({
role: roleAdmin.id,
});
const result = await roleController.findOne(roleAdmin.id, [
'users',
'permissions',
]);
expect(roleService.findOneAndPopulate).toHaveBeenCalledWith(roleAdmin.id);
expect(result).toEqualPayload({
...roleFixtures.find((role) => role.name === 'admin'),
users,
permissions,
});
});
});
describe('count', () => {
it('should count the roles', async () => {
const result = await roleController.filterCount();
expect(result).toEqual({ count: roleFixtures.length });
});
});
describe('create', () => {
it('should return created role', async () => {
jest.spyOn(roleService, 'create');
const roleDto: RoleCreateDto = {
name: 'testRole',
active: true,
};
const result = await roleController.create(roleDto);
expect(roleService.create).toHaveBeenCalledWith(roleDto);
expect(result).toEqualPayload(roleDto);
});
});
describe('deleteOne', () => {
it("should throw ForbiddenException if the role is part of the user's roles", async () => {
const req = { user: { roles: ['role1'] } } as unknown as Request;
const roleId = 'role1';
userService.findOne = jest.fn().mockResolvedValue(null);
await expect(roleController.deleteOne(roleId, req)).rejects.toThrow(
ForbiddenException,
);
});
it('should throw ForbiddenException if the role is associated with other users', async () => {
const req = { user: { roles: ['role2'] } } as unknown as Request;
const roleId = 'role1';
userService.findOne = jest.fn().mockResolvedValue({ id: 'user2' });
await expect(roleController.deleteOne(roleId, req)).rejects.toThrow(
ForbiddenException,
);
});
it('should throw NotFoundException if the role is not found', async () => {
const req = { user: { roles: ['role2'] } } as unknown as Request;
const roleId = 'role1';
userService.findOne = jest.fn().mockResolvedValue(null);
roleService.deleteOne = jest.fn().mockResolvedValue({ deletedCount: 0 });
await expect(roleController.deleteOne(roleId, req)).rejects.toThrow(
NotFoundException,
);
});
it('should return the result if the role is successfully deleted', async () => {
const req = { user: { roles: ['role2'] } } as unknown as Request;
const roleId = 'role1';
userService.findOne = jest.fn().mockResolvedValue(null);
const deleteResult = { deletedCount: 1 };
roleService.deleteOne = jest.fn().mockResolvedValue(deleteResult);
const result = await roleController.deleteOne(roleId, req);
expect(result).toEqual(deleteResult);
});
});
describe('updateOne', () => {
const roleUpdateDto: RoleUpdateDto = {
active: false,
};
it('should return updated role', async () => {
jest.spyOn(roleService, 'updateOne');
const result = await roleController.updateOne(
rolePublic.id,
roleUpdateDto,
);
expect(roleService.updateOne).toHaveBeenCalledWith(
rolePublic.id,
roleUpdateDto,
);
expect(result).toEqualPayload({
...roleFixtures.find((role) => role.name === 'public'),
...roleUpdateDto,
});
});
it('should throw a NotFoundException when attempting to modify a role', async () => {
const notFoundId = 'nonexistentRoleId';
const roleUpdateDto = { name: 'newRoleName' };
roleService.updateOne = jest
.fn()
.mockRejectedValue(new NotFoundException());
await expect(
roleController.updateOne(notFoundId, roleUpdateDto),
).rejects.toThrow(NotFoundException);
});
});
});