package kr.itn.itnhub.provision; import kr.itn.itnhub.AbstractDbTest; import kr.itn.itnhub.mattermost.MattermostClient; import kr.itn.itnhub.mattermost.MattermostException; import kr.itn.itnhub.org.OrgStatus; 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.mock.mockito.MockBean; import org.springframework.jdbc.core.JdbcTemplate; import java.util.Optional; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; class ChannelProvisionServiceTest extends AbstractDbTest { @Autowired ChannelProvisionService service; @Autowired OrganizationMapper mapper; @Autowired JdbcTemplate jdbc; @MockBean MattermostClient mattermost; private Long orgId; @BeforeEach void setUp() { jdbc.update("delete from organization"); Organization org = new Organization(); org.setOrgNo("001"); org.setOrgName("국제방송교류재단"); org.setChannelSlug("001"); org.setDeptName("데이터정보화팀"); org.setManagerName("송민지"); org.setManagerPhone("02-3475-5434"); org.setManagerEmail("ming@arirang.com"); mapper.upsertBySeed(org); orgId = mapper.findAll().get(0).getId(); } @Test void 채널이_없으면_두_개를_생성하고_ID를_저장한다() { when(mattermost.findChannelIdByInternalName(anyString())).thenReturn(Optional.empty()); when(mattermost.findChannelIdByDisplayName(anyString())).thenReturn(Optional.empty()); when(mattermost.createPrivateChannel("org-001-mj", "001_국제방송교류재단 (문정원)")) .thenReturn("id-mj"); when(mattermost.createPrivateChannel("org-001-law", "001_국제방송교류재단 (법률검토)")) .thenReturn("id-law"); ProvisionResult result = service.provision(orgId); assertThat(result.mj()).isEqualTo(ProvisionOutcome.CREATED); assertThat(result.law()).isEqualTo(ProvisionOutcome.CREATED); Organization after = mapper.findById(orgId); assertThat(after.getChannelIdMj()).isEqualTo("id-mj"); assertThat(after.getChannelIdLaw()).isEqualTo("id-law"); assertThat(after.getStatus()).isEqualTo(OrgStatus.ACTIVE); } @Test void 이미_ID가_있으면_아무것도_생성하지_않는다() { mapper.updateChannelIdMj(orgId, "id-mj"); mapper.updateChannelIdLaw(orgId, "id-law"); ProvisionResult result = service.provision(orgId); assertThat(result.mj()).isEqualTo(ProvisionOutcome.CACHED); assertThat(result.law()).isEqualTo(ProvisionOutcome.CACHED); verify(mattermost, never()).createPrivateChannel(anyString(), anyString()); verify(mattermost, never()).findChannelIdByInternalName(anyString()); } @Test void DB에는_없고_서버에_내부명이_있으면_ID를_복구한다() { when(mattermost.findChannelIdByInternalName("org-001-mj")) .thenReturn(Optional.of("recovered-mj")); when(mattermost.findChannelIdByInternalName("org-001-law")) .thenReturn(Optional.of("recovered-law")); ProvisionResult result = service.provision(orgId); assertThat(result.mj()).isEqualTo(ProvisionOutcome.RECOVERED); assertThat(result.law()).isEqualTo(ProvisionOutcome.RECOVERED); verify(mattermost, never()).createPrivateChannel(anyString(), anyString()); Organization after = mapper.findById(orgId); assertThat(after.getChannelIdMj()).isEqualTo("recovered-mj"); assertThat(after.getChannelIdLaw()).isEqualTo("recovered-law"); } @Test void 내부명으로_못_찾으면_표시명으로_복구를_시도한다() { when(mattermost.findChannelIdByInternalName(anyString())).thenReturn(Optional.empty()); when(mattermost.findChannelIdByDisplayName("001_국제방송교류재단 (문정원)")) .thenReturn(Optional.of("bydisplay-mj")); when(mattermost.findChannelIdByDisplayName("001_국제방송교류재단 (법률검토)")) .thenReturn(Optional.empty()); when(mattermost.createPrivateChannel(eq("org-001-law"), anyString())) .thenReturn("new-law"); ProvisionResult result = service.provision(orgId); assertThat(result.mj()).isEqualTo(ProvisionOutcome.RECOVERED); assertThat(result.law()).isEqualTo(ProvisionOutcome.CREATED); verify(mattermost, never()).createPrivateChannel(eq("org-001-mj"), anyString()); } @Test void 법률검토가_실패해도_문정원_ID는_저장된다() { when(mattermost.findChannelIdByInternalName(anyString())).thenReturn(Optional.empty()); when(mattermost.findChannelIdByDisplayName(anyString())).thenReturn(Optional.empty()); when(mattermost.createPrivateChannel(eq("org-001-mj"), anyString())) .thenReturn("id-mj"); when(mattermost.createPrivateChannel(eq("org-001-law"), anyString())) .thenThrow(new MattermostException("서버 오류")); ProvisionResult result = service.provision(orgId); assertThat(result.mj()).isEqualTo(ProvisionOutcome.CREATED); assertThat(result.law()).isEqualTo(ProvisionOutcome.FAILED); assertThat(result.message()).contains("법률검토"); Organization after = mapper.findById(orgId); assertThat(after.getChannelIdMj()).isEqualTo("id-mj"); assertThat(after.getChannelIdLaw()).isNull(); assertThat(after.getStatus()).isEqualTo(OrgStatus.PARTIAL); } @Test void 부분_실패_후_재시도는_없는_쪽만_생성한다() { mapper.updateChannelIdMj(orgId, "id-mj"); when(mattermost.findChannelIdByInternalName("org-001-law")).thenReturn(Optional.empty()); when(mattermost.findChannelIdByDisplayName(anyString())).thenReturn(Optional.empty()); when(mattermost.createPrivateChannel(eq("org-001-law"), anyString())) .thenReturn("retry-law"); ProvisionResult result = service.provision(orgId); assertThat(result.mj()).isEqualTo(ProvisionOutcome.CACHED); assertThat(result.law()).isEqualTo(ProvisionOutcome.CREATED); verify(mattermost, never()).createPrivateChannel(eq("org-001-mj"), anyString()); assertThat(mapper.findById(orgId).getStatus()).isEqualTo(OrgStatus.ACTIVE); } @Test void 치안정책연구소는_policy_내부명으로_생성한다() { Organization special = new Organization(); special.setOrgNo("008"); special.setOrgName("경찰청_치안정책연구소"); special.setChannelSlug("008-policy"); special.setDeptName("연구지원과"); special.setManagerName("담당자"); special.setManagerPhone("02-0000-0000"); special.setManagerEmail("a@police.go.kr"); mapper.upsertBySeed(special); Long specialId = mapper.findAll().stream() .filter(o -> "경찰청_치안정책연구소".equals(o.getOrgName())) .findFirst().orElseThrow().getId(); when(mattermost.findChannelIdByInternalName(anyString())).thenReturn(Optional.empty()); when(mattermost.findChannelIdByDisplayName(anyString())).thenReturn(Optional.empty()); when(mattermost.createPrivateChannel(anyString(), anyString())).thenReturn("x"); service.provision(specialId); verify(mattermost).createPrivateChannel( "org-008-policy-mj", "008_경찰청_치안정책연구소 (문정원)"); verify(mattermost).createPrivateChannel( "org-008-policy-law", "008_경찰청_치안정책연구소 (법률검토)"); } @Test void 담당자정보가_없으면_생성하지_않는다() { Organization pending = new Organization(); pending.setOrgNo("099"); pending.setOrgName("정보없는기관"); pending.setChannelSlug("099"); mapper.upsertBySeed(pending); Long pendingId = mapper.findAll().stream() .filter(o -> "정보없는기관".equals(o.getOrgName())) .findFirst().orElseThrow().getId(); ProvisionResult result = service.provision(pendingId); assertThat(result.mj()).isEqualTo(ProvisionOutcome.FAILED); assertThat(result.law()).isEqualTo(ProvisionOutcome.FAILED); assertThat(result.message()).contains("담당자 정보"); verify(mattermost, never()).createPrivateChannel(anyString(), anyString()); } }