Files
portal/app/tests/Feature/ClientTg/DataStartaTest.php
T

145 lines
6.7 KiB
PHP
Raw Normal View History

<?php
declare(strict_types=1);
use App\Models\ClientTg\Campaign;
use App\Models\Tenant;
use App\Models\User;
use App\Services\ClientTg\RobotResult;
use App\Services\ClientTg\TelegramModerationVerdictApplier;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
/**
* Дата старта рекламы: клиент должен видеть, С КАКОГО ДНЯ пойдут показы.
*
* 🔴 Откуда взялось. Портал не передаёт кабинету МТС ни одной даты — их ставит сам
* кабинет своими умолчаниями. Живьём 06.08.2026 (кампания 2237821) старт оказался
* ЗАВТРАШНИМ, а экран обещал клиенту показы «7 дней», подразумевая сегодня. Клиент
* ждёт рекламу сегодня, её нет, и объяснить это нечем.
*
* Решение владельца — самое дешёвое: дат не выбирать, а ПОКАЗАТЬ ту, что поставил
* кабинет. Робот читает строку кампании в списке раз в час (`read-status`) — дата
* стоит в её начале и приезжает вместе с вердиктом, без единого лишнего захода.
*
* 🔑 Главный случай — именно «ещё на модерации»: дата нужна клиенту РАНЬШЕ вердикта.
* Применение вердикта на «moderating» выходит рано и кампанию не трогает, поэтому
* сохранение даты обязано стоять ДО этого выхода — иначе поле не заполнится никогда.
*/
uses(RefreshDatabase::class);
function kampaniyaSDatoy(int $tenantId): Campaign
{
return Campaign::query()->create([
'tenant_id' => $tenantId,
'status' => Campaign::STATUS_MODERATING,
'ad_text' => 'Приходите к нам за услугой',
'ad_link' => 'https://example.test/promo',
'ord_category' => 'Размещение рекламы',
'budget_cap_rub' => '500.00',
'audience_kind' => Campaign::AUDIENCE_LIST,
'planned_count' => 2,
'estimated_cost_rub' => '315.00',
'mts_campaign_id' => '2237821',
'created_by' => 1,
]);
}
beforeEach(function () {
config()->set('client_tg.sandbox', true);
$this->tenant = Tenant::factory()->create(['balance_rub' => '1000.00']);
DB::statement('SET LOCAL app.current_tenant_id = '.$this->tenant->id);
$this->campaign = kampaniyaSDatoy($this->tenant->id);
});
it('дата старта сохраняется, ПОКА кампания ещё на модерации', function () {
app(TelegramModerationVerdictApplier::class)->apply(
$this->tenant->id,
$this->campaign->id,
RobotResult::fromRobotJson([
'ok' => true,
'moderationStatus' => 'moderating',
'startDate' => '2026-08-07',
]),
true,
);
$fresh = Campaign::find($this->campaign->id);
expect($fresh->starts_on?->toDateString())->toBe('2026-08-07');
// Вердикта нет — статус трогать не смеем.
expect($fresh->status)->toBe(Campaign::STATUS_MODERATING);
});
it('дата сохраняется и вместе с одобрением', function () {
app(TelegramModerationVerdictApplier::class)->apply(
$this->tenant->id,
$this->campaign->id,
RobotResult::fromRobotJson([
'ok' => true,
'moderationStatus' => 'approved',
'startDate' => '2026-08-07',
]),
true,
);
$fresh = Campaign::find($this->campaign->id);
expect($fresh->starts_on?->toDateString())->toBe('2026-08-07');
expect($fresh->status)->toBe(Campaign::STATUS_LAUNCHED);
});
// 🪤 Робот дату читает «как получится»: кабинет мог показать строку без даты. Записать
// в этом случае пустоту значило бы стереть уже известную клиенту дату и превратить
// экран в молчащий — хуже, чем было.
it('робот не прислал дату — прежнюю не затираем', function () {
$this->campaign->starts_on = '2026-08-07';
$this->campaign->save();
app(TelegramModerationVerdictApplier::class)->apply(
$this->tenant->id,
$this->campaign->id,
RobotResult::fromRobotJson(['ok' => true, 'moderationStatus' => 'moderating']),
true,
);
expect(Campaign::find($this->campaign->id)->starts_on?->toDateString())->toBe('2026-08-07');
});
// 🪤 Мусор вместо даты отсекается САМИМ разбором отчёта, а не тем, что ниже по течению
// его проглотит защита. Первая версия этого теста смотрела на колонку в базе и была
// ЛОЖНЫМ сторожем: вырезание проверки её не покрасило — мусор до базы доезжал, база
// ругалась, а ошибку глотал try/catch применения вердикта. Спрашиваем прямо у разбора.
it('мусор вместо даты отсекается разбором отчёта робота', function (mixed $syryo) {
expect(RobotResult::fromRobotJson([
'ok' => true,
'moderationStatus' => 'moderating',
'startDate' => $syryo,
])->startDate)->toBeNull();
})->with([
'слово вместо даты' => 'завтра',
'наш формат наизнанку' => '07.08.2026',
'дня такого не бывает' => '2026-02-31',
'месяца такого не бывает' => '2026-13-01',
'хвост после даты' => '2026-08-07 09:00',
'не строка вовсе' => 20260807,
]);
it('правильная дата разбор проходит', function () {
expect(RobotResult::fromRobotJson([
'ok' => true,
'moderationStatus' => 'moderating',
'startDate' => '2026-08-07',
])->startDate)->toBe('2026-08-07');
});
it('клиент видит дату старта в списке своих кампаний', function () {
$this->campaign->starts_on = '2026-08-07';
$this->campaign->save();
$user = User::factory()->create(['tenant_id' => $this->tenant->id]);
$this->actingAs($user)
->getJson('/api/telegram/campaigns')
->assertOk()
->assertJsonPath('campaigns.0.starts_on', '2026-08-07');
});