diff --git a/README.md b/README.md
index 10ba0b64..18488ae5 100644
--- a/README.md
+++ b/README.md
@@ -2,9 +2,14 @@
-
+
-[💻 WeView](https://weview.club) | [📽 발표 영상](https://www.youtube.com/watch?v=-cujznqSkC4) | [📷 데모 영상](https://youtu.be/bK0whd-zuck) | [📝 팀 위키](https://github.com/boostcampwm-2022/web06-weview/wiki)
+
+
+
+### [💻 WeView](https://weview.club) | [📽 발표 영상](https://www.youtube.com/watch?v=-cujznqSkC4) | [📷 데모 영상](https://youtu.be/bK0whd-zuck) | [📝 팀 위키](https://github.com/boostcampwm-2022/web06-weview/wiki)
+
+
@@ -17,9 +22,9 @@
**💡 WeView 팀은 사용자가 즐길 수 있는 가치를 전달하는 방법으로 3가지를 생각했어요.**
> 1. 간단하게 코드를 확인하고 리뷰 할 수 있어요.
->
+>
> 2. 보고 싶은 코드를 쉽게 찾을 수 있어요.
->
+>
> 3. 한눈에 코드들이 들어오는 디자인을 생각했어요.
## 저희 김가네 식구들을 소개할게요.
@@ -48,11 +53,10 @@
더 궁금하시다면 아래를 참고해주세요.
-- 주요 기능이 궁금해요. 👉 [WeView 사용 설명서 바로가기]()
-- 프로젝트를 직접 설치하고 싶어요. 👉 [설치 가이드 바로가기]()
-- 협업 방식을 알아보고 싶어요. 👉 [협업 가이드 바로가기]()
-- 사용한 기술이 궁금해요. 👉 [기술 스펙 바로가기]()
-- 기술을 선정한 이유가 궁금해요. 👉 [기술 선정 이유 바로가기]()
-- 프로젝트를 하면서 겪은 이야기가 궁금해요. 👉 [기술 블로그 바로가기]()
+- 주요 기능이 궁금해요. 👉 [WeView 사용 설명서 바로가기](https://github.com/boostcampwm-2022/web06-weview/wiki/%EC%82%AC%EC%9A%A9-%EC%84%A4%EB%AA%85%EC%84%9C)
+- 프로젝트를 직접 설치하고 싶어요. 👉 [설치 가이드 바로가기](https://github.com/boostcampwm-2022/web06-weview/wiki/%EC%84%A4%EC%B9%98-%EA%B0%80%EC%9D%B4%EB%93%9C)
+- 협업 방식을 알아보고 싶어요. 👉 [협업 가이드 바로가기](https://github.com/boostcampwm-2022/web06-weview/wiki/%F0%9F%93%9C-%ED%98%91%EC%97%85-%EA%B0%80%EC%9D%B4%EB%93%9C)
+- 사용한 기술이 궁금해요. 👉 [기술 스펙 바로가기](https://github.com/boostcampwm-2022/web06-weview/wiki/%F0%9F%97%82-%EA%B8%B0%EC%88%A0-%EC%8A%A4%ED%8E%99)
+- 프로젝트를 하면서 겪은 이야기가 궁금해요. 👉 [기술 블로그 바로가기](https://github.com/boostcampwm-2022/web06-weview/wiki/%F0%9F%92%BB-%EA%B0%9C%EB%B0%9C-%EB%B8%94%EB%A1%9C%EA%B7%B8)
다른 이야기들은 [📝 팀 위키](https://github.com/boostcampwm-2022/web06-weview/wiki)에서 확인해 보실 수 있어요.
diff --git a/client/src/constants/search.ts b/client/src/constants/search.ts
index 60b2c967..1d07735c 100644
--- a/client/src/constants/search.ts
+++ b/client/src/constants/search.ts
@@ -15,3 +15,4 @@ export const REVIEW_COUNT_ITEMS = [
];
export const MAX_SEARCH_HISTORY = 5;
+export const MAX_SEARCH_TAGS_COUNT = 5;
diff --git a/client/src/hooks/useSearch.ts b/client/src/hooks/useSearch.ts
index 870a4a47..6d9f6c7d 100644
--- a/client/src/hooks/useSearch.ts
+++ b/client/src/hooks/useSearch.ts
@@ -14,6 +14,7 @@ import {
} from "@/utils/label";
import { LABEL_NAME } from "@/constants/label";
import { formatTag } from "@/utils/regExpression";
+import { MAX_SEARCH_TAGS_COUNT } from "@/constants/search";
interface UseLabelResult {
word: string;
@@ -127,7 +128,11 @@ const useSearch = (
};
const handleInsertTag = (e: KeyboardEvent): void => {
- if (word.length === 0 || !isEnterKey(e.key)) {
+ if (
+ word.length === 0 ||
+ !isEnterKey(e.key) ||
+ MAX_SEARCH_TAGS_COUNT <= labels.length
+ ) {
return;
}
const newLabel = createLabel(formatTag(word.trim()), LABEL_NAME.TAGS);
diff --git a/scheduler-server/src/ranking/ranking.service.ts b/scheduler-server/src/ranking/ranking.service.ts
index e3e86a5f..33d9198f 100644
--- a/scheduler-server/src/ranking/ranking.service.ts
+++ b/scheduler-server/src/ranking/ranking.service.ts
@@ -75,28 +75,28 @@ export class RankingService {
return;
}
- const timeWaste = this.calcTimeWaste(idx);
+ const timeWeight = this.calcTimeWeight(idx);
for (const tag in tagCounts) {
if (totalTagCounts[tag]) {
- totalTagCounts[tag] += tagCounts[tag] * timeWaste;
+ totalTagCounts[tag] += tagCounts[tag] * timeWeight;
continue;
}
- totalTagCounts[tag] = tagCounts[tag] * timeWaste;
+ totalTagCounts[tag] = tagCounts[tag] * timeWeight;
}
});
return totalTagCounts;
}
- /**
+ /*
* 시간 가중치는 현재 큐의 인덱스와 배열의 크기에 따라 정해진다
*/
- private calcTimeWaste(idx: number): number {
+ private calcTimeWeight(idx: number): number {
const offset = idx - this.index + ARRAY_SIZE - 1;
- const timeWaste = ((offset % ARRAY_SIZE) + 1) / ARRAY_SIZE;
+ const timeWeight = ((offset % ARRAY_SIZE) + 1) / ARRAY_SIZE;
- return timeWaste;
+ return timeWeight;
}
private addPrevInfo(newRanking: any[]) {
diff --git a/server/package.json b/server/package.json
index ba63f3b1..fce99a14 100644
--- a/server/package.json
+++ b/server/package.json
@@ -90,7 +90,7 @@
"^.+\\.(t|j)s$": "ts-jest"
},
"collectCoverageFrom": [
- "**/*.service.(t|j)s"
+ "src/**/*.service.(t|j)s"
],
"coverageDirectory": "./coverage",
"testEnvironment": "node"
diff --git a/server/src/domain/report/report.service.spec.ts b/server/src/domain/report/report.service.spec.ts
new file mode 100644
index 00000000..94a5f8b2
--- /dev/null
+++ b/server/src/domain/report/report.service.spec.ts
@@ -0,0 +1,111 @@
+import { Test, TestingModule } from '@nestjs/testing';
+import { PostAlreadyReportedException } from 'src/exception/post-already-reported.exception';
+import { PostNotFoundException } from 'src/exception/post-not-found.exception';
+import { UserNotFoundException } from 'src/exception/user-not-found.exception';
+import { Post } from '../post/post.entity';
+import { PostRepository } from '../post/post.repository';
+import { User } from '../user/user.entity';
+import { UserRepository } from '../user/user.repository';
+import { ReportCreateRequestDto } from './dto/controller-request.dto';
+import { Report } from './report.entity';
+import { ReportRepository } from './report.repository';
+import { ReportService } from './report.service';
+
+describe('신고 서비스', () => {
+ let testingModule: TestingModule;
+ let reportService: ReportService;
+ let reportRepository: ReportRepository;
+ let userRepository: UserRepository;
+ let postRepository: PostRepository;
+
+ beforeAll(async () => {
+ testingModule = await Test.createTestingModule({
+ providers: [
+ ReportService,
+ {
+ provide: ReportRepository,
+ useValue: {},
+ },
+ {
+ provide: PostRepository,
+ useValue: {},
+ },
+ {
+ provide: UserRepository,
+ useValue: {},
+ },
+ ],
+ }).compile();
+ });
+
+ beforeEach(() => {
+ reportService = testingModule.get(ReportService);
+ reportRepository = testingModule.get(ReportRepository);
+ postRepository = testingModule.get(PostRepository);
+ userRepository = testingModule.get(UserRepository);
+ });
+
+ describe('신고하기', () => {
+ it('존재하지 않는 유저가 신고를 하면 UserNotFoundException이 발생한다.', async () => {
+ // given
+ const userId = 1;
+ const postId = 1;
+ const requestDto = new ReportCreateRequestDto();
+
+ userRepository.findOneBy = jest.fn();
+ postRepository.findOneBy = jest.fn();
+
+ // when
+ try {
+ await reportService.report(userId, postId, requestDto);
+ throw new Error();
+ } catch (err) {
+ // then
+ expect(err).toBeInstanceOf(UserNotFoundException);
+ }
+ });
+
+ it('존재하지 않는 포스트에 신고를 하면 PostNotFoundException이 발생한다.', async () => {
+ // given
+ const userId = 1;
+ const postId = 1;
+ const requestDto = new ReportCreateRequestDto();
+
+ userRepository.findOneBy = jest.fn(async () => new User());
+ postRepository.findOneBy = jest.fn();
+
+ // when
+ try {
+ await reportService.report(userId, postId, requestDto);
+ throw new Error();
+ } catch (err) {
+ // then
+ expect(err).toBeInstanceOf(PostNotFoundException);
+ }
+ });
+
+ it('신고한 포스트를 다시 신고하면 PostAlreadyReportedException이 발생한다.', async () => {
+ // given
+ const userId = 1;
+ const postId = 1;
+ const requestDto = new ReportCreateRequestDto();
+
+ userRepository.findOneBy = jest.fn(async () => new User());
+ postRepository.findOneBy = jest.fn(async () => new Post());
+ reportRepository.findOneBy = jest.fn();
+ reportRepository.insert = jest.fn();
+
+ // when
+ try {
+ await reportService.report(userId, postId, requestDto);
+ reportRepository.findOneBy = jest.fn(async () => new Report());
+ await reportService.report(userId, postId, requestDto);
+ throw new Error();
+ } catch (err) {
+ // then
+ expect(err).toBeInstanceOf(PostAlreadyReportedException);
+ expect(reportRepository.insert).toHaveBeenCalledTimes(1);
+ }
+ });
+ });
+});
diff --git a/server/src/domain/review/review.service.spec.ts b/server/src/domain/review/review.service.spec.ts
new file mode 100644
index 00000000..b597c80c
--- /dev/null
+++ b/server/src/domain/review/review.service.spec.ts
@@ -0,0 +1,174 @@
+import { Test, TestingModule } from '@nestjs/testing';
+import { PostNotFoundException } from 'src/exception/post-not-found.exception';
+import { UserNotFoundException } from 'src/exception/user-not-found.exception';
+import { Post } from '../post/post.entity';
+import { PostRepository } from '../post/post.repository';
+import { User } from '../user/user.entity';
+import { UserRepository } from '../user/user.repository';
+import {
+ ReviewGetAllRequestDto,
+ ReviewWriteRequestDto,
+} from './dto/controller-request.dto';
+import { Review } from './review.entity';
+import { ReviewRepository } from './review.repository';
+import { ReviewService } from './review.service';
+
+describe('리뷰 서비스', () => {
+ let testingModule: TestingModule;
+ let reviewService: ReviewService;
+ let reviewRepository: ReviewRepository;
+ let postRepository: PostRepository;
+ let userRepository: UserRepository;
+
+ beforeAll(async () => {
+ testingModule = await Test.createTestingModule({
+ providers: [
+ ReviewService,
+ {
+ provide: ReviewRepository,
+ useValue: {},
+ },
+ {
+ provide: PostRepository,
+ useValue: {},
+ },
+ {
+ provide: UserRepository,
+ useValue: {},
+ },
+ ],
+ }).compile();
+ });
+
+ beforeEach(() => {
+ reviewService = testingModule.get(ReviewService);
+ reviewRepository = testingModule.get(ReviewRepository);
+ postRepository = testingModule.get(PostRepository);
+ userRepository = testingModule.get(UserRepository);
+ });
+
+ describe('리뷰 작성', () => {
+ it('존재하지 않는 유저가 리뷰를 작성하면 UserNotFoundException이 발생한다.', async () => {
+ // given
+ const dto: ReviewWriteRequestDto = {
+ postId: 1,
+ content: '내용',
+ };
+ const userId = 1;
+
+ userRepository.findOneBy = jest.fn(() => null);
+ postRepository.findOneBy = jest.fn();
+
+ // when
+ try {
+ await reviewService.write(userId, dto);
+ throw new Error();
+ } catch (err) {
+ // then
+ expect(userRepository.findOneBy).toHaveBeenCalledTimes(1);
+ expect(err).toBeInstanceOf(UserNotFoundException);
+ }
+ });
+
+ it('존재하지 않는 포스트에 리뷰를 작성하면 PostNotFoundException이 발생한다.', async () => {
+ // given
+ const dto: ReviewWriteRequestDto = {
+ postId: 1,
+ content: '내용',
+ };
+ const userId = 1;
+
+ userRepository.findOneBy = jest.fn(async () => new User());
+ postRepository.findOneBy = jest.fn(() => null);
+
+ // when
+ try {
+ await reviewService.write(userId, dto);
+ throw new Error();
+ } catch (err) {
+ expect(postRepository.findOneBy).toHaveBeenCalledTimes(1);
+ expect(err).toBeInstanceOf(PostNotFoundException);
+ }
+ });
+
+ it('유저 정보와 포스트 정보로 리뷰 작성에 성공한다.', async () => {
+ // given
+ const dto: ReviewWriteRequestDto = {
+ postId: 1,
+ content: '내용',
+ };
+ const userId = 1;
+
+ userRepository.findOneBy = jest.fn(async () => new User());
+ postRepository.findOneBy = jest.fn(async () => new Post());
+ postRepository.increaseReviewCount = jest.fn(async () => new Post());
+ reviewRepository.insert = jest.fn();
+
+ // when
+ await reviewService.write(userId, dto);
+
+ // then
+ expect(reviewRepository.insert).toHaveBeenCalledTimes(1);
+ });
+ });
+
+ describe('리뷰 목록 조회', () => {
+ const REQUEST_CNT = 3;
+ it('리뷰를 한 번에 최대 REQUEST_CNT개까지 반환한다.', async () => {
+ // given
+ const reviews = Array.from({ length: REQUEST_CNT + 1 }, (v, i) => {
+ const review = new Review();
+ review.id = i + 1;
+ review.user = new User();
+ return review;
+ });
+ const postId = 1;
+ const requestDto = new ReviewGetAllRequestDto();
+
+ reviewRepository.find = jest.fn(async () => reviews);
+
+ // when
+ const dtos = await reviewService.getReviewsOfPost(postId, requestDto);
+
+ // then
+ expect(dtos.reviews.length).toEqual(REQUEST_CNT);
+ expect(dtos.isLast).toEqual(false);
+ });
+
+ it('리뷰가 요청한 개수보다 적거나 같으면 isLast가 true다.', async () => {
+ // given
+ const reviews = Array.from({ length: REQUEST_CNT }, (v, i) => {
+ const review = new Review();
+ review.id = i + 1;
+ review.user = new User();
+ return review;
+ });
+ const postId = 1;
+ const requestDto = new ReviewGetAllRequestDto();
+
+ reviewRepository.find = jest.fn(async () => reviews);
+
+ // when
+ const dtos = await reviewService.getReviewsOfPost(postId, requestDto);
+
+ // then
+ expect(dtos.lastId).toEqual(dtos.reviews[dtos.reviews.length - 1].id);
+ expect(dtos.isLast).toEqual(true);
+ });
+
+ it('리뷰가 없으면 isLast가 true다.', async () => {
+ // given
+ const postId = 1;
+ const requestDto = new ReviewGetAllRequestDto();
+
+ reviewRepository.find = jest.fn();
+
+ // when
+ const dtos = await reviewService.getReviewsOfPost(postId, requestDto);
+
+ // then
+ expect(dtos.reviews.length).toEqual(0);
+ expect(dtos.isLast).toEqual(true);
+ });
+ });
+});