Files
blog.pyaqa.ru/tests/FEATURE_LIKES.md
Sergey Vanyushkin 3cf6c94da2 feat: add like/unlike toggle on blog posts with per-user tracking
- PostLike domain entity (post_id, liked_by) with BaseEntity integration
- Post entity: add like_count field (default 0) and to_dict serialization
- PostRepository interface: add get_like, add_like, remove_like methods
- TogglePostLikeUseCase: toggle logic (like → unlike, unlike → like)
- PostResponseDTO/PostResponseSchema: add like_count field
- PostLikeORM model with FK to posts and cascade delete
- SQLAlchemyPostRepository: implement like query/add/remove with ORM mapping
- DI provider registration for TogglePostLikeUseCase
- API endpoint POST /api/v1/posts/{id}/like (auth required)
- Unit tests: PostLike entity, Post.like_count, TogglePostLikeUseCase (7 tests)
- API tests: POST /api/v1/posts/{id}/like (4 tests)
- Test model files: FEATURE_LIKES.md, TEST_MODEL.md updated
2026-05-10 18:24:09 +03:00

7.1 KiB
Raw Blame History

Test Model: Post Likes

Feature: Like/unlike toggle on blog posts with per-user tracking, session-based guest identification, and anti-bot protection via JS-only POST.

Unit Test Cases

TogglePostLikeUseCase

TC-UNIT-822: TogglePostLikeUseCase — Like first time

  • Type: Positive
  • Layer: Unit
  • File: unit/application/test_toggle_like.py::TestTogglePostLikeUseCase::test_like_post_first_time
  • Preconditions: Post exists, no existing like for this user
  • Steps: Execute toggle with valid post_id and liked_by
  • Expected:
    • add_like called once
    • remove_like not called
    • Response DTO has like_count=1
  • Last Verified: 2026-05-10

TC-UNIT-823: TogglePostLikeUseCase — Unlike (already liked)

  • Type: Positive
  • Layer: Unit
  • File: unit/application/test_toggle_like.py::TestTogglePostLikeUseCase::test_unlike_post_already_liked
  • Preconditions: Post exists, existing like found for this user
  • Steps: Execute toggle with same post_id and liked_by
  • Expected:
    • remove_like called once
    • add_like not called
    • Response DTO has like_count=0
  • Last Verified: 2026-05-10

TC-UNIT-824: TogglePostLikeUseCase — Post not found

  • Type: Negative
  • Layer: Unit
  • File: unit/application/test_toggle_like.py::TestTogglePostLikeUseCase::test_like_post_not_found
  • Preconditions: Repository returns None for post lookup
  • Steps: Execute toggle with non-existent post_id
  • Expected: NotFoundException raised
  • Last Verified: 2026-05-10

TC-UNIT-825: TogglePostLikeUseCase — Guest via device_id

  • Type: Positive
  • Layer: Unit
  • File: unit/application/test_toggle_like.py::TestTogglePostLikeUseCase::test_like_as_guest_with_device_id
  • Preconditions: Post exists, no existing like, liked_by set to device_id
  • Steps: Execute toggle with device_id instead of user_id
  • Expected:
    • Like created with liked_by == device_id
    • Response DTO has like_count=1
  • Last Verified: 2026-05-10

TC-UNIT-828: TogglePostLikeUseCase — Identity isolation

  • Type: Positive
  • Layer: Unit
  • File: unit/application/test_toggle_like.py::TestTogglePostLikeUseCase::test_two_users_can_both_like
  • Preconditions: Post exists, user1 likes first
  • Steps: User2 toggles like on same post
  • Expected:
    • User2's like added (separate identity)
    • like_count=2
  • Last Verified: 2026-05-10

Domain Entities

TC-UNIT-826: PostLike entity — valid creation

  • Type: Positive
  • Layer: Unit
  • File: unit/domain/test_like_entity.py::TestPostLikeEntity::test_post_like_creation
  • Preconditions: Valid post_id and liked_by values
  • Steps: Create PostLike instance
  • Expected:
    • post_id matches input
    • liked_by matches input
    • id is a valid UUID
    • created_at is set
  • Last Verified: 2026-05-10

TC-UNIT-827: Post entity — like_count default 0

  • Type: Positive
  • Layer: Unit
  • File: unit/domain/test_post_entity.py::TestPostEntity::test_like_count_defaults_to_zero
  • Preconditions:
  • Steps: Create Post via Post.create()
  • Expected: post.like_count == 0
  • Last Verified: 2026-05-10

API Test Cases

TC-API-114: Like Post — authenticated toggle on

  • Type: Positive
  • Layer: API
  • File: api/test_likes.py::TestLikePost::test_like_post_authenticated
  • Preconditions: Post exists, user authenticated
  • Steps: POST /api/v1/posts/{id}/like with auth header
  • Expected:
    • Status 200
    • like_count == 1
  • Last Verified: 2026-05-10

TC-API-115: Like Post — authenticated toggle off

  • Type: Positive
  • Layer: API
  • File: api/test_likes.py::TestLikePost::test_unlike_post_authenticated
  • Preconditions: Post exists, user already liked it
  • Steps: POST /api/v1/posts/{id}/like second time
  • Expected:
    • Status 200
    • like_count == 0
  • Last Verified: 2026-05-10

TC-API-116: Like Post — guest via device_id

  • Type: Positive
  • Layer: API
  • File: api/test_likes.py::TestLikePost::test_like_post_as_guest
  • Preconditions: Post exists, guest token used
  • Steps: POST /api/v1/posts/{id}/like with guest token
  • Expected:
    • Status 200
    • like_count == 1
  • Last Verified: 2026-05-10

TC-API-117: Like Post — not found

  • Type: Negative
  • Layer: API
  • File: api/test_likes.py::TestLikePost::test_like_post_not_found
  • Preconditions: Post does not exist
  • Steps: POST /api/v1/posts/{id}/like with auth header
  • Expected:
    • Status 404
  • Last Verified: 2026-05-10

Web Test Cases

TC-WEB-001: Like count on post list

  • Type: Positive
  • Layer: Web
  • File: tests/web/test_likes.py::TestLikeDisplay::test_like_count_on_homepage
  • Preconditions: Posts exist with known like counts
  • Steps: GET /web/
  • Expected:
    • Each post card shows like count
    • data-testid="like-count-{post.id}" present
  • Last Verified: 2026-05-10

TC-WEB-002: Like button on post detail

  • Type: Positive
  • Layer: Web
  • File: tests/web/test_likes.py::TestLikeDisplay::test_like_button_on_detail
  • Preconditions: Post exists
  • Steps: GET /web/posts/{slug}
  • Expected:
    • Like count displayed
    • data-testid="like-button" present
  • Last Verified: 2026-05-10

TC-WEB-003: Like toggle via POST

  • Type: Positive
  • Layer: Web
  • File: tests/web/test_likes.py::TestLikeToggle::test_like_toggle_via_web
  • Preconditions: Post exists
  • Steps: POST /web/posts/{slug}/like redirects back
  • Expected:
    • 303 redirect to post detail
    • Like count incremented
  • Last Verified: 2026-05-10

E2E Test Cases

TC-E2E-106: Like/Unlike flow via web UI

  • Type: Positive
  • Layer: E2E
  • File: tests/e2e/test_likes.py::test_like_unlike_flow
  • Scenario: Create post → like → verify count → unlike → verify count
  • Expected: Count toggles correctly
  • Last Verified: 2026-05-10

TC-E2E-107: Separate users can both like

  • Type: Positive
  • Layer: E2E
  • File: tests/e2e/test_likes.py::test_multiple_users_can_like
  • Scenario: User1 likes → count=1 → User2 likes → count=2
  • Expected: Count increments per user
  • Last Verified: 2026-05-10

TC-E2E-108: Guest like with different sessions

  • Type: Positive
  • Layer: E2E
  • File: tests/e2e/test_likes.py::test_guest_like_different_sessions
  • Scenario: Guest1 likes → count=1 → different device context
  • Expected: Different guests count separately
  • Last Verified: 2026-05-10

Coverage Summary

Component Cases Status
TogglePostLikeUseCase 5 Verified
Domain Entities (PostLike, Post) 2 Verified
API Endpoints 4 Verified
Web Display 3 Planned
E2E Flows 3 Planned

Gaps (Not Yet Covered)

  • Web tests (TC-WEB-001003) — test infrastructure pending
  • E2E tests (TC-E2E-106108) — test infrastructure pending
  • Full device_id middleware for guest like support