7.4 KiB
7.4 KiB
BAT-014 Verification
- Status: confirmed
- Batch: BAT-014
Acceptance Mapping
REQ-025: POST /v1/portfolios
- AC-1 (admin + user_id → target owner):
tests/test_v1_portfolios_crud.py::test_post_admin_creates_for_target_userPASSED —owner_user_id == _OTHER_USER_ID. - AC-2 (session/env token → own user/fallback):
tests/test_v1_portfolios_crud.py::test_post_user_creates_for_selfPASSED. - AC-3 (service token → 403):
tests/test_v1_portfolios_crud.py::test_post_service_token_returns_403PASSED. - AC-5 (inactive/missing asset → 422):
tests/test_v1_portfolios_crud.py::test_post_inactive_asset_returns_422,tests/test_v1_portfolios_crud.py::test_post_missing_asset_returns_422PASSED. - AC-6 (no token → 401):
tests/test_v1_portfolios_crud.py::test_post_no_bearer_returns_401PASSED. - AC-7 (non-admin body user_id ignored):
tests/test_v1_portfolios_crud.py::test_post_non_admin_user_id_ignoredPASSED. - AC-8 (POST 응답
symbol== joinedassets.symbol):tests/test_v1_portfolios_crud.py::test_post_admin_creates_for_target_userPASSED — response bodysymbol == "005930".
REQ-026: PATCH /v1/portfolios/{portfolio_id}
- AC-1 (admin → any portfolio):
tests/test_v1_portfolios_crud.py::test_patch_admin_updates_any_portfolioPASSED —broker == "ebest". - AC-2 (session token + own portfolio → 200):
tests/test_v1_portfolios_crud.py::test_patch_user_updates_own_portfolioPASSED. - AC-3 (session token + other portfolio → 403):
tests/test_v1_portfolios_crud.py::test_patch_user_other_portfolio_returns_403PASSED. - AC-4 (service token → 403):
tests/test_v1_portfolios_crud.py::test_patch_service_token_returns_403PASSED. - AC-6 (no token → 401):
tests/test_v1_portfolios_crud.py::test_patch_no_bearer_returns_401PASSED. - AC-7 (non-existent → 404):
tests/test_v1_portfolios_crud.py::test_patch_nonexistent_portfolio_returns_404PASSED. - AC-8 (disabled + non-status field → 409):
tests/test_v1_portfolios_crud.py::test_patch_disabled_portfolio_non_status_returns_409PASSED. - AC-9 (disabled + status=enabled → 200):
tests/test_v1_portfolios_crud.py::test_patch_disabled_portfolio_status_restore_returns_200PASSED. - AC-10 (PATCH 응답
symbol== joinedassets.symbol):tests/test_v1_portfolios_crud.py::test_patch_admin_updates_any_portfolio,tests/test_v1_portfolios_crud.py::test_patch_disabled_portfolio_status_restore_returns_200PASSED — both assertsymbol == "005930".
REQ-027: DELETE /v1/portfolios/{portfolio_id}
- AC-1 (session + own → 200, disabled, daemon_id NULL):
tests/test_v1_portfolios_crud.py::test_delete_user_own_active_portfolio_soft_deletesPASSED. - AC-3 (admin + other user → 200):
tests/test_v1_portfolios_crud.py::test_delete_admin_other_user_portfolioPASSED. - AC-4 (session + other → 403):
tests/test_v1_portfolios_crud.py::test_delete_user_other_portfolio_returns_403PASSED. - AC-5 (service token → 403):
tests/test_v1_portfolios_crud.py::test_delete_service_token_returns_403PASSED. - AC-7 (no token → 401):
tests/test_v1_portfolios_crud.py::test_delete_no_bearer_returns_401PASSED. - AC-8 (non-existent → 404):
tests/test_v1_portfolios_crud.py::test_delete_nonexistent_portfolio_returns_404PASSED. - AC-11 (DELETE 200 응답
symbol== joinedassets.symbol):tests/test_v1_portfolios_crud.py::test_delete_user_own_active_portfolio_soft_deletes,tests/test_v1_portfolios_crud.py::test_delete_admin_other_user_portfolioPASSED — both assertsymbol == "005930".
REQ-028: GET /v1/portfolios
- AC-1 (service + daemon_id → own daemon portfolios):
tests/test_v1_portfolios_list.py::test_get_service_token_daemon_bound_returns_own_portfoliosPASSED. - AC-2 (service no daemon → 403):
tests/test_v1_portfolios_list.py::test_get_service_token_no_daemon_returns_403PASSED. - AC-3 (session → own portfolios):
tests/test_v1_portfolios_list.py::test_get_user_token_returns_own_portfoliosPASSED. - AC-4 (admin no ?user_id → default branch):
tests/test_v1_portfolios_list.py::test_get_admin_default_returns_own_portfoliosPASSED. - AC-5 (admin + ?user_id → target):
tests/test_v1_portfolios_list.py::test_get_admin_with_user_id_returns_target_portfoliosPASSED. - AC-7 (disabled excluded by default):
tests/test_v1_portfolios_list.py::test_get_excludes_disabled_by_defaultPASSED. - AC-8 (
include_disabled=true→ all):tests/test_v1_portfolios_list.py::test_get_include_disabled_returns_allPASSED. - AC-9 (client token → 403):
tests/test_v1_portfolios_list.py::test_get_client_token_returns_403PASSED. - AC-10 (no token → 401):
tests/test_v1_portfolios_list.py::test_get_no_bearer_returns_401PASSED. - AC-11 (
asset_idresponse field):tests/test_v1_portfolios_list.py::test_get_response_has_asset_id_fieldPASSED. - AC-12 (
symbol== joinedassets.symbol):tests/test_v1_portfolios_list.py::test_get_service_token_daemon_bound_returns_own_portfolios,tests/test_v1_portfolios_list.py::test_get_response_has_asset_id_fieldPASSED — both assertsymbol == "005930".
REQ-032: portfolio v1 baseline 문서 갱신
- AC-1 (
docs/interface-contract.md에 v1 portfolio route entries 존재): 문서 유지 + POST/PATCH/DELETE/GET/v1/portfolios섹션 갱신 완료. - AC-6 (사람 리뷰 기준 baseline 충돌 없음): 이번 batch 범위에서
docs/interface-contract.md의 portfolio response contract를 코드와 정합화했다. - AC-7 (
symbol필드 + GET join 설명):docs/interface-contract.md에서 POST/PATCH/DELETE/GET/v1/portfolios응답 설명에symbol추가, GET 섹션에portfolios JOIN assets기반 직렬화 설명 추가 완료.
Evidence
- Test:
./.venv/bin/python -m pytest tests/test_v1_portfolios_crud.py tests/test_v1_portfolios_list.py -q→ 32 passed in 2.13s./.venv/bin/python -m pytest -q→ 371 passed in 11.00s
- Manual / code inspection:
app/repositories/postgres.py::_row_to_portfolio()는symbol=row[4]를Portfolio.symbol로 매핑한다.app/repositories/postgres.py::get()/create_portfolio()/update_portfolio()/soft_delete_portfolio()/list_portfolios()는_SELECT_PORTFOLIO기반 join 결과를 재사용한다.- 따라서 GET 뿐 아니라 POST/PATCH/DELETE 응답도 linked asset row의
assets.symbol을 유지한다.
- Logs:
- pytest 출력은
implementation.mdExecution Log 8~9 항목 참조.
- pytest 출력은
Architecture Impact Sync
- Profile이
batch-lite이고 planning.md 의 Design Gate는no다. - 실제 구현은 schema/API/test/docs contract augmentation에 한정되며 신규 쿼리 전략/모듈/마이그레이션이 없다.
app/repositories/postgres.py의 기존 join shape를 재사용했으므로 no architecture impact confirmed.- Baseline docs sync는 REQ-032 범위에 맞춰
docs/interface-contract.md갱신으로 충족했다.docs/project-structure.md/docs/runtime-flows.md/docs/data-model.md는 이번 변경으로 추가 수정이 필요한 구조 변화가 없다.
Result
- Ready for Release: true
- Blocking Issues:
- 없음 (본 batch 범위 내 AC evidence 확보)
- Follow-up:
- BAT-009 verification의 REQ-025/026/027/028 response-contract evidence는 BAT-014 기준으로 superseded 처리.
- BAT-010 verification의 REQ-032 interface-contract evidence는 BAT-014 기준으로 superseded 처리.
- 기존 422 deprecation warning(
HTTP_422_UNPROCESSABLE_ENTITY) 정리는 별도 housekeeping 성격의 후속 change로 분리 가능.