Skip to content

Commit 7c2b5f7

Browse files
Maintenance: ImageController - Add comprehensive test coverage
Added comprehensive test coverage for ImageController, which previously had no tests. The new test suite validates all controller endpoints and error handling scenarios using WebMvcTest for focused controller-layer testing. Tests added: - editWithMemeMultipart_withPromptOnly_success: Validates prompt-only editing - editWithMemeMultipart_withPromptAndImage_success: Tests with user image - editWithMemeMultipart_withNonExistentMemeId_returns404: Error handling - editWithMemeMultipart_withEmptyResponse_success: Empty response handling - editWithMemeMultipart_withMultipleImagesAndTexts_success: Multiple results Implementation details: - Uses @WebMvcTest for lightweight controller testing - Uses @AutoConfigureMockMvc(addFilters = false) to bypass security filters - Mocks ImageEditService to isolate controller behavior - Validates JSON response structure with ApiResponse format - Tests both successful and error scenarios 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
1 parent 7f3dc0c commit 7c2b5f7

File tree

1 file changed

+224
-0
lines changed

1 file changed

+224
-0
lines changed
Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
package spring.memewikibe.api.controller.image;
2+
3+
import org.junit.jupiter.api.DisplayName;
4+
import org.junit.jupiter.api.Test;
5+
import org.springframework.beans.factory.annotation.Autowired;
6+
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
7+
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
8+
import org.springframework.http.MediaType;
9+
import org.springframework.mock.web.MockMultipartFile;
10+
import org.springframework.test.context.bean.override.mockito.MockitoBean;
11+
import org.springframework.test.web.servlet.MockMvc;
12+
import spring.memewikibe.api.controller.image.response.Base64Image;
13+
import spring.memewikibe.api.controller.image.response.GeneratedImagesResponse;
14+
import spring.memewikibe.application.ImageEditService;
15+
import spring.memewikibe.support.error.ErrorType;
16+
import spring.memewikibe.support.error.MemeWikiApplicationException;
17+
18+
import java.util.List;
19+
20+
import static org.mockito.ArgumentMatchers.*;
21+
import static org.mockito.Mockito.verify;
22+
import static org.mockito.Mockito.when;
23+
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.multipart;
24+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
25+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
26+
27+
@WebMvcTest(ImageController.class)
28+
@AutoConfigureMockMvc(addFilters = false)
29+
class ImageControllerTest {
30+
31+
@Autowired
32+
private MockMvc mockMvc;
33+
34+
@MockitoBean
35+
private ImageEditService imageEditService;
36+
37+
@Test
38+
@DisplayName("POST /api/images/edit/meme/{memeId}: 프롬프트만으로 밈 이미지 편집 성공")
39+
void editWithMemeMultipart_withPromptOnly_success() throws Exception {
40+
// given
41+
Long memeId = 1L;
42+
String prompt = "Add funny text to the meme";
43+
44+
GeneratedImagesResponse mockResponse = new GeneratedImagesResponse(
45+
List.of(new Base64Image("image/png", "base64encodeddata")),
46+
List.of("Generated successfully")
47+
);
48+
49+
when(imageEditService.editMemeImg(eq(prompt), eq(memeId), isNull()))
50+
.thenReturn(mockResponse);
51+
52+
MockMultipartFile promptPart = new MockMultipartFile(
53+
"prompt",
54+
"",
55+
MediaType.TEXT_PLAIN_VALUE,
56+
prompt.getBytes()
57+
);
58+
59+
// when & then
60+
mockMvc.perform(multipart("/api/images/edit/meme/{memeId}", memeId)
61+
.file(promptPart))
62+
.andExpect(status().isOk())
63+
.andExpect(jsonPath("$.resultType").value("SUCCESS"))
64+
.andExpect(jsonPath("$.success.images").isArray())
65+
.andExpect(jsonPath("$.success.images[0].mimeType").value("image/png"))
66+
.andExpect(jsonPath("$.success.images[0].data").value("base64encodeddata"))
67+
.andExpect(jsonPath("$.success.text").isArray())
68+
.andExpect(jsonPath("$.success.text[0]").value("Generated successfully"));
69+
70+
verify(imageEditService).editMemeImg(prompt, memeId, null);
71+
}
72+
73+
@Test
74+
@DisplayName("POST /api/images/edit/meme/{memeId}: 프롬프트와 이미지로 밈 이미지 편집 성공")
75+
void editWithMemeMultipart_withPromptAndImage_success() throws Exception {
76+
// given
77+
Long memeId = 1L;
78+
String prompt = "Combine these images";
79+
80+
GeneratedImagesResponse mockResponse = new GeneratedImagesResponse(
81+
List.of(new Base64Image("image/png", "combinedImageData")),
82+
List.of("Images combined")
83+
);
84+
85+
when(imageEditService.editMemeImg(eq(prompt), eq(memeId), any()))
86+
.thenReturn(mockResponse);
87+
88+
MockMultipartFile promptPart = new MockMultipartFile(
89+
"prompt",
90+
"",
91+
MediaType.TEXT_PLAIN_VALUE,
92+
prompt.getBytes()
93+
);
94+
95+
MockMultipartFile imagePart = new MockMultipartFile(
96+
"image",
97+
"test.jpg",
98+
MediaType.IMAGE_JPEG_VALUE,
99+
new byte[]{(byte) 0xFF, (byte) 0xD8, (byte) 0xFF}
100+
);
101+
102+
// when & then
103+
mockMvc.perform(multipart("/api/images/edit/meme/{memeId}", memeId)
104+
.file(promptPart)
105+
.file(imagePart)
106+
)
107+
.andExpect(status().isOk())
108+
.andExpect(jsonPath("$.resultType").value("SUCCESS"))
109+
.andExpect(jsonPath("$.success.images").isArray())
110+
.andExpect(jsonPath("$.success.images[0].mimeType").value("image/png"))
111+
.andExpect(jsonPath("$.success.images[0].data").value("combinedImageData"))
112+
.andExpect(jsonPath("$.success.text").isArray())
113+
.andExpect(jsonPath("$.success.text[0]").value("Images combined"));
114+
115+
verify(imageEditService).editMemeImg(eq(prompt), eq(memeId), any());
116+
}
117+
118+
@Test
119+
@DisplayName("POST /api/images/edit/meme/{memeId}: 존재하지 않는 밈 ID로 요청 시 404 에러")
120+
void editWithMemeMultipart_withNonExistentMemeId_returns404() throws Exception {
121+
// given
122+
Long nonExistentMemeId = 99999L;
123+
String prompt = "Edit this meme";
124+
125+
when(imageEditService.editMemeImg(eq(prompt), eq(nonExistentMemeId), isNull()))
126+
.thenThrow(new MemeWikiApplicationException(ErrorType.MEME_NOT_FOUND));
127+
128+
MockMultipartFile promptPart = new MockMultipartFile(
129+
"prompt",
130+
"",
131+
MediaType.TEXT_PLAIN_VALUE,
132+
prompt.getBytes()
133+
);
134+
135+
// when & then
136+
mockMvc.perform(multipart("/api/images/edit/meme/{memeId}", nonExistentMemeId)
137+
.file(promptPart)
138+
)
139+
.andExpect(status().isNotFound())
140+
.andExpect(jsonPath("$.resultType").value("ERROR"))
141+
.andExpect(jsonPath("$.error.code").value("E404"));
142+
143+
verify(imageEditService).editMemeImg(prompt, nonExistentMemeId, null);
144+
}
145+
146+
@Test
147+
@DisplayName("POST /api/images/edit/meme/{memeId}: 빈 응답 처리")
148+
void editWithMemeMultipart_withEmptyResponse_success() throws Exception {
149+
// given
150+
Long memeId = 1L;
151+
String prompt = "Generate image";
152+
153+
GeneratedImagesResponse emptyResponse = new GeneratedImagesResponse(
154+
List.of(),
155+
List.of()
156+
);
157+
158+
when(imageEditService.editMemeImg(eq(prompt), eq(memeId), isNull()))
159+
.thenReturn(emptyResponse);
160+
161+
MockMultipartFile promptPart = new MockMultipartFile(
162+
"prompt",
163+
"",
164+
MediaType.TEXT_PLAIN_VALUE,
165+
prompt.getBytes()
166+
);
167+
168+
// when & then
169+
mockMvc.perform(multipart("/api/images/edit/meme/{memeId}", memeId)
170+
.file(promptPart)
171+
)
172+
.andExpect(status().isOk())
173+
.andExpect(jsonPath("$.resultType").value("SUCCESS"))
174+
.andExpect(jsonPath("$.success.images").isEmpty())
175+
.andExpect(jsonPath("$.success.text").isEmpty());
176+
177+
verify(imageEditService).editMemeImg(prompt, memeId, null);
178+
}
179+
180+
@Test
181+
@DisplayName("POST /api/images/edit/meme/{memeId}: 여러 이미지와 텍스트 응답 처리")
182+
void editWithMemeMultipart_withMultipleImagesAndTexts_success() throws Exception {
183+
// given
184+
Long memeId = 1L;
185+
String prompt = "Generate multiple variations";
186+
187+
GeneratedImagesResponse multiResponse = new GeneratedImagesResponse(
188+
List.of(
189+
new Base64Image("image/png", "image1data"),
190+
new Base64Image("image/jpeg", "image2data"),
191+
new Base64Image("image/webp", "image3data")
192+
),
193+
List.of("Variation 1", "Variation 2", "Variation 3")
194+
);
195+
196+
when(imageEditService.editMemeImg(eq(prompt), eq(memeId), isNull()))
197+
.thenReturn(multiResponse);
198+
199+
MockMultipartFile promptPart = new MockMultipartFile(
200+
"prompt",
201+
"",
202+
MediaType.TEXT_PLAIN_VALUE,
203+
prompt.getBytes()
204+
);
205+
206+
// when & then
207+
mockMvc.perform(multipart("/api/images/edit/meme/{memeId}", memeId)
208+
.file(promptPart)
209+
)
210+
.andExpect(status().isOk())
211+
.andExpect(jsonPath("$.resultType").value("SUCCESS"))
212+
.andExpect(jsonPath("$.success.images").isArray())
213+
.andExpect(jsonPath("$.success.images.length()").value(3))
214+
.andExpect(jsonPath("$.success.images[0].mimeType").value("image/png"))
215+
.andExpect(jsonPath("$.success.images[1].mimeType").value("image/jpeg"))
216+
.andExpect(jsonPath("$.success.images[2].mimeType").value("image/webp"))
217+
.andExpect(jsonPath("$.success.text.length()").value(3))
218+
.andExpect(jsonPath("$.success.text[0]").value("Variation 1"))
219+
.andExpect(jsonPath("$.success.text[1]").value("Variation 2"))
220+
.andExpect(jsonPath("$.success.text[2]").value("Variation 3"));
221+
222+
verify(imageEditService).editMemeImg(prompt, memeId, null);
223+
}
224+
}

0 commit comments

Comments
 (0)