"""Tests for application use cases.""" from unittest.mock import AsyncMock, Mock from uuid import uuid4 import pytest from app.application.dtos.post import CreatePostDTO, UpdatePostDTO from app.application.use_cases import ( CreatePostUseCase, DeletePostUseCase, GetPostUseCase, ListPostsUseCase, PublishPostUseCase, UpdatePostUseCase, ) from app.domain.entities import Post from app.domain.exceptions import ( AlreadyExistsException, ForbiddenException, NotFoundException, ) @pytest.fixture def test_post() -> Post: """Create a test post.""" return Post.create( title_str="Test Post", content_str="This is test content with enough characters", author_id="user-123", tags=["test"], ) class TestCreatePostUseCase: @pytest.mark.asyncio async def test_create_post_success( self, mock_post_repository: Mock, mock_transaction_manager: Mock, ) -> None: """Test successful post creation.""" # Setup mock_post_repository.slug_exists = AsyncMock(return_value=False) mock_post_repository.add = AsyncMock() use_case = CreatePostUseCase(mock_post_repository, mock_transaction_manager) dto = CreatePostDTO( title="New Post", content="Content with enough characters", author_id="user-123", ) # Execute result = await use_case.execute(dto) # Assert assert result.title == "New Post" assert result.author_id == "user-123" mock_post_repository.add.assert_called_once() mock_transaction_manager.commit.assert_called_once() @pytest.mark.asyncio async def test_create_post_slug_exists( self, mock_post_repository: Mock, mock_transaction_manager: Mock, ) -> None: """Test post creation with existing slug.""" # Setup mock_post_repository.slug_exists = AsyncMock(return_value=True) use_case = CreatePostUseCase(mock_post_repository, mock_transaction_manager) dto = CreatePostDTO( title="Existing Post", content="Content with enough characters", author_id="user-123", ) # Execute & Assert with pytest.raises(AlreadyExistsException): await use_case.execute(dto) mock_post_repository.add.assert_not_called() mock_transaction_manager.commit.assert_not_called() class TestGetPostUseCase: @pytest.mark.asyncio async def test_get_post_by_id_success( self, mock_post_repository: Mock, mock_transaction_manager: Mock, test_post: Post, ) -> None: """Test successful get post by ID.""" # Setup mock_post_repository.get_by_id = AsyncMock(return_value=test_post) use_case = GetPostUseCase(mock_post_repository, mock_transaction_manager) # Execute result = await use_case.by_id(test_post.id) # Assert assert result.id == test_post.id assert result.title == test_post.title.value mock_post_repository.get_by_id.assert_called_once_with(test_post.id) @pytest.mark.asyncio async def test_get_post_by_id_not_found( self, mock_post_repository: Mock, mock_transaction_manager: Mock, ) -> None: """Test get post by ID when not found.""" # Setup mock_post_repository.get_by_id = AsyncMock(return_value=None) use_case = GetPostUseCase(mock_post_repository, mock_transaction_manager) post_id = uuid4() # Execute & Assert with pytest.raises(NotFoundException): await use_case.by_id(post_id) class TestUpdatePostUseCase: @pytest.mark.asyncio async def test_update_post_success( self, mock_post_repository: Mock, mock_transaction_manager: Mock, test_post: Post, ) -> None: """Test successful post update.""" # Setup mock_post_repository.get_by_id = AsyncMock(return_value=test_post) mock_post_repository.update = AsyncMock() use_case = UpdatePostUseCase(mock_post_repository, mock_transaction_manager) dto = UpdatePostDTO(title="Updated Title") # Execute result = await use_case.execute(test_post.id, dto, "user-123") # Assert assert result.title == "Updated Title" mock_post_repository.update.assert_called_once() mock_transaction_manager.commit.assert_called_once() @pytest.mark.asyncio async def test_update_post_not_found( self, mock_post_repository: Mock, mock_transaction_manager: Mock, ) -> None: """Test update post when not found.""" # Setup mock_post_repository.get_by_id = AsyncMock(return_value=None) use_case = UpdatePostUseCase(mock_post_repository, mock_transaction_manager) dto = UpdatePostDTO(title="Updated Title") # Execute & Assert with pytest.raises(NotFoundException): await use_case.execute(uuid4(), dto, "user-123") @pytest.mark.asyncio async def test_update_post_forbidden( self, mock_post_repository: Mock, mock_transaction_manager: Mock, test_post: Post, ) -> None: """Test update post by different user.""" # Setup mock_post_repository.get_by_id = AsyncMock(return_value=test_post) use_case = UpdatePostUseCase(mock_post_repository, mock_transaction_manager) dto = UpdatePostDTO(title="Updated Title") # Execute & Assert with pytest.raises(ForbiddenException): await use_case.execute(test_post.id, dto, "other-user") class TestDeletePostUseCase: @pytest.mark.asyncio async def test_delete_post_success( self, mock_post_repository: Mock, mock_transaction_manager: Mock, test_post: Post, ) -> None: """Test successful post deletion.""" # Setup mock_post_repository.get_by_id = AsyncMock(return_value=test_post) mock_post_repository.delete = AsyncMock() use_case = DeletePostUseCase(mock_post_repository, mock_transaction_manager) # Execute await use_case.execute(test_post.id, "user-123") # Assert mock_post_repository.delete.assert_called_once_with(test_post.id) mock_transaction_manager.commit.assert_called_once() @pytest.mark.asyncio async def test_delete_post_forbidden( self, mock_post_repository: Mock, mock_transaction_manager: Mock, test_post: Post, ) -> None: """Test delete post by different user.""" # Setup mock_post_repository.get_by_id = AsyncMock(return_value=test_post) use_case = DeletePostUseCase(mock_post_repository, mock_transaction_manager) # Execute & Assert with pytest.raises(ForbiddenException): await use_case.execute(test_post.id, "other-user") class TestPublishPostUseCase: @pytest.mark.asyncio async def test_publish_post_success( self, mock_post_repository: Mock, mock_transaction_manager: Mock, test_post: Post, ) -> None: """Test successful post publish.""" # Setup mock_post_repository.get_by_id = AsyncMock(return_value=test_post) mock_post_repository.update = AsyncMock() use_case = PublishPostUseCase(mock_post_repository, mock_transaction_manager) # Execute result = await use_case.publish(test_post.id, "user-123") # Assert assert result.published is True mock_post_repository.update.assert_called_once() mock_transaction_manager.commit.assert_called_once() class TestListPostsUseCase: @pytest.mark.asyncio async def test_list_all_posts( self, mock_post_repository: Mock, mock_transaction_manager: Mock, test_post: Post, ) -> None: """Test listing all posts.""" # Setup mock_post_repository.get_all = AsyncMock(return_value=[test_post]) use_case = ListPostsUseCase(mock_post_repository, mock_transaction_manager) # Execute results = await use_case.all_posts() # Assert assert len(results) == 1 assert results[0].id == test_post.id mock_post_repository.get_all.assert_called_once()