sentinel / tests /test_main.py
jeuko's picture
Sync from GitHub (main)
cc034ee verified
# pylint: disable=missing-docstring
import os
import socket
from unittest.mock import MagicMock, patch
import pytest
import requests
from fastapi.testclient import TestClient
from apps.api.main import app
from sentinel.models import InitialAssessment
client = TestClient(app)
def _is_enabled(value: str | None) -> bool:
if value is None:
return False
return value.strip().lower() not in {"", "0", "false", "no"}
CI_ENABLED = _is_enabled(os.getenv("CI"))
def test_root():
response = client.get("/")
assert response.status_code == 200
assert response.json() == {"message": "Hello, world!"}
@patch("apps.api.main.SentinelFactory")
def test_assess_local(mock_factory):
payload = {
"demographics": {
"age_years": 55,
"sex": "male",
"ethnicity": "white",
"anthropometrics": {"height_cm": 175, "weight_kg": 80},
},
"lifestyle": {
"smoking": {
"status": "former",
"pack_years": 10,
},
"alcohol_consumption": "moderate",
},
"family_history": [
{
"relation": "father",
"cancer_type": "lung_cancer",
"age_at_diagnosis": 60,
"degree": "1",
"side": "paternal",
}
],
"personal_medical_history": {
"previous_cancers": ["melanoma"],
},
}
expected = {
"thinking": None,
"reasoning": None,
"response": None,
"overall_summary": "ok",
"overall_risk_score": None,
"calculated_risk_scores": {},
"identified_risk_factors": [],
"risk_assessments": [],
"dx_recommendations": [],
}
mock_conversation_manager = MagicMock()
mock_conversation_manager.initial_assessment.return_value = expected
mock_factory_instance = mock_factory.return_value
mock_factory_instance.create_conversation_manager.return_value = (
mock_conversation_manager
)
response = client.post("/assess/local", json={"user_input": payload})
if response.status_code != 200:
print(f"Response status: {response.status_code}")
print(f"Response body: {response.text}")
assert response.status_code == 200
assert response.json() == expected
@patch("apps.api.main.SentinelFactory")
def test_assess_bad_provider(mock_factory):
payload = {
"demographics": {
"age_years": 30,
"sex": "male",
"anthropometrics": {"height_cm": 175, "weight_kg": 70},
},
"lifestyle": {
"smoking": {"status": "never"},
"alcohol_consumption": "none",
},
"family_history": [],
"personal_medical_history": {},
}
mock_factory.side_effect = ValueError("bad")
response = client.post("/assess/invalid", json={"user_input": payload})
assert response.status_code == 400
@patch("apps.api.main.SentinelFactory")
def test_assess_with_observations(mock_factory):
payload = {
"demographics": {
"age_years": 60,
"sex": "male",
"anthropometrics": {"height_cm": 175, "weight_kg": 75},
},
"lifestyle": {
"smoking": {"status": "never"},
"alcohol_consumption": "none",
},
"personal_medical_history": {},
"family_history": [],
"clinical_tests": {
"psa": {
"value_ng_ml": 5.0,
}
},
}
expected = {
"thinking": None,
"reasoning": None,
"response": None,
"overall_summary": "ok",
"overall_risk_score": None,
"calculated_risk_scores": {},
"identified_risk_factors": [],
"risk_assessments": [],
"dx_recommendations": [],
}
mock_conversation_manager = MagicMock()
mock_conversation_manager.initial_assessment.return_value = expected
mock_factory_instance = mock_factory.return_value
mock_factory_instance.create_conversation_manager.return_value = (
mock_conversation_manager
)
response = client.post("/assess/local", json={"user_input": payload})
assert response.status_code == 200
assert response.json() == expected
@pytest.mark.skip(reason="Skipping failing test as requested")
@pytest.mark.local_llm
@pytest.mark.skipif(CI_ENABLED, reason="Local LLM not available in CI")
def test_assess_local_integration():
sock = socket.socket()
try:
sock.settimeout(1)
sock.connect(("localhost", 11434))
except OSError:
pytest.skip("Ollama service not running")
finally:
sock.close()
payload = {
"demographics": {"age": 45, "sex": "female", "ethnicity": "Hispanic"},
"lifestyle": {
"smoking_status": "never",
"alcohol_consumption": "light",
"dietary_habits": "Mediterranean diet",
"physical_activity_level": "moderate",
},
"family_history": [
{"relative": "mother", "cancer_type": "breast", "age_at_diagnosis": 52},
{"relative": "sister", "cancer_type": "ovarian", "age_at_diagnosis": 48},
],
"personal_medical_history": {
"known_genetic_mutations": ["BRCA2"],
"chronic_illnesses": ["endometriosis"],
},
"female_specific": {
"age_at_first_period": 13,
"num_live_births": 2,
"age_at_first_live_birth": 28,
},
"current_concerns_or_symptoms": "Experiencing recent pelvic pain.",
}
try:
response = client.post("/assess/local", json={"user_input": payload})
except requests.exceptions.ConnectionError:
pytest.skip("Ollama service not running")
assert response.status_code == 200
data = response.json()
assert isinstance(data, dict)
InitialAssessment.model_validate(data)