diff --git a/projekt-matching/projektmatch/ingest.py b/projekt-matching/projektmatch/ingest.py index 961194d..cb579a0 100644 --- a/projekt-matching/projektmatch/ingest.py +++ b/projekt-matching/projektmatch/ingest.py @@ -106,6 +106,11 @@ def run_ingest(cfg, mailbox=None, dispatch=None, espo=None, resolve=None): result.update(send(result)) except Exception as exc: # noqa: BLE001 result.update(status="failed", error=str(exc)) + if result.get("status") not in SUMMARY_KEYS: + result.update( + status="failed", + error=result.get("error") + or f"unbekannter Status: {result.get('status')!r}") results.append(result) summary[result["status"]] += 1 failed = [r for r in results if r["status"] == "failed"] @@ -114,12 +119,15 @@ def run_ingest(cfg, mailbox=None, dispatch=None, espo=None, resolve=None): f"- {f.get('title', '?')}\n" f" {f.get('canonical', f.get('url', '?'))}\n" f" Fehler: {f.get('error', '?')}" for f in failed) - mailer.send_mail( - cfg.imap_user, cfg.imap_password, cfg.alert_to, - f"[Projekt-Match-Fehler] Freelancermap — " - f"{len(failed)} Projekt(e)", - "Folgende Projekte konnten nicht verarbeitet " - "werden:\n\n" + lines + "\n") + try: + mailer.send_mail( + cfg.imap_user, cfg.imap_password, cfg.alert_to, + f"[Projekt-Match-Fehler] Freelancermap — " + f"{len(failed)} Projekt(e)", + "Folgende Projekte konnten nicht verarbeitet " + "werden:\n\n" + lines + "\n") + except Exception: # noqa: BLE001 + summary["alertFailed"] = summary.get("alertFailed", 0) + 1 box.move_to_trash(uid) box.close() return summary diff --git a/projekt-matching/tests/test_ingest.py b/projekt-matching/tests/test_ingest.py index 94c14ef..5ad20aa 100644 --- a/projekt-matching/tests/test_ingest.py +++ b/projekt-matching/tests/test_ingest.py @@ -90,6 +90,34 @@ def test_lock_prevents_second_run(tmp_path): ingest.release_lock(lock) +def test_unknown_dispatch_status_normalized_to_failed(tmp_path): + mails = {"1": mail("Mail", PROJECT_HTML)} + summary, box, sent = run(tmp_path, mails, + dispatch_result={"weird": True}) + assert summary["failed"] == 1 + box.move_to_trash.assert_called_once() + + +def test_alert_send_failure_still_trashes(tmp_path): + mails = {"1": mail("Mail", PROJECT_HTML)} + box = mock.Mock() + box.unchecked_uids.return_value = list(mails) + box.fetch.side_effect = lambda uid: mails[uid] + espo = mock.Mock() + espo.find_opportunity_by_link.return_value = None + def boom_send(*a): + raise OSError("smtp down") + with mock.patch("projektmatch.ingest.mailparse.bodies", + side_effect=lambda m: (m["html"], "")), \ + mock.patch("projektmatch.ingest.mailer.send_mail", boom_send): + summary = ingest.run_ingest( + cfg(tmp_path), mailbox=box, espo=espo, + resolve=lambda url: "https://www.freelancermap.de/projekt/eins", + dispatch=lambda item: {"status": "failed", "error": "x"}) + assert summary["alertFailed"] == 1 + box.move_to_trash.assert_called_once_with("1") + + def test_parse_run_result_finds_status_json(): inner = json.dumps({"status": "created", "mustMatch": 90}) payload = {"outputs": [{"outputs": [{"results": {"text": {