From 8b3c2166d8990a3560bd391708ce31009fe72673 Mon Sep 17 00:00:00 2001 From: kerry Date: Tue, 22 Sep 2026 20:44:58 +0000 Subject: [PATCH] test(integration): budget_duration change on /budget/update recomputes budget_reset_at (Pylon #6913) Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- tests/integration/contracts.json | 3 ++ .../management/test_budget_updates.py | 28 +++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 tests/integration/management/test_budget_updates.py diff --git a/tests/integration/contracts.json b/tests/integration/contracts.json index c06e0271d45..4f06470a9dd 100644 --- a/tests/integration/contracts.json +++ b/tests/integration/contracts.json @@ -33,6 +33,9 @@ "tests/integration/management/test_key_updates.py::test_update_preserves_independent_fields_and_serving": [ "mgmt.key.update.preserves_independent_fields" ], + "tests/integration/management/test_budget_updates.py::test_shortening_budget_duration_moves_reset_at_onto_the_new_schedule": [ + "mgmt.budget.update.duration_change_recomputes_reset_at" + ], "tests/integration/pricing/test_configured_prices.py::test_custom_price_is_reported_and_charged": [ "quota_management.spend_tracking.custom_price.matches_input_rates" ], diff --git a/tests/integration/management/test_budget_updates.py b/tests/integration/management/test_budget_updates.py new file mode 100644 index 00000000000..45ec6102ca5 --- /dev/null +++ b/tests/integration/management/test_budget_updates.py @@ -0,0 +1,28 @@ +from datetime import datetime +from typing import Final + +import pytest + +from tests.integration._support.client import Gateway, string_value +from tests.integration._support.database import read_rows + + +def _persisted_reset_at(budget_id: str) -> datetime: + rows: Final = read_rows( + 'SELECT budget_reset_at::text AS reset_at FROM "LiteLLM_BudgetTable" WHERE budget_id = %s', (budget_id,) + ) + assert len(rows) == 1, rows + return datetime.fromisoformat(string_value(rows[0]["reset_at"])) + + +@pytest.mark.covers("mgmt.budget.update.duration_change_recomputes_reset_at") +def test_shortening_budget_duration_moves_reset_at_onto_the_new_schedule(gateway: Gateway) -> None: + with gateway.scenario() as scenario: + budget_id: Final = scenario.budget(max_budget=10.0, budget_duration="30d") + monthly_reset_at: Final = _persisted_reset_at(budget_id) + response: Final = gateway.request("POST", "/budget/update", {"budget_id": budget_id, "budget_duration": "1d"}) + assert response.status_code == 200, response.text + control_id: Final = scenario.budget(max_budget=10.0, budget_duration="1d") + daily_reset_at: Final = _persisted_reset_at(control_id) + assert daily_reset_at < monthly_reset_at, f"{daily_reset_at} vs {monthly_reset_at}" + assert _persisted_reset_at(budget_id) == daily_reset_at, response.text