| """ |
| 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""" |
| |
| 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") |
| 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") |
| 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") |
| 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""" |
| |
| 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""" |
| |
| audio_file = tmp_path / "recording.wav" |
| audio_file.write_bytes(b"fake audio data") |
| |
| |
| 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) |
|
|