Skip to content

Commit f1c45d0

Browse files
authored
Merge pull request #405 from boostcampwm-2024/test/get-activity-api
✅ test: activity 조회 API 테스트 작성
2 parents a628253 + 13d928c commit f1c45d0

File tree

5 files changed

+315
-0
lines changed

5 files changed

+315
-0
lines changed
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import { validate } from 'class-validator';
2+
import { ActivityParamRequestDto } from '../../../src/activity/dto/request/activity-param.dto';
3+
4+
describe('ActivityParamRequestDto Test', () => {
5+
it('정상적인 userId로 유효성 검사를 통과한다.', async () => {
6+
// given
7+
const dto = new ActivityParamRequestDto({
8+
userId: 1,
9+
});
10+
11+
// when
12+
const errors = await validate(dto);
13+
14+
// then
15+
expect(errors).toHaveLength(0);
16+
});
17+
18+
it('userId가 정수가 아닌 문자열이면 유효성 검사에 실패한다.', async () => {
19+
// given
20+
const dto = new ActivityParamRequestDto({
21+
userId: 'invalid' as any,
22+
});
23+
24+
// when
25+
const errors = await validate(dto);
26+
27+
// then
28+
expect(errors).toHaveLength(1);
29+
expect(errors[0].constraints).toHaveProperty('isInt');
30+
});
31+
32+
it('userId가 정수가 아닌 실수이면 유효성 검사에 실패한다.', async () => {
33+
// given
34+
const dto = new ActivityParamRequestDto({
35+
userId: 1.5 as any,
36+
});
37+
38+
// when
39+
const errors = await validate(dto);
40+
41+
// then
42+
expect(errors).toHaveLength(1);
43+
expect(errors[0].constraints).toHaveProperty('isInt');
44+
});
45+
46+
it('userId가 1보다 작으면 유효성 검사에 실패한다.', async () => {
47+
// given
48+
const dto = new ActivityParamRequestDto({
49+
userId: 0,
50+
});
51+
52+
// when
53+
const errors = await validate(dto);
54+
55+
// then
56+
expect(errors).toHaveLength(1);
57+
expect(errors[0].constraints).toHaveProperty('min');
58+
});
59+
60+
it('userId가 음수이면 유효성 검사에 실패한다.', async () => {
61+
// given
62+
const dto = new ActivityParamRequestDto({
63+
userId: -1,
64+
});
65+
66+
// when
67+
const errors = await validate(dto);
68+
69+
// then
70+
expect(errors).toHaveLength(1);
71+
expect(errors[0].constraints).toHaveProperty('min');
72+
});
73+
});
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import { validate } from 'class-validator';
2+
import { ActivityQueryRequestDto } from '../../../src/activity/dto/request/activity-query.dto';
3+
4+
describe('ActivityQueryRequestDto Test', () => {
5+
it('정상적인 year로 유효성 검사를 통과한다.', async () => {
6+
// given
7+
const dto = new ActivityQueryRequestDto({
8+
year: 2024,
9+
});
10+
11+
// when
12+
const errors = await validate(dto);
13+
14+
// then
15+
expect(errors).toHaveLength(0);
16+
});
17+
18+
it('year가 정수가 아닌 문자열이면 유효성 검사에 실패한다.', async () => {
19+
// given
20+
const dto = new ActivityQueryRequestDto({
21+
year: 'invalid' as any,
22+
});
23+
24+
// when
25+
const errors = await validate(dto);
26+
27+
// then
28+
expect(errors).toHaveLength(1);
29+
expect(errors[0].constraints).toHaveProperty('isInt');
30+
});
31+
32+
it('year가 정수가 아닌 실수이면 유효성 검사에 실패한다.', async () => {
33+
// given
34+
const dto = new ActivityQueryRequestDto({
35+
year: 2024.5 as any,
36+
});
37+
38+
// when
39+
const errors = await validate(dto);
40+
41+
// then
42+
expect(errors).toHaveLength(1);
43+
expect(errors[0].constraints).toHaveProperty('isInt');
44+
});
45+
46+
it('year가 2000년 미만이면 유효성 검사에 실패한다.', async () => {
47+
// given
48+
const dto = new ActivityQueryRequestDto({
49+
year: 1999,
50+
});
51+
52+
// when
53+
const errors = await validate(dto);
54+
55+
// then
56+
expect(errors).toHaveLength(1);
57+
expect(errors[0].constraints).toHaveProperty('min');
58+
});
59+
60+
it('year가 3000년 초과이면 유효성 검사에 실패한다.', async () => {
61+
// given
62+
const dto = new ActivityQueryRequestDto({
63+
year: 3001,
64+
});
65+
66+
// when
67+
const errors = await validate(dto);
68+
69+
// then
70+
expect(errors).toHaveLength(1);
71+
expect(errors[0].constraints).toHaveProperty('max');
72+
});
73+
74+
it('year가 2000년이면 유효성 검사를 통과한다.', async () => {
75+
// given
76+
const dto = new ActivityQueryRequestDto({
77+
year: 2000,
78+
});
79+
80+
// when
81+
const errors = await validate(dto);
82+
83+
// then
84+
expect(errors).toHaveLength(0);
85+
});
86+
87+
it('year가 3000년이면 유효성 검사를 통과한다.', async () => {
88+
// given
89+
const dto = new ActivityQueryRequestDto({
90+
year: 3000,
91+
});
92+
93+
// when
94+
const errors = await validate(dto);
95+
96+
// then
97+
expect(errors).toHaveLength(0);
98+
});
99+
});
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import { INestApplication } from '@nestjs/common';
2+
import * as request from 'supertest';
3+
import { UserRepository } from '../../../src/user/repository/user.repository';
4+
import { ActivityRepository } from '../../../src/activity/repository/activity.repository';
5+
import { UserFixture } from '../../fixture/user.fixture';
6+
import { ActivityFixture } from '../../fixture/activity.fixture';
7+
import { User } from '../../../src/user/entity/user.entity';
8+
9+
describe('GET /api/activity/:userId E2E Test', () => {
10+
let app: INestApplication;
11+
let userRepository: UserRepository;
12+
let activityRepository: ActivityRepository;
13+
let testUser: User;
14+
let activitiesData: Array<{ activityDate: Date; viewCount: number }>;
15+
16+
beforeAll(async () => {
17+
app = global.testApp;
18+
userRepository = app.get(UserRepository);
19+
activityRepository = app.get(ActivityRepository);
20+
21+
testUser = await userRepository.save(UserFixture.createUserFixture({}));
22+
23+
activitiesData = [
24+
{ activityDate: new Date('2024-01-15'), viewCount: 5 },
25+
{ activityDate: new Date('2024-01-16'), viewCount: 3 },
26+
{ activityDate: new Date('2024-06-01'), viewCount: 8 },
27+
{ activityDate: new Date('2024-12-25'), viewCount: 2 },
28+
];
29+
30+
const activities = ActivityFixture.createMultipleActivitiesFixture(
31+
testUser,
32+
activitiesData,
33+
);
34+
35+
await activityRepository.save(activities);
36+
});
37+
38+
it('존재하는 사용자의 활동 데이터를 정상적으로 조회한다.', async () => {
39+
// given
40+
const userId = testUser.id;
41+
const year = activitiesData[0].activityDate.getFullYear();
42+
43+
// when
44+
const response = await request(app.getHttpServer())
45+
.get(`/api/activity/${userId}`)
46+
.query({ year });
47+
48+
// then
49+
expect(response.status).toBe(200);
50+
expect(response.body.message).toBe('요청이 성공적으로 처리되었습니다.');
51+
52+
const { data } = response.body;
53+
expect(data.maxStreak).toBe(testUser.maxStreak);
54+
expect(data.currentStreak).toBe(testUser.currentStreak);
55+
expect(data.totalViews).toBe(testUser.totalViews);
56+
57+
expect(data.dailyActivities).toHaveLength(activitiesData.length);
58+
expect(data.dailyActivities).toEqual(
59+
expect.arrayContaining(
60+
activitiesData.map((activity) =>
61+
expect.objectContaining({
62+
date: activity.activityDate.toISOString().split('T')[0],
63+
viewCount: activity.viewCount,
64+
}),
65+
),
66+
),
67+
);
68+
});
69+
70+
it('다른 연도를 요청하면 해당 연도의 데이터만 조회된다.', async () => {
71+
// given
72+
const userId = testUser.id;
73+
const year = activitiesData[0].activityDate.getFullYear() - 1;
74+
75+
// when
76+
const response = await request(app.getHttpServer())
77+
.get(`/api/activity/${userId}`)
78+
.query({ year });
79+
80+
// then
81+
expect(response.status).toBe(200);
82+
expect(response.body.message).toBe('요청이 성공적으로 처리되었습니다.');
83+
84+
const { data } = response.body;
85+
expect(data.maxStreak).toBe(testUser.maxStreak);
86+
expect(data.currentStreak).toBe(testUser.currentStreak);
87+
expect(data.totalViews).toBe(testUser.totalViews);
88+
expect(data.dailyActivities).toHaveLength(0);
89+
});
90+
91+
it('존재하지 않는 사용자 ID로 요청하면 404 에러를 반환한다.', async () => {
92+
// given
93+
const nonExistentUserId = 99999;
94+
const year = activitiesData[0].activityDate.getFullYear();
95+
96+
// when
97+
const response = await request(app.getHttpServer())
98+
.get(`/api/activity/${nonExistentUserId}`)
99+
.query({ year });
100+
101+
// then
102+
expect(response.status).toBe(404);
103+
expect(response.body.message).toBe('존재하지 않는 사용자입니다.');
104+
});
105+
});
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { Activity } from '../../src/activity/entity/activity.entity';
2+
import { User } from '../../src/user/entity/user.entity';
3+
4+
export class ActivityFixture {
5+
static readonly DEFAULT_ACTIVITY = {
6+
activityDate: new Date('2024-01-01'),
7+
viewCount: 1,
8+
};
9+
10+
static createActivityFixture(
11+
user: User,
12+
overwrites: Partial<Activity> = {},
13+
): Activity {
14+
const activity = new Activity();
15+
Object.assign(activity, this.DEFAULT_ACTIVITY);
16+
Object.assign(activity, overwrites);
17+
activity.user = user;
18+
return activity;
19+
}
20+
21+
static createMultipleActivitiesFixture(
22+
user: User,
23+
activitiesData: Array<{ activityDate: Date; viewCount: number }>,
24+
): Activity[] {
25+
return activitiesData.map((data) => this.createActivityFixture(user, data));
26+
}
27+
28+
static createActivityWithDateFixture(
29+
user: User,
30+
activityDate: Date,
31+
viewCount: number = 1,
32+
): Activity {
33+
return this.createActivityFixture(user, { activityDate, viewCount });
34+
}
35+
}

server/test/fixture/user.fixture.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ export class UserFixture {
66
email: 'test1234@test.com',
77
password: 'test1234!',
88
userName: 'test1234',
9+
maxStreak: 15,
10+
currentStreak: 7,
11+
totalViewss: 120,
912
};
1013

1114
static async createUserCryptFixture(overwrites: Partial<User> = {}) {

0 commit comments

Comments
 (0)