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

123 lines
4.3 KiB
PHP
Raw Normal View History

<?php
declare(strict_types=1);
use App\Services\ClientTg\RobotResult;
use App\Services\ClientTg\TelegramRobotRunner;
/**
* Обёртка вызова робота (план §Сессия 2, задача 2.2). Робот ЗАМОКАН node-скриптами
* из tests/Fixtures/tg-robot, печатающими известный JSON. Проверяем: формирование
* task.json, разбор {ok,matched,launched,campaignId}, и что таймаут/мусорный вывод
* дают аккуратный ok:false, а не падение воркера.
*/
/** Указывает обёртку на конкретный fixture-скрипт вместо реального робота. */
function useRobotFixture(string $script, float $timeout = 30.0): void
{
config()->set('client_tg.robot', [
'node' => 'node',
'script' => base_path('tests/fixtures/tg-robot/'.$script),
'cwd' => base_path('tests/fixtures/tg-robot'),
'timeout' => $timeout,
]);
}
/** @return array<string, mixed> */
function sampleTaskParams(): array
{
return [
'mode' => 'draft',
'phonesFile' => '/tmp/phones-tg-test.txt',
'adText' => 'Приходите к нам',
'budgetRub' => '1000.00',
'buttonUrl' => 'https://example.test',
'ordCategory' => 'Размещение рекламы',
];
}
it('строит task.json со всеми полями задания робота', function () {
$runner = app(TelegramRobotRunner::class);
$payload = $runner->taskPayload(sampleTaskParams());
expect($payload)->toMatchArray([
'mode' => 'draft',
'phonesFile' => '/tmp/phones-tg-test.txt',
'adText' => 'Приходите к нам',
'budgetRub' => '1000.00',
'buttonUrl' => 'https://example.test',
'ordCategory' => 'Размещение рекламы',
]);
});
it('подставляет дефолты mode=draft и категорию ОРД', function () {
$runner = app(TelegramRobotRunner::class);
$payload = $runner->taskPayload([
'phonesFile' => '/tmp/x.txt',
'adText' => 'текст',
'budgetRub' => '500.00',
'buttonUrl' => 'https://a.test',
]);
expect($payload['mode'])->toBe('draft')
->and($payload['ordCategory'])->toBe('Размещение рекламы')
->and($payload['mediaFile'])->toBeNull();
});
it('запускает робота и разбирает успешный JSON', function () {
useRobotFixture('print-ok.js');
$runner = app(TelegramRobotRunner::class);
$res = $runner->run(sampleTaskParams());
expect($res)->toBeInstanceOf(RobotResult::class)
->and($res->ok)->toBeTrue()
->and($res->matched)->toBe(512)
->and($res->launched)->toBeFalse()
->and($res->campaignId)->toBe('draft');
});
it('штатный отказ робота (exit 1) → ok:false с причиной, без исключения', function () {
useRobotFixture('print-fail.js');
$runner = app(TelegramRobotRunner::class);
$res = $runner->run(sampleTaskParams());
expect($res->ok)->toBeFalse()
->and($res->step)->toBe('upload')
->and($res->reason)->toContain('Недостаточно номеров');
});
it('непарсабельный вывод → аккуратный ok:false, воркер не падает', function () {
useRobotFixture('print-garbage.js');
$runner = app(TelegramRobotRunner::class);
$res = $runner->run(sampleTaskParams());
expect($res->ok)->toBeFalse()
->and($res->reason)->not->toBeNull();
});
it('таймаут робота → аккуратный ok:false, воркер не падает', function () {
useRobotFixture('sleep.js', timeout: 1.0);
$runner = app(TelegramRobotRunner::class);
$res = $runner->run(sampleTaskParams());
expect($res->ok)->toBeFalse()
->and($res->reason)->not->toBeNull();
});
it('НЕ оставляет временный task-файл после запуска (чистка)', function () {
useRobotFixture('print-ok.js');
$runner = app(TelegramRobotRunner::class);
$before = glob(sys_get_temp_dir().'/tg-task-*.json') ?: [];
$runner->run(sampleTaskParams());
$after = glob(sys_get_temp_dir().'/tg-task-*.json') ?: [];
expect(count($after))->toBeLessThanOrEqual(count($before));
});