From da70e344724da4c4fd17a56a4787a3c9446efa51 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Wed, 3 Jun 2026 11:43:52 +0200 Subject: [PATCH] test(api): cover server private key updates Add feature coverage for updating a server from private_key_uuid, rejecting unknown or cross-team private keys, and preserving the existing key when the field is omitted. --- .../Feature/ServerUpdatePrivateKeyApiTest.php | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 tests/Feature/ServerUpdatePrivateKeyApiTest.php diff --git a/tests/Feature/ServerUpdatePrivateKeyApiTest.php b/tests/Feature/ServerUpdatePrivateKeyApiTest.php new file mode 100644 index 000000000..e3f315f24 --- /dev/null +++ b/tests/Feature/ServerUpdatePrivateKeyApiTest.php @@ -0,0 +1,94 @@ +set('app.maintenance.driver', 'file'); + config()->set('cache.default', 'array'); + + InstanceSettings::forceCreate(['id' => 0, 'is_api_enabled' => true]); + + $this->team = Team::factory()->create(); + $this->user = User::factory()->create(); + $this->team->members()->attach($this->user->id, ['role' => 'owner']); + session(['currentTeam' => $this->team]); + + $this->oldPrivateKey = createServerUpdatePrivateKeyApiKey($this->team, 'Old Key'); + $this->newPrivateKey = createServerUpdatePrivateKeyApiKey($this->team, 'New Key'); + + $this->server = Server::factory()->create([ + 'team_id' => $this->team->id, + 'private_key_id' => $this->oldPrivateKey->id, + ]); + + $token = $this->user->createToken('write-token', ['write']); + $token->accessToken->forceFill(['team_id' => $this->team->id])->save(); + $this->bearerToken = $token->plainTextToken; +}); + +function createServerUpdatePrivateKeyApiKey(Team $team, string $name): PrivateKey +{ + return PrivateKey::create([ + 'name' => $name, + 'private_key' => generateSSHKey('ed25519')['private'], + 'team_id' => $team->id, + ]); +} + +function patchServerUpdatePrivateKeyApi(object $test, Server $server, string $bearerToken, array $payload): TestResponse +{ + return $test->withHeaders([ + 'Authorization' => 'Bearer '.$bearerToken, + 'Content-Type' => 'application/json', + ])->patchJson('/api/v1/servers/'.$server->uuid, $payload); +} + +it('updates the server private key from private_key_uuid', function () { + patchServerUpdatePrivateKeyApi($this, $this->server, $this->bearerToken, [ + 'private_key_uuid' => $this->newPrivateKey->uuid, + ])->assertCreated() + ->assertJson(['uuid' => $this->server->uuid]); + + expect($this->server->fresh()->private_key_id)->toBe($this->newPrivateKey->id); +}); + +it('returns not found for an unknown private_key_uuid and leaves the key unchanged', function () { + patchServerUpdatePrivateKeyApi($this, $this->server, $this->bearerToken, [ + 'private_key_uuid' => 'unknown-private-key-uuid', + ])->assertNotFound() + ->assertJson(['message' => 'Private key not found.']); + + expect($this->server->fresh()->private_key_id)->toBe($this->oldPrivateKey->id); +}); + +it('does not allow attaching a private key from another team', function () { + $otherTeam = Team::factory()->create(); + $otherTeamPrivateKey = createServerUpdatePrivateKeyApiKey($otherTeam, 'Other Team Key'); + + patchServerUpdatePrivateKeyApi($this, $this->server, $this->bearerToken, [ + 'private_key_uuid' => $otherTeamPrivateKey->uuid, + ])->assertNotFound() + ->assertJson(['message' => 'Private key not found.']); + + expect($this->server->fresh()->private_key_id)->toBe($this->oldPrivateKey->id); +}); + +it('keeps the existing private key when private_key_uuid is omitted', function () { + patchServerUpdatePrivateKeyApi($this, $this->server, $this->bearerToken, [ + 'name' => 'Renamed Server', + ])->assertCreated() + ->assertJson(['uuid' => $this->server->uuid]); + + $server = $this->server->fresh(); + + expect($server->name)->toBe('Renamed Server') + ->and($server->private_key_id)->toBe($this->oldPrivateKey->id); +});