"""Tests for PublishPostUseCase. Tests publishing and unpublishing posts with authorization. """ 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.publish_post import PublishPostUseCase from app.domain.entities import Post from app.domain.exceptions import ForbiddenException, NotFoundException 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.""" tx = MagicMock(spec=TransactionManager) tx.commit = AsyncMock() return tx @pytest.fixture def publish_use_case( mock_post_repository: MagicMock, mock_transaction_manager: MagicMock, ) -> PublishPostUseCase: """Create publish use case with mocked dependencies.""" return PublishPostUseCase(mock_post_repository, mock_transaction_manager) @pytest.fixture def sample_post() -> Post: """Create a sample unpublished post.""" return Post( id=uuid4(), title=Title("Test Post"), content=Content("Test content"), slug=Slug("test-post"), author_id="author-123", published=False, tags=["test"], created_at=datetime.now(), updated_at=datetime.now(), ) @pytest.fixture def published_post() -> Post: """Create a sample published post.""" post = Post( id=uuid4(), title=Title("Published Post"), content=Content("Published content"), slug=Slug("published-post"), author_id="author-123", published=True, tags=["published"], created_at=datetime.now(), updated_at=datetime.now(), ) return post class TestPublishPost: """Test suite for publish method.""" async def test_publish_success( self, publish_use_case: PublishPostUseCase, mock_post_repository: MagicMock, mock_transaction_manager: MagicMock, sample_post: Post, ) -> None: """Test successful post publishing.""" mock_post_repository.get_by_id = AsyncMock(return_value=sample_post) mock_post_repository.update = AsyncMock() result = await publish_use_case.publish(sample_post.id, sample_post.author_id) assert isinstance(result, PostResponseDTO) assert result.id == sample_post.id assert result.published is True mock_post_repository.update.assert_called_once() mock_transaction_manager.commit.assert_called_once() async def test_publish_not_found( self, publish_use_case: PublishPostUseCase, mock_post_repository: MagicMock, ) -> None: """Test publishing non-existent post raises NotFoundException.""" mock_post_repository.get_by_id = AsyncMock(return_value=None) with pytest.raises(NotFoundException) as exc_info: await publish_use_case.publish(uuid4(), "author-123") assert "not found" in str(exc_info.value).lower() async def test_publish_forbidden( self, publish_use_case: PublishPostUseCase, mock_post_repository: MagicMock, sample_post: Post, ) -> None: """Test publishing other user's post raises ForbiddenException.""" mock_post_repository.get_by_id = AsyncMock(return_value=sample_post) with pytest.raises(ForbiddenException) as exc_info: await publish_use_case.publish(sample_post.id, "different-author") assert "own posts" in str(exc_info.value).lower() class TestUnpublishPost: """Test suite for unpublish method.""" async def test_unpublish_success( self, publish_use_case: PublishPostUseCase, mock_post_repository: MagicMock, mock_transaction_manager: MagicMock, published_post: Post, ) -> None: """Test successful post unpublishing.""" mock_post_repository.get_by_id = AsyncMock(return_value=published_post) mock_post_repository.update = AsyncMock() result = await publish_use_case.unpublish(published_post.id, published_post.author_id) assert isinstance(result, PostResponseDTO) assert result.id == published_post.id assert result.published is False mock_post_repository.update.assert_called_once() mock_transaction_manager.commit.assert_called_once() async def test_unpublish_not_found( self, publish_use_case: PublishPostUseCase, mock_post_repository: MagicMock, ) -> None: """Test unpublishing non-existent post raises NotFoundException.""" mock_post_repository.get_by_id = AsyncMock(return_value=None) with pytest.raises(NotFoundException) as exc_info: await publish_use_case.unpublish(uuid4(), "author-123") assert "not found" in str(exc_info.value).lower() async def test_unpublish_forbidden( self, publish_use_case: PublishPostUseCase, mock_post_repository: MagicMock, published_post: Post, ) -> None: """Test unpublishing other user's post raises ForbiddenException.""" mock_post_repository.get_by_id = AsyncMock(return_value=published_post) with pytest.raises(ForbiddenException) as exc_info: await publish_use_case.unpublish(published_post.id, "different-author") assert "own posts" in str(exc_info.value).lower()