Files
portal/app/tests/Feature/ClientTg/AdHeadlineTest.php
T
Дмитрий 32df332901 fix(телеграм-реклама): заголовок объявления для рекламы сайта — кампания больше не встаёт
Приёмка глазами вскрыла: кабинет МТС требует «Заголовок объявления» (до 40 знаков),
когда в объявлении ссылка на САЙТ, а не на телеграм-канал. Робот про это поле не знал,
«Продолжить» молча не срабатывало, кампания вставала на шаге «Объявление» — в бою уже
ПОСЛЕ списания денег. Проверено живьём: 2234454 (сайт — встала) против 2234462 (канал —
дошла до подтверждения) и 2234490 (сайт с заголовком — дошла).

Портал спрашивает заголовок заранее, на создании черновика: обязателен только для
не-телеграмной ссылки (App\Support\TelegramLink), колонка ad_headline varchar(40),
поле на экране появляется по той же развилке. Робот заполняет его в кабинете.

Три ловушки, добытые живыми прогонами (описаны в коде):
- поле дорисовывается в ОТВЕТ на ссылку, с задержкой — надо ждать, а не спрашивать;
- под описание подходит несколько элементов — нужен .first();
- серая надпись внутри поля НЕ placeholder, а нарисованная подпись: поиск по атрибуту
  давал ноль совпадений при видимом на снимке поле. Опознаём по видимой надписи.

Тесты: робот 130/130, ClientTg 250/250, экран 23/23. Полный прогон бэкенда — те же
13 падающих классов до и после правки (ни одного в телеграм-части). В baseline
статанализа добавлен известный ложный класс Pest для нового файла тестов.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 11:42:46 +03:00

110 lines
4.8 KiB
PHP

<?php
declare(strict_types=1);
use App\Jobs\ClientTg\RunTelegramCampaignJob;
use App\Models\ClientTg\Campaign;
use App\Models\ClientTg\RobotJob;
use App\Models\Tenant;
use App\Models\User;
use App\Services\ClientTg\TelegramAudienceService;
use App\Services\ClientTg\TelegramRobotRunner;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Queue;
/**
* «Заголовок объявления» — находка приёмки глазами 31.07.2026.
*
* 🔴 Кабинет МТС показывает ОБЯЗАТЕЛЬНОЕ поле «Заголовок объявления» (до 40 знаков),
* когда рекламируется САЙТ, а не телеграм-канал. Робот про него не знал: «Продолжить»
* не срабатывало, кампания вставала на шаге «Объявление» с невнятной причиной, а в
* бою деньги к этому моменту уже списаны. Проверено живьём на кампаниях 2234454
* (ссылка на сайт — встала) и 2234462 (ссылка на канал — дошла до подтверждения).
*
* Поэтому портал обязан спросить заголовок ЗАРАНЕЕ — на создании черновика, до денег.
*/
uses(RefreshDatabase::class);
beforeEach(function () {
$this->tenant = Tenant::factory()->create();
$this->user = User::factory()->create(['tenant_id' => $this->tenant->id]);
$this->actingAs($this->user);
});
/** @return array<string, mixed> */
function tgHeadlinePayload(array $overrides = []): array
{
return array_merge([
'ad_text' => 'Приходите к нам',
'ad_link' => 'https://t.me/example_channel',
'audience_kind' => 'list',
'budget_cap_rub' => '1000.00',
'phones' => ['79990000001', '79990000002'],
], $overrides);
}
it('реклама САЙТА без заголовка — не создаём кампанию', function () {
$response = $this->postJson('/api/telegram/campaigns', tgHeadlinePayload([
'ad_link' => 'https://liderra.ru',
]));
$response->assertStatus(422)->assertJsonValidationErrors('ad_headline');
expect(Campaign::count())->toBe(0);
});
it('реклама САЙТА с заголовком — кампания создаётся, заголовок сохранён', function () {
$response = $this->postJson('/api/telegram/campaigns', tgHeadlinePayload([
'ad_link' => 'https://liderra.ru',
'ad_headline' => 'Лидерра — заявки для бизнеса',
]));
$response->assertCreated();
expect(Campaign::first()->ad_headline)->toBe('Лидерра — заявки для бизнеса');
});
it('реклама телеграм-канала — заголовок не нужен', function () {
$response = $this->postJson('/api/telegram/campaigns', tgHeadlinePayload());
$response->assertCreated();
expect(Campaign::first()->ad_headline)->toBeNull();
});
it('заголовок длиннее 40 знаков не принимается — кабинет режет молча', function () {
$response = $this->postJson('/api/telegram/campaigns', tgHeadlinePayload([
'ad_link' => 'https://liderra.ru',
'ad_headline' => str_repeat('я', 41),
]));
$response->assertStatus(422)->assertJsonValidationErrors('ad_headline');
});
it('ссылка на бота t.me тоже считается телеграмом — заголовок не требуем', function () {
$response = $this->postJson('/api/telegram/campaigns', tgHeadlinePayload([
'ad_link' => 'https://t.me/liderra_bot?start=1',
]));
$response->assertCreated();
});
it('заголовок доезжает до задания робота', function () {
config(['client_tg.robot.transport' => 'poll', 'client_tg.sandbox' => true]);
Queue::fake();
$this->postJson('/api/telegram/campaigns', tgHeadlinePayload([
'ad_link' => 'https://liderra.ru',
'ad_headline' => 'Лидерра',
]))->assertCreated();
$campaign = Campaign::first();
$this->postJson("/api/telegram/campaigns/{$campaign->id}/launch")->assertOk();
// Джоб в очереди подделан — выполняем его тело руками, как это делают
// соседние тесты канала: важно, что в задании робота есть заголовок.
(new RunTelegramCampaignJob($campaign->id, $this->tenant->id))->handle(
app(TelegramAudienceService::class),
app(TelegramRobotRunner::class),
);
expect(RobotJob::first()->payload['adHeadline'])->toBe('Лидерра');
});