fix: Lint fixes of core and repositories tests
All checks were successful
Backend CI / lint (push) Successful in 9m26s
Backend CI / test (push) Successful in 4m24s

This commit is contained in:
JSC
2025-08-01 09:17:20 +02:00
parent 389cfe2d6a
commit dc29915fbc
8 changed files with 135 additions and 88 deletions

1
tests/core/__init__.py Normal file
View File

@@ -0,0 +1 @@
"""Tests for core module."""

View File

@@ -1,4 +1,5 @@
"""Tests for API token authentication dependencies.""" """Tests for API token authentication dependencies."""
# ruff: noqa: S106
from datetime import UTC, datetime, timedelta from datetime import UTC, datetime, timedelta
from unittest.mock import AsyncMock from unittest.mock import AsyncMock
@@ -10,17 +11,20 @@ from app.core.dependencies import get_current_user_api_token, get_current_user_f
from app.models.user import User from app.models.user import User
from app.services.auth import AuthService from app.services.auth import AuthService
# Constants
HTTP_401_UNAUTHORIZED = 401
class TestApiTokenDependencies: class TestApiTokenDependencies:
"""Test API token authentication dependencies.""" """Test API token authentication dependencies."""
@pytest.fixture @pytest.fixture
def mock_auth_service(self): def mock_auth_service(self) -> AsyncMock:
"""Create a mock auth service.""" """Create a mock auth service."""
return AsyncMock(spec=AuthService) return AsyncMock(spec=AuthService)
@pytest.fixture @pytest.fixture
def test_user(self): def test_user(self) -> User:
"""Create a test user.""" """Create a test user."""
return User( return User(
id=1, id=1,
@@ -37,9 +41,9 @@ class TestApiTokenDependencies:
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_get_current_user_api_token_success( async def test_get_current_user_api_token_success(
self, self,
mock_auth_service, mock_auth_service: AsyncMock,
test_user, test_user: User,
): ) -> None:
"""Test successful API token authentication.""" """Test successful API token authentication."""
mock_auth_service.get_user_by_api_token.return_value = test_user mock_auth_service.get_user_by_api_token.return_value = test_user
@@ -53,38 +57,46 @@ class TestApiTokenDependencies:
) )
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_get_current_user_api_token_no_header(self, mock_auth_service): async def test_get_current_user_api_token_no_header(
self, mock_auth_service: AsyncMock,
) -> None:
"""Test API token authentication without API-TOKEN header.""" """Test API token authentication without API-TOKEN header."""
with pytest.raises(HTTPException) as exc_info: with pytest.raises(HTTPException) as exc_info:
await get_current_user_api_token(mock_auth_service, None) await get_current_user_api_token(mock_auth_service, None)
assert exc_info.value.status_code == 401 assert exc_info.value.status_code == HTTP_401_UNAUTHORIZED
assert "API-TOKEN header required" in exc_info.value.detail assert "API-TOKEN header required" in exc_info.value.detail
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_get_current_user_api_token_empty_token(self, mock_auth_service): async def test_get_current_user_api_token_empty_token(
self, mock_auth_service: AsyncMock,
) -> None:
"""Test API token authentication with empty token.""" """Test API token authentication with empty token."""
api_token_header = " " api_token_header = " "
with pytest.raises(HTTPException) as exc_info: with pytest.raises(HTTPException) as exc_info:
await get_current_user_api_token(mock_auth_service, api_token_header) await get_current_user_api_token(mock_auth_service, api_token_header)
assert exc_info.value.status_code == 401 assert exc_info.value.status_code == HTTP_401_UNAUTHORIZED
assert "API token required" in exc_info.value.detail assert "API token required" in exc_info.value.detail
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_get_current_user_api_token_whitespace_token(self, mock_auth_service): async def test_get_current_user_api_token_whitespace_token(
self, mock_auth_service: AsyncMock,
) -> None:
"""Test API token authentication with whitespace-only token.""" """Test API token authentication with whitespace-only token."""
api_token_header = " " api_token_header = " "
with pytest.raises(HTTPException) as exc_info: with pytest.raises(HTTPException) as exc_info:
await get_current_user_api_token(mock_auth_service, api_token_header) await get_current_user_api_token(mock_auth_service, api_token_header)
assert exc_info.value.status_code == 401 assert exc_info.value.status_code == HTTP_401_UNAUTHORIZED
assert "API token required" in exc_info.value.detail assert "API token required" in exc_info.value.detail
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_get_current_user_api_token_invalid_token(self, mock_auth_service): async def test_get_current_user_api_token_invalid_token(
self, mock_auth_service: AsyncMock,
) -> None:
"""Test API token authentication with invalid token.""" """Test API token authentication with invalid token."""
mock_auth_service.get_user_by_api_token.return_value = None mock_auth_service.get_user_by_api_token.return_value = None
@@ -93,15 +105,15 @@ class TestApiTokenDependencies:
with pytest.raises(HTTPException) as exc_info: with pytest.raises(HTTPException) as exc_info:
await get_current_user_api_token(mock_auth_service, api_token_header) await get_current_user_api_token(mock_auth_service, api_token_header)
assert exc_info.value.status_code == 401 assert exc_info.value.status_code == HTTP_401_UNAUTHORIZED
assert "Invalid API token" in exc_info.value.detail assert "Invalid API token" in exc_info.value.detail
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_get_current_user_api_token_expired_token( async def test_get_current_user_api_token_expired_token(
self, self,
mock_auth_service, mock_auth_service: AsyncMock,
test_user, test_user: User,
): ) -> None:
"""Test API token authentication with expired token.""" """Test API token authentication with expired token."""
# Set expired token # Set expired token
test_user.api_token_expires_at = datetime.now(UTC) - timedelta(days=1) test_user.api_token_expires_at = datetime.now(UTC) - timedelta(days=1)
@@ -112,15 +124,15 @@ class TestApiTokenDependencies:
with pytest.raises(HTTPException) as exc_info: with pytest.raises(HTTPException) as exc_info:
await get_current_user_api_token(mock_auth_service, api_token_header) await get_current_user_api_token(mock_auth_service, api_token_header)
assert exc_info.value.status_code == 401 assert exc_info.value.status_code == HTTP_401_UNAUTHORIZED
assert "API token has expired" in exc_info.value.detail assert "API token has expired" in exc_info.value.detail
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_get_current_user_api_token_inactive_user( async def test_get_current_user_api_token_inactive_user(
self, self,
mock_auth_service, mock_auth_service: AsyncMock,
test_user, test_user: User,
): ) -> None:
"""Test API token authentication with inactive user.""" """Test API token authentication with inactive user."""
test_user.is_active = False test_user.is_active = False
mock_auth_service.get_user_by_api_token.return_value = test_user mock_auth_service.get_user_by_api_token.return_value = test_user
@@ -130,13 +142,13 @@ class TestApiTokenDependencies:
with pytest.raises(HTTPException) as exc_info: with pytest.raises(HTTPException) as exc_info:
await get_current_user_api_token(mock_auth_service, api_token_header) await get_current_user_api_token(mock_auth_service, api_token_header)
assert exc_info.value.status_code == 401 assert exc_info.value.status_code == HTTP_401_UNAUTHORIZED
assert "Account is deactivated" in exc_info.value.detail assert "Account is deactivated" in exc_info.value.detail
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_get_current_user_api_token_service_exception( async def test_get_current_user_api_token_service_exception(
self, mock_auth_service, self, mock_auth_service: AsyncMock,
): ) -> None:
"""Test API token authentication with service exception.""" """Test API token authentication with service exception."""
mock_auth_service.get_user_by_api_token.side_effect = Exception( mock_auth_service.get_user_by_api_token.side_effect = Exception(
"Database error", "Database error",
@@ -147,15 +159,15 @@ class TestApiTokenDependencies:
with pytest.raises(HTTPException) as exc_info: with pytest.raises(HTTPException) as exc_info:
await get_current_user_api_token(mock_auth_service, api_token_header) await get_current_user_api_token(mock_auth_service, api_token_header)
assert exc_info.value.status_code == 401 assert exc_info.value.status_code == HTTP_401_UNAUTHORIZED
assert "Could not validate API token" in exc_info.value.detail assert "Could not validate API token" in exc_info.value.detail
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_get_current_user_flexible_uses_api_token( async def test_get_current_user_flexible_uses_api_token(
self, self,
mock_auth_service, mock_auth_service: AsyncMock,
test_user, test_user: User,
): ) -> None:
"""Test flexible authentication uses API token when available.""" """Test flexible authentication uses API token when available."""
mock_auth_service.get_user_by_api_token.return_value = test_user mock_auth_service.get_user_by_api_token.return_value = test_user
@@ -174,18 +186,20 @@ class TestApiTokenDependencies:
) )
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_get_current_user_flexible_falls_back_to_jwt(self, mock_auth_service): async def test_get_current_user_flexible_falls_back_to_jwt(
self, mock_auth_service: AsyncMock,
) -> None:
"""Test flexible authentication falls back to JWT when no API token.""" """Test flexible authentication falls back to JWT when no API token."""
# Mock the get_current_user function (normally imported) # Mock the get_current_user function (normally imported)
with pytest.raises(Exception): with pytest.raises(Exception, match="Database error|Could not validate"):
# This will fail because we can't easily mock the get_current_user import # This will fail because we can't easily mock the get_current_user import
# In a real test, you'd mock the import or use dependency injection # In a real test, you'd mock the import or use dependency injection
await get_current_user_flexible(mock_auth_service, "jwt_token", None) await get_current_user_flexible(mock_auth_service, "jwt_token", None)
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_api_token_no_expiry_never_expires( async def test_api_token_no_expiry_never_expires(
self, mock_auth_service, test_user, self, mock_auth_service: AsyncMock, test_user: User,
): ) -> None:
"""Test API token with no expiry date never expires.""" """Test API token with no expiry date never expires."""
test_user.api_token_expires_at = None test_user.api_token_expires_at = None
mock_auth_service.get_user_by_api_token.return_value = test_user mock_auth_service.get_user_by_api_token.return_value = test_user
@@ -197,7 +211,9 @@ class TestApiTokenDependencies:
assert result == test_user assert result == test_user
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_api_token_with_whitespace(self, mock_auth_service, test_user): async def test_api_token_with_whitespace(
self, mock_auth_service: AsyncMock, test_user: User,
) -> None:
"""Test API token with leading/trailing whitespace is handled correctly.""" """Test API token with leading/trailing whitespace is handled correctly."""
mock_auth_service.get_user_by_api_token.return_value = test_user mock_auth_service.get_user_by_api_token.return_value = test_user

View File

@@ -1,4 +1,5 @@
"""Tests for credit transaction repository.""" """Tests for credit transaction repository."""
# ruff: noqa: ARG002, E501
import json import json
from collections.abc import AsyncGenerator from collections.abc import AsyncGenerator
@@ -11,6 +12,18 @@ from sqlmodel.ext.asyncio.session import AsyncSession
from app.models.credit_transaction import CreditTransaction from app.models.credit_transaction import CreditTransaction
from app.models.user import User from app.models.user import User
from app.repositories.credit_transaction import CreditTransactionRepository from app.repositories.credit_transaction import CreditTransactionRepository
from app.repositories.user import UserRepository
# Constants
EXPECTED_TRANSACTION_COUNT = 4
PAGE_SIZE = 2
MIN_VLC_TRANSACTIONS = 2
MIN_SUCCESSFUL_TRANSACTIONS = 3
SUCCESSFUL_TRANSACTION_COUNT = 3
MIN_ALL_TRANSACTIONS = 5
TEST_AMOUNT = -10
TEST_BALANCE_BEFORE = 100
TEST_BALANCE_AFTER = 90
class TestCreditTransactionRepository: class TestCreditTransactionRepository:
@@ -102,11 +115,9 @@ class TestCreditTransactionRepository:
async def other_user_transaction( async def other_user_transaction(
self, self,
test_session: AsyncSession, test_session: AsyncSession,
ensure_plans: tuple[Any, ...], # noqa: ARG002 ensure_plans: tuple[Any, ...],
) -> AsyncGenerator[CreditTransaction, None]: ) -> AsyncGenerator[CreditTransaction, None]:
"""Create a transaction for a different user.""" """Create a transaction for a different user."""
from app.repositories.user import UserRepository
# Create another user # Create another user
user_repo = UserRepository(test_session) user_repo = UserRepository(test_session)
other_user_data = { other_user_data = {
@@ -174,7 +185,7 @@ class TestCreditTransactionRepository:
transactions = await credit_transaction_repository.get_by_user_id(test_user_id) transactions = await credit_transaction_repository.get_by_user_id(test_user_id)
# Should return all transactions for test_user # Should return all transactions for test_user
assert len(transactions) == 4 assert len(transactions) == EXPECTED_TRANSACTION_COUNT
# Should be ordered by created_at desc (newest first) # Should be ordered by created_at desc (newest first)
assert all(t.user_id == test_user_id for t in transactions) assert all(t.user_id == test_user_id for t in transactions)
@@ -194,13 +205,13 @@ class TestCreditTransactionRepository:
first_page = await credit_transaction_repository.get_by_user_id( first_page = await credit_transaction_repository.get_by_user_id(
test_user_id, limit=2, offset=0, test_user_id, limit=2, offset=0,
) )
assert len(first_page) == 2 assert len(first_page) == PAGE_SIZE
# Get next 2 transactions # Get next 2 transactions
second_page = await credit_transaction_repository.get_by_user_id( second_page = await credit_transaction_repository.get_by_user_id(
test_user_id, limit=2, offset=2, test_user_id, limit=2, offset=2,
) )
assert len(second_page) == 2 assert len(second_page) == PAGE_SIZE
# Should not overlap # Should not overlap
first_page_ids = {t.id for t in first_page} first_page_ids = {t.id for t in first_page}
@@ -219,11 +230,13 @@ class TestCreditTransactionRepository:
) )
# Should return 2 VLC transactions (1 successful, 1 failed) # Should return 2 VLC transactions (1 successful, 1 failed)
assert len(vlc_transactions) >= 2 assert len(vlc_transactions) >= MIN_VLC_TRANSACTIONS
assert all(t.action_type == "vlc_play_sound" for t in vlc_transactions) assert all(t.action_type == "vlc_play_sound" for t in vlc_transactions)
extraction_transactions = await credit_transaction_repository.get_by_action_type( extraction_transactions = (
"audio_extraction", await credit_transaction_repository.get_by_action_type(
"audio_extraction",
)
) )
# Should return 1 extraction transaction # Should return 1 extraction transaction
@@ -262,7 +275,7 @@ class TestCreditTransactionRepository:
# Should only return successful transactions # Should only return successful transactions
assert all(t.success is True for t in successful_transactions) assert all(t.success is True for t in successful_transactions)
# Should be at least 3 (vlc_play_sound, audio_extraction, credit_addition) # Should be at least 3 (vlc_play_sound, audio_extraction, credit_addition)
assert len(successful_transactions) >= 3 assert len(successful_transactions) >= MIN_SUCCESSFUL_TRANSACTIONS
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_get_successful_transactions_by_user( async def test_get_successful_transactions_by_user(
@@ -281,7 +294,7 @@ class TestCreditTransactionRepository:
assert all(t.success is True for t in successful_transactions) assert all(t.success is True for t in successful_transactions)
assert all(t.user_id == test_user_id for t in successful_transactions) assert all(t.user_id == test_user_id for t in successful_transactions)
# Should be 3 successful transactions for test_user # Should be 3 successful transactions for test_user
assert len(successful_transactions) == 3 assert len(successful_transactions) == SUCCESSFUL_TRANSACTION_COUNT
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_get_successful_transactions_with_pagination( async def test_get_successful_transactions_with_pagination(
@@ -295,7 +308,7 @@ class TestCreditTransactionRepository:
first_page = await credit_transaction_repository.get_successful_transactions( first_page = await credit_transaction_repository.get_successful_transactions(
user_id=test_user_id, limit=2, offset=0, user_id=test_user_id, limit=2, offset=0,
) )
assert len(first_page) == 2 assert len(first_page) == PAGE_SIZE
assert all(t.success is True for t in first_page) assert all(t.success is True for t in first_page)
# Get next successful transaction # Get next successful transaction
@@ -316,7 +329,7 @@ class TestCreditTransactionRepository:
all_transactions = await credit_transaction_repository.get_all() all_transactions = await credit_transaction_repository.get_all()
# Should return all transactions # Should return all transactions
assert len(all_transactions) >= 5 # 4 from test_transactions + 1 other_user_transaction assert len(all_transactions) >= MIN_ALL_TRANSACTIONS # 4 from test_transactions + 1 other_user_transaction
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_create_transaction( async def test_create_transaction(
@@ -341,9 +354,9 @@ class TestCreditTransactionRepository:
assert transaction.id is not None assert transaction.id is not None
assert transaction.user_id == test_user_id assert transaction.user_id == test_user_id
assert transaction.action_type == "test_action" assert transaction.action_type == "test_action"
assert transaction.amount == -10 assert transaction.amount == TEST_AMOUNT
assert transaction.balance_before == 100 assert transaction.balance_before == TEST_BALANCE_BEFORE
assert transaction.balance_after == 90 assert transaction.balance_after == TEST_BALANCE_AFTER
assert transaction.success is True assert transaction.success is True
assert transaction.metadata_json is not None assert transaction.metadata_json is not None
assert json.loads(transaction.metadata_json) == {"test": "data"} assert json.loads(transaction.metadata_json) == {"test": "data"}

View File

@@ -1,4 +1,5 @@
"""Tests for extraction repository.""" """Tests for extraction repository."""
# ruff: noqa: ANN001, ANN201
from unittest.mock import AsyncMock, Mock from unittest.mock import AsyncMock, Mock
@@ -8,6 +9,9 @@ from sqlmodel.ext.asyncio.session import AsyncSession
from app.models.extraction import Extraction from app.models.extraction import Extraction
from app.repositories.extraction import ExtractionRepository from app.repositories.extraction import ExtractionRepository
# Constants
TEST_SOUND_ID = 42
class TestExtractionRepository: class TestExtractionRepository:
"""Test extraction repository.""" """Test extraction repository."""
@@ -123,6 +127,6 @@ class TestExtractionRepository:
result = await extraction_repo.update(extraction, update_data) result = await extraction_repo.update(extraction, update_data)
assert result.status == "completed" assert result.status == "completed"
assert result.sound_id == 42 assert result.sound_id == TEST_SOUND_ID
extraction_repo.session.commit.assert_called_once() extraction_repo.session.commit.assert_called_once()
extraction_repo.session.refresh.assert_called_once_with(extraction) extraction_repo.session.refresh.assert_called_once_with(extraction)

View File

@@ -1,6 +1,8 @@
"""Tests for playlist repository.""" """Tests for playlist repository."""
# ruff: noqa: PLR2004, ANN401
from collections.abc import AsyncGenerator from collections.abc import AsyncGenerator
from typing import Any
import pytest import pytest
import pytest_asyncio import pytest_asyncio
@@ -10,6 +12,16 @@ from app.models.playlist import Playlist
from app.models.sound import Sound from app.models.sound import Sound
from app.models.user import User from app.models.user import User
from app.repositories.playlist import PlaylistRepository from app.repositories.playlist import PlaylistRepository
from app.utils.auth import PasswordUtils
# Constants
TEST_POSITION = 5
TEST_TOTAL_SOUNDS = 3
ONE_PLAYLIST = 1
ZERO_SOUNDS = 0
ONE_SOUND = 1
TWO_SOUNDS = 2
DEFAULT_POSITION = 0
class TestPlaylistRepository: class TestPlaylistRepository:
@@ -134,11 +146,10 @@ class TestPlaylistRepository:
self, self,
playlist_repository: PlaylistRepository, playlist_repository: PlaylistRepository,
test_session: AsyncSession, test_session: AsyncSession,
ensure_plans, ensure_plans: Any,
) -> None: ) -> None:
"""Test getting playlists by user ID.""" """Test getting playlists by user ID."""
# Create test user within this test # Create test user within this test
from app.utils.auth import PasswordUtils
user = User( user = User(
email="test@example.com", email="test@example.com",
name="Test User", name="Test User",
@@ -172,7 +183,7 @@ class TestPlaylistRepository:
playlists = await playlist_repository.get_by_user_id(user_id) playlists = await playlist_repository.get_by_user_id(user_id)
# Should only return user's playlists, not the main playlist (user_id=None) # Should only return user's playlists, not the main playlist (user_id=None)
assert len(playlists) == 1 assert len(playlists) == ONE_PLAYLIST
assert playlists[0].name == "Test Playlist" assert playlists[0].name == "Test Playlist"
@pytest.mark.asyncio @pytest.mark.asyncio
@@ -210,11 +221,10 @@ class TestPlaylistRepository:
self, self,
playlist_repository: PlaylistRepository, playlist_repository: PlaylistRepository,
test_session: AsyncSession, test_session: AsyncSession,
ensure_plans, ensure_plans: Any,
) -> None: ) -> None:
"""Test getting current playlist when none is set.""" """Test getting current playlist when none is set."""
# Create test user within this test # Create test user within this test
from app.utils.auth import PasswordUtils
user = User( user = User(
email="test2@example.com", email="test2@example.com",
name="Test User 2", name="Test User 2",
@@ -302,11 +312,10 @@ class TestPlaylistRepository:
self, self,
playlist_repository: PlaylistRepository, playlist_repository: PlaylistRepository,
test_session: AsyncSession, test_session: AsyncSession,
ensure_plans, ensure_plans: Any,
) -> None: ) -> None:
"""Test searching playlists by name.""" """Test searching playlists by name."""
# Create test user within this test # Create test user within this test
from app.utils.auth import PasswordUtils
user = User( user = User(
email="test3@example.com", email="test3@example.com",
name="Test User 3", name="Test User 3",
@@ -353,11 +362,12 @@ class TestPlaylistRepository:
# Search with user filter # Search with user filter
user_results = await playlist_repository.search_by_name("playlist", user_id) user_results = await playlist_repository.search_by_name("playlist", user_id)
assert len(user_results) == 1 # Only user's playlists, not main playlist # Only user's playlists, not main playlist
assert len(user_results) == ONE_PLAYLIST
# Search for specific playlist # Search for specific playlist
test_results = await playlist_repository.search_by_name("test", user_id) test_results = await playlist_repository.search_by_name("test", user_id)
assert len(test_results) == 1 assert len(test_results) == ONE_PLAYLIST
assert test_results[0].name == "Test Playlist" assert test_results[0].name == "Test Playlist"
@pytest.mark.asyncio @pytest.mark.asyncio
@@ -365,11 +375,10 @@ class TestPlaylistRepository:
self, self,
playlist_repository: PlaylistRepository, playlist_repository: PlaylistRepository,
test_session: AsyncSession, test_session: AsyncSession,
ensure_plans, ensure_plans: Any,
) -> None: ) -> None:
"""Test adding a sound to a playlist.""" """Test adding a sound to a playlist."""
# Create test user within this test # Create test user within this test
from app.utils.auth import PasswordUtils
user = User( user = User(
email="test4@example.com", email="test4@example.com",
name="Test User 4", name="Test User 4",
@@ -421,18 +430,17 @@ class TestPlaylistRepository:
assert playlist_sound.playlist_id == playlist_id assert playlist_sound.playlist_id == playlist_id
assert playlist_sound.sound_id == sound_id assert playlist_sound.sound_id == sound_id
assert playlist_sound.position == 0 assert playlist_sound.position == DEFAULT_POSITION
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_add_sound_to_playlist_with_position( async def test_add_sound_to_playlist_with_position(
self, self,
playlist_repository: PlaylistRepository, playlist_repository: PlaylistRepository,
test_session: AsyncSession, test_session: AsyncSession,
ensure_plans, ensure_plans: Any,
) -> None: ) -> None:
"""Test adding a sound to a playlist with specific position.""" """Test adding a sound to a playlist with specific position."""
# Create test user within this test # Create test user within this test
from app.utils.auth import PasswordUtils
user = User( user = User(
email="test5@example.com", email="test5@example.com",
name="Test User 5", name="Test User 5",
@@ -485,18 +493,17 @@ class TestPlaylistRepository:
playlist_id, sound_id, position=5, playlist_id, sound_id, position=5,
) )
assert playlist_sound.position == 5 assert playlist_sound.position == TEST_POSITION
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_remove_sound_from_playlist( async def test_remove_sound_from_playlist(
self, self,
playlist_repository: PlaylistRepository, playlist_repository: PlaylistRepository,
test_session: AsyncSession, test_session: AsyncSession,
ensure_plans, ensure_plans: Any,
) -> None: ) -> None:
"""Test removing a sound from a playlist.""" """Test removing a sound from a playlist."""
# Create objects within this test # Create objects within this test
from app.utils.auth import PasswordUtils
user = User( user = User(
email="test@example.com", email="test@example.com",
name="Test User", name="Test User",
@@ -564,11 +571,10 @@ class TestPlaylistRepository:
self, self,
playlist_repository: PlaylistRepository, playlist_repository: PlaylistRepository,
test_session: AsyncSession, test_session: AsyncSession,
ensure_plans, ensure_plans: Any,
) -> None: ) -> None:
"""Test getting sounds in a playlist.""" """Test getting sounds in a playlist."""
# Create objects within this test # Create objects within this test
from app.utils.auth import PasswordUtils
user = User( user = User(
email="test@example.com", email="test@example.com",
name="Test User", name="Test User",
@@ -615,14 +621,14 @@ class TestPlaylistRepository:
# Initially empty # Initially empty
sounds = await playlist_repository.get_playlist_sounds(playlist_id) sounds = await playlist_repository.get_playlist_sounds(playlist_id)
assert len(sounds) == 0 assert len(sounds) == ZERO_SOUNDS
# Add sound # Add sound
await playlist_repository.add_sound_to_playlist(playlist_id, sound_id) await playlist_repository.add_sound_to_playlist(playlist_id, sound_id)
# Check sounds # Check sounds
sounds = await playlist_repository.get_playlist_sounds(playlist_id) sounds = await playlist_repository.get_playlist_sounds(playlist_id)
assert len(sounds) == 1 assert len(sounds) == ONE_SOUND
assert sounds[0].id == sound_id assert sounds[0].id == sound_id
@pytest.mark.asyncio @pytest.mark.asyncio
@@ -630,11 +636,10 @@ class TestPlaylistRepository:
self, self,
playlist_repository: PlaylistRepository, playlist_repository: PlaylistRepository,
test_session: AsyncSession, test_session: AsyncSession,
ensure_plans, ensure_plans: Any,
) -> None: ) -> None:
"""Test getting sound count in a playlist.""" """Test getting sound count in a playlist."""
# Create objects within this test # Create objects within this test
from app.utils.auth import PasswordUtils
user = User( user = User(
email="test@example.com", email="test@example.com",
name="Test User", name="Test User",
@@ -695,11 +700,10 @@ class TestPlaylistRepository:
self, self,
playlist_repository: PlaylistRepository, playlist_repository: PlaylistRepository,
test_session: AsyncSession, test_session: AsyncSession,
ensure_plans, ensure_plans: Any,
) -> None: ) -> None:
"""Test checking if sound is in playlist.""" """Test checking if sound is in playlist."""
# Create objects within this test # Create objects within this test
from app.utils.auth import PasswordUtils
user = User( user = User(
email="test@example.com", email="test@example.com",
name="Test User", name="Test User",
@@ -762,11 +766,10 @@ class TestPlaylistRepository:
self, self,
playlist_repository: PlaylistRepository, playlist_repository: PlaylistRepository,
test_session: AsyncSession, test_session: AsyncSession,
ensure_plans, ensure_plans: Any,
) -> None: ) -> None:
"""Test reordering sounds in a playlist.""" """Test reordering sounds in a playlist."""
# Create objects within this test # Create objects within this test
from app.utils.auth import PasswordUtils
user = User( user = User(
email="test@example.com", email="test@example.com",
name="Test User", name="Test User",
@@ -823,6 +826,6 @@ class TestPlaylistRepository:
# Verify new order # Verify new order
sounds = await playlist_repository.get_playlist_sounds(playlist_id) sounds = await playlist_repository.get_playlist_sounds(playlist_id)
assert len(sounds) == 2 assert len(sounds) == TWO_SOUNDS
assert sounds[0].id == sound2_id # sound2 now at position 5 assert sounds[0].id == sound2_id # sound2 now at position 5
assert sounds[1].id == sound1_id # sound1 now at position 10 assert sounds[1].id == sound1_id # sound1 now at position 10

View File

@@ -1,14 +1,19 @@
"""Tests for sound repository.""" """Tests for sound repository."""
# ruff: noqa: ARG002, PLR2004
from collections.abc import AsyncGenerator from collections.abc import AsyncGenerator
import pytest import pytest
import pytest_asyncio import pytest_asyncio
from sqlalchemy.exc import IntegrityError
from sqlmodel.ext.asyncio.session import AsyncSession from sqlmodel.ext.asyncio.session import AsyncSession
from app.models.sound import Sound from app.models.sound import Sound
from app.repositories.sound import SoundRepository from app.repositories.sound import SoundRepository
# Constants
MIN_POPULAR_SOUNDS = 3
class TestSoundRepository: class TestSoundRepository:
"""Test sound repository operations.""" """Test sound repository operations."""
@@ -306,7 +311,7 @@ class TestSoundRepository:
# Get popular sounds # Get popular sounds
popular_sounds = await sound_repository.get_popular_sounds(limit=10) popular_sounds = await sound_repository.get_popular_sounds(limit=10)
assert len(popular_sounds) >= 3 assert len(popular_sounds) >= MIN_POPULAR_SOUNDS
# Should be ordered by play_count desc # Should be ordered by play_count desc
assert popular_sounds[0].play_count >= popular_sounds[1].play_count assert popular_sounds[0].play_count >= popular_sounds[1].play_count
# The highest play count sound should be first # The highest play count sound should be first
@@ -372,5 +377,5 @@ class TestSoundRepository:
} }
# Should fail due to unique constraint on hash # Should fail due to unique constraint on hash
with pytest.raises(Exception): # SQLAlchemy IntegrityError or similar with pytest.raises(IntegrityError, match="UNIQUE constraint failed"):
await sound_repository.create(duplicate_sound_data) await sound_repository.create(duplicate_sound_data)

View File

@@ -1,4 +1,5 @@
"""Tests for user repository.""" """Tests for user repository."""
# ruff: noqa: ARG002
from collections.abc import AsyncGenerator from collections.abc import AsyncGenerator
@@ -174,24 +175,24 @@ class TestUserRepository:
test_user: User, test_user: User,
) -> None: ) -> None:
"""Test updating a user.""" """Test updating a user."""
UPDATED_CREDITS = 200 updated_credits = 200
update_data = { update_data = {
"name": "Updated Name", "name": "Updated Name",
"credits": UPDATED_CREDITS, "credits": updated_credits,
} }
updated_user = await user_repository.update(test_user, update_data) updated_user = await user_repository.update(test_user, update_data)
assert updated_user.id == test_user.id assert updated_user.id == test_user.id
assert updated_user.name == "Updated Name" assert updated_user.name == "Updated Name"
assert updated_user.credits == UPDATED_CREDITS assert updated_user.credits == updated_credits
assert updated_user.email == test_user.email # Unchanged assert updated_user.email == test_user.email # Unchanged
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_delete_user( async def test_delete_user(
self, self,
user_repository: UserRepository, user_repository: UserRepository,
ensure_plans: tuple[Plan, Plan], # noqa: ARG002 ensure_plans: tuple[Plan, Plan],
test_session: AsyncSession, test_session: AsyncSession,
) -> None: ) -> None:
"""Test deleting a user.""" """Test deleting a user."""

View File

@@ -1,9 +1,11 @@
"""Tests for user OAuth repository.""" """Tests for user OAuth repository."""
# ruff: noqa: ARG002
from collections.abc import AsyncGenerator from collections.abc import AsyncGenerator
import pytest import pytest
import pytest_asyncio import pytest_asyncio
from sqlalchemy.exc import IntegrityError
from sqlmodel.ext.asyncio.session import AsyncSession from sqlmodel.ext.asyncio.session import AsyncSession
from app.models.user import User from app.models.user import User
@@ -156,7 +158,9 @@ class TestUserOauthRepository:
assert updated_oauth.name == "Updated User Name" assert updated_oauth.name == "Updated User Name"
assert updated_oauth.picture == "https://example.com/photo.jpg" assert updated_oauth.picture == "https://example.com/photo.jpg"
assert updated_oauth.provider == test_oauth.provider # Unchanged assert updated_oauth.provider == test_oauth.provider # Unchanged
assert updated_oauth.provider_user_id == test_oauth.provider_user_id # Unchanged assert (
updated_oauth.provider_user_id == test_oauth.provider_user_id
) # Unchanged
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_delete_oauth( async def test_delete_oauth(
@@ -176,7 +180,7 @@ class TestUserOauthRepository:
"picture": None, "picture": None,
} }
oauth = await user_oauth_repository.create(oauth_data) oauth = await user_oauth_repository.create(oauth_data)
oauth_id = oauth.id _ = oauth.id # Store ID but don't use it
# Delete the OAuth record # Delete the OAuth record
await user_oauth_repository.delete(oauth) await user_oauth_repository.delete(oauth)
@@ -206,7 +210,7 @@ class TestUserOauthRepository:
} }
# This should fail due to unique constraint # This should fail due to unique constraint
with pytest.raises(Exception): # SQLAlchemy IntegrityError or similar with pytest.raises(IntegrityError, match="UNIQUE constraint failed"):
await user_oauth_repository.create(duplicate_oauth_data) await user_oauth_repository.create(duplicate_oauth_data)
@pytest.mark.asyncio @pytest.mark.asyncio
@@ -225,7 +229,7 @@ class TestUserOauthRepository:
"name": "Test User Google", "name": "Test User Google",
"picture": None, "picture": None,
} }
google_oauth = await user_oauth_repository.create(google_oauth_data) _ = await user_oauth_repository.create(google_oauth_data)
# Create GitHub OAuth for the same user # Create GitHub OAuth for the same user
github_oauth_data = { github_oauth_data = {
@@ -236,7 +240,7 @@ class TestUserOauthRepository:
"name": "Test User GitHub", "name": "Test User GitHub",
"picture": None, "picture": None,
} }
github_oauth = await user_oauth_repository.create(github_oauth_data) _ = await user_oauth_repository.create(github_oauth_data)
# Verify both exist by querying back from database # Verify both exist by querying back from database
found_google = await user_oauth_repository.get_by_user_id_and_provider( found_google = await user_oauth_repository.get_by_user_id_and_provider(