All checks were successful
ci/woodpecker/pr/pipeline Pipeline was successful
- Write 3 e2e tests (TC-E2E-106-108): like/unlike flow, multi-user like, guest redirect - Add get_like_count() and click_like() to PostDetailPage object - Fix _map_to_dto in 5 use cases (create, get, list, publish, update) to include like_count - Fix pre-existing mypy issues in page object (evaluate returns Any) - Update FEATURE_LIKES.md with verified E2E status
209 lines
7.0 KiB
Markdown
209 lines
7.0 KiB
Markdown
# 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
|