config()->set('client_tg.sandbox', true)); function tgRejectCampaign(int $tenantId, string $status = Campaign::STATUS_QUEUED): Campaign { $campaign = Campaign::query()->create([ 'tenant_id' => $tenantId, 'status' => $status, 'ad_text' => 'Займ под залог недвижимости', 'ad_link' => 'https://t.me/example_test', 'ord_category' => 'Размещение рекламы', 'budget_cap_rub' => '1500.00', 'audience_kind' => Campaign::AUDIENCE_LIST, 'planned_count' => 2, 'estimated_cost_rub' => '0.00', 'created_by' => 1, ]); foreach (['79990000001', '79990000002'] as $phone) { CampaignPhone::query()->create([ 'tenant_id' => $tenantId, 'campaign_id' => $campaign->id, 'phone' => $phone, ]); } return $campaign; } function mockRobotFail(RobotResult $result): void { $mock = Mockery::mock(TelegramRobotRunner::class); $mock->shouldReceive('run')->andReturn($result); app()->instance(TelegramRobotRunner::class, $mock); } it('ошибка робота сохраняет причину в status_reason', function () { $tenant = Tenant::factory()->create(); $campaign = tgRejectCampaign($tenant->id); mockRobotFail(RobotResult::failed('Канал недоступен', 'finalize')); RunTelegramCampaignJob::dispatchSync($campaign->id, $tenant->id); $fresh = Campaign::find($campaign->id); expect($fresh->status)->toBe(Campaign::STATUS_FAILED) ->and($fresh->status_reason)->toBe('Канал недоступен'); }); it('при провале клиент получает in-app уведомление с причиной', function () { $tenant = Tenant::factory()->create(); $user = User::factory()->create(['tenant_id' => $tenant->id, 'is_active' => true]); $campaign = tgRejectCampaign($tenant->id); mockRobotFail(RobotResult::failed('Требуется лицензия ЦБ на займы', 'finalize')); RunTelegramCampaignJob::dispatchSync($campaign->id, $tenant->id); $notif = InAppNotification::where('tenant_id', $tenant->id) ->where('user_id', $user->id) ->first(); expect($notif)->not->toBeNull() ->and($notif->body)->toContain('Требуется лицензия ЦБ на займы'); }); it('уведомление об отказе доходит даже без включённых настроек уведомлений', function () { $tenant = Tenant::factory()->create(); // Дефолтные настройки фабрики НЕ содержат события tg_campaign_rejected — // это и проверяет, что уведомление доходит без pref-гейта. $user = User::factory()->create([ 'tenant_id' => $tenant->id, 'is_active' => true, ]); $campaign = tgRejectCampaign($tenant->id, Campaign::STATUS_RUNNING); $campaign->status_reason = 'Реклама займов запрещена без лицензии'; app(NotificationService::class)->notifyTelegramCampaignRejected($tenant, $campaign); $notif = InAppNotification::where('user_id', $user->id)->first(); expect($notif)->not->toBeNull() ->and($notif->body)->toContain('займов'); }); it('неактивный пользователь уведомление НЕ получает', function () { $tenant = Tenant::factory()->create(); User::factory()->create(['tenant_id' => $tenant->id, 'is_active' => false]); $campaign = tgRejectCampaign($tenant->id, Campaign::STATUS_RUNNING); $campaign->status_reason = 'Причина'; app(NotificationService::class)->notifyTelegramCampaignRejected($tenant, $campaign); expect(InAppNotification::count())->toBe(0); });