Skip to content

Commit b1b386c

Browse files
committed
test: 메모 RestDocs 테스트 및 API 문서 작성
1 parent 4af3b86 commit b1b386c

4 files changed

Lines changed: 436 additions & 1 deletion

File tree

api/src/docs/asciidoc/index.adoc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,5 @@ include::auth-api.adoc[]
1515
include::todo-api.adoc[]
1616
include::friend-api.adoc[]
1717
include::category-api.adoc[]
18-
include::ledger-api.adoc[]
18+
include::ledger-api.adoc[]
19+
include::memo-api.adoc[]
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
= 메모 API
2+
3+
== 카테고리
4+
5+
=== 카테고리 목록 조회
6+
7+
operation::readMemoCategories[snippets='response-fields,http-request,http-response']
8+
9+
=== 카테고리 추가
10+
11+
operation::addMemoCategory[snippets='request-fields,response-fields,http-request,http-response']
12+
13+
=== 카테고리 수정
14+
15+
operation::modifyMemoCategory[snippets='path-parameters,request-fields,response-fields,http-request,http-response']
16+
17+
=== 카테고리 삭제
18+
19+
operation::removeMemoCategory[snippets='path-parameters,response-fields,http-request,http-response']
20+
21+
== 메모
22+
23+
=== 메모 전체 목록 조회
24+
25+
operation::readMemos[snippets='response-fields,http-request,http-response']
26+
27+
=== 메모 추가
28+
29+
operation::addMemo[snippets='request-fields,response-fields,http-request,http-response']
30+
31+
=== 메모 수정
32+
33+
operation::modifyMemo[snippets='path-parameters,request-fields,response-fields,http-request,http-response']
34+
35+
=== 메모 삭제
36+
37+
operation::removeMemo[snippets='path-parameters,response-fields,http-request,http-response']
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
package com.benecia.lifetracker.domain.memo.api
2+
3+
import com.benecia.lifetracker.domain.memo.dto.AddMemoCategoryRequest
4+
import com.benecia.lifetracker.domain.memo.dto.ModifyMemoCategoryRequest
5+
import com.benecia.lifetracker.memocore.model.info.MemoCategoryInfo
6+
import com.benecia.lifetracker.memocore.service.MemoCategoryService
7+
import com.benecia.lifetracker.security.userdetails.LoginUser
8+
import com.benecia.lifetracker.test.api.RestDocsTest
9+
import com.benecia.lifetracker.test.api.RestDocsUtils.requestPreprocessor
10+
import com.benecia.lifetracker.test.api.RestDocsUtils.responsePreprocessor
11+
import io.mockk.every
12+
import io.mockk.mockk
13+
import io.restassured.http.ContentType
14+
import org.junit.jupiter.api.BeforeEach
15+
import org.junit.jupiter.api.Tag
16+
import org.junit.jupiter.api.Test
17+
import org.springframework.http.HttpStatus
18+
import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document
19+
import org.springframework.restdocs.payload.JsonFieldType
20+
import org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath
21+
import org.springframework.restdocs.payload.PayloadDocumentation.requestFields
22+
import org.springframework.restdocs.payload.PayloadDocumentation.responseFields
23+
import org.springframework.restdocs.request.RequestDocumentation.parameterWithName
24+
import org.springframework.restdocs.request.RequestDocumentation.pathParameters
25+
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
26+
import org.springframework.security.core.context.SecurityContextHolder
27+
import java.util.UUID
28+
29+
@Tag("restdocs")
30+
class MemoCategoryControllerTest : RestDocsTest() {
31+
32+
private lateinit var memoCategoryService: MemoCategoryService
33+
private lateinit var controller: MemoCategoryController
34+
35+
@BeforeEach
36+
fun setUp() {
37+
memoCategoryService = mockk()
38+
controller = MemoCategoryController(memoCategoryService)
39+
mockMvc = mockController(controller)
40+
}
41+
42+
private fun setupAuth(userId: UUID) {
43+
val loginUser = LoginUser(
44+
id = userId,
45+
email = "test@test.com",
46+
displayName = "테스트유저",
47+
profileImageUrl = "https://profile.com/img.png",
48+
)
49+
val auth = UsernamePasswordAuthenticationToken(loginUser, null, loginUser.authorities)
50+
SecurityContextHolder.getContext().authentication = auth
51+
}
52+
53+
@Test
54+
fun readCategories() {
55+
val userId = UUID.randomUUID()
56+
setupAuth(userId)
57+
58+
every { memoCategoryService.findAll(userId) } returns listOf(
59+
MemoCategoryInfo(id = 1L, name = "일상", icon = "icon-daily", color = "#FF6B6B"),
60+
MemoCategoryInfo(id = 2L, name = "업무", icon = "icon-work", color = "#4ECDC4"),
61+
)
62+
63+
given()
64+
.contentType(ContentType.JSON)
65+
.get("/api/v1/memos/categories")
66+
.then()
67+
.status(HttpStatus.OK)
68+
.apply(
69+
document(
70+
"readMemoCategories",
71+
requestPreprocessor(),
72+
responsePreprocessor(),
73+
responseFields(
74+
fieldWithPath("status").type(JsonFieldType.NUMBER).description("HTTP 상태 코드"),
75+
fieldWithPath("message").type(JsonFieldType.STRING).description("응답 메시지"),
76+
fieldWithPath("data[].id").type(JsonFieldType.NUMBER).description("카테고리 ID"),
77+
fieldWithPath("data[].name").type(JsonFieldType.STRING).description("카테고리 이름"),
78+
fieldWithPath("data[].icon").type(JsonFieldType.STRING).description("카테고리 아이콘"),
79+
fieldWithPath("data[].color").type(JsonFieldType.STRING).description("카테고리 색상"),
80+
fieldWithPath("timestamp").type(JsonFieldType.STRING).description("응답 생성 시간"),
81+
),
82+
),
83+
)
84+
}
85+
86+
@Test
87+
fun addCategory() {
88+
val userId = UUID.randomUUID()
89+
setupAuth(userId)
90+
91+
every { memoCategoryService.add(userId, any()) } returns 1L
92+
93+
given()
94+
.contentType(ContentType.JSON)
95+
.body(AddMemoCategoryRequest(name = "일상", icon = "icon-daily", color = "#FF6B6B"))
96+
.post("/api/v1/memos/categories")
97+
.then()
98+
.status(HttpStatus.CREATED)
99+
.apply(
100+
document(
101+
"addMemoCategory",
102+
requestPreprocessor(),
103+
responsePreprocessor(),
104+
requestFields(
105+
fieldWithPath("name").type(JsonFieldType.STRING).description("카테고리 이름"),
106+
fieldWithPath("icon").type(JsonFieldType.STRING).description("카테고리 아이콘"),
107+
fieldWithPath("color").type(JsonFieldType.STRING).description("카테고리 색상"),
108+
),
109+
responseFields(
110+
fieldWithPath("status").type(JsonFieldType.NUMBER).description("HTTP 상태 코드"),
111+
fieldWithPath("message").type(JsonFieldType.STRING).description("응답 메시지"),
112+
fieldWithPath("data.id").type(JsonFieldType.NUMBER).description("생성된 카테고리 ID"),
113+
fieldWithPath("timestamp").type(JsonFieldType.STRING).description("응답 생성 시간"),
114+
),
115+
),
116+
)
117+
}
118+
119+
@Test
120+
fun modifyCategory() {
121+
val userId = UUID.randomUUID()
122+
setupAuth(userId)
123+
124+
every { memoCategoryService.modify(userId, 1L, any()) } returns 1L
125+
126+
given()
127+
.contentType(ContentType.JSON)
128+
.body(ModifyMemoCategoryRequest(name = "일상 수정", color = "#FF0000"))
129+
.patch("/api/v1/memos/categories/{id}", 1L)
130+
.then()
131+
.status(HttpStatus.OK)
132+
.apply(
133+
document(
134+
"modifyMemoCategory",
135+
requestPreprocessor(),
136+
responsePreprocessor(),
137+
pathParameters(
138+
parameterWithName("id").description("카테고리 ID"),
139+
),
140+
requestFields(
141+
fieldWithPath("name").type(JsonFieldType.STRING).description("카테고리 이름, null 가능").optional(),
142+
fieldWithPath("icon").type(JsonFieldType.STRING).description("카테고리 아이콘, null 가능").optional(),
143+
fieldWithPath("color").type(JsonFieldType.STRING).description("카테고리 색상, null 가능").optional(),
144+
),
145+
responseFields(
146+
fieldWithPath("status").type(JsonFieldType.NUMBER).description("HTTP 상태 코드"),
147+
fieldWithPath("message").type(JsonFieldType.STRING).description("응답 메시지"),
148+
fieldWithPath("data.id").type(JsonFieldType.NUMBER).description("수정된 카테고리 ID"),
149+
fieldWithPath("timestamp").type(JsonFieldType.STRING).description("응답 생성 시간"),
150+
),
151+
),
152+
)
153+
}
154+
155+
@Test
156+
fun removeCategory() {
157+
val userId = UUID.randomUUID()
158+
setupAuth(userId)
159+
160+
every { memoCategoryService.remove(userId, 1L) } returns 1L
161+
162+
given()
163+
.contentType(ContentType.JSON)
164+
.delete("/api/v1/memos/categories/{id}", 1L)
165+
.then()
166+
.status(HttpStatus.OK)
167+
.apply(
168+
document(
169+
"removeMemoCategory",
170+
requestPreprocessor(),
171+
responsePreprocessor(),
172+
pathParameters(
173+
parameterWithName("id").description("카테고리 ID"),
174+
),
175+
responseFields(
176+
fieldWithPath("status").type(JsonFieldType.NUMBER).description("HTTP 상태 코드"),
177+
fieldWithPath("message").type(JsonFieldType.STRING).description("응답 메시지"),
178+
fieldWithPath("data.id").type(JsonFieldType.NUMBER).description("삭제된 카테고리 ID"),
179+
fieldWithPath("timestamp").type(JsonFieldType.STRING).description("응답 생성 시간"),
180+
),
181+
),
182+
)
183+
}
184+
}

0 commit comments

Comments
 (0)