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:
@@ -0,0 +1,74 @@
|
||||
"""Idempotency contract for the Issue #26 strategy-catalog migration."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib.util
|
||||
from pathlib import Path
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import patch
|
||||
|
||||
|
||||
_MIGRATION_PATH = (
|
||||
Path(__file__).parents[1]
|
||||
/ "migrations/versions/20260814_0029_strategy_catalog_v1.py"
|
||||
)
|
||||
|
||||
|
||||
def _load_migration():
|
||||
spec = importlib.util.spec_from_file_location("strategy_catalog_0029", _MIGRATION_PATH)
|
||||
assert spec is not None and spec.loader is not None
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
|
||||
class _Bind:
|
||||
def __init__(self) -> None:
|
||||
self.executed: list[str] = []
|
||||
self.params: list[dict | None] = []
|
||||
|
||||
def execute(self, statement, params=None):
|
||||
self.executed.append(str(statement))
|
||||
self.params.append(params)
|
||||
return SimpleNamespace()
|
||||
|
||||
|
||||
class _Inspector:
|
||||
def __init__(self, columns: set[str]) -> None:
|
||||
self.columns = columns
|
||||
|
||||
def get_table_names(self) -> list[str]:
|
||||
return ["strategy_sets"]
|
||||
|
||||
def get_columns(self, table_name: str) -> list[dict[str, str]]:
|
||||
assert table_name == "strategy_sets"
|
||||
return [{"name": name} for name in sorted(self.columns)]
|
||||
|
||||
|
||||
def test_upgrade_is_repeatable_and_uses_data_and_constraint_guards() -> None:
|
||||
migration = _load_migration()
|
||||
columns = {"id", "name", "config", "nick", "created_at"}
|
||||
inspector = _Inspector(columns)
|
||||
bind = _Bind()
|
||||
added: list[str] = []
|
||||
|
||||
def add_column(table_name: str, column) -> None:
|
||||
assert table_name == "strategy_sets"
|
||||
columns.add(column.name)
|
||||
added.append(column.name)
|
||||
|
||||
with (
|
||||
patch.object(migration.op, "get_bind", return_value=bind),
|
||||
patch.object(migration.op, "add_column", side_effect=add_column),
|
||||
patch.object(migration, "inspect", return_value=inspector),
|
||||
):
|
||||
migration.upgrade()
|
||||
migration.upgrade()
|
||||
|
||||
assert added == ["status", "revision", "updated_at"]
|
||||
sql = "\n".join(bind.executed)
|
||||
assert "WHERE NOT EXISTS" in sql
|
||||
assert "IF NOT EXISTS" in sql
|
||||
assert "ck_strategy_sets_status" in sql
|
||||
assert "ck_strategy_sets_revision_positive" in sql
|
||||
assert any(params and params.get("nick") == "target_value:bf21a9e8fbc5" for params in bind.params)
|
||||
Reference in New Issue
Block a user