Skip to content

Commit 4723df8

Browse files
author
hersveit
committed
auth service test
1 parent 78f274a commit 4723df8

File tree

5 files changed

+153
-34
lines changed

5 files changed

+153
-34
lines changed
Lines changed: 14 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,17 @@
11
import { ConfigModel } from '../config/config.model';
22

3-
export class ConfigServiceMock implements ConfigModel {
4-
DB_NAME: string;
5-
DB_HOST: string;
6-
DB_USERNAME: string;
7-
DB_PASSWORD: string;
8-
DB_PORT: number;
9-
PORT: number;
10-
JWT_SECRET: string;
11-
JWT_EXPIRES_IN: string;
12-
JWT_REFRESH_SECRET: string;
13-
JWT_REFRESH_EXPIRES_IN: string;
14-
DOMAIN: string;
15-
16-
constructor() {
17-
this.DB_NAME = 'db';
18-
this.DB_HOST = 'localhost';
19-
this.DB_USERNAME = 'admin';
20-
this.DB_PASSWORD = 'pass';
21-
this.DB_PORT = 5432;
22-
this.PORT = 3000;
23-
this.JWT_SECRET = 'jwtSecret';
24-
this.JWT_EXPIRES_IN = '10min';
25-
this.JWT_REFRESH_SECRET = 'jwtRefreshSecret';
26-
this.JWT_REFRESH_EXPIRES_IN = '2days';
27-
this.DOMAIN = `http://localhost:${this.PORT}`;
28-
}
3+
export class ConfigServiceMock {
4+
public env: ConfigModel = {
5+
DB_NAME: 'db',
6+
DB_HOST: 'localhost',
7+
DB_USERNAME: 'admin',
8+
DB_PASSWORD: 'pass',
9+
DB_PORT: 5432,
10+
PORT: 3000,
11+
JWT_SECRET: 'jwtSecret',
12+
JWT_EXPIRES_IN: '10min',
13+
JWT_REFRESH_SECRET: 'jwtRefreshSecret',
14+
JWT_REFRESH_EXPIRES_IN: '2days',
15+
DOMAIN: 'http://localhost:3000',
16+
};
2917
}
Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
import { DeepMocked, createMock } from '@golevelup/ts-jest';
2-
import { RepositoryService } from '@lib/repository';
1+
import { createMock } from '@golevelup/ts-jest';
32
import { UserEntity } from '@lib/repository/entities/user.entity';
43
import { Repository } from 'typeorm';
54

65
export const getRepositoryServiceMock = () => {
76
return {
87
user: createMock<Repository<UserEntity>>(),
9-
} as unknown as DeepMocked<RepositoryService>;
8+
};
109
};

apps/api/src/modules/auth/__tests__/auth.service.spec.ts

Lines changed: 125 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,25 @@
1-
import { createMock } from '@golevelup/ts-jest';
2-
import { CoreConfigService } from '@lib/core';
1+
import { DeepMocked, createMock } from '@golevelup/ts-jest';
2+
import { CoreConfigService, UserNotFound, isError } from '@lib/core';
33
import { RepositoryService } from '@lib/repository';
44
import { JwtService } from '@nestjs/jwt';
55
import { Test } from '@nestjs/testing';
66
import { ConfigServiceMock } from '../../../__mocks__/ConfigServiceMock';
77
import { getRepositoryServiceMock } from '../../../__mocks__/RepositoryServiceMock';
88
import { getUserStub } from '../../../__mocks__/stubs/user.stub';
99
import { ConfigModel } from '../../../config/config.model';
10+
import { UserAlreadyExists } from '../../user/common/user.errors';
1011
import { UserService } from '../../user/user.service';
1112
import { AuthService } from '../auth.service';
13+
import { CannotFindEmailConfirm, EmailAlreadyConfirmed, EmailNotConfirmed } from '../common/auth.errors';
14+
import { GetTokenResult } from '../common/auth.model';
1215

1316
describe('AuthService', () => {
1417
let rep: ReturnType<typeof getRepositoryServiceMock>;
1518
let user: ReturnType<typeof getUserStub>;
1619
let authService: AuthService;
1720
let configService: CoreConfigService<ConfigModel>;
18-
let userService: UserService;
21+
let userService: DeepMocked<UserService>;
22+
let jwt: JwtService;
1923

2024
beforeEach(async () => {
2125
const module = await Test.createTestingModule({
@@ -38,6 +42,7 @@ describe('AuthService', () => {
3842
configService = module.get(CoreConfigService);
3943
rep = module.get(RepositoryService);
4044
userService = module.get(UserService);
45+
jwt = module.get(JwtService);
4146

4247
user = getUserStub();
4348
});
@@ -49,4 +54,121 @@ describe('AuthService', () => {
4954
expect(userService).toBeDefined();
5055
expect(user).toBeDefined();
5156
});
57+
58+
describe('signUp', () => {
59+
it('should correct sign-up', async () => {
60+
userService.createUser.mockResolvedValue(user);
61+
62+
// TODO test sendConfirmEmail
63+
64+
const createResult = await authService.signUp({
65+
email: user.email,
66+
password: 'password',
67+
});
68+
69+
expect(isError(createResult)).toBeFalsy();
70+
expect(createResult).toEqual(undefined);
71+
});
72+
73+
it('should return error if user already exist', async () => {
74+
userService.createUser.mockResolvedValue(new UserAlreadyExists(user.email));
75+
76+
const createResult = await authService.signUp({
77+
email: user.email,
78+
password: 'password',
79+
});
80+
81+
expect(isError(createResult)).toBeTruthy();
82+
expect(createResult).toBeInstanceOf(UserAlreadyExists);
83+
});
84+
});
85+
86+
describe('sendConfirmEmail', () => {
87+
it('should return error if user email is already confirmed', async () => {
88+
const confirmResult = await authService.sendConfirmEmail({ ...user, emailConfirmed: true });
89+
90+
expect(isError(confirmResult)).toBeTruthy();
91+
expect(confirmResult).toBeInstanceOf(EmailAlreadyConfirmed);
92+
});
93+
});
94+
95+
describe('signIn', () => {
96+
it('should success sign-in with correct login and password', async () => {
97+
rep.user.findOne.mockResolvedValue({ ...user, id: 99, emailConfirmed: true });
98+
99+
const signInResult = await authService.signIn({
100+
email: user.email,
101+
password: 'password',
102+
});
103+
104+
expect(isError(signInResult)).toBeFalsy();
105+
expect(rep.user.save).toHaveBeenCalledTimes(1);
106+
expect(rep.user.save).toHaveBeenCalledWith({ id: 99, refreshTokenHash: expect.any(String) });
107+
expect(jwt.decode((signInResult as GetTokenResult).token)).toEqual({
108+
id: 99,
109+
date: expect.any(Number),
110+
exp: expect.any(Number),
111+
iat: expect.any(Number),
112+
});
113+
});
114+
115+
it('should return error with incorrect login or password', async () => {
116+
rep.user.findOne.mockResolvedValue(null);
117+
118+
const signInResult = await authService.signIn({
119+
email: '',
120+
password: '',
121+
});
122+
123+
expect(isError(signInResult)).toBeTruthy();
124+
expect(signInResult).toBeInstanceOf(UserNotFound);
125+
});
126+
127+
it('should return error if email not confirmed', async () => {
128+
rep.user.findOne.mockResolvedValue({ ...user, emailConfirmed: false });
129+
130+
const signInResult = await authService.signIn({
131+
email: user.email,
132+
password: 'password',
133+
});
134+
135+
expect(isError(signInResult)).toBeTruthy();
136+
expect(signInResult).toBeInstanceOf(EmailNotConfirmed);
137+
});
138+
});
139+
140+
describe('confirmEmail', () => {
141+
it('should confirm email', async () => {
142+
rep.user.findOne.mockResolvedValue({ ...user, id: 77, emailConfirmed: false });
143+
144+
const confirmResult = await authService.confirmEmail('token');
145+
146+
expect(rep.user.findOne).toHaveBeenCalledWith({
147+
where: { emailConfirmed: false, emailConfirmToken: 'token' },
148+
});
149+
expect(isError(confirmResult)).toBeFalsy();
150+
expect(rep.user.save).toHaveBeenCalledTimes(1);
151+
expect(rep.user.save).toHaveBeenCalledWith({
152+
...user,
153+
id: 77,
154+
emailConfirmed: true,
155+
refreshTokenHash: expect.any(String),
156+
});
157+
expect(jwt.decode((confirmResult as GetTokenResult).token)).toEqual({
158+
id: 77,
159+
date: expect.any(Number),
160+
exp: expect.any(Number),
161+
iat: expect.any(Number),
162+
});
163+
});
164+
165+
it('should return error if email already confirmed', async () => {
166+
rep.user.findOne.mockResolvedValue(null);
167+
168+
const confirmResult = await authService.confirmEmail('token');
169+
170+
expect(isError(confirmResult)).toBeTruthy();
171+
expect(confirmResult).toBeInstanceOf(CannotFindEmailConfirm);
172+
});
173+
});
52174
});

apps/api/src/modules/auth/auth.service.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { v4 } from 'uuid';
88
import { ConfigModel } from '../../config/config.model';
99
import { UserService } from '../user/user.service';
1010
import { SignInBody, SignUpBody } from './common/auth.dto';
11-
import { CannotFindEmailConfirm, EmailAlreadyConfirmed } from './common/auth.errors';
11+
import { CannotFindEmailConfirm, EmailAlreadyConfirmed, EmailNotConfirmed } from './common/auth.errors';
1212
import { GetTokenResult } from './common/auth.model';
1313

1414
@Injectable()
@@ -32,14 +32,18 @@ export class AuthService {
3232
}
3333
}
3434

35-
async signIn(body: SignInBody): Promise<GetTokenResult | UserNotFound> {
35+
async signIn(body: SignInBody): Promise<GetTokenResult | UserNotFound | EmailNotConfirmed> {
3636
const hash = SHA256(body.password).toString();
3737
const user = await this.rep.user.findOne({ where: { email: body.email, hash } });
3838

3939
if (!user) {
4040
return new UserNotFound({ email: body.email });
4141
}
4242

43+
if (!user.emailConfirmed) {
44+
return new EmailNotConfirmed(body.email);
45+
}
46+
4347
const { accessToken, refreshToken, refreshTokenHash } = this.generateToken(user.id);
4448
await this.rep.user.save({ id: user.id, refreshTokenHash });
4549

apps/api/src/modules/auth/common/auth.errors.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@ export class EmailAlreadyConfirmed extends BaseError<'emailAlreadyConfirmed'> {
66
}
77
}
88

9+
export class EmailNotConfirmed extends BaseError<'emailNotConfirmed'> {
10+
constructor(email: string) {
11+
super('emailNotConfirmed', { message: objToString({ email }) });
12+
}
13+
}
14+
915
export class CannotFindEmailConfirm extends BaseError<'cannotFindEmailConfirm'> {
1016
constructor() {
1117
super('cannotFindEmailConfirm');

0 commit comments

Comments
 (0)