Skip to content

Commit db2bc21

Browse files
authored
[Job Launcher] Added restore password e2e workflow (#1828)
* Added restore password e2e workflow * Added e2e file
1 parent af7c34d commit db2bc21

File tree

2 files changed

+177
-2
lines changed

2 files changed

+177
-2
lines changed

packages/apps/job-launcher/server/src/modules/auth/auth.dto.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ export class ResendEmailVerificationDto {
4646

4747
export class RestorePasswordDto extends ValidatePasswordDto {
4848
@ApiProperty()
49-
@IsString()
49+
@IsUUID()
5050
public token: string;
5151

5252
@ApiProperty({ name: 'h_captcha_token' })
@@ -56,7 +56,7 @@ export class RestorePasswordDto extends ValidatePasswordDto {
5656

5757
export class VerifyEmailDto {
5858
@ApiProperty()
59-
@IsString()
59+
@IsUUID()
6060
public token: string;
6161
}
6262

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
import request from 'supertest';
2+
import * as crypto from 'crypto';
3+
import { HttpStatus, INestApplication } from '@nestjs/common';
4+
import { Test, TestingModule } from '@nestjs/testing';
5+
import { AppModule } from '../../src/app.module';
6+
import { UserRepository } from '../../src/modules/user/user.repository';
7+
import { TokenRepository } from '../../src/modules/auth/token.repository';
8+
import { TokenEntity, TokenType } from '../../src/modules/auth/token.entity';
9+
import { UserStatus } from '../../src/common/enums/user';
10+
import { UserService } from '../../src/modules/user/user.service';
11+
import { UserEntity } from '../../src/modules/user/user.entity';
12+
import { ErrorAuth } from '../../src/common/constants/errors';
13+
import setupE2eEnvironment from './env-setup';
14+
15+
describe('Restore password E2E workflow', () => {
16+
let app: INestApplication;
17+
let userRepository: UserRepository;
18+
let tokenRepository: TokenRepository;
19+
let userService: UserService;
20+
21+
let userEntity: UserEntity;
22+
let tokenEntity: TokenEntity;
23+
24+
const email = `${crypto.randomBytes(16).toString('hex')}@hmt.ai`;
25+
26+
beforeAll(async () => {
27+
setupE2eEnvironment();
28+
const moduleFixture: TestingModule = await Test.createTestingModule({
29+
imports: [AppModule],
30+
}).compile();
31+
32+
app = moduleFixture.createNestApplication();
33+
await app.init();
34+
35+
userRepository = moduleFixture.get<UserRepository>(UserRepository);
36+
tokenRepository = moduleFixture.get<TokenRepository>(TokenRepository);
37+
userService = moduleFixture.get<UserService>(UserService);
38+
39+
userEntity = await userService.create({
40+
email,
41+
password: 'Password1!',
42+
hCaptchaToken: 'string',
43+
});
44+
45+
userEntity.status = UserStatus.ACTIVE;
46+
await userEntity.save();
47+
48+
tokenEntity = new TokenEntity();
49+
tokenEntity.type = TokenType.EMAIL;
50+
tokenEntity.user = userEntity;
51+
const date = new Date();
52+
tokenEntity.expiresAt = new Date(date.getTime() + 100000);
53+
54+
await tokenRepository.createUnique(tokenEntity);
55+
});
56+
57+
afterAll(async () => {
58+
await app.close();
59+
});
60+
61+
it('should restore password successfully', async () => {
62+
// Call forgot password endpoint
63+
await request(app.getHttpServer())
64+
.post('/auth/forgot-password')
65+
.send({ email })
66+
.expect(HttpStatus.NO_CONTENT);
67+
68+
// Ensure old token was deleted and a new token was created
69+
const oldTokenExists = await tokenRepository.findOneByUuidAndType(
70+
tokenEntity.uuid,
71+
TokenType.PASSWORD,
72+
);
73+
expect(oldTokenExists).toBeNull();
74+
75+
const newToken = await tokenRepository.findOneByUserIdAndType(
76+
userEntity.id,
77+
TokenType.PASSWORD,
78+
);
79+
expect(newToken).toBeDefined();
80+
81+
// Call restore password endpoint
82+
const restorePasswordData = {
83+
password: 'NewPassword1!',
84+
token: newToken!.uuid,
85+
hCaptchaToken: 'string',
86+
};
87+
await request(app.getHttpServer())
88+
.post('/auth/restore-password')
89+
.send(restorePasswordData)
90+
.expect(HttpStatus.NO_CONTENT);
91+
92+
// Ensure password was updated
93+
const updatedUser = await userRepository.findById(userEntity!.id);
94+
expect(updatedUser!.password).not.toEqual(userEntity.password);
95+
96+
userEntity = updatedUser!;
97+
98+
// Ensure new token was deleted
99+
const deletedToken = await tokenRepository.findOneByUuidAndType(
100+
newToken!.uuid,
101+
TokenType.PASSWORD,
102+
);
103+
expect(deletedToken).toBeNull();
104+
});
105+
106+
it('should handle invalid token error', async () => {
107+
// Call forgot password endpoint
108+
await request(app.getHttpServer())
109+
.post('/auth/forgot-password')
110+
.send({ email })
111+
.expect(HttpStatus.NO_CONTENT);
112+
113+
// Delete the new token
114+
await tokenRepository.delete(tokenEntity.id);
115+
116+
// Call restore password endpoint with invalid token
117+
const invalidToken = '00000000-0000-0000-0000-000000000000';
118+
const restorePasswordData = {
119+
password: 'NewPassword2!',
120+
token: invalidToken,
121+
hCaptchaToken: 'string',
122+
};
123+
124+
const invalidRestorePasswordResponse = await request(app.getHttpServer())
125+
.post('/auth/restore-password')
126+
.send(restorePasswordData)
127+
.expect(HttpStatus.FORBIDDEN);
128+
129+
expect(invalidRestorePasswordResponse.status).toBe(HttpStatus.FORBIDDEN);
130+
expect(invalidRestorePasswordResponse.body.message).toBe(
131+
ErrorAuth.InvalidToken,
132+
);
133+
134+
// Ensure password was not updated
135+
const updatedUser = await userRepository.findById(userEntity.id);
136+
expect(updatedUser!.password).toEqual(userEntity.password);
137+
});
138+
139+
it('should handle token expired error', async () => {
140+
// Call forgot password endpoint
141+
await request(app.getHttpServer())
142+
.post('/auth/forgot-password')
143+
.send({ email })
144+
.expect(HttpStatus.NO_CONTENT);
145+
146+
// Expire the new token
147+
const date = new Date();
148+
const expiredToken = await tokenRepository.findOneByUserIdAndType(
149+
userEntity.id,
150+
TokenType.PASSWORD,
151+
);
152+
expiredToken!.expiresAt = new Date(date.getTime() - 100000); // Set token expiration in the past
153+
await expiredToken!.save();
154+
155+
// Call restore password endpoint with expired token
156+
const expiredTokenValue = expiredToken!.uuid;
157+
const restorePasswordData = {
158+
password: 'NewPassword2!',
159+
token: expiredTokenValue,
160+
hCaptchaToken: 'string',
161+
};
162+
const invalidRestorePasswordResponse = await request(app.getHttpServer())
163+
.post('/auth/restore-password')
164+
.send(restorePasswordData);
165+
166+
expect(invalidRestorePasswordResponse.status).toBe(HttpStatus.FORBIDDEN);
167+
expect(invalidRestorePasswordResponse.body.message).toBe(
168+
ErrorAuth.TokenExpired,
169+
);
170+
171+
// Ensure password was not updated
172+
const updatedUser = await userRepository.findById(userEntity.id);
173+
expect(updatedUser!.password).toEqual(userEntity.password);
174+
});
175+
});

0 commit comments

Comments
 (0)