technova-ml-api / tests /test_predict.py
github-actions
deploy: snapshot
5fa8558
# tests/test_services_predict.py
import pytest
def test_run_predict_manual_without_engine(monkeypatch):
"""
Cas simple : engine=None => pas d'audit, on renvoie proba/pred/payload enrichi.
"""
from app.services import predict as predict_service
# Fake predict_manual (ML)
def fake_predict_manual(payload, model, threshold):
return 0.8, 1, {"x": 1, "enrich": True}
monkeypatch.setattr(predict_service, "predict_manual", fake_predict_manual)
proba, pred, payload_enrichi = predict_service.run_predict_manual(
payload={"x": 1},
model=object(),
threshold=0.3,
engine=None,
)
assert proba == 0.8
assert pred == 1
assert payload_enrichi["enrich"] is True
def test_run_predict_manual_with_engine_calls_audit(monkeypatch):
"""
Cas engine présent : log_audit doit être appelé.
"""
from app.services import predict as predict_service
# Fake predict_manual
def fake_predict_manual(payload, model, threshold):
return 0.2, 0, {"foo": "bar"}
monkeypatch.setattr(predict_service, "predict_manual", fake_predict_manual)
# Spy log_audit
calls = {"count": 0, "args": None}
def fake_log_audit(conn, payload, proba, prediction, threshold):
calls["count"] += 1
calls["args"] = (conn, payload, proba, prediction, threshold)
return 123
monkeypatch.setattr(predict_service, "log_audit", fake_log_audit)
# Dummy engine.begin() context manager
class DummyEngine:
def begin(self):
return self
def __enter__(self):
return "dummy-conn"
def __exit__(self, exc_type, exc, tb):
return False
proba, pred, payload_enrichi = predict_service.run_predict_manual(
payload={"hello": "world"},
model=object(),
threshold=0.292,
engine=DummyEngine(),
)
assert proba == 0.2
assert pred == 0
assert payload_enrichi == {"foo": "bar"}
assert calls["count"] == 1
assert calls["args"][0] == "dummy-conn"
assert calls["args"][1] == {"foo": "bar"}
assert calls["args"][2] == 0.2
assert calls["args"][3] == 0
assert calls["args"][4] == 0.292
def test_run_predict_by_id_not_found_raises_keyerror(monkeypatch):
"""
Cas id absent : get_employee_features_by_id renvoie None => KeyError attendu.
"""
from app.services import predict as predict_service
def fake_get_employee_features_by_id(engine, id_employee):
return None
monkeypatch.setattr(predict_service, "get_employee_features_by_id", fake_get_employee_features_by_id)
with pytest.raises(KeyError):
predict_service.run_predict_by_id(
id_employee=999,
model=object(),
threshold=0.5,
engine=object(),
)
def test_run_predict_by_id_with_engine_calls_audit_and_adds_id(monkeypatch):
"""
Cas nominal : on récupère un employé, on prédit, on log en audit,
et on ajoute id_employee dans payload_enrichi avant log.
"""
from app.services import predict as predict_service
# Fake features fetch
def fake_get_employee_features_by_id(engine, id_employee):
return {"id_employee": id_employee, "age": 40}
monkeypatch.setattr(predict_service, "get_employee_features_by_id", fake_get_employee_features_by_id)
# Fake predict_from_employee_features
def fake_predict_from_employee_features(employee, model, threshold):
# payload enrichi sans id -> le service doit l'ajouter
return 0.55, 1, {"age": employee["age"]}
monkeypatch.setattr(predict_service, "predict_from_employee_features", fake_predict_from_employee_features)
# Spy log_audit
calls = {"count": 0, "payload": None}
def fake_log_audit(conn, payload, proba, prediction, threshold):
calls["count"] += 1
calls["payload"] = payload
return 456
monkeypatch.setattr(predict_service, "log_audit", fake_log_audit)
class DummyEngine:
def begin(self):
return self
def __enter__(self):
return "dummy-conn"
def __exit__(self, exc_type, exc, tb):
return False
proba, pred, payload_enrichi = predict_service.run_predict_by_id(
id_employee=7,
model=object(),
threshold=0.292,
engine=DummyEngine(),
)
assert proba == 0.55
assert pred == 1
assert payload_enrichi["age"] == 40
assert payload_enrichi["id_employee"] == 7
assert calls["count"] == 1
assert calls["payload"]["id_employee"] == 7