package kr.itn.itnhub.process; import kr.itn.itnhub.AbstractDbTest; import kr.itn.itnhub.org.Organization; import kr.itn.itnhub.org.OrganizationMapper; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import java.io.ByteArrayInputStream; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; /** * 권리처리 엑셀의 업로드↔다운로드 왕복. * *

업로드는 컬럼 번호({@code ProcessParser.COL_*})로, 다운로드는 헤더 이름 * ({@code ProcessService.COLUMN_HEADERS})으로 각각 따로 정의돼 있다. 두 정의가 어긋나면 * "내려받아 고쳐서 다시 올리는" 흐름에서 값이 조용히 사라진다. 그 어긋남을 여기서 잡는다.

*/ class ProcessExcelRoundTripTest extends AbstractDbTest { @Autowired ProcessService service; @Autowired ProcessParser parser; @Autowired OrganizationMapper orgMapper; @Autowired JdbcTemplate jdbc; private Long orgId; @BeforeEach void setUp() { jdbc.update("delete from process_item"); jdbc.update("delete from organization where org_no = '920'"); Organization org = new Organization(); org.setOrgNo("920"); org.setOrgName("왕복테스트기관"); org.setChannelSlug("920"); orgMapper.upsertBySeed(org); orgId = orgMapper.findByOrgNoAndOrgName("920", "왕복테스트기관").getId(); } @AfterEach void cleanUp() { jdbc.update("delete from process_item"); jdbc.update("delete from organization where org_no = '920'"); } /** 헤더 이름과 파서가 읽는 컬럼이 같은 자리를 가리키는지. 한쪽만 고치면 여기서 깨진다. */ @Test void 헤더_이름과_파서_컬럼이_같은_자리를_가리킨다() { assertHeader(ProcessParser.COL_SEQ, "순번"); assertHeader(ProcessParser.COL_SITE_NAME, "사이트명"); assertHeader(ProcessParser.COL_CATEGORY, "범주"); assertHeader(ProcessParser.COL_BOARD_NAME, "게시판명"); assertHeader(ProcessParser.COL_POST_TITLE, "게시물제목"); assertHeader(ProcessParser.COL_URL, "URL주소"); assertHeader(ProcessParser.COL_DESCRIPTION, "설명"); assertHeader(ProcessParser.COL_HAS_ATTACHMENT, "첨부파일여부"); assertHeader(ProcessParser.COL_PRIOR_KOGL_TYPE, "기존 공공누리유형(+AI)"); assertHeader(ProcessParser.COL_CONTRACT_DOCS, "계약서 유무"); assertHeader(ProcessParser.COL_PRODUCED_DATE, "제작일"); assertHeader(ProcessParser.COL_PUBLISHED_DATE, "공표일"); assertHeader(ProcessParser.COL_REVIEW_MAJOR, "권리확인(대분류)"); assertHeader(ProcessParser.COL_REVIEW_MINOR, "권리확인(세부)"); assertHeader(ProcessParser.COL_REVIEW_RESULT, "처리 구분"); assertHeader(ProcessParser.COL_REVIEW_KOGL_TYPE, "공공누리 유형"); assertHeader(ProcessParser.COL_REVIEW_AI_TYPE, "AI유형"); assertHeader(ProcessParser.COL_REVIEW_OPINION, "의견"); assertHeader(ProcessParser.COL_REVIEW_NOTE, "비고"); assertHeader(ProcessParser.COL_JUDGED_KOGL_TYPE, "공공누리유형(판정)"); assertHeader(ProcessParser.COL_JUDGED_AI_TYPE, "AI유형(판정)"); assertHeader(ProcessParser.COL_FINAL_OPINION, "최종의견"); assertHeader(ProcessParser.COL_JUDGMENT_BASIS, "판단근거"); assertHeader(ProcessParser.COL_PROCESS_STATUS, "처리상태"); } private void assertHeader(int column, String expected) { assertThat(ProcessService.COLUMN_HEADERS[column]) .as("%d번 열의 헤더", column) .isEqualTo(expected); } /** * 기존 증빙자료는 예외다. 기관이 보내오는 실제 파일은 헤더가 T열인데 데이터가 U열에 있어 * 파서가 U를 먼저 읽고, 비어 있으면 헤더 자리인 T를 읽는다. 이 어긋남 자체를 명시적으로 * 남겨 두어, 나중에 누가 "헤더랑 다르네" 하고 한쪽만 고치다 깨뜨리지 않게 한다. */ @Test void 기존_증빙자료만_헤더와_데이터_열이_다르다() { assertThat(ProcessService.COLUMN_HEADERS[19]).isEqualTo("기존 증빙자료"); assertThat(ProcessParser.COL_PRIOR_EVIDENCE_HEADER).isEqualTo(19); assertThat(ProcessParser.COL_PRIOR_EVIDENCE).isEqualTo(20); } /** 기관 원본처럼 U열에 값이 있는 파일도 그대로 읽어야 한다(다운로드 파일만 고치면 안 된다). */ @Test void 원본처럼_U열에_값이_있어도_읽는다() throws Exception { byte[] xlsx = ProcessParserFixtures.withPriorEvidenceAt(ProcessParser.COL_PRIOR_EVIDENCE, "원본증빙"); List rows = parser.parse(new ByteArrayInputStream(xlsx)); assertThat(rows).hasSize(1); assertThat(rows.get(0).priorEvidence()).isEqualTo("원본증빙"); } /** 내려받은 파일을 그대로 다시 올렸을 때 값이 살아남아야 한다. */ @Test void 내려받아_다시_올려도_값이_그대로다() { jdbc.update("insert into process_item (org_id, seq, site_name, category, board_name, " + "post_title, url, description, has_attachment, prior_kogl_type, contract_docs, " + "produced_date, published_date, review_major, review_minor, review_result, " + "review_kogl_type, review_ai_type, review_opinion, review_note, prior_evidence, " + "judged_kogl_type, judged_ai_type, final_opinion, judgment_basis, process_status) " + "values (?, 1, '사이트', '범주', '게시판', '제목', 'http://x', '설명', '있음', " + "'4유형', '양도계약서', '2026-01-02', '2026-01-03', '제3자 권리', '초상권 포함', " + "'권리처리 추진', '보류', 'Y', '의견', '비고', '증빙자료', '0유형', 'Y', " + "'최종의견', '판단근거', '처리완료')", orgId); byte[] xlsx = service.downloadWorkbook(orgId); List rows = parser.parse(new ByteArrayInputStream(xlsx)); assertThat(rows).hasSize(1); ProcessRow row = rows.get(0); assertThat(row.siteName()).isEqualTo("사이트"); assertThat(row.category()).isEqualTo("범주"); assertThat(row.boardName()).isEqualTo("게시판"); assertThat(row.postTitle()).isEqualTo("제목"); assertThat(row.url()).isEqualTo("http://x"); assertThat(row.description()).isEqualTo("설명"); assertThat(row.hasAttachment()).isEqualTo("있음"); assertThat(row.priorKoglType()).isEqualTo("4유형"); assertThat(row.contractDocs()).isEqualTo("양도계약서"); assertThat(row.producedDate()).isEqualTo("2026-01-02"); assertThat(row.publishedDate()).isEqualTo("2026-01-03"); assertThat(row.reviewMajor()).isEqualTo("제3자 권리"); assertThat(row.reviewMinor()).isEqualTo("초상권 포함"); assertThat(row.reviewResult()).isEqualTo("권리처리 추진"); assertThat(row.reviewKoglType()).isEqualTo("보류"); assertThat(row.reviewAiType()).isEqualTo("Y"); assertThat(row.reviewOpinion()).isEqualTo("의견"); assertThat(row.reviewNote()).isEqualTo("비고"); assertThat(row.judgedKoglType()).isEqualTo("0유형"); assertThat(row.judgedAiType()).isEqualTo("Y"); assertThat(row.finalOpinion()).isEqualTo("최종의견"); assertThat(row.judgmentBasis()).isEqualTo("판단근거"); assertThat(row.processStatus()).isEqualTo("처리완료"); // 기존 증빙자료는 다운로드가 T열에 쓰고 파서가 U열을 읽어 왕복에서 사라진다. assertThat(row.priorEvidence()) .as("내려받아 다시 올렸을 때 기존 증빙자료") .isEqualTo("증빙자료"); } }