import json from datetime import date from ccma.domain.models import ContributionData from ccma.services.housekeeper import Housekeeper, HousekeeperSettings from ccma.storage.repository import MemberRepository def test_housekeeper_reports_initial_payment_and_open_claims(tmp_path) -> None: repository = MemberRepository(tmp_path) repository.initialize() member = repository.create_member(first_name="Test", last_name="Person", birth_date="1990-01-01") member.status = "accepted_pending_payment" member.accepted_at = "2026-01-01" repository.save_member(member) repository.save_contributions( member.member_id, ContributionData( claims=[ { "claim_id": "claim-1", "title": "Mitgliedsbeitrag 2026", "amount": "150.00", "due_date": "2026-01-31", "status": "open", } ] ), ) findings = Housekeeper(repository).run(today=date(2026, 2, 10)) assert {finding.code for finding in findings} == {"initial_payment_overdue", "claim_overdue"} def test_housekeeper_reports_birthdays_before_today_and_after(tmp_path) -> None: repository = MemberRepository(tmp_path) repository.initialize() dates = ("1990-06-20", "1990-06-21", "1990-06-22") for index, birth_date in enumerate(dates): member = repository.create_member( first_name=f"Birthday{index}", last_name="Member", birth_date=birth_date, ) member.status = "active" repository.save_member(member) settings = HousekeeperSettings.from_values( birthday_days_before=2, birthday_days_after=2, anniversary_days_before=0, anniversary_days_after=0, anniversary_intervals="1Y", ) findings = [ finding for finding in Housekeeper(repository, settings).run(today=date(2026, 6, 21)) if finding.code == "birthday" ] assert {finding.title for finding in findings} == { "Birthday0 Member hatte vor 1 Tag Geburtstag", "Birthday1 Member hat heute Geburtstag", "Birthday2 Member hat in 1 Tag Geburtstag", } def test_housekeeper_reports_day_month_and_year_anniversaries(tmp_path) -> None: repository = MemberRepository(tmp_path) repository.initialize() starts = ("2026-05-22", "2026-04-21", "2025-06-21", "2016-06-22") for index, started_at in enumerate(starts): member = repository.create_member(first_name=f"Anniversary{index}", last_name="Member") member.status = "active" member.membership_started_at = started_at repository.save_member(member) settings = HousekeeperSettings.from_values( birthday_days_before=0, birthday_days_after=0, anniversary_days_before=2, anniversary_days_after=2, anniversary_intervals="30D;2M;1Y;10Y", ) findings = [ finding for finding in Housekeeper(repository, settings).run(today=date(2026, 6, 21)) if finding.code == "membership_anniversary" ] assert {finding.title for finding in findings} == { "Anniversary0 Member hat heute 30-Tage-Mitgliedsjubiläum", "Anniversary1 Member hat heute 2-Monats-Mitgliedsjubiläum", "Anniversary2 Member hat heute 1-jähriges Mitgliedsjubiläum", "Anniversary3 Member hat in 1 Tag 10-jähriges Mitgliedsjubiläum", } def test_housekeeper_reports_invalid_member_dates(tmp_path) -> None: repository = MemberRepository(tmp_path) repository.initialize() member = repository.create_member(first_name="Broken", last_name="Date") member_path = repository.members_root / member.member_id / "member.json" raw = json.loads(member_path.read_text(encoding="utf-8")) raw["person"]["birth_date"] = "31/12/2000" member_path.write_text(json.dumps(raw), encoding="utf-8") findings = Housekeeper(repository).run(today=date(2026, 6, 21)) invalid = [finding for finding in findings if finding.code == "invalid_member_dates"] assert len(invalid) == 1 assert invalid[0].member_id == member.member_id assert "Geburtsdatum" in invalid[0].detail