Files
gh-christianlouis-docuelevate/tests/test_views_status.py
T
2026-03-08 15:10:46 +00:00

493 lines
19 KiB
Python

"""Tests for app/views/status.py module."""
from unittest.mock import MagicMock, Mock, mock_open, patch
import pytest
@pytest.mark.unit
class TestRequireAdmin:
"""Unit tests for the _require_admin helper in views/status.py."""
def test_returns_none_when_no_user_in_session(self):
"""_require_admin returns None when session has no user."""
from app.views.status import _require_admin
mock_request = MagicMock()
mock_request.session = {}
result = _require_admin(mock_request)
assert result is None
def test_returns_none_for_non_admin_user(self):
"""_require_admin returns None when user is not an admin."""
from app.views.status import _require_admin
mock_request = MagicMock()
mock_request.session = {"user": {"email": "user@example.com", "is_admin": False}}
result = _require_admin(mock_request)
assert result is None
def test_logs_warning_for_non_admin(self):
"""_require_admin logs a warning when a non-admin attempts access."""
from app.views.status import _require_admin
mock_request = MagicMock()
mock_request.session = {"user": {"email": "user@example.com", "is_admin": False}}
with patch("app.views.status.logger") as mock_logger:
_require_admin(mock_request)
mock_logger.warning.assert_called_once()
def test_logs_warning_when_no_user(self):
"""_require_admin logs a warning when there is no user in the session."""
from app.views.status import _require_admin
mock_request = MagicMock()
mock_request.session = {}
with patch("app.views.status.logger") as mock_logger:
_require_admin(mock_request)
mock_logger.warning.assert_called_once()
def test_returns_user_for_admin(self):
"""_require_admin returns the user dict when the user is an admin."""
from app.views.status import _require_admin
admin_user = {"email": "admin@example.com", "is_admin": True}
mock_request = MagicMock()
mock_request.session = {"user": admin_user}
result = _require_admin(mock_request)
assert result == admin_user
@pytest.mark.integration
class TestStatusViews:
"""Tests for status view routes."""
def test_status_dashboard(self, client):
"""Test status dashboard page returns 200 (auth disabled in tests)."""
response = client.get("/status")
assert response.status_code == 200
@pytest.mark.unit
class TestStatusDashboard:
"""Tests for status_dashboard function."""
@pytest.mark.asyncio
async def test_redirects_non_admin_to_home(self):
"""status_dashboard redirects to '/' when user is not an admin."""
from fastapi.responses import RedirectResponse
from app.views.status import status_dashboard
mock_request = MagicMock()
mock_request.session = {"user": {"email": "user@example.com", "is_admin": False}}
result = await status_dashboard(mock_request)
assert isinstance(result, RedirectResponse)
assert result.status_code == 302
assert result.headers["location"] == "/"
@pytest.mark.asyncio
async def test_redirects_when_no_user_in_session(self):
"""status_dashboard redirects to '/' when no user is in the session."""
from fastapi.responses import RedirectResponse
from app.views.status import status_dashboard
mock_request = MagicMock()
mock_request.session = {}
result = await status_dashboard(mock_request)
assert isinstance(result, RedirectResponse)
assert result.status_code == 302
@patch("app.views.status.get_provider_status")
@patch("app.views.status.templates")
@patch("app.views.status.settings")
@patch("app.views.status.os.path.exists")
@pytest.mark.asyncio
async def test_returns_template_for_admin_user(self, mock_exists, mock_settings, mock_templates, mock_providers):
"""status_dashboard renders the template for an authenticated admin user."""
from app.views.status import status_dashboard
mock_exists.return_value = False
mock_providers.return_value = {}
mock_settings.version = "1.0.0"
mock_settings.build_date = "2024-01-01"
mock_settings.debug = False
mock_settings.git_sha = "abc123"
mock_settings.notification_urls = []
mock_request = MagicMock()
mock_request.session = {"user": {"email": "admin@example.com", "is_admin": True}}
await status_dashboard(mock_request)
mock_templates.TemplateResponse.assert_called_once()
call_args = mock_templates.TemplateResponse.call_args
assert call_args[0][0] == "status_dashboard.html"
@patch("app.views.status.get_provider_status")
@patch("app.views.status.templates")
@patch("app.views.status.settings")
@patch("app.views.status.os.path.exists")
@pytest.mark.asyncio
async def test_status_dashboard_returns_template(self, mock_exists, mock_settings, mock_templates, mock_providers):
"""Test status dashboard returns template response."""
from app.views.status import status_dashboard
mock_exists.return_value = False
mock_providers.return_value = {
"AI Provider": {"configured": True, "status": "success"},
"Azure AI": {"configured": False},
}
mock_settings.version = "1.0.0"
mock_settings.build_date = "2024-01-01"
mock_settings.debug = False
mock_settings.git_sha = "abc123"
mock_settings.runtime_info = "Python 3.11"
mock_settings.notification_urls = []
mock_request = Mock()
result = await status_dashboard(mock_request)
mock_templates.TemplateResponse.assert_called_once()
call_args = mock_templates.TemplateResponse.call_args
assert call_args[0][0] == "status_dashboard.html"
@patch("app.views.status.get_provider_status")
@patch("app.views.status.templates")
@patch("app.views.status.settings")
@patch("app.views.status.os.path.exists")
@patch("builtins.open", new_callable=mock_open, read_data="12:docker:/container_id")
@pytest.mark.asyncio
async def test_detects_docker_environment(
self, mock_file, mock_exists, mock_settings, mock_templates, mock_providers
):
"""Test detects Docker environment."""
from app.views.status import status_dashboard
mock_exists.return_value = True
mock_providers.return_value = {}
mock_settings.version = "1.0.0"
mock_settings.build_date = "2024-01-01"
mock_settings.git_sha = "abc123"
mock_settings.notification_urls = []
mock_request = Mock()
await status_dashboard(mock_request)
call_args = mock_templates.TemplateResponse.call_args
context = call_args[0][1]
assert context["container_info"]["is_docker"] is True
@patch("app.views.status.get_provider_status")
@patch("app.views.status.templates")
@patch("app.views.status.settings")
@patch("app.views.status.os.path.exists")
@pytest.mark.asyncio
async def test_handles_non_docker_environment(self, mock_exists, mock_settings, mock_templates, mock_providers):
"""Test handles non-Docker environment."""
from app.views.status import status_dashboard
mock_exists.return_value = False
mock_providers.return_value = {}
mock_settings.version = "1.0.0"
mock_settings.build_date = "2024-01-01"
mock_settings.git_sha = "abc123"
mock_settings.notification_urls = []
mock_request = Mock()
await status_dashboard(mock_request)
call_args = mock_templates.TemplateResponse.call_args
context = call_args[0][1]
assert context["container_info"]["is_docker"] is False
@patch("app.views.status.get_provider_status")
@patch("app.views.status.templates")
@patch("app.views.status.settings")
@pytest.mark.asyncio
async def test_includes_git_sha_in_context(self, mock_settings, mock_templates, mock_providers):
"""Test includes git SHA in context."""
from app.views.status import status_dashboard
mock_providers.return_value = {}
mock_settings.version = "1.0.0"
mock_settings.build_date = "2024-01-01"
mock_settings.git_sha = "abc1234567890"
mock_settings.notification_urls = []
mock_request = Mock()
await status_dashboard(mock_request)
call_args = mock_templates.TemplateResponse.call_args
context = call_args[0][1]
assert "git_sha" in context["container_info"]
@patch("app.views.status.get_provider_status")
@patch("app.views.status.templates")
@patch("app.views.status.settings")
@pytest.mark.asyncio
async def test_includes_notification_urls(self, mock_settings, mock_templates, mock_providers):
"""Test includes notification URLs in context."""
from app.views.status import status_dashboard
mock_providers.return_value = {}
mock_settings.version = "1.0.0"
mock_settings.build_date = "2024-01-01"
mock_settings.git_sha = "abc123"
mock_settings.notification_urls = ["https://webhook.example.com/notify"]
mock_request = Mock()
await status_dashboard(mock_request)
call_args = mock_templates.TemplateResponse.call_args
context = call_args[0][1]
assert context["settings"]["notification_urls"] == ["https://webhook.example.com/notify"]
@pytest.mark.unit
class TestContainerInfoDetection:
"""Tests for container information detection logic."""
@patch("app.views.status.get_provider_status")
@patch("app.views.status.templates")
@patch("app.views.status.settings")
@patch("app.views.status.os.path.exists")
@patch("builtins.open", side_effect=IOError("Permission denied"))
@pytest.mark.asyncio
async def test_handles_cgroup_read_error(
self, mock_file, mock_exists, mock_settings, mock_templates, mock_providers
):
"""Test handles cgroup file read errors."""
from app.views.status import status_dashboard
mock_exists.return_value = True # Docker env exists
mock_providers.return_value = {}
mock_settings.version = "1.0.0"
mock_settings.build_date = "2024-01-01"
mock_settings.git_sha = "abc123"
mock_settings.notification_urls = []
mock_request = Mock()
await status_dashboard(mock_request)
call_args = mock_templates.TemplateResponse.call_args
context = call_args[0][1]
# Should handle error gracefully and set id to Unknown
assert context["container_info"]["is_docker"] is True
assert context["container_info"]["id"] == "Unknown"
@patch("app.views.status.get_provider_status")
@patch("app.views.status.templates")
@patch("app.views.status.settings")
@patch("app.views.status.os.path.exists")
@patch("builtins.open", new_callable=mock_open, read_data="12:cpuset:/system.slice\n13:memory:/user.slice")
@pytest.mark.asyncio
async def test_handles_cgroup_without_docker(
self, mock_file, mock_exists, mock_settings, mock_templates, mock_providers
):
"""Test handles cgroup without docker in path."""
from app.views.status import status_dashboard
mock_exists.return_value = True # Docker env exists
mock_providers.return_value = {}
mock_settings.version = "1.0.0"
mock_settings.build_date = "2024-01-01"
mock_settings.git_sha = "abc123"
mock_settings.notification_urls = []
mock_request = Mock()
await status_dashboard(mock_request)
call_args = mock_templates.TemplateResponse.call_args
context = call_args[0][1]
# When docker not found in cgroup, id is not set in the code
# The loop completes without setting id, so it won't be in container_info
assert context["container_info"]["is_docker"] is True
@patch("app.views.status.get_provider_status")
@patch("app.views.status.templates")
@patch("app.views.status.settings")
@patch("app.views.status.os.path.exists")
@patch("builtins.open", new_callable=mock_open, read_data="12:docker:/abc123456789")
@pytest.mark.asyncio
async def test_handles_unknown_git_sha_string(
self, mock_file, mock_exists, mock_settings, mock_templates, mock_providers
):
"""Test handles 'unknown' git_sha string value."""
from app.views.status import status_dashboard
mock_exists.return_value = True
mock_providers.return_value = {}
mock_settings.version = "1.0.0"
mock_settings.build_date = "2024-01-01"
mock_settings.git_sha = "unknown"
mock_settings.notification_urls = []
mock_request = Mock()
await status_dashboard(mock_request)
call_args = mock_templates.TemplateResponse.call_args
context = call_args[0][1]
# Should handle "unknown" string and set to Unknown
assert context["container_info"]["git_sha"] == "Unknown"
@patch("app.views.status.get_provider_status")
@patch("app.views.status.templates")
@patch("app.views.status.settings")
@patch("app.views.status.os.path.exists")
@pytest.mark.asyncio
async def test_handles_complete_exception_in_container_info(
self, mock_exists, mock_settings, mock_templates, mock_providers
):
"""Test handles complete exception in container info extraction."""
from app.views.status import status_dashboard
# Simulate exception when checking Docker env
mock_exists.side_effect = Exception("Unexpected error")
mock_providers.return_value = {}
mock_settings.version = "1.0.0"
mock_settings.build_date = "2024-01-01"
mock_settings.notification_urls = []
mock_request = Mock()
await status_dashboard(mock_request)
call_args = mock_templates.TemplateResponse.call_args
context = call_args[0][1]
# Should have fallback container_info
assert context["container_info"]["is_docker"] is False
assert context["container_info"]["id"] == "Unknown"
assert context["container_info"]["git_sha"] == "Unknown"
@patch("app.views.status.get_provider_status")
@patch("app.views.status.templates")
@patch("app.views.status.settings")
@patch("app.views.status.os.path.exists")
@pytest.mark.asyncio
async def test_handles_null_git_sha(self, mock_exists, mock_settings, mock_templates, mock_providers):
"""Test handles None/null git_sha value."""
from app.views.status import status_dashboard
mock_exists.return_value = False # Not in Docker
mock_providers.return_value = {}
mock_settings.version = "1.0.0"
mock_settings.build_date = "2024-01-01"
mock_settings.git_sha = None # None value
mock_settings.notification_urls = []
mock_request = Mock()
await status_dashboard(mock_request)
call_args = mock_templates.TemplateResponse.call_args
context = call_args[0][1]
# Should handle None and set to Unknown
assert context["container_info"]["git_sha"] == "Unknown"
@patch("app.views.status.get_provider_status")
@patch("app.views.status.templates")
@patch("app.views.status.os.path.exists")
@patch("builtins.open", new_callable=mock_open, read_data="12:docker:/abc123456789")
@pytest.mark.asyncio
async def test_handles_git_sha_exception_in_docker(self, mock_file, mock_exists, mock_templates, mock_providers):
"""Test handles exception when reading git_sha in Docker environment (lines 53-54)."""
import types
from app.views.status import status_dashboard
mock_exists.return_value = True
mock_providers.return_value = {}
# SimpleNamespace without git_sha causes AttributeError on access → triggers except block
fake_settings = types.SimpleNamespace(version="1.0.0")
with patch("app.views.status.settings", fake_settings):
mock_request = Mock()
await status_dashboard(mock_request)
call_args = mock_templates.TemplateResponse.call_args
context = call_args[0][1]
assert context["container_info"]["is_docker"] is True
assert context["container_info"]["git_sha"] == "Unknown"
@patch("app.views.status.get_provider_status")
@patch("app.views.status.templates")
@patch("app.views.status.os.path.exists")
@patch("builtins.open", new_callable=mock_open, read_data="12:docker:/abc123456789")
@pytest.mark.asyncio
async def test_handles_runtime_info_exception_in_docker(
self, mock_file, mock_exists, mock_templates, mock_providers
):
"""Test handles exception when reading runtime_info in Docker environment (lines 59-60)."""
import types
from app.views.status import status_dashboard
mock_exists.return_value = True
mock_providers.return_value = {}
# SimpleNamespace has git_sha but not runtime_info → triggers except block at line 59-60
fake_settings = types.SimpleNamespace(version="1.0.0", git_sha="abc1234567890")
with patch("app.views.status.settings", fake_settings):
mock_request = Mock()
await status_dashboard(mock_request)
call_args = mock_templates.TemplateResponse.call_args
context = call_args[0][1]
assert context["container_info"]["is_docker"] is True
# runtime_info should not be in container_info when it raises an exception
assert "runtime_info" not in context["container_info"]
@patch("app.views.status.get_provider_status")
@patch("app.views.status.templates")
@patch("app.views.status.os.path.exists")
@pytest.mark.asyncio
async def test_handles_git_sha_exception_in_non_docker(self, mock_exists, mock_templates, mock_providers):
"""Test handles exception when reading git_sha in non-Docker environment (lines 68-69)."""
import types
from app.views.status import status_dashboard
mock_exists.return_value = False # Not in Docker
mock_providers.return_value = {}
# SimpleNamespace without git_sha causes AttributeError on access → triggers except block
fake_settings = types.SimpleNamespace(version="1.0.0")
with patch("app.views.status.settings", fake_settings):
mock_request = Mock()
await status_dashboard(mock_request)
call_args = mock_templates.TemplateResponse.call_args
context = call_args[0][1]
assert context["container_info"]["is_docker"] is False
assert context["container_info"]["git_sha"] == "Unknown"
@pytest.mark.integration
class TestStatusEndpointsRequireAuth:
"""Tests for status endpoint authentication."""
def test_status_dashboard_requires_login(self, client):
"""Test status dashboard requires authentication."""
# Should return 200 or redirect to login
response = client.get("/status", follow_redirects=False)
assert response.status_code in [200, 302, 401]