"""Integration tests for mydeepagent budget / stats / costs CLI commands.""" from __future__ import annotations import asyncio import tempfile from unittest.mock import patch from typer.testing import CliRunner from my_deepagent.cli.main import app from my_deepagent.persistence.db import Database from my_deepagent.persistence.models import BudgetLedgerRow runner = CliRunner() # --------------------------------------------------------------------------- # Helpers # --------------------------------------------------------------------------- def _now_iso() -> str: from datetime import UTC, datetime return datetime.now(UTC).isoformat(timespec="seconds") def _today_utc() -> str: from datetime import UTC, datetime return datetime.now(UTC).strftime("%Y-%m-%d") async def _seed_budget_row(db: Database, scope: str, spent: float, cap: float) -> None: from sqlalchemy.dialects.sqlite import insert as sqlite_insert async with db.session() as s: stmt = ( sqlite_insert(BudgetLedgerRow) .values(scope=scope, spent_usd=spent, cap_usd=cap, last_updated=_now_iso()) .on_conflict_do_update( index_elements=["scope"], set_={ "spent_usd": spent, "cap_usd": cap, "last_updated": _now_iso(), }, ) ) await s.execute(stmt) # --------------------------------------------------------------------------- # budget command — empty DB # --------------------------------------------------------------------------- def test_budget_empty_db_shows_no_activity() -> None: with tempfile.TemporaryDirectory() as tmpdir: db_url = f"sqlite+aiosqlite:///{tmpdir}/test.sqlite3" with patch("my_deepagent.cli.stats.load_config") as mock_cfg: cfg = mock_cfg.return_value cfg.database_url = db_url result = runner.invoke(app, ["budget"]) assert result.exit_code == 0, result.output assert "no budget activity yet" in result.output # --------------------------------------------------------------------------- # budget command — with data # --------------------------------------------------------------------------- def test_budget_with_data_shows_ledger() -> None: with tempfile.TemporaryDirectory() as tmpdir: db_url = f"sqlite+aiosqlite:///{tmpdir}/test.sqlite3" db = Database(db_url) asyncio.run(_init_and_seed_budget(db)) with patch("my_deepagent.cli.stats.load_config") as mock_cfg: cfg = mock_cfg.return_value cfg.database_url = db_url result = runner.invoke(app, ["budget"]) assert result.exit_code == 0, result.output assert f"day:{_today_utc()}" in result.output assert "0.5000" in result.output # spent amount async def _init_and_seed_budget(db: Database) -> None: await db.init_schema() await _seed_budget_row(db, f"day:{_today_utc()}", spent=0.5, cap=5.0) # --------------------------------------------------------------------------- # stats command — empty DB # --------------------------------------------------------------------------- def test_stats_empty_db_shows_no_data() -> None: with tempfile.TemporaryDirectory() as tmpdir: db_url = f"sqlite+aiosqlite:///{tmpdir}/test.sqlite3" with patch("my_deepagent.cli.stats.load_config") as mock_cfg: cfg = mock_cfg.return_value cfg.database_url = db_url result = runner.invoke(app, ["stats", "--by", "model"]) assert result.exit_code == 0, result.output assert "no data for the past period" in result.output # --------------------------------------------------------------------------- # stats --by invalid # --------------------------------------------------------------------------- def test_stats_invalid_by_exits_two() -> None: with tempfile.TemporaryDirectory() as tmpdir: db_url = f"sqlite+aiosqlite:///{tmpdir}/test.sqlite3" with patch("my_deepagent.cli.stats.load_config") as mock_cfg: cfg = mock_cfg.return_value cfg.database_url = db_url result = runner.invoke(app, ["stats", "--by", "invalid_group"]) assert result.exit_code == 2, result.output # --------------------------------------------------------------------------- # costs alias # --------------------------------------------------------------------------- def test_costs_empty_db_shows_no_data() -> None: with tempfile.TemporaryDirectory() as tmpdir: db_url = f"sqlite+aiosqlite:///{tmpdir}/test.sqlite3" with patch("my_deepagent.cli.stats.load_config") as mock_cfg: cfg = mock_cfg.return_value cfg.database_url = db_url result = runner.invoke(app, ["costs"]) assert result.exit_code == 0, result.output assert "no data for the past period" in result.output