218 lines
6.7 KiB
Python
218 lines
6.7 KiB
Python
"""
|
|
Tests for profile duplicate name validation.
|
|
|
|
This test suite verifies that the application correctly handles
|
|
duplicate profile names and provides user-friendly error messages.
|
|
"""
|
|
|
|
import pytest
|
|
import tempfile
|
|
import shutil
|
|
from pathlib import Path
|
|
from sqlalchemy import create_engine
|
|
from sqlalchemy.orm import sessionmaker
|
|
|
|
# Add parent directory to path to import backend modules
|
|
import sys
|
|
sys.path.insert(0, str(Path(__file__).parent.parent))
|
|
|
|
from database import Base, VoiceProfile as DBVoiceProfile
|
|
from models import VoiceProfileCreate
|
|
from profiles import create_profile, update_profile
|
|
|
|
|
|
@pytest.fixture
|
|
def test_db():
|
|
"""Create a temporary test database."""
|
|
# Create temporary directory for test database
|
|
temp_dir = tempfile.mkdtemp()
|
|
db_path = Path(temp_dir) / "test.db"
|
|
|
|
# Create engine and session
|
|
engine = create_engine(f"sqlite:///{db_path}")
|
|
Base.metadata.create_all(bind=engine)
|
|
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
|
|
|
db = SessionLocal()
|
|
|
|
yield db
|
|
|
|
# Cleanup
|
|
db.close()
|
|
shutil.rmtree(temp_dir)
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_profiles_dir(monkeypatch, tmp_path):
|
|
"""Mock the profiles directory to use a temporary path."""
|
|
from backend import config
|
|
monkeypatch.setattr(config, 'get_profiles_dir', lambda: tmp_path)
|
|
return tmp_path
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_create_profile_duplicate_name_raises_error(test_db, mock_profiles_dir):
|
|
"""Test that creating a profile with a duplicate name raises a ValueError."""
|
|
# Create first profile
|
|
profile_data_1 = VoiceProfileCreate(
|
|
name="Test Profile",
|
|
description="First profile",
|
|
language="en"
|
|
)
|
|
|
|
profile_1 = await create_profile(profile_data_1, test_db)
|
|
assert profile_1.name == "Test Profile"
|
|
|
|
# Try to create second profile with same name
|
|
profile_data_2 = VoiceProfileCreate(
|
|
name="Test Profile",
|
|
description="Second profile",
|
|
language="en"
|
|
)
|
|
|
|
with pytest.raises(ValueError) as exc_info:
|
|
await create_profile(profile_data_2, test_db)
|
|
|
|
# Verify error message is user-friendly
|
|
assert "already exists" in str(exc_info.value)
|
|
assert "Test Profile" in str(exc_info.value)
|
|
assert "choose a different name" in str(exc_info.value).lower()
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_create_profile_different_names_succeeds(test_db, mock_profiles_dir):
|
|
"""Test that creating profiles with different names succeeds."""
|
|
# Create first profile
|
|
profile_data_1 = VoiceProfileCreate(
|
|
name="Profile One",
|
|
description="First profile",
|
|
language="en"
|
|
)
|
|
|
|
profile_1 = await create_profile(profile_data_1, test_db)
|
|
assert profile_1.name == "Profile One"
|
|
|
|
# Create second profile with different name
|
|
profile_data_2 = VoiceProfileCreate(
|
|
name="Profile Two",
|
|
description="Second profile",
|
|
language="en"
|
|
)
|
|
|
|
profile_2 = await create_profile(profile_data_2, test_db)
|
|
assert profile_2.name == "Profile Two"
|
|
|
|
# Verify both profiles exist
|
|
assert profile_1.id != profile_2.id
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_update_profile_to_duplicate_name_raises_error(test_db, mock_profiles_dir):
|
|
"""Test that updating a profile to a duplicate name raises a ValueError."""
|
|
# Create two profiles with different names
|
|
profile_data_1 = VoiceProfileCreate(
|
|
name="Profile A",
|
|
description="First profile",
|
|
language="en"
|
|
)
|
|
profile_1 = await create_profile(profile_data_1, test_db)
|
|
|
|
profile_data_2 = VoiceProfileCreate(
|
|
name="Profile B",
|
|
description="Second profile",
|
|
language="en"
|
|
)
|
|
profile_2 = await create_profile(profile_data_2, test_db)
|
|
|
|
# Try to update profile_2 to use profile_1's name
|
|
update_data = VoiceProfileCreate(
|
|
name="Profile A", # Duplicate name
|
|
description="Updated description",
|
|
language="en"
|
|
)
|
|
|
|
with pytest.raises(ValueError) as exc_info:
|
|
await update_profile(profile_2.id, update_data, test_db)
|
|
|
|
# Verify error message is user-friendly
|
|
assert "already exists" in str(exc_info.value)
|
|
assert "Profile A" in str(exc_info.value)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_update_profile_keep_same_name_succeeds(test_db, mock_profiles_dir):
|
|
"""Test that updating a profile while keeping the same name succeeds."""
|
|
# Create profile
|
|
profile_data = VoiceProfileCreate(
|
|
name="My Profile",
|
|
description="Original description",
|
|
language="en"
|
|
)
|
|
profile = await create_profile(profile_data, test_db)
|
|
|
|
# Update profile with same name but different description
|
|
update_data = VoiceProfileCreate(
|
|
name="My Profile", # Same name
|
|
description="Updated description",
|
|
language="en"
|
|
)
|
|
|
|
updated_profile = await update_profile(profile.id, update_data, test_db)
|
|
|
|
# Verify update succeeded
|
|
assert updated_profile is not None
|
|
assert updated_profile.id == profile.id
|
|
assert updated_profile.name == "My Profile"
|
|
assert updated_profile.description == "Updated description"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_update_profile_to_new_unique_name_succeeds(test_db, mock_profiles_dir):
|
|
"""Test that updating a profile to a new unique name succeeds."""
|
|
# Create profile
|
|
profile_data = VoiceProfileCreate(
|
|
name="Original Name",
|
|
description="Profile description",
|
|
language="en"
|
|
)
|
|
profile = await create_profile(profile_data, test_db)
|
|
|
|
# Update profile with new unique name
|
|
update_data = VoiceProfileCreate(
|
|
name="New Unique Name",
|
|
description="Updated description",
|
|
language="en"
|
|
)
|
|
|
|
updated_profile = await update_profile(profile.id, update_data, test_db)
|
|
|
|
# Verify update succeeded
|
|
assert updated_profile is not None
|
|
assert updated_profile.id == profile.id
|
|
assert updated_profile.name == "New Unique Name"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_case_sensitive_names_allowed(test_db, mock_profiles_dir):
|
|
"""Test that profile names are case-sensitive (e.g., 'Test' and 'test' are different)."""
|
|
# Create profile with lowercase name
|
|
profile_data_1 = VoiceProfileCreate(
|
|
name="test profile",
|
|
description="Lowercase",
|
|
language="en"
|
|
)
|
|
profile_1 = await create_profile(profile_data_1, test_db)
|
|
|
|
# Create profile with different case
|
|
profile_data_2 = VoiceProfileCreate(
|
|
name="Test Profile",
|
|
description="Title case",
|
|
language="en"
|
|
)
|
|
profile_2 = await create_profile(profile_data_2, test_db)
|
|
|
|
# Both should succeed since SQLite unique constraint is case-sensitive by default
|
|
assert profile_1.name == "test profile"
|
|
assert profile_2.name == "Test Profile"
|
|
assert profile_1.id != profile_2.id
|