fix(billing): unify quota counters, fix Stripe webhooks, tier/plan sync
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 3m16s

This commit is contained in:
2026-06-14 17:39:34 +02:00
parent fa637abff0
commit 45e44dd7b2
9 changed files with 546 additions and 256 deletions

View File

@@ -115,26 +115,22 @@ def client(users_file: Path, monkeypatch):
monkeypatch.setattr(RateLimitManager, "check_request", _check_request_allow)
monkeypatch.setattr(RateLimitManager, "check_translation", _check_translation_allow)
from middleware.tier_quota import TierQuotaService
def _check_usage_limits_allow(user):
return {
"can_translate": True,
"docs_used": 0,
"docs_limit": 5,
"docs_remaining": 5,
"pages_used": 0,
"extra_credits": 0,
"max_pages_per_doc": 50,
"max_file_size_mb": 10,
"allowed_providers": ["google", "deepl"],
}
async def _check_quota_allow(self, user_id, tier):
from middleware.tier_quota import QuotaResult
from datetime import datetime, timezone, timedelta
now = datetime.now(timezone.utc)
tomorrow = now.date() + timedelta(days=1)
reset_at = datetime(
tomorrow.year, tomorrow.month, tomorrow.day, tzinfo=timezone.utc
)
return QuotaResult(
allowed=True, remaining=5, reset_at_utc=reset_at, current_usage=0, limit=5
)
async def _increment_noop(self, user_id):
pass
monkeypatch.setattr(TierQuotaService, "check_quota", _check_quota_allow)
monkeypatch.setattr(TierQuotaService, "increment_on_success", _increment_noop)
monkeypatch.setattr(
"routes.translate_routes.check_usage_limits", _check_usage_limits_allow
)
from main import app
@@ -477,24 +473,22 @@ class TestQuotaExceeded:
def test_returns_429_when_quota_exceeded(self, client, monkeypatch):
"""Returns 429 with QUOTA_EXCEEDED when quota exceeded"""
from middleware.tier_quota import TierQuotaService, QuotaResult
from datetime import datetime, timezone, timedelta
def _check_usage_limits_denied(user):
return {
"can_translate": False,
"docs_used": 5,
"docs_limit": 5,
"docs_remaining": 0,
"pages_used": 0,
"extra_credits": 0,
"max_pages_per_doc": 50,
"max_file_size_mb": 10,
"allowed_providers": ["google", "deepl"],
}
async def _check_quota_denied(self, user_id, tier):
now = datetime.now(timezone.utc)
tomorrow = now.date() + timedelta(days=1)
reset_at = datetime(
tomorrow.year, tomorrow.month, tomorrow.day, tzinfo=timezone.utc
)
return QuotaResult(
allowed=False,
remaining=0,
reset_at_utc=reset_at,
current_usage=5,
limit=5,
)
monkeypatch.setattr(TierQuotaService, "check_quota", _check_quota_denied)
monkeypatch.setattr(
"routes.translate_routes.check_usage_limits", _check_usage_limits_denied
)
# Register and login
client.post(REGISTER_URL, json=VALID_USER)
@@ -530,24 +524,23 @@ class TestQuotaExceeded:
def test_includes_retry_after_header(self, client, monkeypatch):
"""Includes Retry-After header on 429"""
from middleware.tier_quota import TierQuotaService, QuotaResult
from datetime import datetime, timezone, timedelta
async def _check_quota_denied(self, user_id, tier):
now = datetime.now(timezone.utc)
tomorrow = now.date() + timedelta(days=1)
reset_at = datetime(
tomorrow.year, tomorrow.month, tomorrow.day, tzinfo=timezone.utc
)
return QuotaResult(
allowed=False,
remaining=0,
reset_at_utc=reset_at,
current_usage=5,
limit=5,
)
def _check_usage_limits_denied(user):
return {
"can_translate": False,
"docs_used": 5,
"docs_limit": 5,
"docs_remaining": 0,
"pages_used": 0,
"extra_credits": 0,
"max_pages_per_doc": 50,
"max_file_size_mb": 10,
"allowed_providers": ["google", "deepl"],
}
monkeypatch.setattr(TierQuotaService, "check_quota", _check_quota_denied)
monkeypatch.setattr(
"routes.translate_routes.check_usage_limits", _check_usage_limits_denied
)
client.post(REGISTER_URL, json=VALID_USER)
response = client.post(