fix реклама за показы: предел веса картинки проверен по-настоящему, обход модерации не срывается целиком, робот не несёт токен на чужой адрес
Мелочи приёмочного листа v12 §8. Каждая правка с тестом; где защита уже стояла в коде — тест проверен вырезанием этой защиты. Предел веса картинки. Две прежние проверки были пустышками: сравнивали константу саму с собой и с тем же числом в ответе сервера. Вырезание правила max: оставляло обе зелёными. Настоящий тест грузит перевес и ждёт отказа — это четвёртая найденная пустышка за ветку. Обход модерации. Объявление без статуса и причина отказа длиннее колонки роняли запись в базу ВНЕ защиты, и обход обрывался на середине: остальные клиенты не узнавали, приняли их рекламу или отклонили, а деньги за отклонённый набор не возвращались. Запись ответа теперь под той же защитой, что и сеть; пустой статус не пишем вовсе, причину храним обрезанной. Робот. Адрес файла приходил в ответе сервера, а шли по нему со своим токеном без всякой сверки. Теперь адрес обязан вести на портал. Папка снимков экрана росла бесконечно, а на снимках видны логин и остаток счёта — старше двух недель убираются. Ещё: порядок посредников служебного канала — токен раньше служебного соединения; BannerGenerator больше не отдаёт молча файл тяжелее предела и берёт предел из общей константы; нулевой номер креатива ловится намеренно, а не случайно нестрогим сравнением. Портал 300/300, робот 60/60. Мест снятия заморозки денег по-прежнему четыре. Не тронуто намеренно: цена за 1000 показов и бюджет приходят от клиента — но это видимое поле мастера и принятое продуктовое решение, а не недосмотр. Решает владелец.
This commit is contained in:
@@ -121,6 +121,59 @@ it('waits while at least one banner ad is still under moderation', function () {
|
||||
expect($campaign->fresh()->status)->toBe(AdCampaign::STATUS_PENDING_MODERATION);
|
||||
});
|
||||
|
||||
/**
|
||||
* Обход модерации идёт по ВСЕМ кампаниям всех клиентов подряд. Значит любая беда на одной
|
||||
* кампании обязана остаться внутри неё: сорвётся обход — остальные клиенты не узнают, что
|
||||
* их реклама принята или отклонена, а деньги за отклонённый набор не вернутся.
|
||||
*
|
||||
* Здесь беда приходит не из сети (это уже прикрыто), а из САМОГО ответа Яндекса: объявление
|
||||
* пришло без статуса. Запись пустого статуса упирается в запрет базы, и обход обрывается
|
||||
* на середине.
|
||||
*/
|
||||
it('объявление без статуса не срывает обход остальных кампаний', function () {
|
||||
configureYandexForModeration();
|
||||
Http::fake(function ($request) {
|
||||
$ids = $request->data()['params']['SelectionCriteria']['Ids'] ?? [];
|
||||
|
||||
return Http::response(['result' => ['Ads' => in_array(6001, $ids, true)
|
||||
? [['Id' => 6001, 'State' => 'ON']] // статуса нет вовсе
|
||||
: [['Id' => 6002, 'State' => 'ON', 'StatusClarification' => null, 'Status' => 'ACCEPTED']],
|
||||
]]);
|
||||
});
|
||||
|
||||
[$broken, $brokenBanners] = makeModeratedCampaignWithBanners([6001], 601);
|
||||
[$healthy] = makeModeratedCampaignWithBanners([6002], 602);
|
||||
|
||||
(new SyncCampaignModerationJob)->handle();
|
||||
|
||||
// Кампания без статуса осталась ждать — и не утащила за собой соседнюю.
|
||||
expect($broken->fresh()->status)->toBe(AdCampaign::STATUS_PENDING_MODERATION)
|
||||
->and(AdCampaignBanner::find($brokenBanners[0]->id)->moderation_status)->toBe(AdCampaignBanner::MOD_MODERATION)
|
||||
->and($healthy->fresh()->status)->toBe(AdCampaign::STATUS_RUNNING);
|
||||
});
|
||||
|
||||
/**
|
||||
* Причина отказа у Яндекса бывает длинной — там перечисляют все претензии модератора списком.
|
||||
* В нашей колонке 255 знаков. Длинная причина упирается в базу и обрывает тот же обход.
|
||||
* Причину показываем клиенту, поэтому храним сколько влезает, а не теряем целиком.
|
||||
*/
|
||||
it('слишком длинная причина отказа не срывает обход', function () {
|
||||
configureYandexForModeration();
|
||||
$longReason = str_repeat('Текст на баннере не читается. ', 40); // 1200 знаков
|
||||
Http::fake(['*/json/v5/ads' => Http::response(['result' => ['Ads' => [
|
||||
['Id' => 6011, 'State' => 'OFF', 'StatusClarification' => $longReason, 'Status' => 'REJECTED'],
|
||||
]]])]);
|
||||
|
||||
[$campaign, $banners] = makeModeratedCampaignWithBanners([6011], 611);
|
||||
|
||||
(new SyncCampaignModerationJob)->handle();
|
||||
|
||||
$reason = AdCampaignBanner::find($banners[0]->id)->moderation_reason;
|
||||
expect($campaign->fresh()->status)->toBe(AdCampaign::STATUS_REJECTED)
|
||||
->and(mb_strlen((string) $reason))->toBe(255)
|
||||
->and($reason)->toStartWith('Текст на баннере не читается.');
|
||||
});
|
||||
|
||||
// ВЫХОД 2 остаётся ровно один: разморозка зовётся только когда отклонены ВСЕ
|
||||
// объявления набора. Пока живо хоть одно — деньги остаются замороженными.
|
||||
it('не снимает заморозку, пока принято хотя бы одно объявление набора', function () {
|
||||
|
||||
Reference in New Issue
Block a user