""" Tests for Admin Authentication. """ import pytest from datetime import datetime, timedelta from unittest.mock import MagicMock, patch from fastapi import HTTPException from src.data.admin_db import AdminDB from src.data.admin_models import AdminToken from src.web.core.auth import ( get_admin_db, reset_admin_db, validate_admin_token, ) @pytest.fixture def mock_admin_db(): """Create a mock AdminDB.""" db = MagicMock(spec=AdminDB) db.is_valid_admin_token.return_value = True return db @pytest.fixture(autouse=True) def reset_db(): """Reset admin DB after each test.""" yield reset_admin_db() class TestValidateAdminToken: """Tests for validate_admin_token dependency.""" def test_missing_token_raises_401(self, mock_admin_db): """Test that missing token raises 401.""" import asyncio with pytest.raises(HTTPException) as exc_info: asyncio.get_event_loop().run_until_complete( validate_admin_token(None, mock_admin_db) ) assert exc_info.value.status_code == 401 assert "Admin token required" in exc_info.value.detail def test_invalid_token_raises_401(self, mock_admin_db): """Test that invalid token raises 401.""" import asyncio mock_admin_db.is_valid_admin_token.return_value = False with pytest.raises(HTTPException) as exc_info: asyncio.get_event_loop().run_until_complete( validate_admin_token("invalid-token", mock_admin_db) ) assert exc_info.value.status_code == 401 assert "Invalid or expired" in exc_info.value.detail def test_valid_token_returns_token(self, mock_admin_db): """Test that valid token is returned.""" import asyncio token = "valid-test-token" mock_admin_db.is_valid_admin_token.return_value = True result = asyncio.get_event_loop().run_until_complete( validate_admin_token(token, mock_admin_db) ) assert result == token mock_admin_db.update_admin_token_usage.assert_called_once_with(token) class TestAdminDB: """Tests for AdminDB operations.""" def test_is_valid_admin_token_active(self): """Test valid active token.""" with patch("src.data.admin_db.get_session_context") as mock_ctx: mock_session = MagicMock() mock_ctx.return_value.__enter__.return_value = mock_session mock_token = AdminToken( token="test-token", name="Test", is_active=True, expires_at=None, ) mock_session.get.return_value = mock_token db = AdminDB() assert db.is_valid_admin_token("test-token") is True def test_is_valid_admin_token_inactive(self): """Test inactive token.""" with patch("src.data.admin_db.get_session_context") as mock_ctx: mock_session = MagicMock() mock_ctx.return_value.__enter__.return_value = mock_session mock_token = AdminToken( token="test-token", name="Test", is_active=False, expires_at=None, ) mock_session.get.return_value = mock_token db = AdminDB() assert db.is_valid_admin_token("test-token") is False def test_is_valid_admin_token_expired(self): """Test expired token.""" with patch("src.data.admin_db.get_session_context") as mock_ctx: mock_session = MagicMock() mock_ctx.return_value.__enter__.return_value = mock_session mock_token = AdminToken( token="test-token", name="Test", is_active=True, expires_at=datetime.utcnow() - timedelta(days=1), ) mock_session.get.return_value = mock_token db = AdminDB() assert db.is_valid_admin_token("test-token") is False def test_is_valid_admin_token_not_found(self): """Test token not found.""" with patch("src.data.admin_db.get_session_context") as mock_ctx: mock_session = MagicMock() mock_ctx.return_value.__enter__.return_value = mock_session mock_session.get.return_value = None db = AdminDB() assert db.is_valid_admin_token("nonexistent") is False class TestGetAdminDb: """Tests for get_admin_db function.""" def test_returns_singleton(self): """Test that get_admin_db returns singleton.""" reset_admin_db() db1 = get_admin_db() db2 = get_admin_db() assert db1 is db2 def test_reset_clears_singleton(self): """Test that reset clears singleton.""" db1 = get_admin_db() reset_admin_db() db2 = get_admin_db() assert db1 is not db2