Skip to content

Commit 4d09007

Browse files
committed
feat/OPS-246 : 개인 / 공유 파일 관리 분리
2 parents 1496f7b + 0beddbd commit 4d09007

File tree

1 file changed

+270
-0
lines changed

1 file changed

+270
-0
lines changed
Lines changed: 270 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,270 @@
1+
package org.tuna.zoopzoop.backend.domain.datasource.controller;
2+
3+
import com.fasterxml.jackson.databind.ObjectMapper;
4+
import org.junit.jupiter.api.BeforeAll;
5+
import org.junit.jupiter.api.DisplayName;
6+
import org.junit.jupiter.api.Test;
7+
import org.junit.jupiter.api.TestInstance;
8+
import org.mockito.Mock;
9+
import org.mockito.Mockito;
10+
import org.springframework.beans.factory.annotation.Autowired;
11+
import org.springframework.beans.factory.annotation.Qualifier;
12+
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
13+
import org.springframework.boot.test.context.SpringBootTest;
14+
import org.springframework.boot.test.context.TestConfiguration;
15+
import org.springframework.context.annotation.Bean;
16+
import org.springframework.context.annotation.Primary;
17+
import org.springframework.http.MediaType;
18+
import org.springframework.security.test.context.support.TestExecutionEvent;
19+
import org.springframework.security.test.context.support.WithUserDetails;
20+
import org.springframework.test.context.ActiveProfiles;
21+
import org.springframework.test.web.servlet.MockMvc;
22+
import org.springframework.transaction.annotation.Transactional;
23+
import org.tuna.zoopzoop.backend.domain.archive.folder.dto.FolderResponse;
24+
import org.tuna.zoopzoop.backend.domain.archive.folder.entity.Folder;
25+
import org.tuna.zoopzoop.backend.domain.archive.folder.repository.FolderRepository;
26+
import org.tuna.zoopzoop.backend.domain.archive.folder.service.PersonalArchiveFolderService;
27+
import org.tuna.zoopzoop.backend.domain.datasource.dataprocessor.service.DataProcessorService;
28+
import org.tuna.zoopzoop.backend.domain.datasource.dto.DataSourceDto;
29+
import org.tuna.zoopzoop.backend.domain.datasource.dto.reqBodyForCreateDataSource;
30+
import org.tuna.zoopzoop.backend.domain.datasource.dto.reqBodyForMoveDataSource;
31+
import org.tuna.zoopzoop.backend.domain.datasource.entity.Category;
32+
import org.tuna.zoopzoop.backend.domain.datasource.entity.DataSource;
33+
import org.tuna.zoopzoop.backend.domain.datasource.entity.Tag;
34+
import org.tuna.zoopzoop.backend.domain.datasource.repository.DataSourceRepository;
35+
import org.tuna.zoopzoop.backend.domain.datasource.repository.TagRepository;
36+
import org.tuna.zoopzoop.backend.domain.datasource.service.PersonalDataSourceService;
37+
import org.tuna.zoopzoop.backend.domain.member.enums.Provider;
38+
import org.tuna.zoopzoop.backend.domain.member.repository.MemberRepository;
39+
import org.tuna.zoopzoop.backend.domain.member.service.MemberService;
40+
41+
import java.time.LocalDate;
42+
import java.util.List;
43+
import java.util.Map;
44+
45+
import static org.mockito.ArgumentMatchers.anyInt;
46+
import static org.mockito.Mockito.when;
47+
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
48+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
49+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
50+
51+
@ActiveProfiles("test")
52+
@SpringBootTest
53+
@AutoConfigureMockMvc
54+
@Transactional
55+
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
56+
class DataSourceControllerTest {
57+
58+
@Autowired MockMvc mockMvc;
59+
@Autowired ObjectMapper objectMapper;
60+
@Autowired MemberService memberService;
61+
@Autowired MemberRepository memberRepository;
62+
@Autowired PersonalArchiveFolderService folderService;
63+
@Autowired FolderRepository folderRepository;
64+
@Autowired DataSourceRepository dataSourceRepository;
65+
66+
@Mock PersonalDataSourceService personalApp;
67+
68+
final String TEST_PROVIDER_KEY = "testUser_sc1111";
69+
70+
Integer testMemberId;
71+
Integer docsFolderId;
72+
Integer dataSourceId1;
73+
Integer dataSourceId2;
74+
@Qualifier("dataProcessorService")
75+
@Autowired
76+
private DataProcessorService dataProcessorService;
77+
@Qualifier("tagRepository")
78+
@Autowired
79+
private TagRepository tagRepository;
80+
81+
@TestConfiguration
82+
static class StubConfig {
83+
@Bean @Primary
84+
DataProcessorService stubDataProcessorService() {
85+
return new DataProcessorService(null, null) {
86+
@Override
87+
public DataSourceDto process(String url, List<Tag> tagList) {
88+
return new DataSourceDto(
89+
"테스트제목",
90+
"테스트요약",
91+
LocalDate.of(2025, 9, 1),
92+
url,
93+
"https://img.example/test.png",
94+
"example.com",
95+
Category.IT,
96+
List.of("ML", "Infra")
97+
);
98+
}
99+
};
100+
}
101+
102+
103+
@Bean @Primary
104+
TagRepository stubTagRepository() {
105+
TagRepository mock = Mockito.mock(TagRepository.class);
106+
when(mock.findDistinctTagNamesByFolderId(anyInt()))
107+
.thenReturn(List.of("AI","Spring"));
108+
return mock;
109+
}
110+
}
111+
112+
@BeforeAll
113+
void setup() {
114+
try {
115+
memberService.createMember("testUser_sc1111", "http://img", TEST_PROVIDER_KEY, Provider.KAKAO);
116+
} catch (Exception ignored) {}
117+
118+
var member = memberRepository.findByProviderAndProviderKey(Provider.KAKAO, TEST_PROVIDER_KEY)
119+
.orElseThrow();
120+
121+
testMemberId = member.getId();
122+
123+
// docs 폴더 생성 + default 폴더 보장
124+
FolderResponse fr = folderService.createFolder(testMemberId, "docs");
125+
docsFolderId = fr.folderId();
126+
127+
Folder docsFolder = folderRepository.findById(docsFolderId).orElseThrow();
128+
Integer archiveId = docsFolder.getArchive().getId();
129+
130+
folderRepository.findByArchiveIdAndIsDefaultTrue(archiveId).orElseGet(() -> {
131+
Folder df = new Folder("default");
132+
df.setArchive(docsFolder.getArchive());
133+
df.setDefault(true);
134+
return folderRepository.save(df);
135+
});
136+
137+
// seed 자료 2건 생성
138+
DataSource d1 = new DataSource();
139+
d1.setFolder(docsFolder);
140+
d1.setTitle("spec.pdf");
141+
d1.setSummary("요약 A");
142+
d1.setSourceUrl("http://src/a");
143+
d1.setImageUrl("http://img/a");
144+
d1.setDataCreatedDate(LocalDate.now());
145+
d1.setActive(true);
146+
d1.setCategory(Category.IT);
147+
d1.setTags(List.of(new Tag("tag1"), new Tag("tag2")));
148+
dataSourceRepository.save(d1);
149+
dataSourceId1 = d1.getId();
150+
151+
DataSource d2 = new DataSource();
152+
d2.setFolder(docsFolder);
153+
d2.setTitle("notes.txt");
154+
d2.setSummary("요약 B");
155+
d2.setSourceUrl("http://src/b");
156+
d2.setImageUrl("http://img/b");
157+
d2.setDataCreatedDate(LocalDate.now());
158+
d2.setActive(true);
159+
d2.setCategory(Category.SCIENCE);
160+
d2.setTags(List.of());
161+
dataSourceRepository.save(d2);
162+
dataSourceId2 = d2.getId();
163+
}
164+
165+
// ===== 생성 =====
166+
167+
@Test
168+
@DisplayName("[개인] 자료 생성: folderId=0 → default")
169+
@WithUserDetails(value = "KAKAO:testUser_sc1111", setupBefore = TestExecutionEvent.TEST_METHOD)
170+
void create_default() throws Exception {
171+
var body = new reqBodyForCreateDataSource("https://example.com/a", 0);
172+
173+
mockMvc.perform(post("/api/v1/archive")
174+
.contentType(MediaType.APPLICATION_JSON)
175+
.content(objectMapper.writeValueAsString(body)))
176+
.andExpect(status().isOk())
177+
.andExpect(jsonPath("$.status").value(200))
178+
.andExpect(jsonPath("$.msg").value("새로운 자료가 등록됐습니다."))
179+
.andExpect(jsonPath("$.data.dataSourceId").isNumber());
180+
}
181+
182+
@Test
183+
@DisplayName("[개인] 자료 생성: 지정 폴더")
184+
@WithUserDetails(value = "KAKAO:testUser_sc1111", setupBefore = TestExecutionEvent.TEST_METHOD)
185+
void create_specificFolder() throws Exception {
186+
var body = new reqBodyForCreateDataSource("https://example.com/b", docsFolderId);
187+
188+
mockMvc.perform(post("/api/v1/archive")
189+
.contentType(MediaType.APPLICATION_JSON)
190+
.content(objectMapper.writeValueAsString(body)))
191+
.andExpect(status().isOk())
192+
.andExpect(jsonPath("$.status").value(200))
193+
.andExpect(jsonPath("$.data.dataSourceId").isNumber());
194+
}
195+
196+
// ===== 삭제 =====
197+
198+
@Test
199+
@DisplayName("[개인] 단건 삭제")
200+
@WithUserDetails(value = "KAKAO:testUser_sc1111", setupBefore = TestExecutionEvent.TEST_METHOD)
201+
void delete_one() throws Exception {
202+
Folder f = folderRepository.findById(docsFolderId).orElseThrow();
203+
204+
DataSource d = new DataSource();
205+
d.setFolder(f);
206+
d.setTitle("del");
207+
d.setSummary("x");
208+
d.setSourceUrl("s");
209+
d.setImageUrl("i");
210+
d.setDataCreatedDate(LocalDate.now());
211+
d.setActive(true);
212+
d.setCategory(Category.IT);
213+
214+
dataSourceRepository.save(d);
215+
216+
mockMvc.perform(delete("/api/v1/archive/{id}", d.getId()))
217+
.andExpect(status().isOk())
218+
.andExpect(jsonPath("$.status").value(200))
219+
.andExpect(jsonPath("$.data.dataSourceId").value(d.getId()));
220+
}
221+
222+
// ===== 이동 =====
223+
224+
@Test
225+
@DisplayName("[개인] 단건 이동 → 지정 폴더")
226+
@WithUserDetails(value = "KAKAO:testUser_sc1111", setupBefore = TestExecutionEvent.TEST_METHOD)
227+
void move_one() throws Exception {
228+
FolderResponse target = folderService.createFolder(testMemberId, "move-target");
229+
var body = new reqBodyForMoveDataSource(target.folderId());
230+
231+
mockMvc.perform(patch("/api/v1/archive/{id}/move", dataSourceId1)
232+
.contentType(MediaType.APPLICATION_JSON)
233+
.content(objectMapper.writeValueAsString(body)))
234+
.andExpect(status().isOk())
235+
.andExpect(jsonPath("$.data.dataSourceId").value(dataSourceId1))
236+
.andExpect(jsonPath("$.data.folderId").value(target.folderId()));
237+
}
238+
239+
// ===== 수정 =====
240+
241+
@Test
242+
@DisplayName("[개인] 부분 수정(title, summary)")
243+
@WithUserDetails(value = "KAKAO:testUser_sc1111", setupBefore = TestExecutionEvent.TEST_METHOD)
244+
void update_partial() throws Exception {
245+
Map<String, Object> body = Map.of(
246+
"title", "새 제목",
247+
"summary", "짧은 요약"
248+
);
249+
250+
mockMvc.perform(patch("/api/v1/archive/{id}", dataSourceId1)
251+
.contentType(MediaType.APPLICATION_JSON)
252+
.content(objectMapper.writeValueAsString(body)))
253+
.andExpect(status().isOk())
254+
.andExpect(jsonPath("$.status").value(200))
255+
.andExpect(jsonPath("$.data.dataSourceId").value(dataSourceId1));
256+
}
257+
258+
// ===== 검색 =====
259+
260+
@Test
261+
@DisplayName("[개인] 검색: 기본 정렬 createdAt DESC")
262+
@WithUserDetails(value = "KAKAO:testUser_sc1111", setupBefore = TestExecutionEvent.TEST_METHOD)
263+
void search_default() throws Exception {
264+
mockMvc.perform(get("/api/v1/archive"))
265+
.andExpect(status().isOk())
266+
.andExpect(jsonPath("$.status").value(200))
267+
.andExpect(jsonPath("$.data.items").isArray());
268+
}
269+
}
270+

0 commit comments

Comments
 (0)