Files
blog.pyaqa.ru/tests/unit/application/test_create_comment.py
Sergey Vanyushkin 7ff3fa0992
All checks were successful
ci/woodpecker/pr/pipeline Pipeline was successful
feat: add comments feature with nested replies and recursive rendering
Implement full comments system: domain entities (Comment, CommentLike),
value objects (CommentContent), use cases (CRUD, like toggle), SQLAlchemy
repository, API v1 endpoints, web UI with comment form and nested replies,
i18n translations (EN/RU/FR/DE), and E2E tests.

Fix nested reply (reply-to-reply) not displaying — the flat reply_comments
dict was only queried for top-level comment IDs, so deeply nested replies
were saved to DB (incrementing comment count) but never rendered. Switch
to a recursive Jinja2 macro that renders any nesting depth.
2026-05-11 15:34:20 +03:00

129 lines
3.9 KiB
Python

"""Tests for CreateCommentUseCase.
This module tests comment creation use case covering top-level comments,
replies to existing comments, and post-not-found scenarios.
"""
from unittest.mock import AsyncMock, Mock
from uuid import uuid4
import pytest
from app.application.use_cases.create_comment import CreateCommentUseCase
from app.domain.entities import Post
from app.domain.exceptions import NotFoundException
@pytest.fixture
def test_post() -> Post:
"""Create a test post for comment tests."""
return Post.create(
title_str="Commentable Post",
content_str="This post will receive comments. Enough length here.",
author_id="user-123",
tags=["test"],
)
class TestCreateCommentUseCase:
"""Tests for CreateCommentUseCase.
Covers TC-UNIT-832 through TC-UNIT-834.
"""
@pytest.mark.asyncio
async def test_create_comment_on_post(
self,
mock_post_repository: Mock,
mock_transaction_manager: Mock,
test_post: Post,
) -> None:
"""Test creating a top-level comment on a post.
TC-UNIT-832: Positive — create top-level comment.
"""
mock_comment_repository = AsyncMock()
mock_post_repository.get_by_id = AsyncMock(return_value=test_post)
use_case = CreateCommentUseCase(
post_repo=mock_post_repository,
comment_repo=mock_comment_repository,
tx_manager=mock_transaction_manager,
)
result = await use_case.execute(
post_id=test_post.id,
author_id="user-456",
content="Great post! Thanks for sharing.",
)
assert result.post_id == test_post.id
assert result.author_id == "user-456"
assert result.content == "Great post! Thanks for sharing."
assert result.parent_id is None
assert result.like_count == 0
mock_comment_repository.add.assert_called_once()
mock_transaction_manager.commit.assert_called_once()
@pytest.mark.asyncio
async def test_create_comment_reply(
self,
mock_post_repository: Mock,
mock_transaction_manager: Mock,
test_post: Post,
) -> None:
"""Test creating a reply to an existing comment.
TC-UNIT-833: Positive — reply to comment with parent_id.
"""
mock_comment_repository = AsyncMock()
mock_post_repository.get_by_id = AsyncMock(return_value=test_post)
parent_id = uuid4()
use_case = CreateCommentUseCase(
post_repo=mock_post_repository,
comment_repo=mock_comment_repository,
tx_manager=mock_transaction_manager,
)
result = await use_case.execute(
post_id=test_post.id,
author_id="user-456",
content="This is a reply.",
parent_id=parent_id,
)
assert result.parent_id == parent_id
assert result.post_id == test_post.id
mock_comment_repository.add.assert_called_once()
mock_transaction_manager.commit.assert_called_once()
@pytest.mark.asyncio
async def test_create_comment_post_not_found(
self,
mock_post_repository: Mock,
mock_transaction_manager: Mock,
) -> None:
"""Test creating a comment on a non-existent post.
TC-UNIT-834: Negative — post not found.
"""
mock_comment_repository = AsyncMock()
mock_post_repository.get_by_id = AsyncMock(return_value=None)
use_case = CreateCommentUseCase(
post_repo=mock_post_repository,
comment_repo=mock_comment_repository,
tx_manager=mock_transaction_manager,
)
with pytest.raises(NotFoundException):
await use_case.execute(
post_id=uuid4(),
author_id="user-456",
content="Comment on missing post.",
)
mock_comment_repository.add.assert_not_called()
mock_transaction_manager.commit.assert_not_called()