File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
package kr.itn.itnhub.provision;
import kr.itn.itnhub.AbstractDbTest;
import kr.itn.itnhub.contact.Contact;
import kr.itn.itnhub.contact.ContactMapper;
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.boot.test.mock.mockito.SpyBean;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.jdbc.core.JdbcTemplate;
import java.util.List;
import java.util.Optional;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.when;
class ChannelProvisionServiceTest extends AbstractDbTest {
@Autowired
ChannelProvisionService service;
@SpyBean
OrganizationMapper mapper;
@Autowired
ContactMapper contactMapper;
@Autowired
JdbcTemplate jdbc;
@MockBean
MattermostClient mattermost;
private Long orgId;
@BeforeEach
void setUp() {
jdbc.update("delete from organization");
jdbc.update("delete from contact");
Organization org = new Organization();
org.setOrgNo("001");
org.setOrgName("국제방송교류재단");
org.setChannelSlug("001");
mapper.upsertBySeed(org);
orgId = mapper.findAll().get(0).getId();
assignApplicant(orgId, "송민지");
}
private Long assignApplicant(Long id, String name) {
Contact contact = new Contact();
contact.setCategory("APPLICANT");
contact.setName(name);
contactMapper.insert(contact);
mapper.updateAssignments(id, contact.getId(), null, null, null, null);
return contact.getId();
}
/** 멤버 초대 테스트용: 이메일이 채워진 담당자 행을 만들고 id를 돌려준다. */
private Long createContact(String category, String name, String email) {
Contact contact = new Contact();
contact.setCategory(category);
contact.setName(name);
contact.setEmail(email);
contactMapper.insert(contact);
return contact.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 치안정책연구소는_합성코드_008_01로_내부명을_만든다() {
Organization special = new Organization();
special.setOrgNo("008_01");
special.setOrgName("경찰청_치안정책연구소");
special.setChannelSlug("008_01");
mapper.upsertBySeed(special);
Long specialId = mapper.findAll().stream()
.filter(o -> "경찰청_치안정책연구소".equals(o.getOrgName()))
.findFirst().orElseThrow().getId();
assignApplicant(specialId, "담당자");
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-01-mj", "008_01_경찰청_치안정책연구소 (문정원)");
verify(mattermost).createPrivateChannel(
"org-008-01-law", "008_01_경찰청_치안정책연구소 (법률검토)");
}
@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());
}
@Test
void 문정원이_실패해도_법률검토는_시도된다() {
when(mattermost.findChannelIdByInternalName(anyString())).thenReturn(Optional.empty());
when(mattermost.findChannelIdByDisplayName(anyString())).thenReturn(Optional.empty());
when(mattermost.createPrivateChannel(eq("org-001-mj"), anyString()))
.thenThrow(new MattermostException("문정원 서버 오류"));
when(mattermost.createPrivateChannel(eq("org-001-law"), anyString()))
.thenReturn("id-law");
ProvisionResult result = service.provision(orgId);
assertThat(result.mj()).isEqualTo(ProvisionOutcome.FAILED);
assertThat(result.law()).isEqualTo(ProvisionOutcome.CREATED);
assertThat(result.message()).contains("문정원");
Organization after = mapper.findById(orgId);
assertThat(after.getChannelIdMj()).isNull();
assertThat(after.getChannelIdLaw()).isEqualTo("id-law");
assertThat(after.getStatus()).isEqualTo(OrgStatus.PARTIAL);
}
@Test
void 양쪽_다_실패하면_메시지에_두_채널_라벨이_모두_누적된다() {
when(mattermost.findChannelIdByInternalName(anyString())).thenReturn(Optional.empty());
when(mattermost.findChannelIdByDisplayName(anyString())).thenReturn(Optional.empty());
when(mattermost.createPrivateChannel(eq("org-001-mj"), anyString()))
.thenThrow(new MattermostException("문정원 서버 오류"));
when(mattermost.createPrivateChannel(eq("org-001-law"), anyString()))
.thenThrow(new MattermostException("법률검토 서버 오류"));
ProvisionResult result = service.provision(orgId);
assertThat(result.mj()).isEqualTo(ProvisionOutcome.FAILED);
assertThat(result.law()).isEqualTo(ProvisionOutcome.FAILED);
assertThat(result.message()).contains("문정원").contains("법률검토");
Organization after = mapper.findById(orgId);
assertThat(after.getChannelIdMj()).isNull();
assertThat(after.getChannelIdLaw()).isNull();
}
/**
* Finding 1 회귀 테스트: MattermostClient 호출은 성공했는데 그 직후 DB 기록(mapper)이
* DataAccessException 계열로 실패하는 경우. 수정 전에는 ensure()가 MattermostException만
* catch 하므로 이 예외가 provision() 밖으로 전파되어 두 번째(법률검토) 채널은 시도조차
* 되지 않고 호출자는 ProvisionResult를 받지 못한다. 수정 후에는 해당 채널만 FAILED로
* 강등되고 나머지 채널은 정상적으로 계속 시도되어야 한다.
*/
@Test
void 채널_생성_성공_직후_DB_저장_실패는_예외로_전파되지_않고_FAILED로_처리된다() {
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()))
.thenReturn("id-law");
doThrow(new DataIntegrityViolationException("DB 저장 실패"))
.when(mapper).updateChannelIdMj(eq(orgId), anyString());
ProvisionResult result = service.provision(orgId);
assertThat(result.mj()).isEqualTo(ProvisionOutcome.FAILED);
assertThat(result.law()).isEqualTo(ProvisionOutcome.CREATED);
assertThat(result.message()).contains("문정원");
Organization after = mapper.findById(orgId);
assertThat(after.getChannelIdMj()).isNull();
assertThat(after.getChannelIdLaw()).isEqualTo("id-law");
}
@Test
void 채널_2개가_처음_모두_갖춰지면_1단계로_자동_시작하고_이력이_한_행_남는다() {
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())).thenReturn("id-law");
service.provision(orgId);
Organization after = mapper.findById(orgId);
assertThat(after.getStage()).isEqualTo(1);
assertThat(jdbc.queryForObject(
"select count(*) from stage_history where org_id = ?", Integer.class, orgId))
.isEqualTo(1);
}
@Test
void 이미_단계가_있는_기관은_채널_재확인으로_단계가_바뀌지_않는다() {
mapper.updateChannelIdMj(orgId, "id-mj");
mapper.updateChannelIdLaw(orgId, "id-law");
mapper.updateStage(orgId, 5);
service.provision(orgId);
Organization after = mapper.findById(orgId);
assertThat(after.getStage()).isEqualTo(5);
assertThat(jdbc.queryForObject(
"select count(*) from stage_history where org_id = ?", Integer.class, orgId))
.isEqualTo(0);
}
@Test
void 이미_채널이_있어_CACHED만_반환되는_재실행은_이력을_추가하지_않는다() {
mapper.updateChannelIdMj(orgId, "id-mj");
mapper.updateChannelIdLaw(orgId, "id-law");
// 첫 provision으로 자동 1단계 시작 + 이력 1건.
service.provision(orgId);
assertThat(jdbc.queryForObject(
"select count(*) from stage_history where org_id = ?", Integer.class, orgId))
.isEqualTo(1);
// 다시 호출해도(CACHED만 반환) 이력은 늘지 않는다.
ProvisionResult result = service.provision(orgId);
assertThat(result.mj()).isEqualTo(ProvisionOutcome.CACHED);
assertThat(result.law()).isEqualTo(ProvisionOutcome.CACHED);
assertThat(jdbc.queryForObject(
"select count(*) from stage_history where org_id = ?", Integer.class, orgId))
.isEqualTo(1);
}
@Test
void 새로_생성된_채널에는_담당자_안내글을_올리고_핀_고정한다() {
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");
when(mattermost.createPost(eq("id-mj"), anyString(), eq(List.of()))).thenReturn("post-mj");
when(mattermost.createPost(eq("id-law"), anyString(), eq(List.of()))).thenReturn("post-law");
service.provision(orgId);
org.mockito.ArgumentCaptor<String> mjMsg = org.mockito.ArgumentCaptor.forClass(String.class);
verify(mattermost).createPost(eq("id-mj"), mjMsg.capture(), eq(List.of()));
assertThat(mjMsg.getValue())
.contains("국제방송교류재단 업무협의 채널입니다.")
.contains("■ 신청기관 담당자")
.contains("담당자 : 송민지");
org.mockito.ArgumentCaptor<String> lawMsg = org.mockito.ArgumentCaptor.forClass(String.class);
verify(mattermost).createPost(eq("id-law"), lawMsg.capture(), eq(List.of()));
assertThat(lawMsg.getValue()).contains("국제방송교류재단 법률검토 채널입니다.");
verify(mattermost).pinPost("post-mj");
verify(mattermost).pinPost("post-law");
}
@Test
void 이미_있는_채널에는_안내글을_올리지_않는다() {
mapper.updateChannelIdMj(orgId, "id-mj");
mapper.updateChannelIdLaw(orgId, "id-law");
service.provision(orgId);
verify(mattermost, never()).createPost(anyString(), anyString(), eq(List.of()));
verify(mattermost, never()).pinPost(anyString());
}
@Test
void 안내글_등록이_실패해도_채널_생성_결과는_유지된다() {
when(mattermost.findChannelIdByInternalName(anyString())).thenReturn(Optional.empty());
when(mattermost.findChannelIdByDisplayName(anyString())).thenReturn(Optional.empty());
when(mattermost.createPrivateChannel(anyString(), anyString()))
.thenReturn("id-mj", "id-law");
when(mattermost.createPost(anyString(), anyString(), eq(List.of())))
.thenThrow(new MattermostException("게시글 실패"));
ProvisionResult result = service.provision(orgId);
assertThat(result.mj()).isEqualTo(ProvisionOutcome.CREATED);
assertThat(result.law()).isEqualTo(ProvisionOutcome.CREATED);
assertThat(result.message()).contains("안내글 등록에 실패");
verify(mattermost, times(2)).createPost(anyString(), anyString(), eq(List.of()));
}
@Test
void 배정된_담당자들의_Mattermost_계정을_보장하고_알맞은_채널에_추가한다() {
Long mjContactId = createContact("MJ", "문정원담당자", "m@y.com");
Long lawyerContactId = createContact("LAWYER", "변호사", "l@z.com");
// 신청기관 담당자(setUp의 "송민지")는 이메일이 없으므로 새로 이메일이 있는 담당자로 교체한다.
Long applicantContactId = createContact("APPLICANT", "신청담당자", "a@x.com");
mapper.updateAssignments(orgId, applicantContactId, mjContactId, null, lawyerContactId, null);
when(mattermost.findChannelIdByInternalName(anyString())).thenReturn(Optional.empty());
when(mattermost.findChannelIdByDisplayName(anyString())).thenReturn(Optional.empty());
when(mattermost.createPrivateChannel(eq("org-001-mj"), anyString())).thenReturn("chan-mj");
when(mattermost.createPrivateChannel(eq("org-001-law"), anyString())).thenReturn("chan-law");
when(mattermost.findUserIdByEmail("a@x.com")).thenReturn(Optional.empty());
when(mattermost.findUserIdByEmail("m@y.com")).thenReturn(Optional.empty());
when(mattermost.findUserIdByEmail("l@z.com")).thenReturn(Optional.empty());
when(mattermost.createUser(eq("a@x.com"), eq("a"), eq("test1234!"))).thenReturn("uid-a");
when(mattermost.createUser(eq("m@y.com"), eq("m"), eq("test1234!"))).thenReturn("uid-m");
when(mattermost.createUser(eq("l@z.com"), eq("l"), eq("test1234!"))).thenReturn("uid-l");
service.provision(orgId);
verify(mattermost).findUserIdByEmail("a@x.com");
verify(mattermost).findUserIdByEmail("m@y.com");
verify(mattermost).findUserIdByEmail("l@z.com");
verify(mattermost).createUser("a@x.com", "a", "test1234!");
verify(mattermost).createUser("m@y.com", "m", "test1234!");
verify(mattermost).createUser("l@z.com", "l", "test1234!");
verify(mattermost).addUserToTeam("uid-a");
verify(mattermost).addUserToTeam("uid-m");
verify(mattermost).addUserToTeam("uid-l");
verify(mattermost, times(3)).addUserToTeam(anyString());
verify(mattermost).addUserToChannel("chan-mj", "uid-a");
verify(mattermost).addUserToChannel("chan-mj", "uid-m");
verify(mattermost).addUserToChannel("chan-law", "uid-l");
verify(mattermost).addUserToChannel("chan-law", "uid-m");
verify(mattermost, times(4)).addUserToChannel(anyString(), anyString());
}
@Test
void 이미_존재하는_사용자는_생성하지_않고_팀_채널_추가만_한다() {
Long mjContactId = createContact("MJ", "문정원담당자", "m@y.com");
Long lawyerContactId = createContact("LAWYER", "변호사", "l@z.com");
Long applicantContactId = createContact("APPLICANT", "신청담당자", "a@x.com");
mapper.updateAssignments(orgId, applicantContactId, mjContactId, null, lawyerContactId, null);
when(mattermost.findChannelIdByInternalName(anyString())).thenReturn(Optional.empty());
when(mattermost.findChannelIdByDisplayName(anyString())).thenReturn(Optional.empty());
when(mattermost.createPrivateChannel(eq("org-001-mj"), anyString())).thenReturn("chan-mj");
when(mattermost.createPrivateChannel(eq("org-001-law"), anyString())).thenReturn("chan-law");
when(mattermost.findUserIdByEmail("a@x.com")).thenReturn(Optional.of("existing-a"));
when(mattermost.findUserIdByEmail("m@y.com")).thenReturn(Optional.of("existing-m"));
when(mattermost.findUserIdByEmail("l@z.com")).thenReturn(Optional.of("existing-l"));
service.provision(orgId);
verify(mattermost, never()).createUser(anyString(), anyString(), anyString());
verify(mattermost).addUserToTeam("existing-a");
verify(mattermost).addUserToTeam("existing-m");
verify(mattermost).addUserToTeam("existing-l");
verify(mattermost).addUserToChannel("chan-mj", "existing-a");
verify(mattermost).addUserToChannel("chan-mj", "existing-m");
verify(mattermost).addUserToChannel("chan-law", "existing-l");
verify(mattermost).addUserToChannel("chan-law", "existing-m");
}
@Test
void 계정명이_중복되면_로컬파트_점_도메인첫라벨로_한번만_재시도한다() {
Long applicantContactId = createContact("APPLICANT", "신청담당자", "hong@naver.com");
mapper.updateAssignments(orgId, applicantContactId, null, null, null, null);
when(mattermost.findChannelIdByInternalName(anyString())).thenReturn(Optional.empty());
when(mattermost.findChannelIdByDisplayName(anyString())).thenReturn(Optional.empty());
when(mattermost.createPrivateChannel(eq("org-001-mj"), anyString())).thenReturn("chan-mj");
when(mattermost.createPrivateChannel(eq("org-001-law"), anyString())).thenReturn("chan-law");
when(mattermost.findUserIdByEmail("hong@naver.com")).thenReturn(Optional.empty());
when(mattermost.createUser(eq("hong@naver.com"), eq("hong"), anyString()))
.thenThrow(new MattermostException(
"사용자 생성 실패: hong@naver.com - {\"id\":\"app.user.save.username_exists.app_error\"}"));
when(mattermost.createUser(eq("hong@naver.com"), eq("hong.naver"), anyString()))
.thenReturn("uid-hong");
service.provision(orgId);
verify(mattermost).createUser("hong@naver.com", "hong", "test1234!");
verify(mattermost).createUser("hong@naver.com", "hong.naver", "test1234!");
verify(mattermost, times(2)).createUser(eq("hong@naver.com"), anyString(), anyString());
verify(mattermost).addUserToTeam("uid-hong");
verify(mattermost).addUserToChannel("chan-mj", "uid-hong");
}
@Test
void 담당자_한명의_초대_실패는_다른_담당자_처리를_막지_않는다() {
Long mjContactId = createContact("MJ", "문정원담당자", "m@y.com");
Long lawyerContactId = createContact("LAWYER", "변호사", "l@z.com");
Long applicantContactId = createContact("APPLICANT", "신청담당자", "a@x.com");
mapper.updateAssignments(orgId, applicantContactId, mjContactId, null, lawyerContactId, null);
when(mattermost.findChannelIdByInternalName(anyString())).thenReturn(Optional.empty());
when(mattermost.findChannelIdByDisplayName(anyString())).thenReturn(Optional.empty());
when(mattermost.createPrivateChannel(eq("org-001-mj"), anyString())).thenReturn("chan-mj");
when(mattermost.createPrivateChannel(eq("org-001-law"), anyString())).thenReturn("chan-law");
when(mattermost.findUserIdByEmail("a@x.com"))
.thenThrow(new MattermostException("사용자 조회 실패: a@x.com"));
when(mattermost.findUserIdByEmail("m@y.com")).thenReturn(Optional.empty());
when(mattermost.findUserIdByEmail("l@z.com")).thenReturn(Optional.empty());
when(mattermost.createUser(eq("m@y.com"), eq("m"), anyString())).thenReturn("uid-m");
when(mattermost.createUser(eq("l@z.com"), eq("l"), anyString())).thenReturn("uid-l");
ProvisionResult result = service.provision(orgId);
assertThat(result.mj()).isEqualTo(ProvisionOutcome.CREATED);
assertThat(result.law()).isEqualTo(ProvisionOutcome.CREATED);
assertThat(result.message()).contains("[멤버 초대]").contains("신청담당자");
verify(mattermost, never()).addUserToChannel(anyString(), eq("uid-a"));
verify(mattermost).addUserToChannel("chan-mj", "uid-m");
verify(mattermost).addUserToChannel("chan-law", "uid-l");
verify(mattermost).addUserToChannel("chan-law", "uid-m");
}
@Test
void 채널_생성이_모두_실패하면_멤버_초대도_시도되지_않는다() {
Long mjContactId = createContact("MJ", "문정원담당자", "m@y.com");
Long lawyerContactId = createContact("LAWYER", "변호사", "l@z.com");
Long applicantContactId = createContact("APPLICANT", "신청담당자", "a@x.com");
mapper.updateAssignments(orgId, applicantContactId, mjContactId, null, lawyerContactId, null);
when(mattermost.findChannelIdByInternalName(anyString())).thenReturn(Optional.empty());
when(mattermost.findChannelIdByDisplayName(anyString())).thenReturn(Optional.empty());
when(mattermost.createPrivateChannel(eq("org-001-mj"), anyString()))
.thenThrow(new MattermostException("문정원 서버 오류"));
when(mattermost.createPrivateChannel(eq("org-001-law"), anyString()))
.thenThrow(new MattermostException("법률검토 서버 오류"));
ProvisionResult result = service.provision(orgId);
assertThat(result.mj()).isEqualTo(ProvisionOutcome.FAILED);
assertThat(result.law()).isEqualTo(ProvisionOutcome.FAILED);
verify(mattermost, never()).findUserIdByEmail(anyString());
verify(mattermost, never()).createUser(anyString(), anyString(), anyString());
verify(mattermost, never()).addUserToTeam(anyString());
verify(mattermost, never()).addUserToChannel(anyString(), anyString());
}
}