Skip to content

Commit

Permalink
[BE#212] mates 서비스에 대한 테스트 코드 작성 (#306)
Browse files Browse the repository at this point in the history
* test: mate service 테스트 코드 작성

* fix: 친구 통계 조회 시 배열 길이 6개인 오류 수정

* test: getMateAndMyStats() 테스트 케이스 추가
  • Loading branch information
victolee0 authored Dec 4, 2023
1 parent c998ac6 commit 17ee111
Show file tree
Hide file tree
Showing 12 changed files with 386 additions and 7 deletions.
141 changes: 140 additions & 1 deletion BE/src/mates/mates.service.spec.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,53 @@
import { Test, TestingModule } from '@nestjs/testing';
import { MatesService } from './mates.service';
import { getRepositoryToken } from '@nestjs/typeorm';
import { Mates } from './mates.entity';
import { MockMatesRepository } from '../../test/mock-repo/mock-mates-repo';
import { UsersModel } from 'src/users/entity/users.entity';
import { MockUsersRepository } from '../../test/mock-repo/mock-user-repo';
import { StudyLogsService } from 'src/study-logs/study-logs.service';
import { MockStudyLogsService } from '../../test/mock-service/mock-study-logs-service';
import { ConfigService } from '@nestjs/config';
import { RedisService } from 'src/common/redis.service';
import { MockRedisService } from '../../test/mock-service/mock-redis-service';
import { BadRequestException, NotFoundException } from '@nestjs/common';

class MockConfigService {
private ENV = {
CDN_ENDPOINT: 'http://cdn.com',
};
get(key: string) {
return this.ENV[key.split('.')[1]];
}
}
describe('MatesService', () => {
let service: MatesService;

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [MatesService],
providers: [
MatesService,
{
provide: getRepositoryToken(Mates),
useClass: MockMatesRepository,
},
{
provide: getRepositoryToken(UsersModel),
useClass: MockUsersRepository,
},
{
provide: RedisService,
useClass: MockRedisService,
},
{
provide: StudyLogsService,
useClass: MockStudyLogsService,
},
{
provide: ConfigService,
useClass: MockConfigService,
},
],
}).compile();

service = module.get<MatesService>(MatesService);
Expand All @@ -15,4 +56,102 @@ describe('MatesService', () => {
it('should be defined', () => {
expect(service).toBeDefined();
});
const user = {
id: 1,
nickname: '어린콩',
auth_type: 'google',
email: '',
image_url: null,
} as UsersModel;
describe('.addMate()', () => {
it('유효한 데이터가 주어지면 성공적으로 친구 추가를 해야한다.', async () => {
const result = await service.addMate(user, '어린콩3');
expect(result).toStrictEqual({
id: 2,
follower_id: 1,
following_id: 3,
});
});

it('자신을 친구 추가 할 수 없다.', async () => {
expect(service.addMate(user, '어린콩')).rejects.toThrow(
BadRequestException,
);
});

it('존재하지 않는 유저를 친구 추가 할 수 없다.', async () => {
expect(service.addMate(user, '어린콩4')).rejects.toThrow(
NotFoundException,
);
});

it('이미 친구 관계인 유저에게 친구 신청을 할 수 없다.', async () => {
expect(service.addMate(user, '어린콩2')).rejects.toThrow(
BadRequestException,
);
});
});

describe('.deleteMate()', () => {
it('유효한 데이터가 주어지면 성공적으로 친구 삭제를 해야한다.', async () => {
const result = await service.deleteMate(user, 2);
expect(result).toStrictEqual(undefined);
});

it('존재하지 않는 유저를 친구 삭제 할 수 없다.', () => {
expect(service.deleteMate(user, 100)).rejects.toThrow(NotFoundException);
});

it('친구 관계가 아닌 유저를 친구 삭제 할 수 없다.', () => {
expect(service.deleteMate(user, 3)).rejects.toThrow(NotFoundException);
});
});

describe('.getMatesStatus()', () => {
it('유효한 데이터가 주어지면 성공적으로 친구 상태를 가져온다.', async () => {
const result = await service.getMatesStatus(1);
expect(result).toStrictEqual([
{ id: 2, started_at: '2023-11-29 16:00:00' },
]);
});
});

describe('.getMates()', () => {
it('유효한 데이터가 주어지면 성공적으로 친구들을 가져온다.', async () => {
const result = await service.getMates(1, '2023-11-29');
expect(result).toStrictEqual([
{
id: 2,
nickname: '어린콩2',
image_url: null,
total_time: 825,
started_at: '2023-11-29 16:00:00',
},
]);
});
it('친구가 없는 유저는 빈 배열을 가져온다.', async () => {
const result = await service.getMates(3, '2023-11-29');
expect(result).toStrictEqual([]);
});
});

describe('.getMateAndMyStats()', () => {
it('유효한 데이터가 주어지면 성공적으로 친구들의 학습시간과 내 학습시간을 가져온다.', async () => {
const result = await service.getMateAndMyStats(1, 2, '2023-11-29');
expect(result).toStrictEqual({
my_daily_data: [0, 0, 0, 0, 0, 0, 827],
following_daily_data: [0, 0, 0, 0, 0, 0, 825],
following_primary_category: null,
});
});
});

it('학습 시간이 존재하지 않는 경우 모든 값이 0인 배열을 반환한다.', async () => {
const result = await service.getMateAndMyStats(1, 2, '2023-12-29');
expect(result).toStrictEqual({
my_daily_data: [0, 0, 0, 0, 0, 0, 0],
following_daily_data: [0, 0, 0, 0, 0, 0, 0],
following_primary_category: null,
});
});
});
9 changes: 7 additions & 2 deletions BE/src/mates/mates.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ export class MatesService {
const result = await this.matesRepository.find({
where: { follower_id: { id: user_id } },
});
const userIds = result.map(({ following_id: { id } }) => id);
const userIds = result.map((following) => following.following_id);
return Promise.all(
userIds.map(async (id) => {
const started_at = await this.redisService.get(`${id}`);
Expand Down Expand Up @@ -131,12 +131,17 @@ export class MatesService {
const following = await this.userRepository.findOne({
where: { id: following_id },
});

if (!following) {
throw new NotFoundException('해당 유저는 존재하지 않습니다.');
}

const result = await this.matesRepository.delete({
follower_id: user,
following_id: following,
});

if (result.affected === 0) {
if (!result) {
throw new NotFoundException('해당 친구 관계는 존재하지 않습니다.');
}
}
Expand Down
2 changes: 1 addition & 1 deletion BE/src/study-logs/study-logs.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export class StudyLogsService {
end_date: string,
): Promise<number[]> {
const startMoment = moment(start_date);
const diffDays = moment(end_date).diff(startMoment, 'days');
const diffDays = moment(end_date).diff(startMoment, 'days') + 1;
const result = Array.from({ length: diffDays }, () => 0);
const daily_sums = await this.studyLogsRepository
.createQueryBuilder('study_logs')
Expand Down
22 changes: 22 additions & 0 deletions BE/test/mock-repo/mock-categories-repo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import categoriesData from '../mock-table/categories.json';

export class MockCategoriesRepository {
private data = categoriesData;
create(entity: object): object {
return {
id: this.data.length + 1,
...entity,
};
}

save(entity: object): Promise<object> {
return Promise.resolve(entity);
}

find({ where: { user_id } }): Promise<object> {
const categories = this.data.filter(
(category) => category.user_id === user_id.id,
);
return Promise.resolve(categories);
}
}
45 changes: 45 additions & 0 deletions BE/test/mock-repo/mock-mates-repo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import matesData from '../mock-table/mates.json';

export class MockMatesRepository {
private data = matesData;
create(entity: object): object {
return {
id: this.data.length + 1,
...entity,
};
}

save(entity: object): Promise<object> {
return Promise.resolve(entity);
}

find({ where }: { where: { follower_id } }): Promise<object> {
const mates = this.data.filter(
(mate) => mate.follower_id === where.follower_id.id,
);
return Promise.resolve(mates);
}

findOne({
where,
}: {
where: { follower_id; following_id };
}): Promise<object> {
const mate = this.data.find(
(mate) =>
mate.follower_id === where.follower_id.id &&
mate.following_id === where.following_id.id,
);
return Promise.resolve(mate);
}

delete({ follower_id, following_id }): Promise<object> {
const mate = this.data.find(
(mate) =>
mate.follower_id === follower_id.id &&
mate.following_id === following_id.id,
);

return Promise.resolve(mate);
}
}
85 changes: 85 additions & 0 deletions BE/test/mock-repo/mock-user-repo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import usersData from '../mock-table/users.json';
import mateData from '../mock-table/mates.json';
import studyLogsData from '../mock-table/study-logs.json';

export class MockUsersRepository {
private data = usersData;
private mate = mateData;
private studyLogs = studyLogsData;

create(entity: object): object {
return {
id: this.data.length + 1,
...entity,
};
}

save(entity: object): Promise<object> {
return Promise.resolve(entity);
}

find({ where: { id } }): Promise<object> {
const user = this.data.find((user) => user.id === id);
return Promise.resolve(user);
}

findOne({
where,
}: {
where: { id?: number; nickname?: string };
}): Promise<object> {
if (where.id) {
const user = this.data.find((user) => user.id === where.id);
return Promise.resolve(user);
}
const user = this.data.find((user) => user.nickname === where.nickname);
return Promise.resolve(user);
}

delete({ where: { id } }): Promise<object> {
const user = this.data.find((user) => user.id === id);
const index = this.data.indexOf(user);
this.data.splice(index, 1);
return Promise.resolve(user);
}

query(
query: string,
param: [date: string, user_id: number],
): Promise<object> {
switch (query) {
case `
SELECT u.id, u.nickname, u.image_url, COALESCE(SUM(s.learning_time), 0) AS total_time
FROM users_model u
LEFT JOIN mates m ON m.following_id = u.id
LEFT JOIN study_logs s ON s.user_id = u.id AND s.date = ?
WHERE m.follower_id = ?
GROUP BY u.id
ORDER BY total_time DESC
`:
const result = this.data
.filter((user) =>
this.mate.find(
(mate) =>
mate.follower_id === param[1] && mate.following_id === user.id,
),
)
.map((user) => {
const total_time = this.studyLogs
.filter(
(studyLog) =>
studyLog.user_id === user.id && studyLog.date === param[0],
)
.reduce((acc, cur) => acc + cur.learning_time, 0);
return {
id: user.id,
nickname: user.nickname,
image_url: user.image_url,
total_time,
};
})
.sort((a, b) => b.total_time - a.total_time);
return Promise.resolve(result);
}
}
}
17 changes: 17 additions & 0 deletions BE/test/mock-service/mock-redis-service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export class MockRedisService {
private redis: Map<string, string> = new Map();
constructor() {
this.redis.set('2', '2023-11-29 16:00:00');
}
set(key: string, value: string) {
this.redis.set(key, value);
}

get(key: string): Promise<string | null> {
return Promise.resolve(this.redis.get(key));
}

async del(key: string): Promise<void> {
await this.redis.delete(key);
}
}
25 changes: 25 additions & 0 deletions BE/test/mock-service/mock-study-logs-service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import moment from 'moment';
import studyLogsData from '../mock-table/study-logs.json';

export class MockStudyLogsService {
private data = studyLogsData;

calculateTotalTimes(id, start_date, end_date) {
const startMoment = moment(start_date);
const diffDays = moment(end_date).diff(startMoment, 'days') + 1;
const result = Array.from({ length: diffDays }, () => 0);
const daily_sums = this.data
.filter(
(studyLog) =>
studyLog.user_id === id &&
studyLog.date >= start_date &&
studyLog.date <= end_date,
)
.reduce((acc, cur) => {
const index = moment(cur.date).diff(startMoment, 'days');
acc[index] += cur.learning_time;
return acc;
}, result);
return daily_sums;
}
}
Loading

0 comments on commit 17ee111

Please sign in to comment.