From b84fd2f61a6766c0ee458f6d8181a0d35810e7fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=86=90=EC=84=B1=EC=A4=80?= Date: Tue, 2 Jun 2026 09:31:51 +0900 Subject: [PATCH] =?UTF-8?q?test(xlsx):=20=EB=B3=B5=EC=9E=A1=20=EA=B2=AC?= =?UTF-8?q?=EC=A0=81=EC=84=9C=20=ED=9A=8C=EA=B7=80=20(=EA=B0=80=EB=A1=9C+?= =?UTF-8?q?=EC=84=B8=EB=A1=9C=20=EB=B3=91=ED=95=A9=C2=B7=EC=88=98=EC=8B=9D?= =?UTF-8?q?=C2=B7=ED=86=B5=ED=99=94=EC=84=9C=EC=8B=9D=C2=B7=EB=8B=A4?= =?UTF-8?q?=EC=A4=91=EC=8B=9C=ED=8A=B8=20=EB=B3=B4=EC=A1=B4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "복잡 엑셀도 검증했냐" 지적에 따라, parser 단위테스트용 작은 파일이 아니라 실제 복잡 구조(병합 제목 5열, 세로병합 섹션 3행, =B*C 수식, =SUM 합계, 통화 number_format, 2시트)를 만들어 깊게 검증. set_cell 편집 후에도 수식·서식· 병합·다른 시트가 모두 보존됨을 확인하는 회귀 테스트 추가. 79개 통과. Co-Authored-By: Claude Opus 4.8 (1M context) --- tests/test_scenarios.py | 48 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/tests/test_scenarios.py b/tests/test_scenarios.py index 0ec3562..4fa0a8d 100644 --- a/tests/test_scenarios.py +++ b/tests/test_scenarios.py @@ -574,6 +574,54 @@ def test_xlsx_value_typing(tmp_path: Path) -> None: assert wb2.active["B4"].value == "00100" # 선행 0 보존 +def test_xlsx_complex_merges_formulas_roundtrip(tmp_path: Path) -> None: + """복잡 xlsx: 가로+세로 병합, 수식, 통화서식, 다중시트가 편집 후에도 보존. + + set_cell 편집 시 다른 셀의 수식(=B*C, =SUM)·number_format·병합 영역· + 다른 시트가 그대로 유지돼야 한다 (openpyxl round-trip 정합성). + """ + from openpyxl import Workbook, load_workbook + + src = tmp_path / "quote.xlsx" + wb = Workbook() + ws = wb.active + ws.title = "견적서" + ws["A1"] = "견적서" + ws.merge_cells("A1:E1") # 가로 병합 제목 + ws["A2"] = "공급자" + ws.merge_cells("A2:A4") # 세로 병합 섹션 + for r, (nm, qty, price) in enumerate( + [("노트북", 2, 1500000), ("모니터", 3, 300000)], start=7): + ws.cell(row=r, column=1, value=nm) + ws.cell(row=r, column=2, value=qty) + ws.cell(row=r, column=3, value=price) + fcell = ws.cell(row=r, column=4, value=f"=B{r}*C{r}") + fcell.number_format = '#,##0"원"' + ws["D10"] = "=SUM(D7:D8)" + wb.create_sheet("거래내역")["B1"] = 42 + wb.save(str(src)) + + ad = load(src) + t = ad.get_tables()[0] + merges = {(m.anchor, m.span) for m in t.merges} + assert ((0, 0), (1, 5)) in merges # 가로 병합 제목 + assert ((1, 0), (3, 1)) in merges # 세로 병합 섹션 + assert ad.get_cell(0, 0, 2).is_anchor is False # 병합 non-anchor + assert ad.get_cell(0, 6, 3).text == "=B7*C7" # 수식 노출 + ad.set_cell(0, 6, 1, "10") # 노트북 수량 편집 + ad.save(src) + ad.close() + + wb2 = load_workbook(str(src)) + w = wb2["견적서"] + assert w["B7"].value == 10 # 숫자 보존 + assert w["D7"].value == "=B7*C7" # 수식 보존 + assert w["D10"].value == "=SUM(D7:D8)" + assert w["D7"].number_format == '#,##0"원"' # 서식 보존 + assert "A1:E1" in {str(m) for m in w.merged_cells.ranges} + assert wb2["거래내역"]["B1"].value == 42 # 다른 시트 보존 + + def test_xlsx_merged_cell_write_rejected(tmp_path: Path) -> None: """병합 non-anchor 좌표 쓰기는 MergedCellWriteError (allow_merge_redirect로 우회).""" from document_adapter.base import MergedCellWriteError