Skip to content

Commit d1b42c4

Browse files
committed
fix sonar qube findings and add tests
1 parent 8acf8db commit d1b42c4

File tree

6 files changed

+290
-24
lines changed

6 files changed

+290
-24
lines changed

src/users/db/dbusers.service.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,19 @@
1-
import { HttpException, HttpStatus } from '@nestjs/common';
1+
import { HttpException, HttpStatus, Logger } from '@nestjs/common';
22
import { CreateUserDto } from '../dto/user-create.dto';
33
import { UserLoginResponseDto } from '../dto/user-login-response.dto';
44
import { PrismaService } from '../../prisma/prisma.service';
55
import { User } from '@prisma/client';
66
import { UpdateUserDto } from '../dto/user-update.dto';
77
import { AuthService } from '../../auth/auth.service';
88
import { UserLoginRequestDto } from '../dto/user-login-request.dto';
9-
import { Logger } from '@nestjs/common';
109
import { Users } from '../users.interface';
1110

1211
export class DbUsersService implements Users {
1312
private readonly logger: Logger = new Logger(DbUsersService.name);
1413

1514
constructor(
16-
private prismaService: PrismaService,
17-
private authService: AuthService
15+
private readonly prismaService: PrismaService,
16+
private readonly authService: AuthService
1817
) {}
1918

2019
async create(createUserDto: CreateUserDto): Promise<UserLoginResponseDto> {
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
import { Test, TestingModule } from '@nestjs/testing';
2+
import { LdapUsersService } from './ldapusers.service';
3+
import { AuthService } from '../../auth/auth.service';
4+
import { PrismaService } from '../../prisma/prisma.service';
5+
import { ConfigService } from '@nestjs/config';
6+
import { Role, User } from '@prisma/client';
7+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
8+
import { Entry as LdapEntry, Client as LdapClient, SearchOptions, SearchResult } from 'ldapts';
9+
10+
jest.mock('ldapts', () => {
11+
const mockLdapClient = {
12+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
13+
search: jest.fn((_searchDN, searchOptions: SearchOptions): Promise<SearchResult> => {
14+
if (searchOptions.filter.toString().includes('[email protected]')) {
15+
return Promise.resolve({ searchEntries: [{dn:'dn',LDAP_ATTRIBUTE_MAIL:'[email protected]',LDAP_ATTRIBUTE_FIRST_NAME:'first',LDAP_ATTRIBUTE_LAST_NAME:'last'}], searchReferences: [] });
16+
} else {
17+
return Promise.resolve({ searchEntries: [], searchReferences: [] });
18+
}
19+
}),
20+
bind: jest.fn(),
21+
unbind: jest.fn(),
22+
};
23+
24+
return {
25+
Client: jest.fn(() => mockLdapClient),
26+
};
27+
});
28+
29+
const user: User = {
30+
id: '1',
31+
32+
firstName: 'John',
33+
lastName: 'Doe',
34+
apiKey: 'generatedApiKey',
35+
password: 'encryptedPassword',
36+
isActive: true,
37+
role: Role.editor,
38+
updatedAt: new Date(),
39+
createdAt: new Date(),
40+
};
41+
42+
describe('LdapUsersService match from ldap', () => {
43+
let service: LdapUsersService;
44+
let prismaService: PrismaService;
45+
let configService: ConfigService;
46+
let authService: AuthService;
47+
48+
beforeEach(async () => {
49+
const module: TestingModule = await Test.createTestingModule({
50+
providers: [
51+
{
52+
provide: ConfigService,
53+
useValue: {
54+
get: jest.fn().mockReturnValue('true'),
55+
getOrThrow: jest.fn((string) => (string === 'LDAP_USERS_SEARCH_FILTER' ? '(&(mail={{email}}))' : string)),
56+
},
57+
},
58+
{
59+
provide: PrismaService,
60+
useValue: {
61+
user: {
62+
findMany: jest.fn().mockResolvedValueOnce([]),
63+
findUnique: jest.fn(),
64+
create: jest.fn(),
65+
update: jest.fn(),
66+
},
67+
},
68+
},
69+
{
70+
provide: AuthService,
71+
useValue: {
72+
generateApiKey: jest.fn(() => 'generatedApiKey'),
73+
encryptPassword: jest.fn(() => 'encryptedPassword'),
74+
signToken: jest.fn(() => 'token'),
75+
},
76+
},
77+
],
78+
}).compile();
79+
configService = module.get<ConfigService>(ConfigService);
80+
prismaService = module.get<PrismaService>(PrismaService);
81+
authService = module.get<AuthService>(AuthService);
82+
service = new LdapUsersService(configService, prismaService, authService);
83+
});
84+
85+
it('should be defined', () => {
86+
expect(service).toBeDefined();
87+
});
88+
89+
it('should create new user on login', async () => {
90+
jest.spyOn(prismaService.user, 'findUnique').mockResolvedValue(undefined);
91+
const prismaUserCreateMock = jest.spyOn(prismaService.user, 'create').mockResolvedValue(user);
92+
await service.login({ email: '[email protected]', password: 'password' });
93+
expect(prismaUserCreateMock).toBeCalled();
94+
});
95+
96+
it('should login with user already in db', async () => {
97+
jest.spyOn(prismaService.user, 'findUnique').mockResolvedValue(user);
98+
const prismaUserCreateMock = jest.spyOn(prismaService.user, 'create');
99+
await service.login({ email: '[email protected]', password: 'password' });
100+
expect(prismaUserCreateMock).not.toBeCalled();
101+
});
102+
});
103+
104+
describe('LdapUsersService with no results from ldap', () => {
105+
let service: LdapUsersService;
106+
let prismaService: PrismaService;
107+
let configService: ConfigService;
108+
let authService: AuthService;
109+
110+
beforeEach(async () => {
111+
const module: TestingModule = await Test.createTestingModule({
112+
providers: [
113+
{
114+
provide: ConfigService,
115+
useValue: {
116+
get: jest.fn().mockReturnValue('true'),
117+
getOrThrow: jest.fn().mockReturnValue('string'),
118+
},
119+
},
120+
{
121+
provide: PrismaService,
122+
useValue: {
123+
user: {
124+
findMany: jest.fn().mockResolvedValueOnce([]),
125+
create: jest.fn(),
126+
update: jest.fn(),
127+
},
128+
},
129+
},
130+
{
131+
provide: AuthService,
132+
useValue: {
133+
generateApiKey: jest.fn(() => 'generatedApiKey'),
134+
encryptPassword: jest.fn(() => 'encryptedPassword'),
135+
},
136+
},
137+
],
138+
}).compile();
139+
configService = module.get<ConfigService>(ConfigService);
140+
prismaService = module.get<PrismaService>(PrismaService);
141+
authService = module.get<AuthService>(AuthService);
142+
service = new LdapUsersService(configService, prismaService, authService);
143+
});
144+
145+
it('should be defined', () => {
146+
expect(service).toBeDefined();
147+
});
148+
149+
it('should never change a password', async () => {
150+
const prismaUserCreateMock = jest.spyOn(prismaService.user, 'create');
151+
const prismaUserUpdateMock = jest.spyOn(prismaService.user, 'update');
152+
const result = await service.changePassword(user, 'newPassword');
153+
expect(prismaUserCreateMock).not.toBeCalled();
154+
expect(prismaUserUpdateMock).not.toBeCalled();
155+
expect(result).toBe(true);
156+
});
157+
158+
it('should not login when ldap search results are empty', async () => {
159+
expect.assertions(1);
160+
try {
161+
await service.login({ email: 'testÜ*()\\\[email protected]', password: 'password' });
162+
} catch (e) {
163+
expect(e.message).toBe('Invalid email or password.');
164+
}
165+
});
166+
});

src/users/ldap/ldapusers.service.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1-
import { HttpException, HttpStatus } from '@nestjs/common';
1+
import { HttpException, HttpStatus, Logger } from '@nestjs/common';
22
import { CreateUserDto } from '../dto/user-create.dto';
33
import { UserLoginResponseDto } from '../dto/user-login-response.dto';
44
import { PrismaService } from '../../prisma/prisma.service';
55
import { Role, User } from '@prisma/client';
66
import { UpdateUserDto } from '../dto/user-update.dto';
77
import { AuthService } from '../../auth/auth.service';
88
import { UserLoginRequestDto } from '../dto/user-login-request.dto';
9-
import { Logger } from '@nestjs/common';
109
import { Entry as LdapEntry, Client as LdapClient } from 'ldapts';
1110
import { Users } from '../users.interface';
1211
import { ConfigService } from '@nestjs/config';

src/users/users.factory.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ export class UsersFactoryService {
1111
private readonly logger: Logger = new Logger(UsersFactoryService.name);
1212

1313
constructor(
14-
private configService: ConfigService,
15-
private prismaService: PrismaService,
16-
private authService: AuthService
14+
private readonly configService: ConfigService,
15+
private readonly prismaService: PrismaService,
16+
private readonly authService: AuthService
1717
) {}
1818

1919
getUsersService(): Users {

src/users/users.service.spec.ts

Lines changed: 116 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,28 @@ import { AuthService } from '../auth/auth.service';
44
import { PrismaService } from '../prisma/prisma.service';
55
import { ConfigService } from '@nestjs/config';
66
import { UsersFactoryService } from './users.factory';
7-
import { Role } from '@prisma/client';
7+
import { Role, User } from '@prisma/client';
88
import { UserLoginResponseDto } from './dto/user-login-response.dto';
9+
import { AssignRoleDto } from './dto/assign-role.dto';
10+
import { randomUUID } from 'crypto';
11+
import { UserDto } from './dto/user.dto';
12+
import { identity } from 'lodash';
13+
import { UpdateUserDto } from './dto/user-update.dto';
914

10-
describe('UsersService', () => {
15+
const user: User = {
16+
id: '1',
17+
18+
firstName: 'John',
19+
lastName: 'Doe',
20+
apiKey: 'generatedApiKey',
21+
password: 'encryptedPassword',
22+
isActive: true,
23+
role: Role.editor,
24+
updatedAt: new Date(),
25+
createdAt: new Date(),
26+
};
27+
28+
describe('UsersService with DbUserService implementation', () => {
1129
let service: UsersService;
1230
let prismaService: PrismaService;
1331

@@ -28,6 +46,9 @@ describe('UsersService', () => {
2846
user: {
2947
findMany: jest.fn().mockResolvedValueOnce([]),
3048
create: jest.fn(),
49+
update: jest.fn(),
50+
delete: jest.fn(),
51+
findUnique: jest.fn().mockResolvedValueOnce(user),
3152
},
3253
},
3354
},
@@ -36,6 +57,8 @@ describe('UsersService', () => {
3657
useValue: {
3758
generateApiKey: jest.fn(() => 'generatedApiKey'),
3859
encryptPassword: jest.fn(() => 'encryptedPassword'),
60+
compare: jest.fn(() => true),
61+
signToken: jest.fn(() => 'token'),
3962
},
4063
},
4164
],
@@ -49,6 +72,19 @@ describe('UsersService', () => {
4972
expect(service).toBeDefined();
5073
});
5174

75+
it('should generate new api key', async () => {
76+
// Arrange
77+
const prismaUserUpdateMock = jest.spyOn(prismaService.user, 'update');
78+
prismaUserUpdateMock.mockResolvedValueOnce(user);
79+
80+
// Act
81+
const result = await service.generateNewApiKey(user);
82+
83+
// Assert
84+
expect(prismaUserUpdateMock).toHaveBeenCalled();
85+
expect(typeof result).toBe('string');
86+
});
87+
5288
it('should create a new user', async () => {
5389
// Arrange
5490
const createUserDto = {
@@ -59,18 +95,7 @@ describe('UsersService', () => {
5995
};
6096

6197
const prismaUserCreateMock = jest.spyOn(prismaService.user, 'create');
62-
prismaUserCreateMock.mockResolvedValueOnce({
63-
id: '1',
64-
65-
firstName: 'John',
66-
lastName: 'Doe',
67-
apiKey: 'generatedApiKey',
68-
password: 'encryptedPassword',
69-
isActive: true,
70-
role: Role.editor,
71-
updatedAt: new Date(),
72-
createdAt: new Date(),
73-
});
98+
prismaUserCreateMock.mockResolvedValueOnce(user);
7499

75100
// Act
76101
const result = await service.create(createUserDto);
@@ -87,4 +112,81 @@ describe('UsersService', () => {
87112
});
88113
expect(result).toEqual(expect.any(UserLoginResponseDto));
89114
});
115+
116+
it('should update a user', async () => {
117+
// Arrange
118+
const updateUserDto: UpdateUserDto = {
119+
120+
firstName: 'John',
121+
lastName: 'Doe',
122+
};
123+
124+
const prismaUserUpdateMock = jest.spyOn(prismaService.user, 'update');
125+
prismaUserUpdateMock.mockResolvedValueOnce(user);
126+
127+
// Act
128+
const result = await service.update(user.id, updateUserDto);
129+
130+
// Assert
131+
expect(prismaUserUpdateMock).toHaveBeenCalled();
132+
expect(result).toEqual(expect.any(UserLoginResponseDto));
133+
});
134+
135+
it('should change password', async () => {
136+
// Arrange
137+
const prismaUserUpdateMock = jest.spyOn(prismaService.user, 'update');
138+
prismaUserUpdateMock.mockResolvedValueOnce(user);
139+
140+
// Act
141+
const result = await service.changePassword(user, 'newPassword');
142+
143+
// Assert
144+
expect(result).toEqual(true);
145+
});
146+
147+
it('should login existing user', async () => {
148+
// Act
149+
const result = await service.login({email: '[email protected]', password: 'doesntmatter'});
150+
151+
// Assert
152+
expect(result).toEqual(expect.any(UserLoginResponseDto));
153+
});
154+
155+
it('should delete existing user', async () => {
156+
// Arrange
157+
const prismaUserDeleteMock = jest.spyOn(prismaService.user, 'delete');
158+
prismaUserDeleteMock.mockResolvedValueOnce(user);
159+
160+
// Act
161+
await service.delete(user.id);
162+
163+
// Assert
164+
expect(prismaUserDeleteMock).toHaveBeenCalled();
165+
});
166+
167+
it('should get existing user', async () => {
168+
// Act
169+
const result = await service.get(user.id);
170+
171+
// Assert
172+
expect(result).toBeInstanceOf(UserDto);
173+
});
174+
175+
it('should assign role to user', async () => {
176+
// Arrange
177+
const assignRoleDto: AssignRoleDto = {
178+
id: randomUUID(),
179+
role: Role.editor,
180+
};
181+
182+
const prismaUserCreateMock = jest.spyOn(prismaService.user, 'update');
183+
prismaUserCreateMock.mockResolvedValueOnce(user);
184+
185+
// Act
186+
const result = await service.assignRole(assignRoleDto);
187+
188+
// Assert
189+
expect(prismaUserCreateMock).toHaveBeenCalled();
190+
expect(result).toBeInstanceOf(UserDto);
191+
});
90192
});

src/users/users.service.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export class UsersService {
1818
private readonly usersService: Users;
1919

2020
constructor(
21-
private usersFactoryService: UsersFactoryService,
21+
private readonly usersFactoryService: UsersFactoryService,
2222
private prismaService: PrismaService,
2323
private authService: AuthService
2424
) {

0 commit comments

Comments
 (0)