Spaces:
Build error
Build error
"""Unit tests for VoiceSettings value object.""" | |
import pytest | |
from src.domain.models.voice_settings import VoiceSettings | |
class TestVoiceSettings: | |
"""Test cases for VoiceSettings value object.""" | |
def test_valid_voice_settings_creation(self): | |
"""Test creating valid VoiceSettings instance.""" | |
settings = VoiceSettings( | |
voice_id="en_male_001", | |
speed=1.2, | |
language="en", | |
pitch=0.1, | |
volume=0.8 | |
) | |
assert settings.voice_id == "en_male_001" | |
assert settings.speed == 1.2 | |
assert settings.language == "en" | |
assert settings.pitch == 0.1 | |
assert settings.volume == 0.8 | |
def test_voice_settings_with_optional_none(self): | |
"""Test creating VoiceSettings with optional parameters as None.""" | |
settings = VoiceSettings( | |
voice_id="en_male_001", | |
speed=1.0, | |
language="en" | |
) | |
assert settings.pitch is None | |
assert settings.volume is None | |
def test_non_string_voice_id_raises_error(self): | |
"""Test that non-string voice_id raises TypeError.""" | |
with pytest.raises(TypeError, match="Voice ID must be a string"): | |
VoiceSettings( | |
voice_id=123, # type: ignore | |
speed=1.0, | |
language="en" | |
) | |
def test_empty_voice_id_raises_error(self): | |
"""Test that empty voice_id raises ValueError.""" | |
with pytest.raises(ValueError, match="Voice ID cannot be empty"): | |
VoiceSettings( | |
voice_id="", | |
speed=1.0, | |
language="en" | |
) | |
def test_whitespace_voice_id_raises_error(self): | |
"""Test that whitespace-only voice_id raises ValueError.""" | |
with pytest.raises(ValueError, match="Voice ID cannot be empty"): | |
VoiceSettings( | |
voice_id=" ", | |
speed=1.0, | |
language="en" | |
) | |
def test_invalid_voice_id_format_raises_error(self): | |
"""Test that invalid voice_id format raises ValueError.""" | |
invalid_ids = ["voice id", "voice@id", "voice.id", "voice/id", "voice\\id"] | |
for voice_id in invalid_ids: | |
with pytest.raises(ValueError, match="Invalid voice ID format"): | |
VoiceSettings( | |
voice_id=voice_id, | |
speed=1.0, | |
language="en" | |
) | |
def test_valid_voice_id_formats(self): | |
"""Test valid voice_id formats.""" | |
valid_ids = ["voice1", "voice_1", "voice-1", "en_male_001", "female-voice", "Voice123"] | |
for voice_id in valid_ids: | |
settings = VoiceSettings( | |
voice_id=voice_id, | |
speed=1.0, | |
language="en" | |
) | |
assert settings.voice_id == voice_id | |
def test_non_numeric_speed_raises_error(self): | |
"""Test that non-numeric speed raises TypeError.""" | |
with pytest.raises(TypeError, match="Speed must be a number"): | |
VoiceSettings( | |
voice_id="voice1", | |
speed="fast", # type: ignore | |
language="en" | |
) | |
def test_speed_too_low_raises_error(self): | |
"""Test that speed below 0.1 raises ValueError.""" | |
with pytest.raises(ValueError, match="Speed must be between 0.1 and 3.0"): | |
VoiceSettings( | |
voice_id="voice1", | |
speed=0.05, | |
language="en" | |
) | |
def test_speed_too_high_raises_error(self): | |
"""Test that speed above 3.0 raises ValueError.""" | |
with pytest.raises(ValueError, match="Speed must be between 0.1 and 3.0"): | |
VoiceSettings( | |
voice_id="voice1", | |
speed=3.1, | |
language="en" | |
) | |
def test_valid_speed_boundaries(self): | |
"""Test valid speed boundaries.""" | |
# Test minimum valid speed | |
settings_min = VoiceSettings( | |
voice_id="voice1", | |
speed=0.1, | |
language="en" | |
) | |
assert settings_min.speed == 0.1 | |
# Test maximum valid speed | |
settings_max = VoiceSettings( | |
voice_id="voice1", | |
speed=3.0, | |
language="en" | |
) | |
assert settings_max.speed == 3.0 | |
def test_non_string_language_raises_error(self): | |
"""Test that non-string language raises TypeError.""" | |
with pytest.raises(TypeError, match="Language must be a string"): | |
VoiceSettings( | |
voice_id="voice1", | |
speed=1.0, | |
language=123 # type: ignore | |
) | |
def test_empty_language_raises_error(self): | |
"""Test that empty language raises ValueError.""" | |
with pytest.raises(ValueError, match="Language cannot be empty"): | |
VoiceSettings( | |
voice_id="voice1", | |
speed=1.0, | |
language="" | |
) | |
def test_invalid_language_code_format_raises_error(self): | |
"""Test that invalid language code format raises ValueError.""" | |
invalid_codes = ["e", "ENG", "en-us", "en-USA", "123", "en_US"] | |
for code in invalid_codes: | |
with pytest.raises(ValueError, match="Invalid language code format"): | |
VoiceSettings( | |
voice_id="voice1", | |
speed=1.0, | |
language=code | |
) | |
def test_valid_language_codes(self): | |
"""Test valid language code formats.""" | |
valid_codes = ["en", "fr", "de", "es", "zh", "ja", "en-US", "fr-FR", "zh-CN"] | |
for code in valid_codes: | |
settings = VoiceSettings( | |
voice_id="voice1", | |
speed=1.0, | |
language=code | |
) | |
assert settings.language == code | |
def test_non_numeric_pitch_raises_error(self): | |
"""Test that non-numeric pitch raises TypeError.""" | |
with pytest.raises(TypeError, match="Pitch must be a number"): | |
VoiceSettings( | |
voice_id="voice1", | |
speed=1.0, | |
language="en", | |
pitch="high" # type: ignore | |
) | |
def test_pitch_too_low_raises_error(self): | |
"""Test that pitch below -2.0 raises ValueError.""" | |
with pytest.raises(ValueError, match="Pitch must be between -2.0 and 2.0"): | |
VoiceSettings( | |
voice_id="voice1", | |
speed=1.0, | |
language="en", | |
pitch=-2.1 | |
) | |
def test_pitch_too_high_raises_error(self): | |
"""Test that pitch above 2.0 raises ValueError.""" | |
with pytest.raises(ValueError, match="Pitch must be between -2.0 and 2.0"): | |
VoiceSettings( | |
voice_id="voice1", | |
speed=1.0, | |
language="en", | |
pitch=2.1 | |
) | |
def test_valid_pitch_boundaries(self): | |
"""Test valid pitch boundaries.""" | |
# Test minimum valid pitch | |
settings_min = VoiceSettings( | |
voice_id="voice1", | |
speed=1.0, | |
language="en", | |
pitch=-2.0 | |
) | |
assert settings_min.pitch == -2.0 | |
# Test maximum valid pitch | |
settings_max = VoiceSettings( | |
voice_id="voice1", | |
speed=1.0, | |
language="en", | |
pitch=2.0 | |
) | |
assert settings_max.pitch == 2.0 | |
def test_non_numeric_volume_raises_error(self): | |
"""Test that non-numeric volume raises TypeError.""" | |
with pytest.raises(TypeError, match="Volume must be a number"): | |
VoiceSettings( | |
voice_id="voice1", | |
speed=1.0, | |
language="en", | |
volume="loud" # type: ignore | |
) | |
def test_volume_too_low_raises_error(self): | |
"""Test that volume below 0.0 raises ValueError.""" | |
with pytest.raises(ValueError, match="Volume must be between 0.0 and 2.0"): | |
VoiceSettings( | |
voice_id="voice1", | |
speed=1.0, | |
language="en", | |
volume=-0.1 | |
) | |
def test_volume_too_high_raises_error(self): | |
"""Test that volume above 2.0 raises ValueError.""" | |
with pytest.raises(ValueError, match="Volume must be between 0.0 and 2.0"): | |
VoiceSettings( | |
voice_id="voice1", | |
speed=1.0, | |
language="en", | |
volume=2.1 | |
) | |
def test_valid_volume_boundaries(self): | |
"""Test valid volume boundaries.""" | |
# Test minimum valid volume | |
settings_min = VoiceSettings( | |
voice_id="voice1", | |
speed=1.0, | |
language="en", | |
volume=0.0 | |
) | |
assert settings_min.volume == 0.0 | |
# Test maximum valid volume | |
settings_max = VoiceSettings( | |
voice_id="voice1", | |
speed=1.0, | |
language="en", | |
volume=2.0 | |
) | |
assert settings_max.volume == 2.0 | |
def test_is_default_speed_property(self): | |
"""Test is_default_speed property.""" | |
# Default speed (1.0) | |
settings_default = VoiceSettings( | |
voice_id="voice1", | |
speed=1.0, | |
language="en" | |
) | |
assert settings_default.is_default_speed is True | |
# Non-default speed | |
settings_non_default = VoiceSettings( | |
voice_id="voice1", | |
speed=1.5, | |
language="en" | |
) | |
assert settings_non_default.is_default_speed is False | |
def test_is_default_pitch_property(self): | |
"""Test is_default_pitch property.""" | |
# Default pitch (None) | |
settings_none = VoiceSettings( | |
voice_id="voice1", | |
speed=1.0, | |
language="en" | |
) | |
assert settings_none.is_default_pitch is True | |
# Default pitch (0.0) | |
settings_zero = VoiceSettings( | |
voice_id="voice1", | |
speed=1.0, | |
language="en", | |
pitch=0.0 | |
) | |
assert settings_zero.is_default_pitch is True | |
# Non-default pitch | |
settings_non_default = VoiceSettings( | |
voice_id="voice1", | |
speed=1.0, | |
language="en", | |
pitch=0.5 | |
) | |
assert settings_non_default.is_default_pitch is False | |
def test_is_default_volume_property(self): | |
"""Test is_default_volume property.""" | |
# Default volume (None) | |
settings_none = VoiceSettings( | |
voice_id="voice1", | |
speed=1.0, | |
language="en" | |
) | |
assert settings_none.is_default_volume is True | |
# Default volume (1.0) | |
settings_one = VoiceSettings( | |
voice_id="voice1", | |
speed=1.0, | |
language="en", | |
volume=1.0 | |
) | |
assert settings_one.is_default_volume is True | |
# Non-default volume | |
settings_non_default = VoiceSettings( | |
voice_id="voice1", | |
speed=1.0, | |
language="en", | |
volume=0.5 | |
) | |
assert settings_non_default.is_default_volume is False | |
def test_with_speed_method(self): | |
"""Test with_speed method creates new instance.""" | |
original = VoiceSettings( | |
voice_id="voice1", | |
speed=1.0, | |
language="en", | |
pitch=0.1, | |
volume=0.8 | |
) | |
new_settings = original.with_speed(1.5) | |
assert new_settings.speed == 1.5 | |
assert new_settings.voice_id == original.voice_id | |
assert new_settings.language == original.language | |
assert new_settings.pitch == original.pitch | |
assert new_settings.volume == original.volume | |
assert new_settings is not original # Different instances | |
def test_with_pitch_method(self): | |
"""Test with_pitch method creates new instance.""" | |
original = VoiceSettings( | |
voice_id="voice1", | |
speed=1.0, | |
language="en", | |
pitch=0.1, | |
volume=0.8 | |
) | |
new_settings = original.with_pitch(0.5) | |
assert new_settings.pitch == 0.5 | |
assert new_settings.voice_id == original.voice_id | |
assert new_settings.speed == original.speed | |
assert new_settings.language == original.language | |
assert new_settings.volume == original.volume | |
assert new_settings is not original # Different instances | |
def test_with_pitch_none(self): | |
"""Test with_pitch method with None value.""" | |
original = VoiceSettings( | |
voice_id="voice1", | |
speed=1.0, | |
language="en", | |
pitch=0.1 | |
) | |
new_settings = original.with_pitch(None) | |
assert new_settings.pitch is None | |
def test_voice_settings_is_immutable(self): | |
"""Test that VoiceSettings is immutable (frozen dataclass).""" | |
settings = VoiceSettings( | |
voice_id="voice1", | |
speed=1.0, | |
language="en" | |
) | |
with pytest.raises(AttributeError): | |
settings.speed = 1.5 # type: ignore |