# 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)