"""Tests for ListPostsUseCase. Tests listing posts with various filters. """ from datetime import datetime from unittest.mock import AsyncMock, MagicMock from uuid import uuid4 import pytest from app.application.dtos import PostResponseDTO from app.application.interfaces import TransactionManager from app.application.use_cases.list_posts import ListPostsUseCase from app.domain.entities import Post from app.domain.repositories import PostRepository from app.domain.value_objects import Content, Slug, Title @pytest.fixture def mock_post_repository() -> MagicMock: """Create mock post repository.""" return MagicMock(spec=PostRepository) @pytest.fixture def mock_transaction_manager() -> MagicMock: """Create mock transaction manager.""" return MagicMock(spec=TransactionManager) @pytest.fixture def list_use_case( mock_post_repository: MagicMock, mock_transaction_manager: MagicMock, ) -> ListPostsUseCase: """Create list use case with mocked dependencies.""" return ListPostsUseCase(mock_post_repository, mock_transaction_manager) @pytest.fixture def sample_posts() -> list[Post]: """Create sample posts for testing.""" return [ Post( id=uuid4(), title=Title(f"Post {i}"), content=Content(f"Content for post number {i}"), slug=Slug(f"post-{i}"), author_id="author-123", published=i % 2 == 0, tags=["python"] if i == 0 else [], created_at=datetime.now(), updated_at=datetime.now(), ) for i in range(3) ] class TestAllPosts: """Test suite for all_posts method.""" async def test_all_posts( self, list_use_case: ListPostsUseCase, mock_post_repository: MagicMock, sample_posts: list[Post], ) -> None: """Test getting all posts.""" mock_post_repository.get_all = AsyncMock(return_value=sample_posts) result = await list_use_case.all_posts() assert len(result) == 3 assert all(isinstance(dto, PostResponseDTO) for dto in result) mock_post_repository.get_all.assert_called_once() async def test_all_posts_empty( self, list_use_case: ListPostsUseCase, mock_post_repository: MagicMock, ) -> None: """Test getting all posts when empty.""" mock_post_repository.get_all = AsyncMock(return_value=[]) result = await list_use_case.all_posts() assert result == [] class TestPublishedPosts: """Test suite for published_posts method.""" async def test_published_posts( self, list_use_case: ListPostsUseCase, mock_post_repository: MagicMock, sample_posts: list[Post], ) -> None: """Test getting published posts.""" published = [p for p in sample_posts if p.published] mock_post_repository.get_published = AsyncMock(return_value=published) result = await list_use_case.published_posts() assert len(result) == 2 # posts 0 and 2 are published assert all(dto.published for dto in result) async def test_published_posts_with_limit_offset( self, list_use_case: ListPostsUseCase, mock_post_repository: MagicMock, ) -> None: """Test getting published posts with pagination.""" mock_post_repository.get_published = AsyncMock(return_value=[]) result = await list_use_case.published_posts(limit=5, offset=10) mock_post_repository.get_published.assert_called_once_with(limit=5, offset=10) assert result == [] class TestByAuthor: """Test suite for by_author method.""" async def test_by_author( self, list_use_case: ListPostsUseCase, mock_post_repository: MagicMock, sample_posts: list[Post], ) -> None: """Test getting posts by author.""" mock_post_repository.get_by_author = AsyncMock(return_value=sample_posts) result = await list_use_case.by_author("author-123") assert len(result) == 3 mock_post_repository.get_by_author.assert_called_once_with( "author-123", limit=None, offset=None ) async def test_by_author_with_pagination( self, list_use_case: ListPostsUseCase, mock_post_repository: MagicMock, ) -> None: """Test getting posts by author with pagination.""" mock_post_repository.get_by_author = AsyncMock(return_value=[]) await list_use_case.by_author("author-123", limit=5, offset=0) mock_post_repository.get_by_author.assert_called_once_with("author-123", limit=5, offset=0) class TestByTag: """Test suite for by_tag method.""" async def test_by_tag( self, list_use_case: ListPostsUseCase, mock_post_repository: MagicMock, sample_posts: list[Post], ) -> None: """Test getting posts by tag.""" tagged_posts = [sample_posts[0]] mock_post_repository.get_by_tag = AsyncMock(return_value=tagged_posts) result = await list_use_case.by_tag("python") assert len(result) == 1 assert "python" in result[0].tags async def test_by_tag_empty( self, list_use_case: ListPostsUseCase, mock_post_repository: MagicMock, ) -> None: """Test getting posts by non-existent tag.""" mock_post_repository.get_by_tag = AsyncMock(return_value=[]) result = await list_use_case.by_tag("nonexistent") assert result == [] class TestSearch: """Test suite for search method.""" async def test_search( self, list_use_case: ListPostsUseCase, mock_post_repository: MagicMock, sample_posts: list[Post], ) -> None: """Test searching posts.""" mock_post_repository.search = AsyncMock(return_value=sample_posts) result = await list_use_case.search("test query") assert len(result) == 3 mock_post_repository.search.assert_called_once_with("test query", limit=None, offset=None) async def test_search_with_pagination( self, list_use_case: ListPostsUseCase, mock_post_repository: MagicMock, ) -> None: """Test searching posts with pagination.""" mock_post_repository.search = AsyncMock(return_value=[]) await list_use_case.search("query", limit=10, offset=5) mock_post_repository.search.assert_called_once_with("query", limit=10, offset=5) async def test_search_no_results( self, list_use_case: ListPostsUseCase, mock_post_repository: MagicMock, ) -> None: """Test searching with no matches.""" mock_post_repository.search = AsyncMock(return_value=[]) result = await list_use_case.search("xyz123") assert result == []