# 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 (0→1→0) - **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 redirect on like - **Type:** Positive - **Layer:** E2E - **File:** `tests/e2e/test_likes.py::test_guest_redirect_on_like` - **Scenario:** Guest opens published post → clicks like → redirected to login - **Expected:** 401 redirects to `/auth/dev-login` - **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 | ✅ Verified | ## Gaps (Not Yet Covered) - [ ] Web tests (TC-WEB-001–003) — test infrastructure pending - [ ] Full device_id middleware for guest like support