Add ProofLine criteria documentation and Strategy Catalog v1 implementation
- Add acceptance criteria documentation (AC-0013 through AC-0023) - Add Line-0003 discovery documentation - Implement Strategy Catalog v1 with new database migration - Update portfolio API endpoints and schemas - Refactor strategy and portfolio services - Add strategy management CLI commands - Update domain models with strategy catalog support - Add comprehensive tests for v1 strategies - Update design documentation (data model, interfaces, runtime flows)
This commit is contained in:
@@ -79,6 +79,9 @@ class _FakeRepo:
|
||||
def get_or_create_strategy_set(self, name: str, config: str) -> StrategySet:
|
||||
return self._strategy_set
|
||||
|
||||
def get_strategy_set(self, id: str) -> StrategySet | None:
|
||||
return self._strategy_set if id == self._strategy_set.id else None
|
||||
|
||||
def create_portfolio(
|
||||
self,
|
||||
portfolio_id: str,
|
||||
@@ -190,7 +193,7 @@ def test_post_admin_creates_for_target_user() -> None:
|
||||
with _with_repo(repo):
|
||||
resp = client.post(
|
||||
"/v1/portfolios",
|
||||
json={"asset_id": _ASSET_UUID, "broker": "kiwoom", "user_id": _OTHER_USER_ID},
|
||||
json={"asset_id": _ASSET_UUID, "broker": "kiwoom", "strategy_id": "strat-001", "user_id": _OTHER_USER_ID},
|
||||
headers=_auth("admin-tok"),
|
||||
)
|
||||
assert resp.status_code == 201
|
||||
@@ -209,7 +212,7 @@ def test_post_user_creates_for_self() -> None:
|
||||
with _with_repo(repo):
|
||||
resp = client.post(
|
||||
"/v1/portfolios",
|
||||
json={"asset_id": _ASSET_UUID, "broker": "kiwoom"},
|
||||
json={"asset_id": _ASSET_UUID, "broker": "kiwoom", "strategy_id": "strat-001"},
|
||||
headers=_auth("user-tok"),
|
||||
)
|
||||
assert resp.status_code == 201
|
||||
@@ -230,7 +233,7 @@ def test_post_service_token_returns_403() -> None:
|
||||
with _with_repo(repo, db_auth=True):
|
||||
resp = client.post(
|
||||
"/v1/portfolios",
|
||||
json={"asset_id": _ASSET_UUID, "broker": "kiwoom"},
|
||||
json={"asset_id": _ASSET_UUID, "broker": "kiwoom", "strategy_id": "strat-001"},
|
||||
headers=_auth("svc-raw-token"),
|
||||
)
|
||||
assert resp.status_code == 403
|
||||
@@ -244,7 +247,7 @@ def test_post_inactive_asset_returns_422() -> None:
|
||||
with _with_repo(repo):
|
||||
resp = client.post(
|
||||
"/v1/portfolios",
|
||||
json={"asset_id": _ASSET_UUID, "broker": "kiwoom"},
|
||||
json={"asset_id": _ASSET_UUID, "broker": "kiwoom", "strategy_id": "strat-001"},
|
||||
headers=_auth("admin-tok"),
|
||||
)
|
||||
assert resp.status_code == 422
|
||||
@@ -259,7 +262,7 @@ def test_post_missing_asset_returns_422() -> None:
|
||||
with _with_repo(repo):
|
||||
resp = client.post(
|
||||
"/v1/portfolios",
|
||||
json={"asset_id": "no-such-uuid", "broker": "kiwoom"},
|
||||
json={"asset_id": "no-such-uuid", "broker": "kiwoom", "strategy_id": "strat-001"},
|
||||
headers=_auth("admin-tok"),
|
||||
)
|
||||
assert resp.status_code == 422
|
||||
@@ -268,7 +271,7 @@ def test_post_missing_asset_returns_422() -> None:
|
||||
def test_post_no_bearer_returns_401() -> None:
|
||||
"""REQ-025 AC-6: no token → HTTP 401."""
|
||||
client = _make_client()
|
||||
resp = client.post("/v1/portfolios", json={"asset_id": _ASSET_UUID, "broker": "kiwoom"})
|
||||
resp = client.post("/v1/portfolios", json={"asset_id": _ASSET_UUID, "broker": "kiwoom", "strategy_id": "strat-001"})
|
||||
assert resp.status_code == 401
|
||||
|
||||
|
||||
@@ -280,7 +283,7 @@ def test_post_non_admin_user_id_ignored() -> None:
|
||||
with _with_repo(repo):
|
||||
resp = client.post(
|
||||
"/v1/portfolios",
|
||||
json={"asset_id": _ASSET_UUID, "broker": "kiwoom", "user_id": _OTHER_USER_ID},
|
||||
json={"asset_id": _ASSET_UUID, "broker": "kiwoom", "strategy_id": "strat-001", "user_id": _OTHER_USER_ID},
|
||||
headers=_auth("user-tok"),
|
||||
)
|
||||
assert resp.status_code == 201
|
||||
@@ -499,3 +502,48 @@ def test_delete_already_disabled_is_idempotent() -> None:
|
||||
resp = client.delete(f"/v1/portfolios/{_PORTFOLIO_UUID}", headers=_auth("admin-tok"))
|
||||
assert resp.status_code == 200
|
||||
assert resp.json()["status"] == "disabled"
|
||||
|
||||
|
||||
# Issue #26: strategy catalog references on portfolio mutations.
|
||||
def test_post_strategy_reference_errors_and_required_contract() -> None:
|
||||
repo = _FakeRepo(asset=_make_asset(_ASSET_UUID, "active"))
|
||||
client = _make_client()
|
||||
with _with_repo(repo):
|
||||
missing = client.post(
|
||||
"/v1/portfolios",
|
||||
json={"asset_id": _ASSET_UUID, "broker": "kiwoom", "strategy_id": "missing"},
|
||||
headers=_auth("admin-tok"),
|
||||
)
|
||||
invalid = client.post(
|
||||
"/v1/portfolios",
|
||||
json={"asset_id": _ASSET_UUID, "broker": "kiwoom"},
|
||||
headers=_auth("admin-tok"),
|
||||
)
|
||||
repo._strategy_set.status = "inactive"
|
||||
inactive = client.post(
|
||||
"/v1/portfolios",
|
||||
json={"asset_id": _ASSET_UUID, "broker": "kiwoom", "strategy_id": "strat-001"},
|
||||
headers=_auth("admin-tok"),
|
||||
)
|
||||
assert missing.status_code == 404
|
||||
assert invalid.status_code == 422
|
||||
assert inactive.status_code == 409
|
||||
|
||||
|
||||
def test_patch_strategy_reference_errors_are_404_and_409() -> None:
|
||||
repo = _repo_with_portfolio(owner=_OTHER_USER_ID)
|
||||
client = _make_client()
|
||||
with _with_repo(repo):
|
||||
missing = client.patch(
|
||||
f"/v1/portfolios/{_PORTFOLIO_UUID}",
|
||||
json={"strategy_id": "missing"},
|
||||
headers=_auth("admin-tok"),
|
||||
)
|
||||
repo._strategy_set.status = "inactive"
|
||||
inactive = client.patch(
|
||||
f"/v1/portfolios/{_PORTFOLIO_UUID}",
|
||||
json={"strategy_id": "strat-001"},
|
||||
headers=_auth("admin-tok"),
|
||||
)
|
||||
assert missing.status_code == 404
|
||||
assert inactive.status_code == 409
|
||||
|
||||
Reference in New Issue
Block a user