Skip to content

Commit

Permalink
Merge pull request #174 from jihwooon/issue-100
Browse files Browse the repository at this point in the history
좋아요 취소 기능 - JWT 인증 및 에러 처리 개선
  • Loading branch information
jihwooon authored Mar 14, 2024
2 parents aa7049c + 8faee7d commit 4dca8be
Show file tree
Hide file tree
Showing 14 changed files with 77 additions and 70 deletions.
4 changes: 2 additions & 2 deletions server/src/cartItems/web/cartItem-remove.controller.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ describe('cartItemSave Controller', () => {

context('사용자가 장바구니에 카트 id 입력하면', () => {
it('200 상태코드를 반환한다.', async () => {
const { status, body } = await request(app).delete(`/cart/${existingCartItem.id}`);
const { status, body } = await request(app).delete(`/carts/${existingCartItem.id}`);

expect(status).toBe(200);
expect(body).toEqual(true);
Expand All @@ -34,7 +34,7 @@ describe('cartItemSave Controller', () => {
});

it('400 상태코드와 에러 메세지를 반환한다.', async () => {
const { status, body } = await request(app).delete(`/cart/${nonExistingCartItem.id}`);
const { status, body } = await request(app).delete(`/carts/${nonExistingCartItem.id}`);

expect(status).toBe(400);
expect(body).toEqual({
Expand Down
6 changes: 3 additions & 3 deletions server/src/cartItems/web/cartItem-save.controller.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ import { addToCart } from '../application/cartItem-save.service';
jest.mock('../application/cartItem-save.service.ts');

describe('cartItemSave Controller', () => {
describe('POST /cart', () => {
describe('POST /carts', () => {
beforeEach(() => {
(addToCart as jest.Mock).mockResolvedValue(true);
});

context('사용자가 장바구니에 도서와 수량을 추가하면', () => {
it('201 상태코드를 반환한다.', async () => {
const { status, body } = await request(app).post('/cart').send({
const { status, body } = await request(app).post('/carts').send({
userId: existingCartItem.userId,
bookId: existingCartItem.bookId,
count: existingCartItem.count,
Expand All @@ -38,7 +38,7 @@ describe('cartItemSave Controller', () => {
});

it('400 상태코드와 에러 메세지를 반환한다.', async () => {
const { status, body } = await request(app).post('/cart').send({
const { status, body } = await request(app).post('/carts').send({
userId: nonExistingCartItem.userId,
bookId: nonExistingCartItem.bookId,
count: nonExistingCartItem.count,
Expand Down
2 changes: 2 additions & 0 deletions server/src/fixture/jwt.fixture.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const ACCESS_TOKEN =
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjMsImVtYWlsIjoiYWJjZGVAZ21haWwuY29tIiwiaWF0IjoxNzEwNDIzODUxLCJleHAiOjE3MTA0MjQxNTF9.m1GUUsCHpMtaX3jScLJ-GACfZ_BEUQUnyMxrq_bg5FQ';
24 changes: 13 additions & 11 deletions server/src/likes/application/add-like.service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,34 +6,36 @@ import { existingLike, nonExistingLike } from 'src/fixture/likes.fixture';

import { when } from 'jest-when';

import Like from '../domain/like';
import { validateToken } from 'src/users/jwt/jwt.provider';

import { ACCESS_TOKEN } from 'src/fixture/jwt.fixture';

import { save } from '../domain/likes.repository';
import { addLike } from './add-like.service';

jest.mock('../domain/likes.repository.ts');
jest.mock('../../users/jwt/jwt.provider.ts');

describe('addLike Service', () => {
beforeEach(() => {
when(save as jest.Mock)
.calledWith(
new Like({
userId: existingLike.userId,
likedBookId: existingLike.likedBookId,
}),
)
.mockResolvedValue(true);
when(validateToken as jest.Mock).mockResolvedValue({ userId: existingLike.userId });
when(save as jest.Mock).mockResolvedValue(true);
});

context('사용자 id와 도서 정보 id가 좋아요 추가에 성공하면', () => {
it('true를 반환한다.', async () => {
const savedLike = await addLike(existingLike.userId, existingLike.likedBookId);
const savedLike = await addLike(ACCESS_TOKEN, existingLike.likedBookId);

expect(savedLike).toBe(true);
});
});

context('사용자 id와 도서 정보 id가 좋아요 추가에 실패하면', () => {
beforeEach(() => {
when(save as jest.Mock).mockResolvedValue(false);
});
it('error를 던져야 한다.', async () => {
await expect(addLike(nonExistingLike.userId, nonExistingLike.likedBookId)).rejects.toThrow(
await expect(addLike(ACCESS_TOKEN, nonExistingLike.likedBookId)).rejects.toThrow(
new HttpException('좋아요 추가에 실패했습니다.', StatusCodes.BAD_REQUEST),
);
});
Expand Down
10 changes: 4 additions & 6 deletions server/src/likes/application/add-like.service.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
import { StatusCodes } from 'http-status-codes';
import { validateToken } from 'src/users/jwt/jwt.provider';

import HttpException from 'src/utils/httpException';

import { validateToken } from 'src/users/jwt/jwt.provider';
import { StatusCodes } from 'http-status-codes';

import Like from '../domain/like';
import { save } from '../domain/likes.repository';

export const addLike = async (accessToken: any, likedBookId: number): Promise<boolean> => {
const { userId } = validateToken(accessToken);
const like = new Like({ userId, likedBookId });
const likeData = like.getDataOfLike();

const savedLike = await save(likeData);
const savedLike = await save({ userId, likedBookId });
if (!savedLike) {
throw new HttpException('좋아요 추가에 실패했습니다.', StatusCodes.BAD_REQUEST);
}
Expand Down
17 changes: 12 additions & 5 deletions server/src/likes/application/cancel-like.service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,35 @@ import { existingLike, nonExistingLike } from 'src/fixture/likes.fixture';

import { when } from 'jest-when';

import { ACCESS_TOKEN } from 'src/fixture/jwt.fixture';

import { validateToken } from 'src/users/jwt/jwt.provider';

import { deleteByUserIdAndLikedBookId } from '../domain/likes.repository';
import { cancelLike } from './cancel-like.service';

jest.mock('../domain/likes.repository.ts');
jest.mock('../../users/jwt/jwt.provider.ts');

describe('cancelLike Service', () => {
beforeEach(() => {
when(deleteByUserIdAndLikedBookId as jest.Mock)
.calledWith(existingLike.userId, existingLike.likedBookId)
.mockResolvedValue(true);
when(validateToken as jest.Mock).mockResolvedValue({ userId: existingLike.userId });
when(deleteByUserIdAndLikedBookId as jest.Mock).mockResolvedValue(true);
});
context('사용자 id와 도서 정보 id가 좋아요 취소에 성공하면', () => {
it('true를 반환한다.', async () => {
const canceledLike = await cancelLike(existingLike.userId, existingLike.likedBookId);
const canceledLike = await cancelLike(ACCESS_TOKEN, existingLike.likedBookId);

expect(canceledLike).toBe(true);
});
});

context('사용자 id와 도서 정보 id가 좋아요 취소에 실패하면', () => {
beforeEach(() => {
when(deleteByUserIdAndLikedBookId as jest.Mock).mockResolvedValue(false);
});
it('error를 던져야 한다.', async () => {
await expect(cancelLike(nonExistingLike.userId, nonExistingLike.likedBookId)).rejects.toThrow(
await expect(cancelLike(ACCESS_TOKEN, nonExistingLike.likedBookId)).rejects.toThrow(
new HttpException('좋아요 취소에 실패했습니다.', StatusCodes.BAD_REQUEST),
);
});
Expand Down
6 changes: 5 additions & 1 deletion server/src/likes/application/cancel-like.service.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import { StatusCodes } from 'http-status-codes';
import HttpException from 'src/utils/httpException';

import { validateToken } from 'src/users/jwt/jwt.provider';

import { deleteByUserIdAndLikedBookId } from '../domain/likes.repository';

export const cancelLike = async (userId: number, likedBookId: number): Promise<boolean> => {
export const cancelLike = async (accessToken: any, likedBookId: number): Promise<boolean> => {
const { userId } = validateToken(accessToken);
const canceledLike = await deleteByUserIdAndLikedBookId(userId, likedBookId);

if (!canceledLike) {
throw new HttpException('좋아요 취소에 실패했습니다.', StatusCodes.BAD_REQUEST);
}
Expand Down
10 changes: 8 additions & 2 deletions server/src/likes/web/cancel-like.controller.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ describe('cancelLike Controller', () => {
it('200 상태코드를 반환한다.', async () => {
const { statusCode, body } = await request(app)
.delete(`/likes/${existingLike.likedBookId}`)
.send({ userId: existingLike.userId });
.set(
'Authorization',
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjMsImVtYWlsIjoiYWJjZGVAZ21haWwuY29tIiwiaWF0IjoxNzEwNDIzODUxLCJleHAiOjE3MTA0MjQxNTF9.m1GUUsCHpMtaX3jScLJ-GACfZ_BEUQUnyMxrq_bg5FQ',
);

expect(statusCode).toBe(200);
expect(body).toBe(true);
Expand All @@ -35,7 +38,10 @@ describe('cancelLike Controller', () => {
it('400 상태코드와 에러 메세지를 반환한다.', async () => {
const { statusCode, body } = await request(app)
.delete(`/likes/${nonExistingLike.likedBookId}`)
.send({ userId: nonExistingLike.userId });
.set(
'Authorization',
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjMsImVtYWlsIjoiYWJjZGVAZ21haWwuY29tIiwiaWF0IjoxNzEwNDIzODUxLCJleHAiOjE3MTA0MjQxNTF9.m1GUUsCHpMtaX3jScLJ-GACfZ_BEUQUnyMxrq_bg5FQ',
);

expect(statusCode).toBe(400);
expect(body).toEqual({
Expand Down
5 changes: 3 additions & 2 deletions server/src/likes/web/cancel-like.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import { StatusCodes } from 'http-status-codes';

import { cancelLike } from '../application/cancel-like.service';

const cancelLikeHandler = ({ params: { id }, body: { userId } }: Request, res: Response) => {
ResponseHandler(() => cancelLike(userId, Number(id)), StatusCodes.OK, res);
const cancelLikeHandler = ({ params: { id }, headers }: Request, res: Response) => {
const accessToken = headers.authorization;
ResponseHandler(() => cancelLike(accessToken, Number(id)), StatusCodes.OK, res);
};

export default cancelLikeHandler;
25 changes: 1 addition & 24 deletions server/src/orders/web/orders-detail.controller.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,31 +6,8 @@ describe('getOrdersDetailHandler', () => {
context('주문 상세 상품 조회가 성공하면', () => {
it('상태코드 200과 응답 정보를 반환한다.', async () => {
const { status, body } = await request(app).get('/orders/1');

expect(status).toBe(200);
expect(body).toEqual([
{
bookId: 1,
bookTitle: '홍길동전',
author: '작가미상',
price: 8000,
count: 2,
},
{
bookId: 2,
bookTitle: '러닝 리액트',
author: '알렉스 뱅크스',
price: 25000,
count: 1,
},
{
bookId: 3,
bookTitle: '우아한 타입스크립트 with 리액트',
author: '우아한형제들 웹프론트그룹 지음',
price: 22500,
count: 10,
},
]);
expect(body).toEqual([{ author: '걍구두', bookId: 2, bookTitle: '신델렐라', price: 20000, quantity: 3 }]);
});
});
});
Expand Down
24 changes: 15 additions & 9 deletions server/src/orders/web/orders-list.controller.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,22 @@ describe('getAllOrdersHandler Controller', () => {
expect(status).toBe(200);
expect(body).toEqual([
{
address: '강원도 춘천시 동내면 대룡산길 227-314 24408 한국',
bookTitle: '어린왕자',
contact: '010-1234-5667',
orderId: 1,
createAt: expect.any(String),
delivery: {
address: '서울시 경인로',
receiver: '홍길동',
contact: '010-1234-5667',
},
bookTitle: '홍길동전',
totalPrice: 25000,
totalCount: 5,
receiver: '홍길동',
totalPrice: 6000,
totalQuantity: 3,
},
{
address: '강원도 춘천시 동내면 대룡산길 227-314 24408 한국',
bookTitle: '어린왕자',
contact: '010-1234-5667',
orderId: 2,
receiver: '홍길동',
totalPrice: 6000,
totalQuantity: 3,
},
]);
});
Expand Down
4 changes: 2 additions & 2 deletions server/src/orders/web/orders-save.controller.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import request from 'supertest';

import { order } from '../application/orders-save.service';

jest.mock('../application/order-save.service.ts');
jest.mock('../application/orders-save.service.ts');

describe('saveOrdersHandler', () => {
describe('POST /orders', () => {
Expand Down Expand Up @@ -36,7 +36,7 @@ describe('saveOrdersHandler', () => {
totalPrice: 50000,
});

expect(status).toBe(200);
expect(status).toBe(201);
expect(body).toEqual('주문 등록 완료');
});
});
Expand Down
6 changes: 4 additions & 2 deletions server/src/users/jwt/jwt.provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,11 @@ export const validateToken = (token: string) => {
}

try {
const decodedJwt = jwt.verify(token, process.env.JWT_SECRET as Secret) as jwt.JwtPayload;
const { userId } = jwt.verify(token, process.env.JWT_SECRET as Secret) as jwt.JwtPayload;

return decodedJwt;
return {
userId,
};
} catch (e) {
throw new HttpException('인증 할 수 없는 token 입니다', StatusCodes.UNAUTHORIZED);
}
Expand Down
4 changes: 3 additions & 1 deletion server/src/users/signin/web/signin.controller.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ describe('signin Controller', () => {
});

expect(statusCode).toBe(200);
expect(body).toBe(ACCESS_TOKEN);
expect(body).toEqual({
token: ACCESS_TOKEN,
});
});
});

Expand Down

0 comments on commit 4dca8be

Please sign in to comment.