Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 93 additions & 0 deletions features/connect/apis/fetchPostsClient.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { server } from "@/mocks/server";
import { http, HttpResponse } from "msw";
import { fetchPostsClient, toggleConnectLike, deleteConnectLike } from "./fetchPostsClient";
import { BASE_URL } from "@/mocks/constants";

// 각 테스트 전후 MSW 서버 생명주기 관리
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers()); // 테스트마다 핸들러 초기화
afterAll(() => server.close());

describe("fetchPostsClient", () => {
// 정상 흐름
it("정상 응답 시 게시글 목록을 반환한다", async () => {
const result = await fetchPostsClient({ type: "all", limit: 5 });
expect(result.data).toBeDefined();
});

// 반환 타입 검증
it("반환 데이터가 배열이다", async () => {
const result = await fetchPostsClient({ type: "all", limit: 5 });
expect(Array.isArray(result.data)).toBe(true);
});

// type 파라미터 동작 검증
it("type=best로 HOT 게시글을 조회할 수 있다", async () => {
const result = await fetchPostsClient({ type: "best", limit: 20 });
expect(result.data).toBeDefined();
});

// keyword 파라미터 동작 검증
it("keyword 파라미터를 포함해 요청할 수 있다", async () => {
const result = await fetchPostsClient({ type: "all", keyword: "달램핏" });
expect(result.data).toBeDefined();
});

// 500 에러 → throw 검증
it("서버 에러 시 에러를 throw한다", async () => {
server.use(http.get(`${BASE_URL}/posts`, () => HttpResponse.json({}, { status: 500 })));
await expect(fetchPostsClient({ type: "all" })).rejects.toThrow("게시글 조회 실패");
});

// 404 에러 → throw 검증
it("404 응답 시 에러를 throw한다", async () => {
server.use(http.get(`${BASE_URL}/posts`, () => HttpResponse.json({}, { status: 404 })));
await expect(fetchPostsClient({ type: "all" })).rejects.toThrow("게시글 조회 실패");
});
});

describe("toggleConnectLike", () => {
// 정상 흐름
it("정상 응답 시 에러 없이 완료된다", async () => {
await expect(toggleConnectLike(1)).resolves.not.toThrow();
});

// 500 에러 → throw 검증
it("서버 에러 시 에러를 throw한다", async () => {
server.use(
http.post(`${BASE_URL}/posts/:postId/like`, () => HttpResponse.json({}, { status: 500 })),
);
await expect(toggleConnectLike(1)).rejects.toThrow("좋아요 요청 실패");
});

// 존재하지 않는 게시글 → throw 검증
it("존재하지 않는 게시글에 좋아요 시 에러를 throw한다", async () => {
server.use(
http.post(`${BASE_URL}/posts/:postId/like`, () => HttpResponse.json({}, { status: 404 })),
);
await expect(toggleConnectLike(9999)).rejects.toThrow("좋아요 요청 실패");
});
});

describe("deleteConnectLike", () => {
// 정상 흐름
it("정상 응답 시 에러 없이 완료된다", async () => {
await expect(deleteConnectLike(1)).resolves.not.toThrow();
});

// 500 에러 → throw 검증
it("서버 에러 시 에러를 throw한다", async () => {
server.use(
http.delete(`${BASE_URL}/posts/:postId/like`, () => HttpResponse.json({}, { status: 500 })),
);
await expect(deleteConnectLike(1)).rejects.toThrow("좋아요 취소 실패");
});

// 존재하지 않는 게시글 → throw 검증
it("존재하지 않는 게시글에 좋아요 취소 시 에러를 throw한다", async () => {
server.use(
http.delete(`${BASE_URL}/posts/:postId/like`, () => HttpResponse.json({}, { status: 404 })),
);
await expect(deleteConnectLike(9999)).rejects.toThrow("좋아요 취소 실패");
});
});
43 changes: 43 additions & 0 deletions features/connect/comment/mappers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ const baseComment: PostComment = {
id: 10,
content: "좋은 글이에요!",
createdAt: "2024-02-01T12:00:00.000Z",
likeCount: 0,
isLiked: false,
author: {
id: 99,
name: "김댓글",
Expand All @@ -26,6 +28,20 @@ describe("mapCommentToCard", () => {

expect(result.authorName).toBe("김댓글");
});

it("반환 객체가 CommentCardItem에 맞는 구조를 가진다", () => {
const result = mapCommentToCard(baseComment);

expect(result).toEqual({
id: 10,
content: "좋은 글이에요!",
authorName: "김댓글",
authorImage: "https://example.com/avatar.jpg",
date: new Date("2024-02-01T12:00:00.000Z").getTime(),
likeCount: 0,
isLiked: false,
});
});
});

describe("authorImage 매핑", () => {
Expand Down Expand Up @@ -59,5 +75,32 @@ describe("mapCommentToCard", () => {

expect(result.date).not.toBe("2024-02-01T12:00:00.000Z");
});

it("서로 다른 createdAt은 서로 다른 date를 반환한다", () => {
const comment1 = { ...baseComment, createdAt: "2024-01-01T00:00:00.000Z" };
const comment2 = { ...baseComment, createdAt: "2024-06-01T00:00:00.000Z" };

expect(mapCommentToCard(comment1).date).not.toBe(mapCommentToCard(comment2).date);
});
});

describe("엣지 케이스", () => {
it("content가 빈 문자열이어도 그대로 매핑된다", () => {
const result = mapCommentToCard({ ...baseComment, content: "" });

expect(result.content).toBe("");
});

it("content에 특수문자가 있어도 그대로 매핑된다", () => {
const result = mapCommentToCard({ ...baseComment, content: "<b>굵게</b> & '따옴표'" });

expect(result.content).toBe("<b>굵게</b> & '따옴표'");
});

it("id가 0이어도 올바르게 매핑된다", () => {
const result = mapCommentToCard({ ...baseComment, id: 0 });

expect(result.id).toBe(0);
});
});
});
15 changes: 0 additions & 15 deletions features/connect/post/mappers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,19 +84,4 @@ describe("mapPostToCard", () => {
expect(result.imageUrl).toBeNull();
});
});

describe("createdAt → date 변환", () => {
it("createdAt 문자열이 timestamp(number)로 변환된다", () => {
const result = mapPostToCard(basePost);

expect(typeof result.date).toBe("number");
expect(result.date).toBe(new Date("2024-01-15T09:00:00.000Z").getTime());
});

it("date가 문자열이 아닌 숫자 타입이다", () => {
const result = mapPostToCard(basePost);

expect(result.date).not.toBe("2024-01-15T09:00:00.000Z");
});
});
});
Loading