diff --git a/features/connect/apis/fetchPostsClient.test.ts b/features/connect/apis/fetchPostsClient.test.ts
new file mode 100644
index 00000000..0096d344
--- /dev/null
+++ b/features/connect/apis/fetchPostsClient.test.ts
@@ -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("좋아요 취소 실패");
+ });
+});
diff --git a/features/connect/comment/mappers.test.ts b/features/connect/comment/mappers.test.ts
index 8670ac41..cee7eecc 100644
--- a/features/connect/comment/mappers.test.ts
+++ b/features/connect/comment/mappers.test.ts
@@ -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: "김댓글",
@@ -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 매핑", () => {
@@ -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: "굵게 & '따옴표'" });
+
+ expect(result.content).toBe("굵게 & '따옴표'");
+ });
+
+ it("id가 0이어도 올바르게 매핑된다", () => {
+ const result = mapCommentToCard({ ...baseComment, id: 0 });
+
+ expect(result.id).toBe(0);
+ });
});
});
diff --git a/features/connect/post/mappers.test.ts b/features/connect/post/mappers.test.ts
index 33c7c3c2..9def92d5 100644
--- a/features/connect/post/mappers.test.ts
+++ b/features/connect/post/mappers.test.ts
@@ -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");
- });
- });
});