package kr.itn.itnhub.system; import kr.itn.itnhub.AbstractDbTest; import kr.itn.itnhub.mattermost.MattermostClient; import kr.itn.itnhub.mattermost.MattermostException; import kr.itn.itnhub.org.Organization; import kr.itn.itnhub.org.OrganizationMapper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.http.MediaType; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.web.servlet.MockMvc; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @AutoConfigureMockMvc @WithMockUser(roles = "ADMIN") class SystemResetControllerTest extends AbstractDbTest { @Autowired MockMvc mvc; @Autowired OrganizationMapper orgMapper; @Autowired JdbcTemplate jdbc; @MockBean MattermostClient mattermost; private Long bothChannelsId; private Long noChannelId; @BeforeEach void setUp() { jdbc.update("delete from organization"); bothChannelsId = seed("001", "채널있는기관"); orgMapper.updateChannelIdMj(bothChannelsId, "ch-mj"); orgMapper.updateChannelIdLaw(bothChannelsId, "ch-law"); orgMapper.updateStage(bothChannelsId, 3); jdbc.update("insert into stage_history (org_id, stage) values (?, 3)", bothChannelsId); noChannelId = seed("002", "채널없는기관"); } private Long seed(String orgNo, String orgName) { Organization org = new Organization(); org.setOrgNo(orgNo); org.setOrgName(orgName); org.setChannelSlug(orgNo); orgMapper.upsertBySeed(org); return orgMapper.findByOrgNoAndOrgName(orgNo, orgName).getId(); } @Test void 미리보기는_DB가_아는_채널만_보여준다() throws Exception { mvc.perform(get("/api/system/reset/preview")) .andExpect(status().isOk()) // 채널 없는 기관은 아예 목록에 없다 .andExpect(jsonPath("$.length()").value(1)) .andExpect(jsonPath("$[0].orgNo").value("001")) .andExpect(jsonPath("$[0].stage").value(3)) .andExpect(jsonPath("$[0].channels.length()").value(2)) .andExpect(jsonPath("$[0].channels[0].kind").value("문정원")) .andExpect(jsonPath("$[0].channels[1].kind").value("법률검토")); } @Test void 미리보기는_아무것도_바꾸지_않는다() throws Exception { mvc.perform(get("/api/system/reset/preview")).andExpect(status().isOk()); assertThat(orgMapper.findById(bothChannelsId).getChannelIdMj()).isEqualTo("ch-mj"); verify(mattermost, never()).archiveChannel(anyString()); } @Test void 확인문구가_틀리면_400이고_아무것도_지우지_않는다() throws Exception { mvc.perform(post("/api/system/reset/channels").with(csrf()) .contentType(MediaType.APPLICATION_JSON) .content("{\"confirm\": \"초기화\"}")) .andExpect(status().isBadRequest()); verify(mattermost, never()).archiveChannel(anyString()); assertThat(orgMapper.findById(bothChannelsId).getChannelIdMj()).isEqualTo("ch-mj"); } @Test void 초기화하면_채널을_보관하고_DB의_채널과_단계를_비운다() throws Exception { mvc.perform(post("/api/system/reset/channels").with(csrf()) .contentType(MediaType.APPLICATION_JSON) .content("{\"confirm\": \"채널 초기화\"}")) .andExpect(status().isOk()) .andExpect(jsonPath("$.archivedChannels").value(2)) .andExpect(jsonPath("$.clearedOrgs").value(1)) .andExpect(jsonPath("$.failures.length()").value(0)); verify(mattermost).archiveChannel("ch-mj"); verify(mattermost).archiveChannel("ch-law"); Organization after = orgMapper.findById(bothChannelsId); assertThat(after.getChannelIdMj()).isNull(); assertThat(after.getChannelIdLaw()).isNull(); assertThat(after.getStage()).isNull(); assertThat(jdbc.queryForObject("select count(*) from stage_history where org_id = ?", Integer.class, bothChannelsId)).isZero(); } @Test void 담당자_배정과_권리확인_데이터는_건드리지_않는다() throws Exception { jdbc.update("insert into review_item (org_id, seq, review_result) values (?, 1, '이용가능')", bothChannelsId); jdbc.update("insert into work_memo (org_id, contact_name, body) values (?, '시스템', '메모')", bothChannelsId); mvc.perform(post("/api/system/reset/channels").with(csrf()) .contentType(MediaType.APPLICATION_JSON) .content("{\"confirm\": \"채널 초기화\"}")) .andExpect(status().isOk()); assertThat(jdbc.queryForObject("select count(*) from review_item where org_id = ?", Integer.class, bothChannelsId)).isEqualTo(1); assertThat(jdbc.queryForObject("select count(*) from work_memo where org_id = ?", Integer.class, bothChannelsId)).isEqualTo(1); // 기관 자체도 남는다 assertThat(orgMapper.findById(bothChannelsId)).isNotNull(); } @Test void 한쪽_채널이_실패하면_그_기관의_DB는_그대로_두고_실패를_보고한다() throws Exception { doThrow(new MattermostException("권한 없음")).when(mattermost).archiveChannel("ch-law"); mvc.perform(post("/api/system/reset/channels").with(csrf()) .contentType(MediaType.APPLICATION_JSON) .content("{\"confirm\": \"채널 초기화\"}")) .andExpect(status().isOk()) .andExpect(jsonPath("$.archivedChannels").value(1)) .andExpect(jsonPath("$.clearedOrgs").value(0)) .andExpect(jsonPath("$.failures.length()").value(1)); // 남은 채널을 다시 찾을 수 있어야 하므로 ID를 지우지 않는다 assertThat(orgMapper.findById(bothChannelsId).getChannelIdLaw()).isEqualTo("ch-law"); } }