"""End-to-end checks using the fixed 2026 workbook and the PDF template.""" import sys from pathlib import Path import fitz import pytest PROJECT_ROOT = Path(__file__).resolve().parent.parent sys.path.insert(0, str(PROJECT_ROOT)) from src.config import load_settings, load_input_schema, load_output_mapping from src.parser import parse_input_file, group_by_employee, get_unique_employees from src.writer import generate_activity_reports CONFIG_DIR = PROJECT_ROOT / "config" INPUT_FILE = Path(__file__).resolve().parent / "fixture" / "2026 JM.xlsx" TEMPLATE_FILE = PROJECT_ROOT / "template" / "06-2026 Tätigkeitsnachweise Stellenförderung.pdf" @pytest.fixture(scope="module") def app_config(): config = load_settings(CONFIG_DIR, PROJECT_ROOT) config.input_schema = load_input_schema(CONFIG_DIR) config.output_mapping = load_output_mapping(CONFIG_DIR) return config @pytest.fixture(scope="module") def parsed_records(app_config): assert INPUT_FILE.is_file(), f"Testdatei nicht gefunden: {INPUT_FILE}" return parse_input_file( filepath=INPUT_FILE, schema=app_config.input_schema, month_sheet_pattern=app_config.month_sheet_pattern, skip_empty_name=app_config.skip_empty_name, skip_empty_antragsnummer=app_config.skip_empty_antragsnummer, ) @pytest.fixture def output_dir(tmp_path): out = tmp_path / "output" out.mkdir() return out class TestEndToEndFirstPerson: def test_input_file_exists(self): assert INPUT_FILE.is_file(), f"Testdatei nicht gefunden: {INPUT_FILE}" def test_template_file_exists(self): assert TEMPLATE_FILE.is_file(), f"PDF-Template nicht gefunden: {TEMPLATE_FILE}" def test_template_has_field_markers(self, app_config): with fitz.open(TEMPLATE_FILE) as doc: missing = [ mapping.pdf_field for mapping in app_config.output_mapping if mapping.pdf_field is not None and not any(page.search_for(mapping.pdf_field) for page in doc) ] assert not missing, f"Folgende _Field-Platzhalter fehlen im PDF: {missing}" def test_parse_real_input(self, parsed_records): # Fixed expectations for tests/fixture/2026 JM.xlsx. assert len(parsed_records) == 100 employees = get_unique_employees(parsed_records) assert len(employees) == 17 assert employees[0] == "Bardaus, Madalina" first_records = group_by_employee(parsed_records)[employees[0]] assert [record.monat for record in first_records] == [ "01.2026", "02.2026", "03.2026", "04.2026", "05.2026" ] for record in first_records: assert record.antragsnummer == "100781516" assert record.funktion == "Psychologin" assert record.stellenanteil == "50" assert record.qualifikation == "" assert record.taetigkeit.startswith("Psychologische Betreuung") def test_full_pipeline_first_person(self, app_config, parsed_records, output_dir): grouped = group_by_employee(parsed_records) assert len(grouped) == 17 first_person = sorted(grouped)[0] assert first_person == "Bardaus, Madalina" assert len(grouped[first_person]) == 5 generated = generate_activity_reports( employee_records={first_person: grouped[first_person]}, template_path=TEMPLATE_FILE, mapping=app_config.output_mapping, output_dir=output_dir, filename_pattern=app_config.filename_pattern, fontsize=app_config.fontsize, ) assert len(generated) == 1 output_file = generated[0] assert output_file.is_file() assert output_file.name == "Bardaus_Madalina_Tätigkeitsnachweis.pdf" with fitz.open(output_file) as doc: assert doc.page_count == 10 # Five months, two pages per month. assert "_Field" not in "".join(page.get_text() for page in doc) # Independently specified month ends include February's 28 days. expected_months = [ ("01.2026", "31.01.2026"), ("02.2026", "28.02.2026"), ("03.2026", "31.03.2026"), ("04.2026", "30.04.2026"), ("05.2026", "31.05.2026"), ] for index, (month, month_end) in enumerate(expected_months): data_text = doc[index * 2].get_text() signature_text = doc[index * 2 + 1].get_text() for value in [ "100781516", "Bardaus, Madalina", "50,00", month, "Psychologin", "Psychologische Betreuung", ]: assert value in data_text, f"{value!r} fehlt für {month}" assert "100781516" not in signature_text assert "Dresden" in signature_text assert month_end in signature_text