4042890b0a
- AutopodborChargeService::chargeForRun — DB::transaction + lockForUpdate на AutopodborRun (guard идемпотентности по balance_transaction_id) и Tenant; bcmath (bcsub/bccomp/bcmul), никаких float; throw InsufficientBalanceException до любых изменений баланса при нехватке средств. - Миграция 2026_06_28_110100: расширяет CHECK constraint balance_transactions_type_check — добавляет 'autopodbor_charge'. - Тест: 2 money-инварианта (идемпотентность + noop при нехватке). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
38 lines
1.8 KiB
PHP
38 lines
1.8 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Models\AutopodborRun;
|
|
use App\Models\BalanceTransaction;
|
|
use App\Models\Tenant;
|
|
use App\Services\Autopodbor\AutopodborChargeService;
|
|
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
uses(DatabaseTransactions::class, \Tests\Concerns\SharesSupplierPdo::class);
|
|
|
|
it('списывает один раз и идемпотентно по run_id', function () {
|
|
$tenant = Tenant::factory()->create(['balance_rub' => '1000.00']);
|
|
DB::statement("SET LOCAL app.current_tenant_id = ".$tenant->id);
|
|
$run = AutopodborRun::create(['tenant_id' => $tenant->id, 'kind' => 'search', 'status' => 'running', 'params' => []]);
|
|
|
|
$svc = app(AutopodborChargeService::class);
|
|
$svc->chargeForRun($run, '300.00');
|
|
$svc->chargeForRun($run->fresh(), '300.00'); // повтор НЕ должен списать второй раз
|
|
|
|
expect((string) $tenant->fresh()->balance_rub)->toBe('700.00')
|
|
->and($run->fresh()->price_rub_charged)->not->toBeNull()
|
|
->and(BalanceTransaction::where('type', 'autopodbor_charge')->where('related_id', $run->id)->count())->toBe(1);
|
|
});
|
|
|
|
it('не списывает при нехватке баланса (бросает, баланс цел)', function () {
|
|
$tenant = Tenant::factory()->create(['balance_rub' => '100.00']);
|
|
DB::statement("SET LOCAL app.current_tenant_id = ".$tenant->id);
|
|
$run = AutopodborRun::create(['tenant_id' => $tenant->id, 'kind' => 'study', 'status' => 'running', 'params' => []]);
|
|
|
|
expect(fn () => app(AutopodborChargeService::class)->chargeForRun($run, '300.00'))
|
|
->toThrow(\App\Exceptions\Billing\InsufficientBalanceException::class);
|
|
expect((string) $tenant->fresh()->balance_rub)->toBe('100.00')
|
|
->and(BalanceTransaction::where('related_id', $run->id)->count())->toBe(0);
|
|
});
|