tests: two questions at once with one credit, with real threads

The claim that the user-row lock plus SQLite IMMEDIATE stops one credit
being spent twice was untested. Two threads on separate connections now
ask at the same time: one answer, one credit spent; the same
Idempotency-Key arriving twice at once also costs one credit. Removing
transaction_mode IMMEDIATE turns both red (500, database is locked).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-02 13:07:08 +03:30
parent fe658c697a
commit 67cbe05609
3 changed files with 77 additions and 4 deletions

View File

@@ -57,8 +57,9 @@ Message.objects.filter(role='assistant', created_at__gte=since).values('conversa
سوال از کجا پرداخت شد (`free` / `credit` / `none`).
- **دفترِ اعتبار** `subscription.QuestionCredit`: هر خرید یک ردیفِ مثبت دارد (هر سفارش فقط یک بار، با
قیدِ یکتا) و هر سوال یک ردیفِ ۱−. کم شدن داخلِ همان تراکنشی است که ردیفِ کاربر را قفل می‌کند. دو
سوالِ هم‌زمان با یک اعتبار آزموده نشده است؛ جلوگیری به آن قفل و `transaction_mode: IMMEDIATE`ِ
SQLite سپرده شده.
سوالِ هم‌زمان با یک اعتبار، و یک Idempotency-Key که هم‌زمان دو بار برسد، از ۲۰۲۶-۱۰-۰۲ با دو thread
واقعی آزموده می‌شوند (`ai_tutor/tests_credit_concurrency.py`): یک جواب و یک اعتبار. بدونِ
`transaction_mode: IMMEDIATE`ِ SQLite همین تست ۵۰۰ می‌گیرد. روی Postgres سنجیده نشده.
- **قلبِ نامحدودِ بسته** در `HeartsPass` است، جدا از `Subscription`، تا خریدِ بسته اشتراکِ کسی را عوض
نکند. از پایانِ کارتِ قبلی ادامه پیدا می‌کند. `HEARTS_ENFORCED` حالا پیش‌فرض روشن است، پس قلبِ صفر
واقعاً جلوی جواب را می‌گیرد.