$name, 'ip' => $ip, 'ssh_port' => 22, 'status' => 'online']); } public function test_runs_the_command_on_every_server_and_collects_results(): void { $a = $this->server('a', '10.0.0.1'); $b = $this->server('b', '10.0.0.2'); $fleet = Mockery::mock(FleetService::class); $fleet->shouldReceive('runPlain')->twice()->andReturn(['ok' => true, 'output' => 'done']); $results = (new CommandRunner($fleet))->run('uptime', new Collection([$a, $b])); $this->assertCount(2, $results); $this->assertSame('a', $results[0]['server']); $this->assertTrue($results[0]['ok']); $this->assertSame('done', $results[0]['output']); } public function test_one_server_failing_does_not_abort_the_batch(): void { $a = $this->server('a', '10.0.0.1'); $b = $this->server('b', '10.0.0.2'); $fleet = Mockery::mock(FleetService::class); $fleet->shouldReceive('runPlain')->once()->with($a, Mockery::any(), Mockery::any())->andThrow(new \RuntimeException('ssh timeout')); $fleet->shouldReceive('runPlain')->once()->with($b, Mockery::any(), Mockery::any())->andReturn(['ok' => true, 'output' => 'ok']); $results = (new CommandRunner($fleet))->run('uptime', new Collection([$a, $b])); $this->assertFalse($results[0]['ok']); // a failed $this->assertStringContainsString('ssh timeout', $results[0]['output']); $this->assertTrue($results[1]['ok']); // b still ran } public function test_a_nonzero_exit_is_reported_as_not_ok(): void { $a = $this->server('a', '10.0.0.1'); $fleet = Mockery::mock(FleetService::class); $fleet->shouldReceive('runPlain')->once()->andReturn(['ok' => false, 'output' => 'command not found']); $results = (new CommandRunner($fleet))->run('nope', new Collection([$a])); $this->assertFalse($results[0]['ok']); } public function test_output_is_byte_capped_and_utf8_scrubbed(): void { $a = $this->server('a', '10.0.0.1'); $fleet = Mockery::mock(FleetService::class); // 300 KiB of output + an invalid UTF-8 byte → must be capped and scrubbed (no broken snapshot). $fleet->shouldReceive('runPlain')->once()->andReturn(['ok' => true, 'output' => str_repeat('x', 300000)."\xB1"]); $results = (new CommandRunner($fleet))->run('cat big', new Collection([$a])); $this->assertLessThanOrEqual(262144, strlen($results[0]['output'])); $this->assertSame($results[0]['output'], mb_scrub($results[0]['output'], 'UTF-8')); // already valid UTF-8 } }