Update processrunner. Closes #1867

This commit is contained in:
DariusIII
2026-08-07 08:47:47 +02:00
parent be6c49245b
commit dbbe5e6f56
3 changed files with 87 additions and 7 deletions
+1 -1
View File
@@ -305,7 +305,7 @@ class ForkingService
protected function executeCommand(string $command): string
{
$process = Process::fromShellCommandline($command);
$process->setTimeout(1800);
$process->setTimeout((int) config('nntmux.multiprocessing_max_child_time', 1800));
$process->run(function ($type, $buffer) {
if ($type === Process::ERR) {
echo $buffer;
+16 -6
View File
@@ -5,6 +5,8 @@ declare(strict_types=1);
namespace App\Services\Runners;
use Illuminate\Support\Facades\Log;
use RuntimeException;
use Symfony\Component\Process\Exception\ProcessTimedOutException;
use Symfony\Component\Process\Process;
abstract class BaseRunner
@@ -141,12 +143,20 @@ abstract class BaseRunner
protected function executeCommand(string $command): string
{
$process = Process::fromShellCommandline($command);
$process->setTimeout(1800);
$process->run(function ($type, $buffer) {
if ($type === Process::ERR) {
echo $buffer;
}
});
$process->setTimeout($this->concurrencyTimeout());
try {
$process->run(function ($type, $buffer) {
if ($type === Process::ERR) {
echo $buffer;
}
});
} catch (ProcessTimedOutException $e) {
// Rethrow as RuntimeException: Laravel's Concurrency ProcessDriver cannot
// reconstruct ProcessTimedOutException (its constructor requires a Process
// object), which would otherwise surface as an unrelated TypeError.
throw new RuntimeException($e->getMessage());
}
return $process->getOutput();
}
@@ -0,0 +1,70 @@
<?php
declare(strict_types=1);
namespace Tests\Unit\Services\Runners;
use App\Services\Runners\BaseRunner;
use PHPUnit\Framework\Attributes\Test;
use RuntimeException;
use Tests\TestCase;
class BaseRunnerTest extends TestCase
{
#[Test]
public function execute_command_returns_output_on_success(): void
{
$runner = new BaseRunnerTestDouble;
$this->assertSame('hello', trim($runner->runCommand('echo hello')));
}
#[Test]
public function execute_command_throws_runtime_exception_with_clear_message_on_timeout(): void
{
config(['nntmux.concurrency_timeout' => 1]);
$runner = new BaseRunnerTestDouble;
try {
$runner->runCommand('sleep 5');
$this->fail('Expected RuntimeException was not thrown');
} catch (RuntimeException $e) {
// Laravel's Concurrency ProcessDriver cannot reconstruct
// ProcessTimedOutException, so executeCommand() must surface a
// RuntimeException carrying the original timeout message instead.
$this->assertStringContainsString('exceeded the timeout', $e->getMessage());
}
}
#[Test]
public function concurrency_timeout_prefers_concurrency_timeout_config(): void
{
config(['nntmux.concurrency_timeout' => 60]);
config(['nntmux.multiprocessing_max_child_time' => 42]);
$this->assertSame(60, (new BaseRunnerTestDouble)->timeout());
}
#[Test]
public function concurrency_timeout_falls_back_to_multiprocessing_max_child_time(): void
{
config(['nntmux.concurrency_timeout' => null]);
config(['nntmux.multiprocessing_max_child_time' => 42]);
$this->assertSame(42, (new BaseRunnerTestDouble)->timeout());
}
}
class BaseRunnerTestDouble extends BaseRunner
{
public function runCommand(string $command): string
{
return $this->executeCommand($command);
}
public function timeout(): int
{
return $this->concurrencyTimeout();
}
}