"""Global daily cost circuit breaker (架构文档 §13 三道成本闸门). The requirement is enforcement, not alerting: once the platform-wide daily spend crosses the threshold, non-critical AI calls are rejected. """ from __future__ import annotations from datetime import UTC, datetime from decimal import Decimal import pytest from packages.llm.budget import DailyBudgetGuard, InMemoryCostCounter from packages.llm.errors import DailyBudgetExceededError def make_guard( budget: str = "100", *, enabled: bool = True, now: datetime | None = None ) -> tuple[DailyBudgetGuard, InMemoryCostCounter]: counter = InMemoryCostCounter() guard = DailyBudgetGuard( counter, daily_budget_cents=Decimal(budget), enabled=enabled, clock=(lambda: now) if now else None, ) return guard, counter async def test_allows_calls_below_the_threshold() -> None: guard, _ = make_guard("100") await guard.add_cost(Decimal("99.9")) await guard.ensure_within_budget(scene="agent_chat") async def test_rejects_non_critical_scenes_once_the_budget_is_reached() -> None: guard, _ = make_guard("100") await guard.add_cost(Decimal("100")) with pytest.raises(DailyBudgetExceededError) as excinfo: await guard.ensure_within_budget(scene="agent_chat") assert "agent_chat" in str(excinfo.value) async def test_critical_scenes_bypass_the_breaker() -> None: guard, _ = make_guard("100") await guard.add_cost(Decimal("500")) # Degrade the product, do not break it: 意图识别/越界拒答 must keep working. await guard.ensure_within_budget(scene="intent_classify", critical=True) async def test_disabled_guard_never_blocks_and_never_counts() -> None: guard, counter = make_guard("1", enabled=False) await guard.add_cost(Decimal("999")) await guard.ensure_within_budget(scene="agent_chat") assert await counter.get(guard.current_key()) == Decimal("0") async def test_cost_accumulates_across_calls() -> None: guard, _ = make_guard("100") await guard.add_cost(Decimal("1.5")) await guard.add_cost(Decimal("2.25")) assert await guard.spent_today() == Decimal("3.75") async def test_zero_cost_is_not_counted() -> None: guard, _ = make_guard("100") await guard.add_cost(Decimal("0")) assert await guard.spent_today() == Decimal("0") async def test_budget_day_boundary_follows_utc_plus_8() -> None: """FR-BILL-01: 跨时区按 UTC+8 统一。17:00Z already belongs to the next day.""" guard, _ = make_guard(now=datetime(2026, 8, 13, 17, 0, tzinfo=UTC)) assert guard.current_key().endswith("2026-08-14") guard_before, _ = make_guard(now=datetime(2026, 8, 13, 15, 0, tzinfo=UTC)) assert guard_before.current_key().endswith("2026-08-13") async def test_budget_properties_are_exposed_for_dashboards() -> None: guard, _ = make_guard("42") assert guard.enabled is True assert guard.budget_cents == Decimal("42")