fix(projekt-matching): normalize unknown statuses, alert failure cannot block trash
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -106,6 +106,11 @@ def run_ingest(cfg, mailbox=None, dispatch=None, espo=None, resolve=None):
|
|||||||
result.update(send(result))
|
result.update(send(result))
|
||||||
except Exception as exc: # noqa: BLE001
|
except Exception as exc: # noqa: BLE001
|
||||||
result.update(status="failed", error=str(exc))
|
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)
|
results.append(result)
|
||||||
summary[result["status"]] += 1
|
summary[result["status"]] += 1
|
||||||
failed = [r for r in results if r["status"] == "failed"]
|
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('title', '?')}\n"
|
||||||
f" {f.get('canonical', f.get('url', '?'))}\n"
|
f" {f.get('canonical', f.get('url', '?'))}\n"
|
||||||
f" Fehler: {f.get('error', '?')}" for f in failed)
|
f" Fehler: {f.get('error', '?')}" for f in failed)
|
||||||
mailer.send_mail(
|
try:
|
||||||
cfg.imap_user, cfg.imap_password, cfg.alert_to,
|
mailer.send_mail(
|
||||||
f"[Projekt-Match-Fehler] Freelancermap — "
|
cfg.imap_user, cfg.imap_password, cfg.alert_to,
|
||||||
f"{len(failed)} Projekt(e)",
|
f"[Projekt-Match-Fehler] Freelancermap — "
|
||||||
"Folgende Projekte konnten nicht verarbeitet "
|
f"{len(failed)} Projekt(e)",
|
||||||
"werden:\n\n" + lines + "\n")
|
"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.move_to_trash(uid)
|
||||||
box.close()
|
box.close()
|
||||||
return summary
|
return summary
|
||||||
|
|||||||
@@ -90,6 +90,34 @@ def test_lock_prevents_second_run(tmp_path):
|
|||||||
ingest.release_lock(lock)
|
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():
|
def test_parse_run_result_finds_status_json():
|
||||||
inner = json.dumps({"status": "created", "mustMatch": 90})
|
inner = json.dumps({"status": "created", "mustMatch": 90})
|
||||||
payload = {"outputs": [{"outputs": [{"results": {"text": {
|
payload = {"outputs": [{"outputs": [{"results": {"text": {
|
||||||
|
|||||||
Reference in New Issue
Block a user