You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
autoflow-server-mgmt/src/test/java/kr/re/etri/autoflow/ProjectControllerTest.java

191 lines
6.9 KiB

package kr.re.etri.autoflow.controllers;
import com.fasterxml.jackson.databind.ObjectMapper;
import kr.re.etri.autoflow.entity.ProjectEntity;
import kr.re.etri.autoflow.payload.request.ProjectRequest;
import kr.re.etri.autoflow.service.ProjectService;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.springframework.http.MediaType;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.test.web.servlet.MockMvc;
import java.time.LocalDate;
import java.util.List;
import java.util.Optional;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import org.springframework.beans.factory.annotation.Autowired;
@WebMvcTest(ProjectController.class)
@Import(ProjectControllerTest.MockConfig.class)
class ProjectControllerTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private ProjectService projectService;
@Autowired
private ObjectMapper objectMapper;
@TestConfiguration
static class MockConfig {
@Bean
public ProjectService projectService() {
return Mockito.mock(ProjectService.class);
}
// 테스트 시 Security 완전 무시
@Bean
public SecurityFilterChain testSecurityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authz -> authz.anyRequest().permitAll())
.csrf(csrf -> csrf.disable());
return http.build();
}
}
private ProjectEntity sampleProject(Long id, String code, String name) {
return ProjectEntity.builder()
.id(id)
.prjCd(code)
.prjNm(name)
.prjStartDt(LocalDate.of(2025, 8, 1))
.prjEndDt(LocalDate.of(2025, 12, 31))
.build();
}
@Test
@DisplayName("모든 프로젝트 조회")
void getAllProjects() throws Exception {
ProjectEntity entity = sampleProject(1L, "PRJ001", "AI 프로젝트");
when(projectService.findAll()).thenReturn(List.of(entity));
mockMvc.perform(get("/api/projects"))
.andExpect(status().isOk())
.andExpect(jsonPath("$[0].prjCd").value("PRJ001"));
}
@Nested
class GetProjectByIdTests {
@Test
@DisplayName("ID로 프로젝트 조회 - 성공")
void success() throws Exception {
ProjectEntity entity = sampleProject(1L, "PRJ001", "AI 프로젝트");
when(projectService.findById(1L)).thenReturn(Optional.of(entity));
mockMvc.perform(get("/api/projects/1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.prjCd").value("PRJ001"));
}
@Test
@DisplayName("ID로 프로젝트 조회 - 실패")
void notFound() throws Exception {
when(projectService.findById(999L)).thenReturn(Optional.empty());
mockMvc.perform(get("/api/projects/999"))
.andExpect(status().isNotFound());
}
}
@Nested
class CreateProjectTests {
@Test
@DisplayName("프로젝트 생성 - 성공")
void success() throws Exception {
ProjectEntity input = sampleProject(null, "PRJ002", "새 프로젝트");
when(projectService.create(any())).thenReturn(input);
mockMvc.perform(post("/api/projects")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(input)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.prjCd").value("PRJ002"));
}
@Test
@DisplayName("프로젝트 생성 - 실패(중복 등)")
void failure() throws Exception {
ProjectEntity input = ProjectEntity.builder().prjCd("PRJ_DUP").build();
when(projectService.create(any())).thenThrow(new IllegalArgumentException("중복된 프로젝트 코드입니다."));
mockMvc.perform(post("/api/projects")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(input)))
.andExpect(status().isBadRequest())
.andExpect(content().string("중복된 프로젝트 코드입니다."));
}
}
@Nested
class UpdateProjectTests {
@Test
@DisplayName("프로젝트 수정 - 성공")
void success() throws Exception {
ProjectRequest request = new ProjectRequest();
request.setPrjNm("수정된 이름");
ProjectEntity updated = sampleProject(1L, "PRJ001", "수정된 이름");
when(projectService.update(Mockito.eq(1L), any())).thenReturn(Optional.of(updated));
mockMvc.perform(put("/api/projects/1")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.prjNm").value("수정된 이름"));
}
@Test
@DisplayName("프로젝트 수정 - 실패")
void notFound() throws Exception {
ProjectRequest request = new ProjectRequest();
request.setPrjNm("수정된 이름");
when(projectService.update(Mockito.eq(999L), any())).thenReturn(Optional.empty());
mockMvc.perform(put("/api/projects/999")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request)))
.andExpect(status().isNotFound());
}
}
@Nested
class DeleteProjectTests {
@Test
@DisplayName("프로젝트 삭제 - 성공")
void success() throws Exception {
when(projectService.delete(1L)).thenReturn(true);
mockMvc.perform(delete("/api/projects/1"))
.andExpect(status().isNoContent());
}
@Test
@DisplayName("프로젝트 삭제 - 실패")
void notFound() throws Exception {
when(projectService.delete(999L)).thenReturn(false);
mockMvc.perform(delete("/api/projects/999"))
.andExpect(status().isNotFound());
}
}
}