From 27b8f343923b32b966bbac9783be9fbecfcfd353 Mon Sep 17 00:00:00 2001 From: michele Date: Mon, 10 Aug 2026 09:11:18 +0200 Subject: [PATCH] feat(ops): log detailed wrapper path, stdin length, env keys, stdout and stderr when proc_open wrapper exits non-zero --- .../Commands/ControlTowerPollCommand.php | 17 ++++++++++- tests/Feature/ControlTowerPollCommandTest.php | 28 +++++++++++++++---- 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/app/Console/Commands/ControlTowerPollCommand.php b/app/Console/Commands/ControlTowerPollCommand.php index 8b585d6..39bca30 100644 --- a/app/Console/Commands/ControlTowerPollCommand.php +++ b/app/Console/Commands/ControlTowerPollCommand.php @@ -106,8 +106,23 @@ public function handle(): int $exitCode = proc_close($process); + $stdinLength = strlen($promptInput); + $envKeys = implode(', ', array_keys($env)); + if ($exitCode !== 0) { - $this->error("❌ WRAPPER AGY FALLITO (Exit Code: {$exitCode}): {$stderr}"); + $this->error('❌ WRAPPER AGY FALLITO'); + $this->line("WRAPPER_PATH: {$wrapperScript}"); + $this->line("STDIN_LENGTH: {$stdinLength}"); + $this->line("ENV_KEYS: {$envKeys}"); + $this->line("EXIT_CODE: {$exitCode}"); + $this->line('STDOUT:'); + if ($stdout !== '') { + $this->line(trim($stdout)); + } + $this->line('STDERR:'); + if ($stderr !== '') { + $this->line(trim($stderr)); + } return self::FAILURE; } diff --git a/tests/Feature/ControlTowerPollCommandTest.php b/tests/Feature/ControlTowerPollCommandTest.php index 26219d2..ce0337c 100644 --- a/tests/Feature/ControlTowerPollCommandTest.php +++ b/tests/Feature/ControlTowerPollCommandTest.php @@ -76,7 +76,7 @@ }); }); -it('does not publish report when wrapper exit code is non-zero', function () { +it('does not publish report when wrapper exit code is non-zero and displays stdout and stderr', function () { $taskId = 'task-test-err-001'; Http::fake([ @@ -84,18 +84,34 @@ ]); $mockScript = tempnam(sys_get_temp_dir(), 'mock_wrapper_err_') . '.sh'; - file_put_contents($mockScript, "#!/usr/bin/env bash\necho 'Fatal wrapper error' >&2\nexit 1\n"); + $jsonErrorOutput = json_encode(['ok' => false, 'missing' => ['task_id']], JSON_PRETTY_PRINT); + + $scriptContent = <<&2 +cat << JSON +{$jsonErrorOutput} +JSON +exit 1 +BASH; + + file_put_contents($mockScript, $scriptContent); chmod($mockScript, 0755); - $exitCode = $this->artisan('netgescon:control-tower-poll', [ + $this->artisan('netgescon:control-tower-poll', [ '--task-id' => $taskId, '--wrapper-script' => $mockScript, - ])->run(); + ]) + ->expectsOutputToContain('WRAPPER AGY FALLITO') + ->expectsOutputToContain('EXIT_CODE: 1') + ->expectsOutputToContain('STDOUT:') + ->expectsOutputToContain('"ok": false') + ->expectsOutputToContain('STDERR:') + ->expectsOutputToContain('Standard error log message') + ->assertExitCode(Command::FAILURE); @unlink($mockScript); - expect($exitCode)->toBe(Command::FAILURE); - Http::assertNotSent(function ($request) { return str_contains($request->url(), '/api/reports'); });