feat(bat-019): REQ-044 GET /v1/assets require_read + REQ-045 GET /v1/daemons caller-scope
REQ-044: list_catalog_assets 의존성을 require_admin → require_read로 완화. asset 쓰기(POST/PATCH/DELETE)는 require_admin 유지. REQ-045: GET /v1/daemons 신규 엔드포인트 — caller 소유 데몬만 반환(require_read). user_id=None(env 토큰)은 빈 목록 200. DaemonResponse/DaemonListResponse 스키마 추가. 기존 테스트(test_v1.py, test_asset_catalog.py, test_daemon_assignment.py, test_tags_master.py) REQ-044 권한 완화에 맞게 업데이트. test_v1_daemons.py 신규 추가(13 케이스). pytest 472 passed, 0 failed. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VHtM5Ws1HKCKQ4JGi6GRb5
This commit is contained in:
@@ -64,30 +64,38 @@ def _ledger_trade_payload() -> dict:
|
||||
# (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)."""
|
||||
def test_assets_200_when_token_has_no_daemon_id(monkeypatch) -> None:
|
||||
"""REQ-044: GET /v1/assets now requires only read — trade-write service token (no daemon_id) gets 200."""
|
||||
client = _app_with_db(monkeypatch)
|
||||
principal = _principal(daemon_id=None)
|
||||
mock_repo = MagicMock()
|
||||
mock_repo.get_token_principal.return_value = principal
|
||||
mock_repo.list_assets.return_value = []
|
||||
mock_repo.get_tags_by_ids.return_value = {}
|
||||
|
||||
with patch("app.core.auth.get_portfolio_repository_from_settings", return_value=mock_repo):
|
||||
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):
|
||||
resp = client.get("/v1/assets", headers=_svc_header())
|
||||
|
||||
assert resp.status_code == 403
|
||||
assert resp.status_code == 200
|
||||
assert resp.json()["total"] == 0
|
||||
|
||||
|
||||
def test_assets_200_with_daemon_scoped_filter(monkeypatch) -> None:
|
||||
"""GET /v1/assets requires admin — service token with daemon_id is also rejected."""
|
||||
"""REQ-044: GET /v1/assets now requires only read — trade-write service token with daemon_id also gets 200."""
|
||||
client = _app_with_db(monkeypatch)
|
||||
principal = _principal(daemon_id="d-1")
|
||||
mock_repo = MagicMock()
|
||||
mock_repo.get_token_principal.return_value = principal
|
||||
mock_repo.list_assets.return_value = []
|
||||
mock_repo.get_tags_by_ids.return_value = {}
|
||||
|
||||
with patch("app.core.auth.get_portfolio_repository_from_settings", return_value=mock_repo):
|
||||
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):
|
||||
resp = client.get("/v1/assets", headers=_svc_header())
|
||||
|
||||
assert resp.status_code == 403
|
||||
assert resp.status_code == 200
|
||||
assert resp.json()["total"] == 0
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user