feat: REQ-001~018 Implemented 전환, REQ-010 GET /v1/assets admin catalog 재정의, REL-001 draft
- confirm-req-implemented: REQ-001~018 (17건, REQ-013 제외) Approved → Implemented - BAT-001(REQ-009/011/014), BAT-002(REQ-010/012/015), BAT-003(REQ-001/002/003/006) - BAT-004(REQ-004/005), BAT-005(REQ-007/008), BAT-006(REQ-016/017/018) - CHG-20260521-01 entry 각 REQ에 추가; SRS index Recent Change Log 갱신 - REQ-010 CHG-20260521-02: GET /v1/assets admin catalog 전용 재정의 - 기존 token_type 분기 portfolio 조회 코드 제거, admin permission + list_assets() 호출로 대체 - AssetCatalogListResponse 스키마 추가 (items/total) - REQ-010 Status: Implemented → Approved (재검증 필요) - 테스트 343건 전체 통과: service/user token 403, admin token 200 + catalog schema 검증 - migration 보강: portfolios_asset_migration market 컬럼 부재 fallback, portfolio_tags 빈 테이블 early-return, service_token index fix autocommit_block 정리 - docs/releases/rel-001: REL-001 draft 추가 (BAT-001~011 포함) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -60,10 +60,12 @@ def _ledger_trade_payload() -> dict:
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# REQ-006: GET /v1/assets daemon-scoped + NULL fail-closed
|
||||
# REQ-010 CHG-20260521-02: GET /v1/assets admin catalog — service token rejected
|
||||
# (daemon-scoped portfolio list moved to GET /v1/portfolios)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def test_assets_403_when_token_has_no_daemon_id(monkeypatch) -> None:
|
||||
"""GET /v1/assets requires admin — service token with no daemon_id gets 403 (Insufficient permission)."""
|
||||
client = _app_with_db(monkeypatch)
|
||||
principal = _principal(daemon_id=None)
|
||||
mock_repo = MagicMock()
|
||||
@@ -73,34 +75,19 @@ def test_assets_403_when_token_has_no_daemon_id(monkeypatch) -> None:
|
||||
resp = client.get("/v1/assets", headers=_svc_header())
|
||||
|
||||
assert resp.status_code == 403
|
||||
assert "daemon_id" in resp.json()["detail"]
|
||||
|
||||
|
||||
def test_assets_200_with_daemon_scoped_filter(monkeypatch) -> None:
|
||||
"""GET /v1/assets requires admin — service token with daemon_id is also rejected."""
|
||||
client = _app_with_db(monkeypatch)
|
||||
principal = _principal(daemon_id="d-1")
|
||||
portfolio = Portfolio(
|
||||
portfolio_id="p1", broker="kis", status="enabled", name="T",
|
||||
symbol="005930", cash_balance=0.0, asset_quantity=0.0,
|
||||
price=0.0, profit_buffer=0.0, owner_user_id="user-1", managed_by_daemon_id="d-1",
|
||||
)
|
||||
mock_repo = MagicMock()
|
||||
mock_repo.get_token_principal.return_value = principal
|
||||
mock_repo.list_portfolios_with_strategies.return_value = [portfolio]
|
||||
mock_repo.get_strategy_set.return_value = None
|
||||
|
||||
with patch("app.core.auth.get_portfolio_repository_from_settings", return_value=mock_repo), \
|
||||
patch("app.api.v1_assets.get_portfolio_repository_from_settings", return_value=mock_repo):
|
||||
with patch("app.core.auth.get_portfolio_repository_from_settings", return_value=mock_repo):
|
||||
resp = client.get("/v1/assets", headers=_svc_header())
|
||||
|
||||
assert resp.status_code == 200
|
||||
assert resp.json()["total"] == 1
|
||||
mock_repo.list_portfolios_with_strategies.assert_called_once_with(
|
||||
owner_user_id="user-1",
|
||||
include_disabled=False,
|
||||
managed_by_daemon_id="d-1",
|
||||
market=None,
|
||||
)
|
||||
assert resp.status_code == 403
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user