$tenantId, 'enabled' => true, 'ad_text' => 'Заходите в наш канал', 'ad_link' => 'https://t.me/example_channel', 'ord_category' => 'Размещение рекламы', 'budget_cap_rub' => '2500.00', 'daily_limit_rub' => $dailyLimit, ]); } /** @return list */ function safeguardPhones(int $n): array { $out = []; for ($i = 1; $i <= $n; $i++) { $out[] = '7999'.str_pad((string) $i, 7, '0', STR_PAD_LEFT); } return $out; } function feed(int $tenantId, int $n): void { foreach (safeguardPhones($n) as $phone) { app(TelegramAutoAccumulator::class)->accumulate($tenantId, $phone); } } beforeEach(function () { Carbon::setTestNow('2026-07-28 10:00:00'); Queue::fake(); config(['client_tg.auto_batch_threshold' => 3]); // порог занижен для теста config(['client_tg.sandbox' => false]); // деньги включены — проверяем боевой гейт $this->tenant = Tenant::factory()->create(); // Смета пачки из 3 кандидатов = 3 × 0.45 (ступень min_qty=1) = 1.35 ₽. $this->cost = app(TelegramTariffService::class)->estimateRub(3); }); afterEach(function () { Carbon::setTestNow(); }); it('в бою при нехватке денег не ставит в очередь и пишет причину', function () { app(AdWalletService::class)->topup($this->tenant->id, '1.00', 'telegram', 'test'); // < 1.35 safeguardRule($this->tenant->id, dailyLimit: '1000.00'); Log::spy(); feed($this->tenant->id, 3); Queue::assertNothingPushed(); $campaign = Campaign::where('tenant_id', $this->tenant->id)->first(); expect($campaign->status)->toBe(Campaign::STATUS_DRAFT); // остаётся черновиком Log::shouldHaveReceived('info')->withArgs(fn ($msg) => $msg === 'client_tg.auto_skipped')->atLeast()->once(); }); it('в бою при исчерпанном дневном лимите не ставит в очередь', function () { app(AdWalletService::class)->topup($this->tenant->id, '10000.00', 'telegram', 'test'); // денег вдоволь safeguardRule($this->tenant->id, dailyLimit: '1.00'); // лимит < сметы 1.35 feed($this->tenant->id, 3); Queue::assertNothingPushed(); expect(Campaign::where('tenant_id', $this->tenant->id)->first()->status)->toBe(Campaign::STATUS_DRAFT); }); it('дневной лимит по умолчанию 0 → авто выключено даже при деньгах', function () { app(AdWalletService::class)->topup($this->tenant->id, '10000.00', 'telegram', 'test'); // daily_limit_rub не задан явно → дефолт 0.00 AutoRule::create([ 'tenant_id' => $this->tenant->id, 'enabled' => true, 'ad_text' => 'Текст', 'ad_link' => 'https://t.me/x', 'ord_category' => 'Размещение рекламы', 'budget_cap_rub' => '2500.00', ]); feed($this->tenant->id, 3); Queue::assertNothingPushed(); }); it('в бою при деньгах и лимите в норме — ставит в очередь и учитывает трату за день', function () { app(AdWalletService::class)->topup($this->tenant->id, '10000.00', 'telegram', 'test'); $rule = safeguardRule($this->tenant->id, dailyLimit: '1000.00'); feed($this->tenant->id, 3); Queue::assertPushed(RunTelegramCampaignJob::class, 1); expect(Campaign::where('tenant_id', $this->tenant->id)->first()->status)->toBe(Campaign::STATUS_QUEUED); $rule->refresh(); expect((string) $rule->spent_today_rub)->toBe($this->cost); // 1.35 записано expect($rule->spent_date->toDateString())->toBe('2026-07-28'); }); it('счётчик за день сбрасывается на новую дату', function () { app(AdWalletService::class)->topup($this->tenant->id, '10000.00', 'telegram', 'test'); $rule = safeguardRule($this->tenant->id, dailyLimit: '1.35'); // Вчерашняя трата «под завязку» — но сегодня лимит должен освободиться. $rule->update(['spent_today_rub' => '1.35', 'spent_date' => '2026-07-27']); feed($this->tenant->id, 3); Queue::assertPushed(RunTelegramCampaignJob::class, 1); $rule->refresh(); expect((string) $rule->spent_today_rub)->toBe($this->cost); // счётчик пересчитан с нуля за сегодня }); it('в песочнице предохранитель не вмешивается — ставит в очередь без денег', function () { config(['client_tg.sandbox' => true]); safeguardRule($this->tenant->id, dailyLimit: '0.00'); // ни денег, ни лимита feed($this->tenant->id, 3); Queue::assertPushed(RunTelegramCampaignJob::class, 1); });