package kr.itn.itnhub.re; import kr.itn.itnhub.AbstractDbTest; import kr.itn.itnhub.mattermost.MattermostClient; 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.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; 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.request.MockMvcRequestBuilders.put; 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 ReMemoControllerTest extends AbstractDbTest { @Autowired MockMvc mvc; @Autowired OrganizationMapper orgMapper; @Autowired JdbcTemplate jdbc; @MockBean MattermostClient mattermost; private Long orgId; @BeforeEach void setUp() { jdbc.update("delete from re_memo"); jdbc.update("delete from organization"); Organization org = new Organization(); org.setOrgNo("001"); org.setOrgName("RE테스트기관"); org.setChannelSlug("001"); orgMapper.upsertBySeed(org); orgId = orgMapper.findByOrgNoAndOrgName("001", "RE테스트기관").getId(); } private String body(String content, String author, String date) { return "{\"content\":\"" + content + "\",\"author\":\"" + author + "\",\"memoDate\":\"" + date + "\",\"resolved\":false}"; } @Test void 메모를_등록하면_목록에_최신순으로_쌓인다() throws Exception { mvc.perform(post("/api/orgs/{id}/re-memos", orgId).with(csrf()) .contentType(MediaType.APPLICATION_JSON) .content(body("ooo 계약서 및 제안요청서 송부 요청", "장영익", "2026-07-08"))) .andExpect(status().isOk()); mvc.perform(post("/api/orgs/{id}/re-memos", orgId).with(csrf()) .contentType(MediaType.APPLICATION_JSON) .content(body("추가 자료 요청", "장영익", "2026-07-10"))) .andExpect(status().isOk()) .andExpect(jsonPath("$.length()").value(2)) // 일자 최신순 .andExpect(jsonPath("$[0].content").value("추가 자료 요청")) .andExpect(jsonPath("$[0].memoDate").value("2026-07-10")) .andExpect(jsonPath("$[0].author").value("장영익")) .andExpect(jsonPath("$[0].resolved").value(false)); } @Test void 내용과_작성자는_비울_수_없다() throws Exception { mvc.perform(post("/api/orgs/{id}/re-memos", orgId).with(csrf()) .contentType(MediaType.APPLICATION_JSON) .content(body("", "장영익", "2026-07-08"))) .andExpect(status().isBadRequest()); mvc.perform(post("/api/orgs/{id}/re-memos", orgId).with(csrf()) .contentType(MediaType.APPLICATION_JSON) .content(body("내용", "", "2026-07-08"))) .andExpect(status().isBadRequest()); } @Test void 해결로_바꾸면_대시보드_미해결_수에서_빠진다() throws Exception { mvc.perform(post("/api/orgs/{id}/re-memos", orgId).with(csrf()) .contentType(MediaType.APPLICATION_JSON) .content(body("미해결 건", "장영익", "2026-07-08"))) .andExpect(status().isOk()); mvc.perform(get("/api/dashboard")) .andExpect(jsonPath("$.summary.unresolvedRe").value(1)); Long memoId = jdbc.queryForObject( "select id from re_memo where org_id = ?", Long.class, orgId); mvc.perform(put("/api/orgs/{id}/re-memos/{memoId}", orgId, memoId).with(csrf()) .contentType(MediaType.APPLICATION_JSON) .content("{\"content\":\"미해결 건\",\"author\":\"장영익\"," + "\"memoDate\":\"2026-07-08\",\"resolved\":true}")) .andExpect(status().isOk()) .andExpect(jsonPath("$[0].resolved").value(true)); mvc.perform(get("/api/dashboard")) .andExpect(jsonPath("$.summary.unresolvedRe").value(0)); } @Test void 메모를_지울_수_있다() throws Exception { mvc.perform(post("/api/orgs/{id}/re-memos", orgId).with(csrf()) .contentType(MediaType.APPLICATION_JSON) .content(body("지울 건", "장영익", "2026-07-08"))) .andExpect(status().isOk()); Long memoId = jdbc.queryForObject( "select id from re_memo where org_id = ?", Long.class, orgId); mvc.perform(delete("/api/orgs/{id}/re-memos/{memoId}", orgId, memoId).with(csrf())) .andExpect(status().isNoContent()); mvc.perform(get("/api/orgs/{id}/re-memos", orgId)) .andExpect(jsonPath("$.length()").value(0)); } @Test void 없는_메모를_고치면_404다() throws Exception { mvc.perform(put("/api/orgs/{id}/re-memos/{memoId}", orgId, 999999L).with(csrf()) .contentType(MediaType.APPLICATION_JSON) .content(body("없는 건", "장영익", "2026-07-08"))) .andExpect(status().isNotFound()); } }