create(['balance_rub' => '0.00']); tgKoshelekTopup($tenant->id, $walletRub); return Campaign::create([ 'tenant_id' => $tenant->id, 'ad_text' => 'Приходите к нам в канал', 'ad_link' => 'https://t.me/example_channel', 'audience_kind' => 'list', 'budget_cap_rub' => '1000.00', 'status' => Campaign::STATUS_MODERATING, ]); } it('морозит смету под боевой ролью с контекстом клиента — как это делает запуск', function () { $campaign = tgDenezhnayaKampaniya('5000.00'); $svc = app(TelegramCampaignChargeService::class); podTgBoevoyRolyu(fn () => DB::transaction(function () use ($campaign, $svc) { DB::statement('SET LOCAL app.current_tenant_id = '.$campaign->tenant_id); $svc->freeze($campaign, '1200.00'); })); // Читаем суперюзером — прибор не должен зависеть от той же защиты, что проверяем. $wallet = AdWallet::where('tenant_id', $campaign->tenant_id)->first(); expect((string) $wallet->balance_rub)->toBe('5000.00') ->and((string) $wallet->frozen_rub)->toBe('1200.00'); }); it('списывает по факту под боевой ролью — как это делает джоб модерации', function () { $campaign = tgDenezhnayaKampaniya('5000.00'); $svc = app(TelegramCampaignChargeService::class); $svc->freeze($campaign, '1200.00'); // Модерация одобрила: списываем фактические 840 ₽, остаток сметы отпускаем. podTgBoevoyRolyu(fn () => DB::transaction(function () use ($campaign, $svc) { DB::statement('SET LOCAL app.current_tenant_id = '.$campaign->tenant_id); $svc->chargeByFact($campaign, '840.00'); })); $wallet = AdWallet::where('tenant_id', $campaign->tenant_id)->first(); expect((string) $wallet->balance_rub)->toBe('4160.00') ->and((string) $wallet->frozen_rub)->toBe('0.00'); }); it('даже без контекста от вызывающего деньги двигаются — сервис ставит контекст сам', function () { $campaign = tgDenezhnayaKampaniya('5000.00'); $svc = app(TelegramCampaignChargeService::class); $svc->freeze($campaign, '1200.00'); // Ровно та же работа, но вызывающий контекст НЕ поставил. Прежде это был класс // «тихий ноль»: отказ модерации молча не возвращал клиенту деньги. Теперь контекст // ставит сам сервис — и заморозка отпускается как надо. podTgBoevoyRolyu(fn () => $svc->release($campaign)); $wallet = AdWallet::where('tenant_id', $campaign->tenant_id)->first(); expect((string) $wallet->frozen_rub)->toBe('0.00') ->and((string) $wallet->balance_rub)->toBe('5000.00'); });