'box', 'ip' => '10.0.0.5', 'ssh_port' => 22, 'status' => 'online']); $server->credential()->create(['username' => 'admin', 'auth_type' => 'password', 'secret' => 'pw-secret']); return $server->fresh('credential'); } public function test_happy_path_installs_key_switches_credential_and_disables_password(): void { $server = $this->passwordServer(); $fleet = Mockery::mock(FleetService::class); $fleet->shouldReceive('addAuthorizedKey')->once(); $fleet->shouldReceive('testConnection')->once()->andReturn(['ok' => true, 'error' => null]); $hardening = Mockery::mock(HardeningService::class); $hardening->shouldReceive('apply')->once()->with(Mockery::type(Server::class), 'ssh_password', false) ->andReturn(['ok' => true, 'output' => '']); $result = (new SshKeyProvisioner($fleet, $hardening))->enableKeyOnlyAccess($server); $this->assertTrue($result['ok']); $this->assertNotEmpty($result['privateKey']); $this->assertStringContainsString('ssh-ed25519', $result['publicKey']); $cred = $server->fresh('credential')->credential; $this->assertSame('key', $cred->auth_type); $this->assertStringContainsString('PRIVATE KEY', $cred->secret); // now holds the private PEM } public function test_verify_failure_rolls_back_the_credential_and_does_not_disable_password(): void { $server = $this->passwordServer(); $fleet = Mockery::mock(FleetService::class); $fleet->shouldReceive('addAuthorizedKey')->once(); $fleet->shouldReceive('testConnection')->once()->andReturn(['ok' => false, 'error' => 'auth failed']); $hardening = Mockery::mock(HardeningService::class); $hardening->shouldNotReceive('apply'); // never disable password if the key didn't verify $result = (new SshKeyProvisioner($fleet, $hardening))->enableKeyOnlyAccess($server); $this->assertFalse($result['ok']); $this->assertNotEmpty($result['error']); $cred = $server->fresh('credential')->credential; $this->assertSame('password', $cred->auth_type, 'credential restored to password'); $this->assertSame('pw-secret', $cred->secret, 'original secret restored'); } public function test_already_key_credential_skips_keygen_and_just_disables_password(): void { $server = Server::create(['name' => 'box2', 'ip' => '10.0.0.6', 'ssh_port' => 22, 'status' => 'online']); $server->credential()->create(['username' => 'admin', 'auth_type' => 'key', 'secret' => 'EXISTING-PRIVATE-KEY']); $server = $server->fresh('credential'); $fleet = Mockery::mock(FleetService::class); $fleet->shouldNotReceive('addAuthorizedKey'); // already key — no new key generated $fleet->shouldNotReceive('testConnection'); $hardening = Mockery::mock(HardeningService::class); $hardening->shouldReceive('apply')->once()->with(Mockery::type(Server::class), 'ssh_password', false) ->andReturn(['ok' => true, 'output' => '']); $result = (new SshKeyProvisioner($fleet, $hardening))->enableKeyOnlyAccess($server); $this->assertTrue($result['ok']); $this->assertArrayNotHasKey('privateKey', $result); // nothing new to reveal $this->assertSame('EXISTING-PRIVATE-KEY', $server->fresh('credential')->credential->secret); } public function test_no_credential_returns_error(): void { $server = Server::create(['name' => 'box3', 'ip' => '10.0.0.7', 'ssh_port' => 22, 'status' => 'online']); $result = (new SshKeyProvisioner(Mockery::mock(FleetService::class), Mockery::mock(HardeningService::class))) ->enableKeyOnlyAccess($server); $this->assertFalse($result['ok']); } }