1313
1414from __future__ import annotations
1515
16- import asyncio
1716import io
1817from unittest .mock import AsyncMock , MagicMock , patch
1918
2019from PIL import Image
21- from sqlalchemy import select
2220
2321from src .config import settings
24- from src .models .db import User
2522
2623_CONSENT_HEADERS = {
2724 "X-Consent-Data-Processing" : "1" ,
@@ -60,20 +57,11 @@ def _auth(token: str) -> dict[str, str]:
6057 return {"Authorization" : f"Bearer { token } " , ** _CONSENT_HEADERS }
6158
6259
63- def _grant_credits (client , telegram_id : int , credits : int = 10 ) -> None :
64- """Premium tier reserves 5 credits — seed balance for integration tests."""
65-
66- async def _run () -> None :
67- sessionmaker = client .app .state .db_sessionmaker
68- async with sessionmaker () as db :
69- row = await db .execute (
70- select (User ).where (User .telegram_id == telegram_id )
71- )
72- user = row .scalar_one ()
73- user .image_credits = credits
74- await db .commit ()
75-
76- asyncio .run (_run ())
60+ async def _stub_credit_reserve (user , db , * , amount : int = 1 ):
61+ """Bypass DB credit checks — this test asserts tier context, not billing."""
62+ user ._credit_reserved = True
63+ user ._credits_remaining = max (0 , int (getattr (user , "image_credits" , 1 ) or 1 ) - amount )
64+ return user
7765
7866
7967class _TaskCtxCapture :
@@ -260,16 +248,22 @@ def test_analyze_standard_tier_when_ab_flag_off(
260248 assert "image_refine" not in ctx
261249
262250
251+ @patch ("src.api.deps.reserve_additional_credit" , new_callable = AsyncMock )
252+ @patch ("src.api.deps._reserve_credit_for" , new_callable = AsyncMock )
263253@patch ("src.api.v1.analyze._get_arq" , new_callable = AsyncMock )
264254@patch ("src.api.v1.analyze.get_storage" )
265255def test_analyze_premium_tier_when_ab_flag_off (
266256 mock_get_storage ,
267257 mock_get_arq ,
258+ mock_reserve_first ,
259+ mock_reserve_extra ,
268260 client ,
269261 monkeypatch ,
270262):
271263 """v1.77 — Premium must persist high + clarity even when AB flag is off."""
272264 monkeypatch .setattr (settings , "ab_test_enabled" , False )
265+ mock_reserve_first .side_effect = _stub_credit_reserve
266+ mock_reserve_extra .side_effect = _stub_credit_reserve
273267 storage = MagicMock ()
274268 storage .upload = AsyncMock (return_value = "inputs/u/k.jpg" )
275269 mock_get_storage .return_value = storage
@@ -278,7 +272,6 @@ def test_analyze_premium_tier_when_ab_flag_off(
278272 mock_get_arq .return_value = pool
279273
280274 token = _register_user (client , telegram_id = 999108 )
281- _grant_credits (client , telegram_id = 999108 , credits = 10 )
282275 with _TaskCtxCapture () as cap :
283276 r = client .post (
284277 "/api/v1/analyze" ,
0 commit comments