Files
portal/app/tests/Support/Imitation/LeadInjector.php
T

137 lines
5.3 KiB
PHP

<?php
declare(strict_types=1);
namespace Tests\Support\Imitation;
use App\Jobs\RouteSupplierLeadJob;
use App\Models\SupplierLead;
/**
* Инъектор синтетических заявок для имитационного стенда (Phase 1).
*
* Создаёт SupplierLead в БД и синхронно прогоняет RouteSupplierLeadJob —
* без HTTP-слоя, минуя secret/IP/rate-limit SupplierWebhookController.
*
* raw_payload формируется с теми же ключами (vid/project/phone/time/tag),
* что контроллер кладёт из $request->validate(). platform парсится по тому же
* правилу: B[123]_ → B1/B2/B3, иначе DIRECT.
*
* Используется исключительно в тест-инфраструктуре (Tests-namespace).
*
* Task 2 — Phase 1 Portal Client Imitation Harness.
* Spec: docs/superpowers/specs/2026-06-03-portal-client-imitation-phase1-design.md
*/
final class LeadInjector
{
/**
* Инъектировать заявку с сигналом «сайт».
*
* @param string $domain Домен сигнала (например, 'vashinvestor.ru').
* Составляет identifier в project-поле: "{$platform}_{$domain}".
* @param string $phone Телефон в формате 7XXXXXXXXXX.
* @param string|null $tag Тег региона (например, 'Москва'); может быть null.
* @param string $platform Префикс платформы: 'B1', 'B2', 'B3' или иное (→ DIRECT).
* @param int|null $vid Внешний ID заявки поставщика. Если null — генерируется уникальный.
*/
public function site(
string $domain,
string $phone,
?string $tag = null,
string $platform = 'B1',
?int $vid = null,
): SupplierLead {
$project = "{$platform}_{$domain}";
return $this->inject($project, $phone, $tag, $platform, $vid);
}
/**
* Инъектировать заявку с сигналом «звонок».
*
* @param string $number Номер телефона для call-сигнала (7XXXXXXXXXX).
* Составляет identifier в project-поле: "{$platform}_{$number}".
* @param string $phone Телефон звонящего в формате 7XXXXXXXXXX.
* @param string|null $tag Тег региона; может быть null.
* @param string $platform Префикс платформы: 'B1', 'B2', 'B3' или иное (→ DIRECT).
* @param int|null $vid Внешний ID заявки поставщика. Если null — генерируется уникальный.
*/
public function call(
string $number,
string $phone,
?string $tag = null,
string $platform = 'B1',
?int $vid = null,
): SupplierLead {
$project = "{$platform}_{$number}";
return $this->inject($project, $phone, $tag, $platform, $vid);
}
/**
* Общий внутренний метод создания SupplierLead + синхронный dispatch Job.
*
* raw_payload содержит ровно те ключи, что SupplierWebhookController кладёт
* из $request->validate(): vid, project, phone, time, tag (null → не добавляем,
* чтобы не нарушить nullable-контракт RouteSupplierLeadJob).
*
* platform парсится по тому же правилу, что Controller::parsePlatform():
* /^(B[123])_/ → B1/B2/B3, иначе DIRECT.
*/
private function inject(
string $project,
string $phone,
?string $tag,
string $platform,
?int $vid,
): SupplierLead {
$resolvedVid = $vid ?? $this->generateVid();
$parsedPlatform = $this->parsePlatform($project);
$rawPayload = [
'vid' => $resolvedVid,
'project' => $project,
'phone' => $phone,
'time' => time(),
];
if ($tag !== null) {
$rawPayload['tag'] = $tag;
}
$lead = SupplierLead::create([
'platform' => $parsedPlatform,
'raw_payload' => $rawPayload,
'vid' => $resolvedVid,
'phone' => $phone,
'received_at' => now(),
'source' => 'webhook',
]);
RouteSupplierLeadJob::dispatchSync($lead->id);
return $lead->refresh();
}
/**
* Парсит platform из project-поля — идентично Controller::parsePlatform().
* B[123]_ → B1/B2/B3, иначе DIRECT.
*/
private function parsePlatform(string $project): string
{
if (preg_match('/^(B[123])_/', $project, $m) === 1) {
return $m[1];
}
return 'DIRECT';
}
/**
* Генерирует уникальный vid для использования, когда явный vid не передан.
* Диапазон 1_000_000_000..9_999_999_999 — вне реальных vid поставщика (< 10^9).
*/
private function generateVid(): int
{
return random_int(1_000_000_000, 9_999_999_999);
}
}