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.contact;
import kr.itn.itnhub.AbstractDbTest;
import kr.itn.itnhub.org.Organization;
import kr.itn.itnhub.org.OrganizationMapper;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
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.jdbc.core.JdbcTemplate;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.web.servlet.MockMvc;
import java.io.ByteArrayOutputStream;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.multipart;
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 MemberImportControllerTest extends AbstractDbTest {
@Autowired
MockMvc mvc;
@Autowired
ContactMapper contactMapper;
@Autowired
OrganizationMapper orgMapper;
@Autowired
JdbcTemplate jdbc;
@BeforeEach
void clean() {
jdbc.update("delete from organization");
jdbc.update("delete from contact");
}
/** 회원정보 시트: 1행 헤더, 2행부터 데이터. A=순번 B=구분 C=기관명 D=부서명 E=담당자명 F=직급 G=전화 H=이메일 */
private MockMultipartFile workbook(String[]... dataRows) throws Exception {
try (XSSFWorkbook wb = new XSSFWorkbook();
ByteArrayOutputStream out = new ByteArrayOutputStream()) {
Sheet sheet = wb.createSheet(MemberDirectoryParser.SHEET_NAME);
Row header = sheet.createRow(0);
String[] headers = {"순번", "구분", "기관명", "부서명", "담당자명", "직급/직함", "유선전화", "이메일"};
for (int i = 0; i < headers.length; i++) {
header.createCell(i).setCellValue(headers[i]);
}
int rowNum = 1;
for (String[] data : dataRows) {
Row row = sheet.createRow(rowNum++);
for (int i = 0; i < data.length; i++) {
if (data[i] == null) {
continue;
}
row.createCell(i).setCellValue(data[i]);
}
}
wb.write(out);
return new MockMultipartFile("file", "members.xlsx",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
out.toByteArray());
}
}
@Test
void 구분을_주관기관_변호사_신청기관_수행기관으로_각각_매핑한다() throws Exception {
MockMultipartFile file = workbook(
new String[]{"1", "주관기관", null, "저작권정책과", "김문정", "사무관", "02-1", "mj@mcst.go.kr"},
new String[]{"2", "변호사", null, null, "이변호", null, "02-2", "law@firm.kr"},
new String[]{"3", "신청기관", "국제방송교류재단", "데이터정보화팀", "송민지", "과장", "02-3", "ming@arirang.com"},
new String[]{"4", "수행기관", "수행사", "운영팀", "홍수행", "대리", "02-4", "op@vendor.kr"});
mvc.perform(multipart("/api/contacts/import").file(file).with(csrf()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.created").value(4))
.andExpect(jsonPath("$.skipped").value(0));
List<Contact> all = contactMapper.findAll(null);
assertThat(all).hasSize(4);
assertThat(all).extracting(Contact::getCategory)
.containsExactlyInAnyOrder("MJ", "LAWYER", "APPLICANT", "OPERATOR");
Contact operator = all.stream().filter(c -> "OPERATOR".equals(c.getCategory())).findFirst().orElseThrow();
assertThat(operator.getName()).isEqualTo("홍수행");
assertThat(operator.getAffiliation()).isEqualTo("수행사");
assertThat(operator.getDeptName()).isEqualTo("운영팀");
}
@Test
void 같은_사람이_여러_행에_있으면_한_명만_생성되고_두번째부터는_갱신으로_집계된다() throws Exception {
MockMultipartFile file = workbook(
new String[]{"1", "신청기관", "경찰청", "데이터정책계", "박진우", "경위", "02-3150-3205", "hi@police.go.kr"},
new String[]{"2", "신청기관", "서울청", "데이터정책계", "박진우", "경위", "02-3150-3205", "hi@police.go.kr"},
new String[]{"3", "신청기관", "부산청", "데이터정책계", "박진우", "경위", "02-3150-3205", "hi@police.go.kr"});
mvc.perform(multipart("/api/contacts/import").file(file).with(csrf()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.created").value(1))
.andExpect(jsonPath("$.updated").value(2));
assertThat(jdbc.queryForObject("select count(*) from contact", Integer.class)).isEqualTo(1);
}
@Test
void 신청기관_담당자는_기관명이_같은_기관에_자동_배정된다() throws Exception {
Organization org = new Organization();
org.setOrgNo("001_00");
org.setOrgName("국제방송교류재단");
org.setChannelSlug("001_00");
orgMapper.upsertBySeed(org);
Long orgId = orgMapper.findAll().get(0).getId();
assertThat(orgMapper.findById(orgId).getApplicantContactId()).isNull();
MockMultipartFile file = workbook(new String[]{
"1", "신청기관", "국제방송교류재단", "데이터정보화팀", "송민지", "과장", "02-3475-5434", "ming@arirang.com"});
mvc.perform(multipart("/api/contacts/import").file(file).with(csrf()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.assigned").value(1));
Organization after = orgMapper.findById(orgId);
assertThat(after.getApplicantContactId()).isNotNull();
assertThat(after.getApplicant().getName()).isEqualTo("송민지");
}
@Test
void 이미_배정된_기관은_재업로드해도_배정이_바뀌지_않는다() throws Exception {
Organization org = new Organization();
org.setOrgNo("001_00");
org.setOrgName("국제방송교류재단");
org.setChannelSlug("001_00");
orgMapper.upsertBySeed(org);
Long orgId = orgMapper.findAll().get(0).getId();
MockMultipartFile file = workbook(new String[]{
"1", "신청기관", "국제방송교류재단", "데이터정보화팀", "송민지", "과장", "02-3475-5434", "ming@arirang.com"});
mvc.perform(multipart("/api/contacts/import").file(file).with(csrf()))
.andExpect(status().isOk());
Long firstContactId = orgMapper.findById(orgId).getApplicantContactId();
mvc.perform(multipart("/api/contacts/import").file(file).with(csrf()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.assigned").value(0));
assertThat(orgMapper.findById(orgId).getApplicantContactId()).isEqualTo(firstContactId);
}
@Test
void 알수없는_구분은_건너뛰고_skipped로_집계된다() throws Exception {
MockMultipartFile file = workbook(
new String[]{"1", "신청기관", "국제방송교류재단", "팀", "송민지", "과장", "02-1", "a@b.kr"},
new String[]{"2", "알수없음", "어떤기관", "팀", "홍길동", "대리", "02-2", "b@c.kr"});
mvc.perform(multipart("/api/contacts/import").file(file).with(csrf()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.created").value(1))
.andExpect(jsonPath("$.skipped").value(1));
assertThat(jdbc.queryForObject("select count(*) from contact", Integer.class)).isEqualTo(1);
}
@Test
void 확장자가_xlsx_xlsm이_아니면_400이다() throws Exception {
MockMultipartFile txt = new MockMultipartFile("file", "members.txt",
"text/plain", "아무 내용".getBytes());
mvc.perform(multipart("/api/contacts/import").file(txt).with(csrf()))
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.message").value(containsString("xlsx")));
}
}