""" Unit Tests for Check Identity Endpoint (US-013) Tests the dual login support: email + username verification """ import pytest from unittest.mock import MagicMock, patch, AsyncMock # ============================================================================ # TEST CHECK IDENTITY REQUEST MODEL # ============================================================================ class TestCheckIdentityRequestModel: """Tests for CheckIdentityRequest model validation.""" def test_valid_email_normalized_to_lowercase(self): """Email inputs should be normalized to lowercase.""" from shared.auth.models import CheckIdentityRequest request = CheckIdentityRequest(identity="User@Example.COM") assert request.identity == "user@example.com" def test_valid_username_normalized_to_uppercase(self): """Username inputs (without @) should be normalized to uppercase.""" from shared.auth.models import CheckIdentityRequest request = CheckIdentityRequest(identity="marius") assert request.identity == "MARIUS" def test_username_with_spaces_normalized(self): """Username with spaces should be preserved but uppercased.""" from shared.auth.models import CheckIdentityRequest request = CheckIdentityRequest(identity="marius m") assert request.identity == "MARIUS M" def test_whitespace_trimmed(self): """Leading/trailing whitespace should be trimmed.""" from shared.auth.models import CheckIdentityRequest request = CheckIdentityRequest(identity=" user@test.com ") assert request.identity == "user@test.com" def test_empty_identity_raises_error(self): """Empty identity should raise validation error.""" from pydantic import ValidationError from shared.auth.models import CheckIdentityRequest with pytest.raises(ValidationError): CheckIdentityRequest(identity="") def test_too_short_identity_raises_error(self): """Identity shorter than 2 chars should raise validation error.""" from pydantic import ValidationError from shared.auth.models import CheckIdentityRequest with pytest.raises(ValidationError): CheckIdentityRequest(identity="a") # ============================================================================ # TEST CHECK IDENTITY RESPONSE MODEL # ============================================================================ class TestCheckIdentityResponseModel: """Tests for CheckIdentityResponse model.""" def test_response_with_email_type(self): """Response should include identity_type field.""" from shared.auth.models import CheckIdentityResponse, ServerInfo response = CheckIdentityResponse( exists=True, servers=[ServerInfo(id="server1", name="Server 1")], identity_type="email" ) assert response.exists is True assert response.identity_type == "email" assert len(response.servers) == 1 def test_response_with_username_type(self): """Response should support username identity type.""" from shared.auth.models import CheckIdentityResponse response = CheckIdentityResponse( exists=True, servers=[], identity_type="username" ) assert response.identity_type == "username" def test_response_default_identity_type(self): """Default identity_type should be 'unknown'.""" from shared.auth.models import CheckIdentityResponse response = CheckIdentityResponse(exists=False, servers=[]) assert response.identity_type == "unknown" # ============================================================================ # TEST IDENTITY TYPE DETECTION # ============================================================================ class TestIdentityTypeDetection: """Tests for email vs username detection logic.""" def test_email_detected_by_at_sign(self): """Identity with @ should be treated as email.""" # This is tested via the model validator from shared.auth.models import CheckIdentityRequest request = CheckIdentityRequest(identity="test@example.com") # Email should be lowercase assert request.identity == "test@example.com" assert "@" in request.identity def test_username_detected_without_at_sign(self): """Identity without @ should be treated as username.""" from shared.auth.models import CheckIdentityRequest request = CheckIdentityRequest(identity="MARIUS") # Username should be uppercase assert request.identity == "MARIUS" assert "@" not in request.identity # ============================================================================ # TEST EMAIL SERVER CACHE USERNAME LOOKUP # ============================================================================ class TestEmailServerCacheUsernameLookup: """Tests for username lookup in EmailServerCache.""" @pytest.fixture def reset_cache(self): """Reset the cache singleton before each test.""" from shared.auth.email_server_cache import EmailServerCache # Reset singleton EmailServerCache._instance = None yield EmailServerCache._instance = None def test_get_servers_for_username_method_exists(self, reset_cache): """EmailServerCache should have get_servers_for_username method.""" from shared.auth.email_server_cache import EmailServerCache cache = EmailServerCache() assert hasattr(cache, 'get_servers_for_username') assert callable(cache.get_servers_for_username) def test_empty_username_returns_empty_list(self, reset_cache): """Empty username should return empty list.""" import asyncio from shared.auth.email_server_cache import EmailServerCache cache = EmailServerCache() async def test(): # Mock settings to return empty servers with patch('backend.config.settings') as mock_settings: mock_settings.get_oracle_servers.return_value = [] result = await cache.get_servers_for_username("") return result result = asyncio.get_event_loop().run_until_complete(test()) assert result == [] # ============================================================================ # TEST BACKWARD COMPATIBILITY # ============================================================================ class TestBackwardCompatibility: """Tests for backward compatibility with check-email endpoint.""" def test_check_email_request_still_works(self): """CheckEmailRequest should still work for backward compatibility.""" from shared.auth.models import CheckEmailRequest request = CheckEmailRequest(email="user@example.com") assert request.email == "user@example.com" def test_check_email_response_still_works(self): """CheckEmailResponse should still work for backward compatibility.""" from shared.auth.models import CheckEmailResponse, ServerInfo response = CheckEmailResponse( exists=True, servers=[ServerInfo(id="s1", name="Server 1")] ) assert response.exists is True assert len(response.servers) == 1 # ============================================================================ # TEST ACCEPTANCE CRITERIA (US-013) # ============================================================================ class TestAcceptanceCriteria: """Tests verifying US-013 acceptance criteria.""" def test_ac1_check_identity_request_model_exists(self): """AC1: CheckIdentityRequest model exists.""" from shared.auth.models import CheckIdentityRequest assert CheckIdentityRequest is not None def test_ac2_email_detection_with_at_sign(self): """AC2: Input with @ is treated as email.""" from shared.auth.models import CheckIdentityRequest request = CheckIdentityRequest(identity="test@domain.com") # Email normalized to lowercase assert "@" in request.identity assert request.identity.islower() def test_ac3_username_detection_without_at_sign(self): """AC3: Input without @ is treated as username.""" from shared.auth.models import CheckIdentityRequest request = CheckIdentityRequest(identity="admin") # Username normalized to uppercase assert "@" not in request.identity assert request.identity == "ADMIN" def test_ac4_check_email_backward_compatible(self): """AC4: Old check-email models still work.""" from shared.auth.models import CheckEmailRequest, CheckEmailResponse # Both models should be importable and usable req = CheckEmailRequest(email="test@test.com") resp = CheckEmailResponse(exists=False, servers=[]) assert req.email == "test@test.com" assert resp.exists is False def test_ac5_response_includes_identity_type(self): """AC5: Response includes identity_type field.""" from shared.auth.models import CheckIdentityResponse response = CheckIdentityResponse( exists=True, servers=[], identity_type="email" ) assert hasattr(response, 'identity_type') assert response.identity_type in ["email", "username", "unknown"] # ============================================================================ # TEST UI REQUIREMENTS # ============================================================================ class TestUIRequirements: """Tests verifying UI-related requirements.""" def test_placeholder_label_correct(self): """UI should use 'Email sau utilizator' as label.""" # This is a documentation test - the actual UI change is in Vue # We verify the backend accepts both formats from shared.auth.models import CheckIdentityRequest # Should accept email format email_req = CheckIdentityRequest(identity="user@example.com") assert email_req.identity == "user@example.com" # Should accept username format username_req = CheckIdentityRequest(identity="UTILIZATOR") assert username_req.identity == "UTILIZATOR" def test_username_with_romanian_chars_handled(self): """Username with spaces (like 'MARIUS M') should be handled.""" from shared.auth.models import CheckIdentityRequest request = CheckIdentityRequest(identity="marius m") assert request.identity == "MARIUS M"