Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit f433e12

Browse files
committedMar 19, 2025·
user regisor & delete flow test
1 parent a51dbb4 commit f433e12

File tree

2 files changed

+272
-94
lines changed

2 files changed

+272
-94
lines changed
 

‎test/e2e/user-lifecycle.e2e-spec.ts

+272
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,272 @@
1+
import { MailModule, MailService } from '@lib/mail';
2+
import { ConfigModule } from '@nestjs/config';
3+
import { NestApplication } from '@nestjs/core';
4+
import { Test } from '@nestjs/testing';
5+
import request from 'supertest';
6+
7+
import { AppModule } from '../../src/app.module';
8+
import { MockConfigModule } from './mock/config/mock-config.module';
9+
import { MockMailModule } from './mock/mail/mock-mail.module';
10+
import { MockMailService } from './mock/mail/mock-mail.service';
11+
12+
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
13+
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
14+
/* eslint-disable @typescript-eslint/no-unsafe-call */
15+
16+
const TEST_DATA = {
17+
USER: {
18+
EMAIL: 'test-user-lifecycle@gm.gist.ac.kr',
19+
INITIAL_PASSWORD: 'TestPswd!213',
20+
NAME: 'testUserLifecycle',
21+
STUDENT_ID: '20000001',
22+
PHONE_NUMBER: '01012345678',
23+
},
24+
INVALID_USER: {
25+
EMAIL: 'test-wrong-user-lifecycle@gm.gist.ac.kr',
26+
NAME: 'testUser2Lifecycle',
27+
STUDENT_ID: '20000002',
28+
PHONE_NUMBER: '01087654321',
29+
},
30+
INVALID_EMAILS: {
31+
NON_GIST: 'test@gmail.com',
32+
WRONG_FORMAT: 'not-an-email',
33+
WRONG_DOMAIN: 'test@wrong.gist.ac.kr',
34+
},
35+
VERIFICATION: {
36+
WRONG_CODE: '123456abcdef',
37+
INVALID_TOKEN: 'invalid-token',
38+
EMAIL_HINT: 'email',
39+
},
40+
PROFILE: {
41+
IMAGE_LENGTH: 1024,
42+
},
43+
NEW_PASSWORD: 'NewTest123!@#',
44+
} as const;
45+
46+
describe('User Lifecycle Flow', () => {
47+
let app: NestApplication;
48+
let httpRequest: any;
49+
let mailService: MockMailService;
50+
let verificationToken: string;
51+
let userToken: string;
52+
let currentPassword: string = TEST_DATA.USER.INITIAL_PASSWORD;
53+
54+
beforeAll(async () => {
55+
const moduleFixture = await Test.createTestingModule({
56+
imports: [AppModule],
57+
})
58+
.overrideModule(ConfigModule)
59+
.useModule(MockConfigModule.forRoot())
60+
.overrideModule(MailModule)
61+
.useModule(MockMailModule)
62+
.compile();
63+
64+
app = moduleFixture.createNestApplication();
65+
mailService = moduleFixture.get<MailService>(
66+
MailService,
67+
) as unknown as MockMailService;
68+
await app.init();
69+
httpRequest = request(app.getHttpServer());
70+
});
71+
72+
afterAll(async () => {
73+
await app.close();
74+
});
75+
76+
it('[1] Email Verification: Should send and verify code with valid email', async () => {
77+
mailService.clearSentEmails();
78+
79+
await httpRequest
80+
.post('/verify/email')
81+
.send({ email: TEST_DATA.USER.EMAIL })
82+
.expect(201);
83+
84+
const email = mailService.getLastEmailSentTo(TEST_DATA.USER.EMAIL);
85+
if (!email) {
86+
fail('이메일이 전송되지 않았습니다');
87+
return;
88+
}
89+
90+
expect(email.to).toEqual(TEST_DATA.USER.EMAIL);
91+
expect(email.subject).toContain('Verification Code');
92+
93+
const content = email.content || fail('이메일 내용이 없습니다');
94+
const codeRegex = /verification code is <b>([a-zA-Z0-9]+)<\/b>/i;
95+
const matches = content.match(codeRegex);
96+
const code = matches ? matches[1] : fail('인증 코드를 찾을 수 없습니다');
97+
98+
const verifyResponse = await httpRequest
99+
.post('/verify')
100+
.send({
101+
subject: TEST_DATA.USER.EMAIL,
102+
code,
103+
hint: TEST_DATA.VERIFICATION.EMAIL_HINT,
104+
})
105+
.expect(201);
106+
107+
verificationToken = verifyResponse.body.verificationJwtToken;
108+
expect(verificationToken).toBeDefined();
109+
expect(typeof verificationToken).toBe('string');
110+
});
111+
112+
it('[1-1] Email Verification: Should fail verification with wrong code', async () => {
113+
await httpRequest
114+
.post('/verify')
115+
.send({
116+
subject: TEST_DATA.USER.EMAIL,
117+
code: TEST_DATA.VERIFICATION.WRONG_CODE,
118+
hint: TEST_DATA.VERIFICATION.EMAIL_HINT,
119+
})
120+
.expect(400);
121+
});
122+
123+
it('[1-2] Email Verification: Should fail with non-GIST email', async () => {
124+
await httpRequest
125+
.post('/verify/email')
126+
.send({ email: TEST_DATA.INVALID_EMAILS.NON_GIST })
127+
.expect(400);
128+
});
129+
130+
it('[1-3] Email Verification: Should fail with wrong email format', async () => {
131+
await httpRequest
132+
.post('/verify/email')
133+
.send({ email: TEST_DATA.INVALID_EMAILS.WRONG_FORMAT })
134+
.expect(400);
135+
});
136+
137+
it('[1-4] Email Verification: Should fail with wrong GIST email domain', async () => {
138+
await httpRequest
139+
.post('/verify/email')
140+
.send({ email: TEST_DATA.INVALID_EMAILS.WRONG_DOMAIN })
141+
.expect(400);
142+
});
143+
144+
it('[2] Registration: Should register new user after email verification', async () => {
145+
const registerData = {
146+
email: TEST_DATA.USER.EMAIL,
147+
password: currentPassword,
148+
name: TEST_DATA.USER.NAME,
149+
studentId: TEST_DATA.USER.STUDENT_ID,
150+
phoneNumber: TEST_DATA.USER.PHONE_NUMBER,
151+
verificationJwtToken: verificationToken,
152+
};
153+
154+
await httpRequest.post('/user').send(registerData).expect(201);
155+
});
156+
157+
it('[2-1] Registration: Should fail registration with invalid verification token', async () => {
158+
const registerData = {
159+
email: TEST_DATA.INVALID_USER.EMAIL,
160+
password: currentPassword,
161+
name: TEST_DATA.INVALID_USER.NAME,
162+
studentId: TEST_DATA.INVALID_USER.STUDENT_ID,
163+
phoneNumber: TEST_DATA.INVALID_USER.PHONE_NUMBER,
164+
verificationJwtToken: TEST_DATA.VERIFICATION.INVALID_TOKEN,
165+
};
166+
167+
await httpRequest.post('/user').send(registerData).expect(400);
168+
});
169+
170+
it('[3] Authentication: Should login and receive access token', async () => {
171+
const loginResponse = await httpRequest
172+
.post('/auth/login')
173+
.send({
174+
email: TEST_DATA.USER.EMAIL,
175+
password: currentPassword,
176+
})
177+
.expect(201);
178+
179+
userToken = loginResponse.body.accessToken;
180+
expect(userToken).toBeDefined();
181+
});
182+
183+
it('[4] User Profile: Should retrieve user information when authenticated', async () => {
184+
const response = await httpRequest
185+
.get('/user')
186+
.set('Authorization', `Bearer ${userToken}`)
187+
.expect(200);
188+
189+
expect(response.body).toHaveProperty('email', TEST_DATA.USER.EMAIL);
190+
expect(response.body).toHaveProperty('name', TEST_DATA.USER.NAME);
191+
expect(response.body).toHaveProperty(
192+
'studentId',
193+
TEST_DATA.USER.STUDENT_ID,
194+
);
195+
expect(response.body).toHaveProperty(
196+
'phoneNumber',
197+
TEST_DATA.USER.PHONE_NUMBER,
198+
);
199+
});
200+
201+
it('[5] Profile Management: Should get presigned URL for profile image upload', async () => {
202+
const response = await httpRequest
203+
.patch('/user/profile')
204+
.set('Authorization', `Bearer ${userToken}`)
205+
.query({ length: TEST_DATA.PROFILE.IMAGE_LENGTH })
206+
.expect(200);
207+
208+
expect(response.body).toHaveProperty('presignedUrl');
209+
expect(typeof response.body.presignedUrl).toBe('string');
210+
});
211+
212+
it('[6] Password Update: Should change password after email verification and login with new password', async () => {
213+
await httpRequest
214+
.post('/verify/email')
215+
.send({ email: TEST_DATA.USER.EMAIL })
216+
.expect(201);
217+
218+
const email = mailService.getLastEmailSentTo(TEST_DATA.USER.EMAIL);
219+
const content = email?.content || fail('이메일 내용이 없습니다');
220+
const codeRegex = /verification code is <b>([a-zA-Z0-9]+)<\/b>/i;
221+
const matches = content.match(codeRegex);
222+
const code = matches ? matches[1] : fail('인증 코드를 찾을 수 없습니다');
223+
224+
const verifyResponse = await httpRequest
225+
.post('/verify')
226+
.send({
227+
subject: TEST_DATA.USER.EMAIL,
228+
code,
229+
hint: TEST_DATA.VERIFICATION.EMAIL_HINT,
230+
})
231+
.expect(201);
232+
233+
const newVerificationToken = verifyResponse.body.verificationJwtToken;
234+
235+
await httpRequest
236+
.patch('/user/password')
237+
.send({
238+
email: TEST_DATA.USER.EMAIL,
239+
verificationJwtToken: newVerificationToken,
240+
password: TEST_DATA.NEW_PASSWORD,
241+
})
242+
.expect(200);
243+
244+
await httpRequest
245+
.post('/auth/login')
246+
.send({
247+
email: TEST_DATA.USER.EMAIL,
248+
password: TEST_DATA.NEW_PASSWORD,
249+
})
250+
.expect(201);
251+
252+
currentPassword = TEST_DATA.NEW_PASSWORD;
253+
});
254+
255+
it('[7] Account Deletion: Should delete account after password confirmation', async () => {
256+
await httpRequest
257+
.delete('/user')
258+
.set('Authorization', `Bearer ${userToken}`)
259+
.send({
260+
password: currentPassword,
261+
})
262+
.expect(200);
263+
264+
await httpRequest
265+
.post('/auth/login')
266+
.send({
267+
email: TEST_DATA.USER.EMAIL,
268+
password: currentPassword,
269+
})
270+
.expect(401);
271+
});
272+
});

‎test/e2e/user.e2e-spec.ts

-94
This file was deleted.

0 commit comments

Comments
 (0)
Please sign in to comment.