Model/tests/infrastructure/xlsx/test_scenario_export_workbook.py
Khalim Conn-Kowlessar a8c90b0d95 Sanitise and deduplicate scenario sheet names 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 09:20:17 +00:00

50 lines
1.9 KiB
Python

"""The Scenario Export workbook renderer (ADR-0065): one branded .xlsx with one
sheet per Scenario, headers then rows, verified by re-opening the bytes."""
from __future__ import annotations
from io import BytesIO
from openpyxl import load_workbook
from domain.scenario_export.scenario_sheet import ExportSheet
from infrastructure.xlsx.scenario_export_workbook import render_workbook
def test_renders_a_sheet_named_for_the_scenario_with_headers_then_rows() -> None:
# arrange — one scenario's shaped sheet.
sheet = ExportSheet(
columns=("property_id", "loft_insulation"),
rows=({"property_id": 1, "loft_insulation": 1200.0},),
)
# act
data = render_workbook([("Fabric first", sheet)])
# assert — a real workbook with a sheet named for the scenario, the column
# contract as the header row, and the data beneath it.
workbook = load_workbook(BytesIO(data))
assert workbook.sheetnames == ["Fabric first"]
worksheet = workbook["Fabric first"]
assert [cell.value for cell in worksheet[1]] == ["property_id", "loft_insulation"]
assert [cell.value for cell in worksheet[2]] == [1, 1200.0]
def test_sanitises_and_deduplicates_scenario_sheet_names() -> None:
# arrange — a name over Excel's 31-char limit (used twice, so it must be
# deduplicated) and one with characters Excel forbids in a sheet title.
empty = ExportSheet(columns=("property_id",), rows=())
long_name = "A really long scenario name that exceeds Excel's limit"
illegal_name = "Bad:/\\?*[]name"
# act
data = render_workbook(
[(long_name, empty), (long_name, empty), (illegal_name, empty)]
)
# assert — three distinct, Excel-legal sheet names.
names = load_workbook(BytesIO(data)).sheetnames
assert len(names) == 3
assert len(set(names)) == 3
assert all(len(name) <= 31 for name in names)
assert not any(ch in name for name in names for ch in r":/\?*[]")