""" Unit tests for common.validators module Tests all validation functions with various inputs and edge cases. """ import pytest from pathlib import Path from datetime import datetime from tempfile import NamedTemporaryFile, TemporaryDirectory from common import ( Validator, ValidationException, AudioFileException ) class TestValidateAudioFile: """Test cases for validate_audio_file function""" def test_validate_audio_file_with_empty_path(self): """Should raise ValidationException for empty path""" with pytest.raises(ValidationException) as exc_info: Validator.validate_audio_file("") assert "audio_file" in str(exc_info.value) def test_validate_audio_file_nonexistent(self): """Should raise AudioFileException for non-existent file""" with pytest.raises(AudioFileException) as exc_info: Validator.validate_audio_file("/nonexistent/path/to/audio.wav") assert "does not exist" in str(exc_info.value) def test_validate_audio_file_unsupported_format(self, tmp_path): """Should raise AudioFileException for unsupported file format""" # Create a file with unsupported extension unsupported_file = tmp_path / "audio.xyz" unsupported_file.write_text("fake audio data") with pytest.raises(AudioFileException) as exc_info: Validator.validate_audio_file(str(unsupported_file)) assert "Unsupported format" in str(exc_info.value) def test_validate_audio_file_empty_file(self, tmp_path): """Should raise AudioFileException for empty file""" empty_file = tmp_path / "empty.wav" empty_file.write_bytes(b"") with pytest.raises(AudioFileException) as exc_info: Validator.validate_audio_file(str(empty_file)) assert "empty" in str(exc_info.value).lower() def test_validate_audio_file_valid_wav(self, tmp_path): """Should return Path object for valid WAV file""" valid_file = tmp_path / "audio.wav" valid_file.write_bytes(b"fake audio data") result = Validator.validate_audio_file(str(valid_file)) assert isinstance(result, Path) assert result.exists() assert result.suffix.lower() == ".wav" def test_validate_audio_file_valid_mp3(self, tmp_path): """Should return Path object for valid MP3 file""" valid_file = tmp_path / "audio.mp3" valid_file.write_bytes(b"fake audio data") result = Validator.validate_audio_file(str(valid_file)) assert isinstance(result, Path) assert result.suffix.lower() == ".mp3" def test_validate_audio_file_valid_m4a(self, tmp_path): """Should return Path object for valid M4A file""" valid_file = tmp_path / "audio.m4a" valid_file.write_bytes(b"fake audio data") result = Validator.validate_audio_file(str(valid_file)) assert isinstance(result, Path) assert result.suffix.lower() == ".m4a" def test_validate_audio_file_is_directory(self, tmp_path): """Should raise AudioFileException if path is directory, not file""" with pytest.raises(AudioFileException) as exc_info: Validator.validate_audio_file(str(tmp_path)) assert "not a file" in str(exc_info.value) class TestValidateText: """Test cases for validate_text function""" def test_validate_text_empty(self): """Should raise ValidationException for empty text""" with pytest.raises(ValidationException): Validator.validate_text("") def test_validate_text_none(self): """Should raise ValidationException for None text""" with pytest.raises(ValidationException): Validator.validate_text(None) def test_validate_text_too_short(self): """Should raise ValidationException if text is too short""" with pytest.raises(ValidationException) as exc_info: Validator.validate_text("ab") # MIN is 10 assert "at least" in str(exc_info.value) def test_validate_text_valid_minimum(self): """Should accept text at minimum length""" result = Validator.validate_text("1234567890") # 10 chars assert result == "1234567890" def test_validate_text_valid_normal(self): """Should accept normal text""" text = "This is a valid text" result = Validator.validate_text(text) assert result == text def test_validate_text_with_whitespace(self): """Should strip whitespace from text""" text = " valid text " result = Validator.validate_text(text) assert result == "valid text" def test_validate_text_custom_field_name(self): """Should use custom field name in error message""" with pytest.raises(ValidationException) as exc_info: Validator.validate_text("short", field_name="custom_field") assert "custom_field" in str(exc_info.value) class TestValidatePatientName: """Test cases for validate_patient_name function""" def test_validate_patient_name_none(self): """Should return None for None input""" result = Validator.validate_patient_name(None) assert result is None def test_validate_patient_name_empty(self): """Should return None for empty string""" result = Validator.validate_patient_name("") assert result is None def test_validate_patient_name_too_short(self): """Should raise ValidationException for too short name""" with pytest.raises(ValidationException) as exc_info: Validator.validate_patient_name("Ab") assert "at least 3" in str(exc_info.value) def test_validate_patient_name_valid_cyrillic(self): """Should accept valid Cyrillic name""" name = "Иван Петров" result = Validator.validate_patient_name(name) assert result == name def test_validate_patient_name_valid_latin(self): """Should accept valid Latin name""" name = "John Smith" result = Validator.validate_patient_name(name) assert result == name def test_validate_patient_name_with_hyphen(self): """Should accept name with hyphen""" name = "Mary-Jane Watson" result = Validator.validate_patient_name(name) assert result == name def test_validate_patient_name_with_numbers(self): """Should reject name with numbers""" with pytest.raises(ValidationException): Validator.validate_patient_name("Ivan123 Petrov") def test_validate_patient_name_with_special_chars(self): """Should reject name with special characters""" with pytest.raises(ValidationException): Validator.validate_patient_name("Ivan@Petrov") def test_validate_patient_name_with_whitespace(self): """Should strip whitespace from name""" name = " Ivan Petrov " result = Validator.validate_patient_name(name) assert result == "Ivan Petrov" class TestValidateDate: """Test cases for validate_date function""" def test_validate_date_none(self): """Should return None for None input""" result = Validator.validate_date(None) assert result is None def test_validate_date_empty(self): """Should return None for empty string""" result = Validator.validate_date("") assert result is None def test_validate_date_valid_format(self): """Should accept valid date in DD.MM.YYYY format""" date_str = "15.01.2026" result = Validator.validate_date(date_str) assert result == date_str def test_validate_date_invalid_format(self): """Should reject invalid date format""" with pytest.raises(ValidationException) as exc_info: Validator.validate_date("2026-01-15") # Wrong format assert "Invalid date format" in str(exc_info.value) def test_validate_date_invalid_day(self): """Should reject invalid day""" with pytest.raises(ValidationException): Validator.validate_date("32.01.2026") def test_validate_date_invalid_month(self): """Should reject invalid month""" with pytest.raises(ValidationException): Validator.validate_date("15.13.2026") def test_validate_date_custom_format(self): """Should accept custom date format""" date_str = "2026-01-15" result = Validator.validate_date(date_str, date_format="%Y-%m-%d") assert result == date_str def test_validate_date_with_whitespace(self): """Should strip whitespace from date""" date_str = " 15.01.2026 " result = Validator.validate_date(date_str) assert result == "15.01.2026" class TestValidateApiKey: """Test cases for validate_api_key function""" def test_validate_api_key_none(self): """Should return None for None input""" result = Validator.validate_api_key(None) assert result is None def test_validate_api_key_empty(self): """Should return None for empty string""" result = Validator.validate_api_key("") assert result is None def test_validate_api_key_too_short(self): """Should raise ValidationException for too short key""" with pytest.raises(ValidationException) as exc_info: Validator.validate_api_key("short") assert "too short" in str(exc_info.value) def test_validate_api_key_valid(self): """Should accept valid API key""" api_key = "sk_test_1234567890" result = Validator.validate_api_key(api_key) assert result == api_key def test_validate_api_key_with_whitespace(self): """Should strip whitespace from API key""" api_key = " sk_test_1234567890 " result = Validator.validate_api_key(api_key) assert result == "sk_test_1234567890" class TestValidateFilePath: """Test cases for validate_file_path function""" def test_validate_file_path_empty(self): """Should raise ValidationException for empty path""" with pytest.raises(ValidationException): Validator.validate_file_path("") def test_validate_file_path_nonexistent_not_required(self): """Should accept non-existent path if must_exist=False""" result = Validator.validate_file_path("/nonexistent/path.txt", must_exist=False) assert isinstance(result, Path) def test_validate_file_path_nonexistent_required(self): """Should reject non-existent path if must_exist=True""" with pytest.raises(ValidationException) as exc_info: Validator.validate_file_path("/nonexistent/path.txt", must_exist=True) assert "does not exist" in str(exc_info.value) def test_validate_file_path_existing_file(self, tmp_path): """Should accept existing file path""" file_path = tmp_path / "test.txt" file_path.write_text("test content") result = Validator.validate_file_path(str(file_path), must_exist=True) assert isinstance(result, Path) assert result.exists() def test_validate_file_path_existing_directory(self, tmp_path): """Should accept existing directory path""" result = Validator.validate_file_path(str(tmp_path), must_exist=True) assert isinstance(result, Path) assert result.exists() def test_validate_file_path_resolves_relative(self): """Should resolve relative paths""" result = Validator.validate_file_path(".", must_exist=False) assert isinstance(result, Path) assert result.is_absolute() class TestValidatorIntegration: """Integration tests for validators""" def test_full_patient_validation_flow(self): """Test complete patient data validation""" # Valid data name = Validator.validate_patient_name("Ivan Petrov") date = Validator.validate_date("15.01.1990") assert name == "Ivan Petrov" assert date == "15.01.1990" def test_full_audio_file_validation(self, tmp_path): """Test complete audio file validation flow""" # Create a valid audio file audio_file = tmp_path / "recording.wav" audio_file.write_bytes(b"fake audio data") # Validate result = Validator.validate_audio_file(str(audio_file)) assert result.exists() assert result.suffix.lower() == ".wav" def test_validation_error_handling(self): """Test that validation errors have proper context""" with pytest.raises(ValidationException) as exc_info: Validator.validate_patient_name("X") exc = exc_info.value assert exc.field == "patient_name" assert "3" in str(exc)