"""„Jetzt suchen“ im Radar als Auftrag (24.09.2026): Die Route antwortet sofort mit einer Auftrags-ID, gesucht wird in radar_lauf.py --nur-suche. Vorher lief die Suche synchron in der Anfrage und konnte Minuten dauern.""" import sys import pytest import radar_lauf from fastapi import FastAPI from fastapi.testclient import TestClient from routers import radar as radar_router from services import radar @pytest.fixture def auftraege(monkeypatch): """Job-Engine-Attrappe: merkt sich den Start; läuft schon einer, kommt der zurück.""" stand: dict = {"gestartet": [], "laeuft": None} def start_job_exklusiv(gruppe, args, label, **kw): if stand["laeuft"]: return None, stand["laeuft"] stand["gestartet"].append({"gruppe": gruppe, "args": args, "label": label, **kw}) return "job123", None monkeypatch.setattr(radar.jobengine, "start_job_exklusiv", start_job_exklusiv) return stand def test_suche_startet_einen_auftrag(auftraege): ergebnis = radar.suche_starten() assert ergebnis["ok"] is True and ergebnis["job_id"] == "job123" and "Aufträge" in ergebnis["text"] (start,) = auftraege["gestartet"] assert start["gruppe"] == "radar" and start["zeitlimit_s"] == radar.SUCHE_ZEITLIMIT_S assert start["args"][0] == sys.executable assert start["args"][1].endswith("radar_lauf.py") and start["args"][2:] == ["--nur-suche"] def test_laufende_suche_wird_nicht_doppelt_gestartet(auftraege): auftraege["laeuft"] = {"id": "job-alt", "state": "running"} ergebnis = radar.suche_starten() assert (ergebnis["job_id"], ergebnis["laeuft_schon"]) == ("job-alt", True) assert auftraege["gestartet"] == [] def test_route_antwortet_sofort_ohne_selbst_zu_suchen(auftraege, monkeypatch): def nicht_hier(): raise AssertionError("Die Route darf nicht selbst suchen.") monkeypatch.setattr(radar, "suche", nicht_hier) app = FastAPI() app.include_router(radar_router.router) antwort = TestClient(app).post("/api/radar/suche") assert antwort.status_code == 200 and antwort.json()["job_id"] == "job123" @pytest.mark.parametrize("ergebnis, code, zeile", [ ({"neu": ["Ornith 1.5"], "wartend": 2, "quellen": {"watchlist": True, "discover": True}}, 0, "Suche fertig: 1 neu (Ornith 1.5), 2 warten auf den Nachttest."), ({"neu": [], "wartend": 1, "quellen": {"watchlist": True, "discover": False}}, 0, "Suche fertig: nichts Neues, 1 warten auf den Nachttest, Hugging Face war nicht erreichbar."), ({"neu": [], "wartend": 0, "quellen": {"watchlist": False, "discover": False}}, 1, ("Suche fertig: nichts Neues, 0 warten auf den Nachttest, Hugging Face war nicht erreichbar, " "die Merkliste war nicht lesbar.")), ]) def test_nur_suche_sagt_das_ergebnis_in_der_letzten_zeile(monkeypatch, capsys, ergebnis, code, zeile): monkeypatch.setattr(radar_lauf.radar, "suche", lambda: ergebnis) assert radar_lauf.nur_suche() == code assert capsys.readouterr().out.strip().splitlines()[-1] == zeile def test_nur_suche_bei_absturz(monkeypatch, capsys): def kaputt(): raise OSError("Platte voll") monkeypatch.setattr(radar_lauf.radar, "suche", kaputt) assert radar_lauf.nur_suche() == 1 assert capsys.readouterr().out.strip().endswith("Die Suche ist gescheitert: OSError: Platte voll")