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(); } }