from pathlib import Path from applepy.findings import Finding, Severity from applepy.reporters.markdown import write_markdown_report from applepy.reporters.xlsx import ( _evidence_chunks_for_excel, _xlsx_cell_text, write_xlsx_report, ) def test_markdown_and_xlsx(tmp_path: Path) -> None: f = Finding( id="t-1", title="Test finding", category="Test", severity=Severity.INFORMATIONAL, description="Desc", evidence="Ev", worksheet="Test checks", mitre_techniques=("T1082",), ) cis = Finding( id="cis-099", title="CIS tab sample", category="Compliance", severity=Severity.INFORMATIONAL, description="d", evidence="e", worksheet="CIS", ) md = tmp_path / "r.md" xl = tmp_path / "f.xlsx" write_markdown_report(md, [f, cis], ["warn1"]) write_xlsx_report(xl, [f, cis]) md_text = md.read_text(encoding="utf-8") assert md_text.startswith("# ApplePY") assert "## Contents" in md_text assert "[Executive summary](#executive-summary)" in md_text assert "overarching themes" in md_text.lower() assert "## Other findings" in md_text or "## Core platform and controls" in md_text assert "Detail category:" in md_text assert "Granular categories covered here:" in md_text assert "#### Evidence" in md_text assert "```" in md_text assert "CIS macOS worksheet index" in md_text assert "cis-099" in md_text assert xl.is_file() and xl.stat().st_size > 200 from openpyxl import load_workbook wb = load_workbook(xl) assert wb.sheetnames[0] == "Summary" assert wb.sheetnames[1] == "Findings list" assert "CIS" in wb.sheetnames assert wb["CIS"]["A2"].value == "cis-099" fl = wb["Findings list"] assert fl["A1"].value == "ID" assert fl["E1"].value == "Detail worksheet" assert fl.max_row == 1 # Summary sheet must contain the worksheet index section summary_ws = wb["Summary"] col_a_vals = [summary_ws.cell(row=r, column=1).value for r in range(1, summary_ws.max_row + 1)] assert "Findings list (non-informational)" in col_a_vals assert "CIS" in col_a_vals def test_xlsx_cell_text_strips_control_chars() -> None: raw = "a\x00b\x08c" assert _xlsx_cell_text(raw) == "a b c" def test_evidence_chunks_split_for_excel_limit() -> None: long_ev = "X" * 40000 chunks = _evidence_chunks_for_excel(long_ev) assert len(chunks) >= 2 assert all(len(c) <= 32767 for c in chunks) assert "".join(chunks) == _xlsx_cell_text(long_ev) def test_xlsx_sorts_by_severity_descending(tmp_path: Path) -> None: from openpyxl import load_workbook lo = Finding( id="a-low", title="Later", category="Test", severity=Severity.INFORMATIONAL, description="d", evidence="e", worksheet="Zebra", ) hi = Finding( id="z-high", title="First", category="Test", severity=Severity.HIGH, description="d", evidence="e", worksheet="Zebra", ) xl = tmp_path / "sort.xlsx" write_xlsx_report(xl, [lo, hi]) wb = load_workbook(xl) assert wb.sheetnames[:2] == ["Summary", "Findings list"] fl = wb["Findings list"] assert fl.max_row == 2 assert fl["A2"].value == "z-high" assert fl["C2"].value == "high" ws = wb["Zebra"] assert ws["A2"].value == "z-high" assert ws["A3"].value == "a-low" def test_xlsx_findings_list_includes_non_informational_only(tmp_path: Path) -> None: from openpyxl import load_workbook info = Finding( id="i-1", title="Noise", category="Test", severity=Severity.INFORMATIONAL, description="d", evidence="e", worksheet="Alpha", ) med = Finding( id="m-1", title="Material", category="Test", severity=Severity.MEDIUM, description="d", evidence="e", worksheet="Alpha", ) xl = tmp_path / "fl.xlsx" write_xlsx_report(xl, [info, med]) wb = load_workbook(xl) fl = wb["Findings list"] assert fl.max_row == 2 assert fl["A2"].value == "m-1" assert fl["E2"].value == "Alpha" def test_xlsx_severity_row_has_distinct_fill(tmp_path: Path) -> None: from openpyxl import load_workbook high_f = Finding( id="h-sev", title="High item", category="Test", severity=Severity.HIGH, description="d", evidence="e", worksheet="Alpha", ) low_f = Finding( id="l-sev", title="Low item", category="Test", severity=Severity.LOW, description="d", evidence="e", worksheet="Alpha", ) xl = tmp_path / "sevfill.xlsx" write_xlsx_report(xl, [high_f, low_f]) wb = load_workbook(xl) ws = wb["Alpha"] hi_fill = ws["A2"].fill lo_fill = ws["A3"].fill assert hi_fill.fill_type == "solid" assert lo_fill.fill_type == "solid" assert hi_fill.start_color.rgb != lo_fill.start_color.rgb fl = wb["Findings list"] assert fl["A2"].fill.start_color.rgb == hi_fill.start_color.rgb def test_xlsx_long_evidence_writes_continuation_rows(tmp_path: Path) -> None: from openpyxl import load_workbook long_ev = "Y" * 35000 f = Finding( id="long-1", title="Long evidence", category="Test", severity=Severity.INFORMATIONAL, description="d", evidence=long_ev, worksheet="Evidence sheet", ) xl = tmp_path / "big.xlsx" write_xlsx_report(xl, [f]) wb = load_workbook(xl) assert wb.sheetnames[1] == "Findings list" assert wb["Findings list"].max_row == 1 ws = wb["Evidence sheet"] assert ws.max_row >= 3