115 lines
4.5 KiB
Python
115 lines
4.5 KiB
Python
from unittest.mock import AsyncMock
|
|
from io import BytesIO
|
|
|
|
import pytest
|
|
from openpyxl import Workbook, load_workbook
|
|
from xlsxwriter import Workbook as XlsxWorkbook
|
|
|
|
from src.services.export_service import ExportService
|
|
from src.services.export_service.export_mappings import (
|
|
FORM1_FLAT_COLUMNS,
|
|
SMETA_COLUMNS,
|
|
)
|
|
from src.services.export_service.export_writers import (
|
|
DepthSheetWriter,
|
|
TabularSheetWriter,
|
|
XlsxDepthSheetWriter,
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize("backend", ["openpyxl", "xlsxwriter", "xlsxwriter_columns", "xlsxwriter_rows", "xlsxwriter_palette"])
|
|
@pytest.mark.parametrize("name_key", ["name", "header.name"])
|
|
def test_depth_writer_preserves_collapsible_row_hierarchy(backend, name_key):
|
|
stream = BytesIO()
|
|
workbook = Workbook() if backend == "openpyxl" else XlsxWorkbook(stream)
|
|
worksheet = workbook.active if backend == "openpyxl" else workbook.add_worksheet()
|
|
options = {}
|
|
if backend == "xlsxwriter_columns":
|
|
options["column_format_resolver"] = lambda key, column: {"bold": True}
|
|
if backend == "xlsxwriter_rows":
|
|
options["row_format_resolver"] = lambda row, key, column: {"bold": True}
|
|
if backend == "xlsxwriter_palette":
|
|
options["colors_config"] = {"header": {"palette": "green"}}
|
|
writer_class = DepthSheetWriter if backend == "openpyxl" else XlsxDepthSheetWriter
|
|
if backend != "openpyxl":
|
|
options["workbook"] = workbook
|
|
writer = writer_class(
|
|
columns_dict={"header": {"name": "Header", "key": "", "children": {
|
|
"name": {"name": "Name", "key": name_key},
|
|
"code": {"name": "Code"},
|
|
"contract.name": {"name": "Contract name"},
|
|
}}},
|
|
row_normalizer=lambda row: row,
|
|
value_serializer=lambda value: value,
|
|
**options,
|
|
)
|
|
# Missing GROUP/SUB_ITEM levels and misleading numeric depths match frontend cases.
|
|
types = ["ROOT", "ITEM", "INPUT", "META", "INPUT", "ITEM", "SUB_ITEM", "INPUT", "ROOT"]
|
|
writer.write(worksheet, [(kind, 42, index, {name_key: str(index), "code": index, "contract.name": "Contract"}) for index, kind in enumerate(types)])
|
|
if backend == "openpyxl":
|
|
workbook.save(stream)
|
|
else:
|
|
workbook.close()
|
|
stream.seek(0)
|
|
sheet = load_workbook(stream).active
|
|
assert sheet.sheet_properties.outlinePr.summaryBelow is False
|
|
assert [sheet.row_dimensions[index].outlineLevel for index in range(3, 11)] == [0, 1, 2, 2, 1, 2, 3, 0]
|
|
assert [sheet.cell(index, 1).value for index in range(3, 11)] == ["0", "1", "2", "4", "5", "6", "7", "8"]
|
|
assert all(not sheet.row_dimensions[index].hidden for index in range(3, 11))
|
|
assert [sheet.cell(index, 1).alignment.indent for index in range(3, 11)] == [0, 1, 2, 2, 1, 2, 3, 0]
|
|
assert all(sheet.cell(index, column).alignment.indent == 0 for index in range(3, 11) for column in (2, 3))
|
|
if backend in ("xlsxwriter_columns", "xlsxwriter_rows"):
|
|
assert sheet.cell(4, 1).font.bold is True
|
|
if backend == "xlsxwriter_palette":
|
|
assert sheet.cell(4, 1).fill.fgColor == sheet.cell(4, 2).fill.fgColor
|
|
|
|
|
|
def test_tabular_sheet_writer_writes_headers_and_rows():
|
|
writer = TabularSheetWriter(
|
|
columns=[("a", "A"), ("b", "B")],
|
|
row_normalizer=lambda row: row,
|
|
value_serializer=lambda value: value,
|
|
)
|
|
wb = Workbook()
|
|
ws = wb.active
|
|
|
|
rows = [(None, None, None, {"a": 1, "b": 2})]
|
|
writer.write(ws, rows)
|
|
|
|
assert ws.cell(row=1, column=1).value == "A"
|
|
assert ws.cell(row=1, column=2).value == "B"
|
|
assert ws.cell(row=2, column=1).value == 1
|
|
assert ws.cell(row=2, column=2).value == 2
|
|
|
|
|
|
def test_sheet_column_contracts_are_stable():
|
|
assert len(FORM1_FLAT_COLUMNS) == 17
|
|
assert len(SMETA_COLUMNS) == 41
|
|
assert FORM1_FLAT_COLUMNS[0] == ("section", "Код раздела")
|
|
assert SMETA_COLUMNS[0] == ("section_code", "Код раздела")
|
|
|
|
|
|
def test_export_service_uses_sheet_writer_strategy():
|
|
service = ExportService(AsyncMock())
|
|
|
|
class Recorder:
|
|
def __init__(self):
|
|
self.calls = 0
|
|
|
|
def write(self, worksheet, rows):
|
|
self.calls += 1
|
|
worksheet.append(["ok"])
|
|
|
|
default_writer = Recorder()
|
|
smeta_writer = Recorder()
|
|
service.sheet_writers = {"__default__": default_writer, "SMETA": smeta_writer}
|
|
|
|
wb = Workbook()
|
|
ws_default = wb.active
|
|
ws_smeta = wb.create_sheet("smeta")
|
|
service._write_sheet_rows(ws_default, [], "AHR")
|
|
service._write_sheet_rows(ws_smeta, [], "SMETA")
|
|
|
|
assert default_writer.calls == 1
|
|
assert smeta_writer.calls == 1
|