|
|
""" |
|
|
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) |
|
|
|