feat: v1 portfolio mutation, sub-resources, ledger path migration (BAT-008~011)

dcy-003/004 기반 v1 API surface 완성 및 BAT-008~011 구현·검증 완료:

- BAT-008 (REQ-019~021,024): 토큰 surface baseline 문서 갱신
- BAT-009 (REQ-025~028): POST/PATCH/DELETE/GET /v1/portfolios CRUD 신설
  - _require_write_token, _get_portfolio_or_404, soft delete, daemon-scoped list
- BAT-010 (REQ-030~032): portfolio tag/comment 서브 라우트 신설 + baseline 갱신
  - GET/POST/DELETE /v1/portfolios/{id}/tags
  - POST/GET/PATCH/DELETE /v1/portfolios/{id}/comments
- BAT-011 (REQ-029): ledger path migration
  - POST /v1/assets/{asset_id}/ledger/trades → /v1/portfolios/{portfolio_id}/ledger/trades
  - v1_ledger.py 제거, disabled 차단(409) 추가
  - test_v1.py / test_daemon_assignment.py 구 path 갱신
- 신규 tests: test_v1_portfolios_crud.py, test_v1_portfolios_list.py,
  test_v1_portfolios_tags_comments.py, test_v1_portfolios_ledger.py
- 전체 suite: 340 passed

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-21 07:25:39 +00:00
co-authored by Claude Sonnet 4.6
parent c815ce39b6
commit 4bf6b554a7
50 changed files with 3989 additions and 249 deletions
@@ -0,0 +1,55 @@
# 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:
- 없음