create(); app(AdWalletService::class)->topup($tenant->id, '1000.00', 'telegram', 'Пополнение'); app(AdWalletService::class)->freeze($tenant->id, 'telegram', 'campaign', 777, '500.00'); app(AdWalletService::class)->release($tenant->id, 'telegram', 'campaign', 777); app(AdWalletService::class)->freeze($tenant->id, 'telegram', 'campaign', 777, '500.00'); $wallet = AdWallet::where('tenant_id', $tenant->id)->first(); expect((string) $wallet->frozen_rub)->toBe('500.00'); $holds = AdWalletHold::where('tenant_id', $tenant->id) ->where('source_type', 'campaign')->where('source_id', 777)->get(); expect($holds)->toHaveCount(1) ->and($holds->first()->status)->toBe(AdWalletHold::STATUS_ACTIVE); }); it('повторная бронь после возврата берёт новую сумму', function () { $tenant = Tenant::factory()->create(); app(AdWalletService::class)->topup($tenant->id, '1000.00', 'telegram', 'Пополнение'); app(AdWalletService::class)->freeze($tenant->id, 'telegram', 'campaign', 777, '500.00'); app(AdWalletService::class)->release($tenant->id, 'telegram', 'campaign', 777); app(AdWalletService::class)->freeze($tenant->id, 'telegram', 'campaign', 777, '300.00'); $wallet = AdWallet::where('tenant_id', $tenant->id)->first(); expect((string) $wallet->frozen_rub)->toBe('300.00'); }); it('активная бронь по-прежнему идемпотентна (двойной freeze не удваивает frozen)', function () { $tenant = Tenant::factory()->create(); app(AdWalletService::class)->topup($tenant->id, '1000.00', 'telegram', 'Пополнение'); app(AdWalletService::class)->freeze($tenant->id, 'telegram', 'campaign', 777, '500.00'); app(AdWalletService::class)->freeze($tenant->id, 'telegram', 'campaign', 777, '500.00'); $wallet = AdWallet::where('tenant_id', $tenant->id)->first(); expect((string) $wallet->frozen_rub)->toBe('500.00'); });