test: add comprehensive unit tests for groups and auth modules

Added unit tests for `GroupsService`, `GroupsController`, `AuthService`, `AuthController`, and `JwtAuthGuard` to ensure functionality and coverage. Includes tests for entity operations, exception handling, and integration scenarios.
This commit is contained in:
Mathis H (Avnyr) 2025-05-15 19:29:12 +02:00
parent 04aba190ed
commit 7eae25d5de
5 changed files with 918 additions and 0 deletions

View File

@ -0,0 +1,117 @@
import { Test, TestingModule } from '@nestjs/testing';
import { ConfigService } from '@nestjs/config';
import { UnauthorizedException } from '@nestjs/common';
import { AuthController } from './auth.controller';
import { AuthService } from '../services/auth.service';
describe('AuthController', () => {
let controller: AuthController;
let authService: AuthService;
let configService: ConfigService;
const mockAuthService = {
validateGithubUser: jest.fn(),
generateTokens: jest.fn(),
refreshTokens: jest.fn(),
};
const mockConfigService = {
get: jest.fn(),
};
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [AuthController],
providers: [
{ provide: AuthService, useValue: mockAuthService },
{ provide: ConfigService, useValue: mockConfigService },
],
}).compile();
controller = module.get<AuthController>(AuthController);
authService = module.get<AuthService>(AuthService);
configService = module.get<ConfigService>(ConfigService);
});
afterEach(() => {
jest.clearAllMocks();
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
describe('githubAuth', () => {
it('should be defined', () => {
expect(controller.githubAuth).toBeDefined();
});
});
describe('githubAuthCallback', () => {
it('should redirect to frontend with tokens', async () => {
const req = {
user: { id: 'user1', name: 'Test User' },
};
const res = {
redirect: jest.fn(),
};
const tokens = {
accessToken: 'access-token',
refreshToken: 'refresh-token',
};
const frontendUrl = 'http://localhost:3001';
const expectedRedirectUrl = `${frontendUrl}/auth/callback?accessToken=${tokens.accessToken}&refreshToken=${tokens.refreshToken}`;
mockAuthService.generateTokens.mockResolvedValue(tokens);
mockConfigService.get.mockReturnValue(frontendUrl);
await controller.githubAuthCallback(req as any, res as any);
expect(mockAuthService.generateTokens).toHaveBeenCalledWith('user1');
expect(mockConfigService.get).toHaveBeenCalledWith('FRONTEND_URL');
expect(res.redirect).toHaveBeenCalledWith(expectedRedirectUrl);
});
it('should throw UnauthorizedException if user is not provided', async () => {
const req = {};
const res = {
redirect: jest.fn(),
};
await expect(controller.githubAuthCallback(req as any, res as any)).rejects.toThrow(
UnauthorizedException,
);
expect(res.redirect).not.toHaveBeenCalled();
});
});
describe('refreshTokens', () => {
it('should refresh tokens', async () => {
const user = {
id: 'user1',
refreshToken: 'refresh-token',
};
const tokens = {
accessToken: 'new-access-token',
refreshToken: 'new-refresh-token',
};
mockAuthService.refreshTokens.mockResolvedValue(tokens);
const result = await controller.refreshTokens(user);
expect(mockAuthService.refreshTokens).toHaveBeenCalledWith('user1', 'refresh-token');
expect(result).toEqual(tokens);
});
});
describe('getProfile', () => {
it('should return user profile', () => {
const user = { id: 'user1', name: 'Test User' };
const result = controller.getProfile(user);
expect(result).toEqual(user);
});
});
});

View File

@ -0,0 +1,82 @@
import { ExecutionContext, UnauthorizedException } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { JwtAuthGuard } from './jwt-auth.guard';
import { IS_PUBLIC_KEY } from '../decorators/public.decorator';
describe('JwtAuthGuard', () => {
let guard: JwtAuthGuard;
let reflector: Reflector;
beforeEach(() => {
reflector = new Reflector();
guard = new JwtAuthGuard(reflector);
});
describe('canActivate', () => {
it('should return true if the route is public', () => {
const context = {
getHandler: jest.fn(),
getClass: jest.fn(),
switchToHttp: jest.fn().mockReturnValue({
getRequest: jest.fn().mockReturnValue({}),
getResponse: jest.fn().mockReturnValue({}),
}),
} as unknown as ExecutionContext;
jest.spyOn(reflector, 'getAllAndOverride').mockReturnValue(true);
expect(guard.canActivate(context)).toBe(true);
expect(reflector.getAllAndOverride).toHaveBeenCalledWith(IS_PUBLIC_KEY, [
context.getHandler(),
context.getClass(),
]);
});
it('should call super.canActivate if the route is not public', () => {
const context = {
getHandler: jest.fn(),
getClass: jest.fn(),
switchToHttp: jest.fn().mockReturnValue({
getRequest: jest.fn().mockReturnValue({}),
getResponse: jest.fn().mockReturnValue({}),
}),
} as unknown as ExecutionContext;
jest.spyOn(reflector, 'getAllAndOverride').mockReturnValue(false);
// Mock the AuthGuard's canActivate method
const canActivateSpy = jest.spyOn(guard, 'canActivate');
// We can't easily test the super.canActivate call directly,
// so we'll just verify our method was called with the right context
guard.canActivate(context);
expect(reflector.getAllAndOverride).toHaveBeenCalledWith(IS_PUBLIC_KEY, [
context.getHandler(),
context.getClass(),
]);
expect(canActivateSpy).toHaveBeenCalledWith(context);
});
});
describe('handleRequest', () => {
it('should return the user if no error and user exists', () => {
const user = { id: 'user1', name: 'Test User' };
const result = guard.handleRequest(null, user, null);
expect(result).toBe(user);
});
it('should throw the error if an error exists', () => {
const error = new Error('Test error');
expect(() => guard.handleRequest(error, null, null)).toThrow(error);
});
it('should throw UnauthorizedException if no error but user does not exist', () => {
expect(() => guard.handleRequest(null, null, null)).toThrow(UnauthorizedException);
expect(() => guard.handleRequest(null, null, null)).toThrow('Authentication required');
});
});
});

View File

@ -0,0 +1,208 @@
import { Test, TestingModule } from '@nestjs/testing';
import { JwtService } from '@nestjs/jwt';
import { ConfigService } from '@nestjs/config';
import { AuthService } from './auth.service';
import { UsersService } from '../../users/services/users.service';
import { UnauthorizedException } from '@nestjs/common';
describe('AuthService', () => {
let service: AuthService;
let usersService: UsersService;
let jwtService: JwtService;
let configService: ConfigService;
const mockUsersService = {
findByGithubId: jest.fn(),
findById: jest.fn(),
create: jest.fn(),
update: jest.fn(),
};
const mockJwtService = {
signAsync: jest.fn(),
verifyAsync: jest.fn(),
};
const mockConfigService = {
get: jest.fn(),
};
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
AuthService,
{ provide: UsersService, useValue: mockUsersService },
{ provide: JwtService, useValue: mockJwtService },
{ provide: ConfigService, useValue: mockConfigService },
],
}).compile();
service = module.get<AuthService>(AuthService);
usersService = module.get<UsersService>(UsersService);
jwtService = module.get<JwtService>(JwtService);
configService = module.get<ConfigService>(ConfigService);
});
afterEach(() => {
jest.clearAllMocks();
});
it('should be defined', () => {
expect(service).toBeDefined();
});
describe('validateGithubUser', () => {
it('should create a new user if one does not exist', async () => {
const githubId = 'github123';
const email = 'test@example.com';
const name = 'Test User';
const avatarUrl = 'https://example.com/avatar.jpg';
const newUser = { id: 'user1', githubId, name, avatar: avatarUrl };
mockUsersService.findByGithubId.mockResolvedValue(null);
mockUsersService.create.mockResolvedValue(newUser);
const result = await service.validateGithubUser(githubId, email, name, avatarUrl);
expect(mockUsersService.findByGithubId).toHaveBeenCalledWith(githubId);
expect(mockUsersService.create).toHaveBeenCalledWith({
githubId,
name,
avatar: avatarUrl,
metadata: { email },
});
expect(result).toEqual(newUser);
});
it('should return an existing user if one exists', async () => {
const githubId = 'github123';
const email = 'test@example.com';
const name = 'Test User';
const avatarUrl = 'https://example.com/avatar.jpg';
const existingUser = { id: 'user1', githubId, name, avatar: avatarUrl };
mockUsersService.findByGithubId.mockResolvedValue(existingUser);
const result = await service.validateGithubUser(githubId, email, name, avatarUrl);
expect(mockUsersService.findByGithubId).toHaveBeenCalledWith(githubId);
expect(mockUsersService.create).not.toHaveBeenCalled();
expect(result).toEqual(existingUser);
});
});
describe('generateTokens', () => {
it('should generate access and refresh tokens', async () => {
const userId = 'user1';
const accessToken = 'access-token';
const refreshToken = 'refresh-token';
const refreshExpiration = '7d';
const refreshSecret = 'refresh-secret';
mockJwtService.signAsync.mockResolvedValueOnce(accessToken);
mockJwtService.signAsync.mockResolvedValueOnce(refreshToken);
mockConfigService.get.mockReturnValueOnce(refreshExpiration);
mockConfigService.get.mockReturnValueOnce(refreshSecret);
const result = await service.generateTokens(userId);
expect(mockJwtService.signAsync).toHaveBeenCalledWith({ sub: userId });
expect(mockJwtService.signAsync).toHaveBeenCalledWith(
{ sub: userId, isRefreshToken: true },
{
expiresIn: refreshExpiration,
secret: refreshSecret,
},
);
expect(result).toEqual({
accessToken,
refreshToken,
});
});
});
describe('refreshTokens', () => {
it('should refresh tokens if refresh token is valid', async () => {
const userId = 'user1';
const refreshToken = 'valid-refresh-token';
const newAccessToken = 'new-access-token';
const newRefreshToken = 'new-refresh-token';
const payload = { sub: userId, isRefreshToken: true };
mockJwtService.verifyAsync.mockResolvedValue(payload);
mockJwtService.signAsync.mockResolvedValueOnce(newAccessToken);
mockJwtService.signAsync.mockResolvedValueOnce(newRefreshToken);
const result = await service.refreshTokens(userId, refreshToken);
expect(mockJwtService.verifyAsync).toHaveBeenCalledWith(refreshToken, {
secret: undefined,
});
expect(result).toEqual({
accessToken: newAccessToken,
refreshToken: newRefreshToken,
});
});
it('should throw UnauthorizedException if refresh token is invalid', async () => {
const userId = 'user1';
const refreshToken = 'invalid-refresh-token';
mockJwtService.verifyAsync.mockRejectedValue(new Error('Invalid token'));
await expect(service.refreshTokens(userId, refreshToken)).rejects.toThrow(
UnauthorizedException,
);
});
it('should throw UnauthorizedException if token is not a refresh token', async () => {
const userId = 'user1';
const refreshToken = 'not-a-refresh-token';
const payload = { sub: userId }; // Missing isRefreshToken: true
mockJwtService.verifyAsync.mockResolvedValue(payload);
await expect(service.refreshTokens(userId, refreshToken)).rejects.toThrow(
UnauthorizedException,
);
});
it('should throw UnauthorizedException if user ID does not match', async () => {
const userId = 'user1';
const refreshToken = 'wrong-user-token';
const payload = { sub: 'user2', isRefreshToken: true }; // Different user ID
mockJwtService.verifyAsync.mockResolvedValue(payload);
await expect(service.refreshTokens(userId, refreshToken)).rejects.toThrow(
UnauthorizedException,
);
});
});
describe('validateJwtUser', () => {
it('should return user if user exists', async () => {
const userId = 'user1';
const user = { id: userId, name: 'Test User' };
const payload = { sub: userId };
mockUsersService.findById.mockResolvedValue(user);
const result = await service.validateJwtUser(payload);
expect(mockUsersService.findById).toHaveBeenCalledWith(userId);
expect(result).toEqual(user);
});
it('should throw UnauthorizedException if user does not exist', async () => {
const userId = 'nonexistent';
const payload = { sub: userId };
mockUsersService.findById.mockResolvedValue(null);
await expect(service.validateJwtUser(payload)).rejects.toThrow(
UnauthorizedException,
);
});
});
});

View File

@ -0,0 +1,194 @@
import { Test, TestingModule } from '@nestjs/testing';
import { GroupsController } from './groups.controller';
import { GroupsService } from '../services/groups.service';
import { CreateGroupDto } from '../dto/create-group.dto';
import { UpdateGroupDto } from '../dto/update-group.dto';
describe('GroupsController', () => {
let controller: GroupsController;
let service: GroupsService;
// Mock data
const mockGroup = {
id: 'group1',
name: 'Test Group',
projectId: 'project1',
metadata: {},
createdAt: new Date(),
updatedAt: new Date(),
};
const mockPerson = {
id: 'person1',
name: 'Test Person',
projectId: 'project1',
createdAt: new Date(),
updatedAt: new Date(),
};
// Mock service
const mockGroupsService = {
create: jest.fn(),
findAll: jest.fn(),
findByProjectId: jest.fn(),
findById: jest.fn(),
update: jest.fn(),
remove: jest.fn(),
addPersonToGroup: jest.fn(),
removePersonFromGroup: jest.fn(),
getPersonsInGroup: jest.fn(),
};
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [GroupsController],
providers: [
{
provide: GroupsService,
useValue: mockGroupsService,
},
],
}).compile();
controller = module.get<GroupsController>(GroupsController);
service = module.get<GroupsService>(GroupsService);
});
afterEach(() => {
jest.clearAllMocks();
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
describe('create', () => {
it('should create a new group', async () => {
const createGroupDto: CreateGroupDto = {
name: 'Test Group',
projectId: 'project1',
metadata: {},
};
mockGroupsService.create.mockResolvedValue(mockGroup);
const result = await controller.create(createGroupDto);
expect(mockGroupsService.create).toHaveBeenCalledWith(createGroupDto);
expect(result).toEqual(mockGroup);
});
});
describe('findAll', () => {
it('should return all groups when no projectId is provided', async () => {
mockGroupsService.findAll.mockResolvedValue([mockGroup]);
const result = await controller.findAll();
expect(mockGroupsService.findAll).toHaveBeenCalled();
expect(mockGroupsService.findByProjectId).not.toHaveBeenCalled();
expect(result).toEqual([mockGroup]);
});
it('should return groups for a specific project when projectId is provided', async () => {
const projectId = 'project1';
mockGroupsService.findByProjectId.mockResolvedValue([mockGroup]);
const result = await controller.findAll(projectId);
expect(mockGroupsService.findByProjectId).toHaveBeenCalledWith(projectId);
expect(mockGroupsService.findAll).not.toHaveBeenCalled();
expect(result).toEqual([mockGroup]);
});
});
describe('findOne', () => {
it('should return a group by id', async () => {
const id = 'group1';
mockGroupsService.findById.mockResolvedValue(mockGroup);
const result = await controller.findOne(id);
expect(mockGroupsService.findById).toHaveBeenCalledWith(id);
expect(result).toEqual(mockGroup);
});
});
describe('update', () => {
it('should update a group', async () => {
const id = 'group1';
const updateGroupDto: UpdateGroupDto = {
name: 'Updated Group',
};
mockGroupsService.update.mockResolvedValue({
...mockGroup,
name: 'Updated Group',
});
const result = await controller.update(id, updateGroupDto);
expect(mockGroupsService.update).toHaveBeenCalledWith(id, updateGroupDto);
expect(result).toEqual({
...mockGroup,
name: 'Updated Group',
});
});
});
describe('remove', () => {
it('should remove a group', async () => {
const id = 'group1';
mockGroupsService.remove.mockResolvedValue(mockGroup);
const result = await controller.remove(id);
expect(mockGroupsService.remove).toHaveBeenCalledWith(id);
expect(result).toEqual(mockGroup);
});
});
describe('addPersonToGroup', () => {
it('should add a person to a group', async () => {
const groupId = 'group1';
const personId = 'person1';
const mockRelation = { groupId, personId };
mockGroupsService.addPersonToGroup.mockResolvedValue(mockRelation);
const result = await controller.addPersonToGroup(groupId, personId);
expect(mockGroupsService.addPersonToGroup).toHaveBeenCalledWith(groupId, personId);
expect(result).toEqual(mockRelation);
});
});
describe('removePersonFromGroup', () => {
it('should remove a person from a group', async () => {
const groupId = 'group1';
const personId = 'person1';
const mockRelation = { groupId, personId };
mockGroupsService.removePersonFromGroup.mockResolvedValue(mockRelation);
const result = await controller.removePersonFromGroup(groupId, personId);
expect(mockGroupsService.removePersonFromGroup).toHaveBeenCalledWith(groupId, personId);
expect(result).toEqual(mockRelation);
});
});
describe('getPersonsInGroup', () => {
it('should get all persons in a group', async () => {
const groupId = 'group1';
const mockPersons = [{ person: mockPerson }];
mockGroupsService.getPersonsInGroup.mockResolvedValue(mockPersons);
const result = await controller.getPersonsInGroup(groupId);
expect(mockGroupsService.getPersonsInGroup).toHaveBeenCalledWith(groupId);
expect(result).toEqual(mockPersons);
});
});
});

View File

@ -0,0 +1,317 @@
import { Test, TestingModule } from '@nestjs/testing';
import { GroupsService } from './groups.service';
import { NotFoundException } from '@nestjs/common';
import { DRIZZLE } from '../../../database/database.module';
describe('GroupsService', () => {
let service: GroupsService;
let mockDb: any;
// Mock data
const mockGroup = {
id: 'group1',
name: 'Test Group',
projectId: 'project1',
metadata: {},
createdAt: new Date(),
updatedAt: new Date(),
};
const mockPerson = {
id: 'person1',
name: 'Test Person',
projectId: 'project1',
createdAt: new Date(),
updatedAt: new Date(),
};
const mockPersonToGroup = {
personId: 'person1',
groupId: 'group1',
};
// Mock database operations
const mockDbOperations = {
select: jest.fn().mockReturnThis(),
from: jest.fn().mockReturnThis(),
where: jest.fn().mockReturnThis(),
insert: jest.fn().mockReturnThis(),
values: jest.fn().mockReturnThis(),
update: jest.fn().mockReturnThis(),
set: jest.fn().mockReturnThis(),
delete: jest.fn().mockReturnThis(),
innerJoin: jest.fn().mockReturnThis(),
returning: jest.fn().mockImplementation(() => {
return [mockGroup];
}),
};
beforeEach(async () => {
mockDb = {
...mockDbOperations,
};
const module: TestingModule = await Test.createTestingModule({
providers: [
GroupsService,
{
provide: DRIZZLE,
useValue: mockDb,
},
],
}).compile();
service = module.get<GroupsService>(GroupsService);
});
afterEach(() => {
jest.clearAllMocks();
});
it('should be defined', () => {
expect(service).toBeDefined();
});
describe('create', () => {
it('should create a new group', async () => {
const createGroupDto = {
name: 'Test Group',
projectId: 'project1',
metadata: {},
};
const result = await service.create(createGroupDto);
expect(mockDb.insert).toHaveBeenCalled();
expect(mockDb.values).toHaveBeenCalledWith({
...createGroupDto,
});
expect(result).toEqual(mockGroup);
});
});
describe('findAll', () => {
it('should return all groups', async () => {
mockDb.select.mockImplementationOnce(() => mockDbOperations);
mockDbOperations.from.mockImplementationOnce(() => [mockGroup]);
const result = await service.findAll();
expect(mockDb.select).toHaveBeenCalled();
expect(mockDb.from).toHaveBeenCalled();
expect(result).toEqual([mockGroup]);
});
});
describe('findByProjectId', () => {
it('should return groups for a specific project', async () => {
const projectId = 'project1';
mockDb.select.mockImplementationOnce(() => mockDbOperations);
mockDbOperations.from.mockImplementationOnce(() => mockDbOperations);
mockDbOperations.where.mockImplementationOnce(() => [mockGroup]);
const result = await service.findByProjectId(projectId);
expect(mockDb.select).toHaveBeenCalled();
expect(mockDb.from).toHaveBeenCalled();
expect(mockDb.where).toHaveBeenCalled();
expect(result).toEqual([mockGroup]);
});
});
describe('findById', () => {
it('should return a group by id', async () => {
const id = 'group1';
mockDb.select.mockImplementationOnce(() => mockDbOperations);
mockDbOperations.from.mockImplementationOnce(() => mockDbOperations);
mockDbOperations.where.mockImplementationOnce(() => [mockGroup]);
const result = await service.findById(id);
expect(mockDb.select).toHaveBeenCalled();
expect(mockDb.from).toHaveBeenCalled();
expect(mockDb.where).toHaveBeenCalled();
expect(result).toEqual(mockGroup);
});
it('should throw NotFoundException if group not found', async () => {
const id = 'nonexistent';
mockDb.select.mockImplementationOnce(() => mockDbOperations);
mockDbOperations.from.mockImplementationOnce(() => mockDbOperations);
mockDbOperations.where.mockImplementationOnce(() => [undefined]);
await expect(service.findById(id)).rejects.toThrow(NotFoundException);
});
});
describe('update', () => {
it('should update a group', async () => {
const id = 'group1';
const updateGroupDto = {
name: 'Updated Group',
};
const result = await service.update(id, updateGroupDto);
expect(mockDb.update).toHaveBeenCalled();
expect(mockDb.set).toHaveBeenCalled();
expect(mockDb.where).toHaveBeenCalled();
expect(result).toEqual(mockGroup);
});
it('should throw NotFoundException if group not found', async () => {
const id = 'nonexistent';
const updateGroupDto = {
name: 'Updated Group',
};
mockDb.update.mockImplementationOnce(() => mockDbOperations);
mockDbOperations.set.mockImplementationOnce(() => mockDbOperations);
mockDbOperations.where.mockImplementationOnce(() => mockDbOperations);
mockDbOperations.returning.mockImplementationOnce(() => [undefined]);
await expect(service.update(id, updateGroupDto)).rejects.toThrow(NotFoundException);
});
});
describe('remove', () => {
it('should remove a group', async () => {
const id = 'group1';
const result = await service.remove(id);
expect(mockDb.delete).toHaveBeenCalled();
expect(mockDb.where).toHaveBeenCalled();
expect(result).toEqual(mockGroup);
});
it('should throw NotFoundException if group not found', async () => {
const id = 'nonexistent';
mockDb.delete.mockImplementationOnce(() => mockDbOperations);
mockDbOperations.where.mockImplementationOnce(() => mockDbOperations);
mockDbOperations.returning.mockImplementationOnce(() => [undefined]);
await expect(service.remove(id)).rejects.toThrow(NotFoundException);
});
});
describe('addPersonToGroup', () => {
it('should add a person to a group', async () => {
const groupId = 'group1';
const personId = 'person1';
// Mock findById to return the group
jest.spyOn(service, 'findById').mockResolvedValueOnce(mockGroup);
// Reset and setup mocks for this test
jest.clearAllMocks();
// Mock person lookup
mockDb.select.mockImplementationOnce(() => mockDbOperations);
mockDbOperations.from.mockImplementationOnce(() => mockDbOperations);
mockDbOperations.where.mockImplementationOnce(() => [[mockPerson]]);
// Mock relation lookup
mockDb.select.mockImplementationOnce(() => mockDbOperations);
mockDbOperations.from.mockImplementationOnce(() => mockDbOperations);
mockDbOperations.where.mockImplementationOnce(() => mockDbOperations);
mockDbOperations.where.mockImplementationOnce(() => [undefined]);
// Mock relation creation
mockDb.insert.mockImplementationOnce(() => mockDbOperations);
mockDbOperations.values.mockImplementationOnce(() => mockDbOperations);
mockDbOperations.returning.mockImplementationOnce(() => [mockPersonToGroup]);
const result = await service.addPersonToGroup(groupId, personId);
expect(service.findById).toHaveBeenCalledWith(groupId);
expect(mockDb.select).toHaveBeenCalled();
expect(mockDb.from).toHaveBeenCalled();
expect(mockDb.insert).toHaveBeenCalled();
expect(mockDb.values).toHaveBeenCalledWith({
personId,
groupId,
});
expect(result).toEqual(mockPersonToGroup);
});
it('should throw NotFoundException if person not found', async () => {
const groupId = 'group1';
const personId = 'nonexistent';
// Mock findById to return the group
jest.spyOn(service, 'findById').mockResolvedValueOnce(mockGroup);
// Reset and setup mocks for this test
jest.clearAllMocks();
// Mock person lookup to return no person
mockDb.select.mockImplementationOnce(() => mockDbOperations);
mockDbOperations.from.mockImplementationOnce(() => mockDbOperations);
mockDbOperations.where.mockImplementationOnce(() => [undefined]);
await expect(service.addPersonToGroup(groupId, personId)).rejects.toThrow(NotFoundException);
});
});
describe('removePersonFromGroup', () => {
it('should remove a person from a group', async () => {
const groupId = 'group1';
const personId = 'person1';
// Reset and setup mocks for this test
jest.clearAllMocks();
mockDb.delete.mockImplementationOnce(() => mockDbOperations);
mockDbOperations.where.mockImplementationOnce(() => mockDbOperations);
mockDbOperations.where.mockImplementationOnce(() => mockDbOperations);
mockDbOperations.returning.mockImplementationOnce(() => [mockPersonToGroup]);
const result = await service.removePersonFromGroup(groupId, personId);
expect(mockDb.delete).toHaveBeenCalled();
expect(mockDb.where).toHaveBeenCalled();
expect(result).toEqual(mockPersonToGroup);
});
it('should throw NotFoundException if relation not found', async () => {
const groupId = 'group1';
const personId = 'nonexistent';
// Reset and setup mocks for this test
jest.clearAllMocks();
mockDb.delete.mockImplementationOnce(() => mockDbOperations);
mockDbOperations.where.mockImplementationOnce(() => mockDbOperations);
mockDbOperations.where.mockImplementationOnce(() => mockDbOperations);
mockDbOperations.returning.mockImplementationOnce(() => [undefined]);
await expect(service.removePersonFromGroup(groupId, personId)).rejects.toThrow(NotFoundException);
});
});
describe('getPersonsInGroup', () => {
it('should get all persons in a group', async () => {
const groupId = 'group1';
const mockPersons = [{ person: mockPerson }];
// Mock findById to return the group
jest.spyOn(service, 'findById').mockResolvedValueOnce(mockGroup);
mockDb.select.mockImplementationOnce(() => mockDbOperations);
mockDbOperations.from.mockImplementationOnce(() => mockDbOperations);
mockDbOperations.innerJoin.mockImplementationOnce(() => mockDbOperations);
mockDbOperations.where.mockImplementationOnce(() => mockPersons);
const result = await service.getPersonsInGroup(groupId);
expect(service.findById).toHaveBeenCalledWith(groupId);
expect(mockDb.select).toHaveBeenCalled();
expect(mockDb.from).toHaveBeenCalled();
expect(mockDb.innerJoin).toHaveBeenCalled();
expect(mockDb.where).toHaveBeenCalled();
expect(result).toEqual(mockPersons);
});
});
});