# BAT-010 Verification - Status: verified - Batch: BAT-010 ## Acceptance Mapping ### REQ-030 (portfolio tag routes) - AC-1 (admin tag → POST 200): `test_post_tag_admin_tag_200` PASSED — `repo.append_portfolio_tag_id` called - AC-2 (own tag → POST 200): `test_post_tag_own_tag_200` PASSED - AC-3 (타 user non-admin tag → 403): `test_post_tag_other_user_non_admin_tag_403` PASSED - AC-4 (inactive tag → 403): `test_post_tag_inactive_tag_403` PASSED - AC-5 (admin + other portfolio + admin tag → 200): `test_post_tag_admin_other_portfolio_admin_tag_200` PASSED - AC-6 (other portfolio → 403): `test_post_tag_other_portfolio_403` PASSED - AC-7 (service/client → 403): `test_post_tag_service_token_403`, `test_post_tag_client_token_403` PASSED - AC-8 (disabled + POST/DELETE → 409): `test_post_tag_disabled_portfolio_409`, `test_delete_tag_disabled_portfolio_409` PASSED - AC-9 (DELETE own portfolio → 200): `test_delete_tag_own_portfolio_200` PASSED — `repo.remove_portfolio_tag_id` called - AC-10 (GET tags → 200 + list): `test_get_portfolio_tags_200`, `test_get_portfolio_tags_empty_200` PASSED - AC-11 (nonexistent portfolio → 404): `test_get_tags_nonexistent_portfolio_404` PASSED ### REQ-031 (portfolio comment routes) - AC-1 (POST → 201): `test_post_comment_201` PASSED — comment_id, portfolio_id, content verified - AC-2 (GET → 200 + list): `test_get_comments_200` PASSED — total=2 verified - AC-3 (PATCH → 200 + updated): `test_patch_comment_200` PASSED — `repo.update_comment` called with correct args - AC-4 (DELETE → 204): `test_delete_comment_204` PASSED — `repo.delete_comment` called - AC-5 (other portfolio → 403): `test_post_comment_other_portfolio_403` PASSED - AC-6 (service/client → 403): `test_post_comment_service_token_403`, `test_post_comment_client_token_403`, `test_get_comments_service_token_403` PASSED - AC-7 (disabled + POST/PATCH/DELETE → 409): `test_post_comment_disabled_409`, `test_patch_comment_disabled_409`, `test_delete_comment_disabled_409` PASSED - AC-8 (disabled + GET → 200): `test_get_comments_disabled_portfolio_200` PASSED - AC-9 (admin + other portfolio → 201/200): `test_post_comment_admin_other_portfolio_201`, `test_get_comments_admin_other_portfolio_200` PASSED - AC-10 (no auth → 401): `test_post_comment_no_auth_401` PASSED - AC-11 (nonexistent comment/portfolio → 404): `test_patch_comment_nonexistent_404`, `test_delete_comment_nonexistent_404`, `test_post_comment_nonexistent_portfolio_404` PASSED ### REQ-032 (baseline docs) - AC-1 (`docs/interface-contract.md` 갱신): v1 Portfolio API 섹션 신규 추가; GET/POST/DELETE /v1/portfolios/{id}/tags; POST/GET/PATCH/DELETE /v1/portfolios/{id}/comments; ledger "pending REQ-029" 마킹 - AC-2 (`docs/runtime-flows.md` 갱신): portfolio mutation flow, GET branching flow, soft delete + dependent data preservation flow, comment CRUD flow 추가 - AC-3 (`docs/data-model.md` 갱신): Portfolio.status='disabled' 의미 + 의존 데이터 보존 정책 추가 - AC-4 (`docs/project-structure.md` 갱신): v1_portfolios.py 책임 갱신 + v1.py schema 목록 갱신 - AC-5 (코드-문서 정합성): interface-contract 명세가 구현 라우트와 일치 확인 (7개 신규 라우트) - AC-6 (ledger path "pending REQ-029" 마킹): interface-contract.md에 "pending REQ-029" 섹션 추가 ## Evidence - Test: - `tests/test_v1_portfolios_tags_comments.py`: 32/32 PASSED (3.27s) - Full suite: 330/330 PASSED (14.11s, 2 deprecation warnings — 무관) - No regressions in existing tests (BAT-004 tag tests, BAT-009 portfolio CRUD/list tests) - Manual: - `app/api/v1_portfolios.py` 코드 검토: disabled 409 check는 ownership 검증 직후, mutation 직전 위치 확인; `_require_write_token` service/client 차단 확인; GET routes는 disabled 허용 확인 - Logs: - Full suite output: 330 passed, 2 warnings in 14.11s ## Result - Ready for Release: true - Blocking Issues: - 없음