From 79174b749d8e59bf8cc5d3828ecc7ae051837705 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Thu, 30 Apr 2026 14:48:48 +0200 Subject: [PATCH] refactor(helpers): extract STANDALONE_DATABASE_MODELS registry, add tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace 8× repeated per-type if-blocks in `queryDatabaseByUuidWithinTeam` and `queryResourcesByUuid` with a single loop over the new `STANDALONE_DATABASE_MODELS` constant. Add unit tests to guard the registry against drift (keys mirror `DATABASE_TYPES`, every entry is a valid Eloquent model with `team()`), and feature tests covering team-ownership, wrong-team, and unknown-UUID cases for `queryDatabaseByUuidWithinTeam`. --- bootstrap/helpers/constants.php | 19 +++++ bootstrap/helpers/shared.php | 75 +++---------------- .../QueryDatabaseByUuidWithinTeamTest.php | 70 +++++++++++++++++ tests/Unit/StandaloneDatabaseRegistryTest.php | 45 +++++++++++ 4 files changed, 145 insertions(+), 64 deletions(-) create mode 100644 tests/Feature/QueryDatabaseByUuidWithinTeamTest.php create mode 100644 tests/Unit/StandaloneDatabaseRegistryTest.php diff --git a/bootstrap/helpers/constants.php b/bootstrap/helpers/constants.php index bae2573de..043a3346d 100644 --- a/bootstrap/helpers/constants.php +++ b/bootstrap/helpers/constants.php @@ -1,7 +1,26 @@ '; const DATABASE_TYPES = ['postgresql', 'redis', 'mongodb', 'mysql', 'mariadb', 'keydb', 'dragonfly', 'clickhouse']; +const STANDALONE_DATABASE_MODELS = [ + 'postgresql' => StandalonePostgresql::class, + 'redis' => StandaloneRedis::class, + 'mongodb' => StandaloneMongodb::class, + 'mysql' => StandaloneMysql::class, + 'mariadb' => StandaloneMariadb::class, + 'keydb' => StandaloneKeydb::class, + 'dragonfly' => StandaloneDragonfly::class, + 'clickhouse' => StandaloneClickhouse::class, +]; const VALID_CRON_STRINGS = [ 'every_minute' => '* * * * *', 'hourly' => '0 * * * *', diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php index 9f0f2cd73..f76995c6f 100644 --- a/bootstrap/helpers/shared.php +++ b/bootstrap/helpers/shared.php @@ -1058,44 +1058,17 @@ function getResourceByUuid(string $uuid, ?int $teamId = null) } function queryDatabaseByUuidWithinTeam(string $uuid, string $teamId) { - $postgresql = StandalonePostgresql::whereUuid($uuid)->first(); - if ($postgresql && $postgresql->team()->id == $teamId) { - return $postgresql->unsetRelation('environment'); - } - $redis = StandaloneRedis::whereUuid($uuid)->first(); - if ($redis && $redis->team()->id == $teamId) { - return $redis->unsetRelation('environment'); - } - $mongodb = StandaloneMongodb::whereUuid($uuid)->first(); - if ($mongodb && $mongodb->team()->id == $teamId) { - return $mongodb->unsetRelation('environment'); - } - $mysql = StandaloneMysql::whereUuid($uuid)->first(); - if ($mysql && $mysql->team()->id == $teamId) { - return $mysql->unsetRelation('environment'); - } - $mariadb = StandaloneMariadb::whereUuid($uuid)->first(); - if ($mariadb && $mariadb->team()->id == $teamId) { - return $mariadb->unsetRelation('environment'); - } - $keydb = StandaloneKeydb::whereUuid($uuid)->first(); - if ($keydb && $keydb->team()->id == $teamId) { - return $keydb->unsetRelation('environment'); - } - $dragonfly = StandaloneDragonfly::whereUuid($uuid)->first(); - if ($dragonfly && $dragonfly->team()->id == $teamId) { - return $dragonfly->unsetRelation('environment'); - } - $clickhouse = StandaloneClickhouse::whereUuid($uuid)->first(); - if ($clickhouse && $clickhouse->team()->id == $teamId) { - return $clickhouse->unsetRelation('environment'); + foreach (STANDALONE_DATABASE_MODELS as $modelClass) { + $database = $modelClass::whereUuid($uuid)->first(); + if ($database && $database->team()->id == $teamId) { + return $database->unsetRelation('environment'); + } } return null; } function queryResourcesByUuid(string $uuid) { - $resource = null; $application = Application::whereUuid($uuid)->first(); if ($application) { return $application; @@ -1104,37 +1077,11 @@ function queryResourcesByUuid(string $uuid) if ($service) { return $service; } - $postgresql = StandalonePostgresql::whereUuid($uuid)->first(); - if ($postgresql) { - return $postgresql; - } - $redis = StandaloneRedis::whereUuid($uuid)->first(); - if ($redis) { - return $redis; - } - $mongodb = StandaloneMongodb::whereUuid($uuid)->first(); - if ($mongodb) { - return $mongodb; - } - $mysql = StandaloneMysql::whereUuid($uuid)->first(); - if ($mysql) { - return $mysql; - } - $mariadb = StandaloneMariadb::whereUuid($uuid)->first(); - if ($mariadb) { - return $mariadb; - } - $keydb = StandaloneKeydb::whereUuid($uuid)->first(); - if ($keydb) { - return $keydb; - } - $dragonfly = StandaloneDragonfly::whereUuid($uuid)->first(); - if ($dragonfly) { - return $dragonfly; - } - $clickhouse = StandaloneClickhouse::whereUuid($uuid)->first(); - if ($clickhouse) { - return $clickhouse; + foreach (STANDALONE_DATABASE_MODELS as $modelClass) { + $database = $modelClass::whereUuid($uuid)->first(); + if ($database) { + return $database; + } } // Check for ServiceDatabase by its own UUID @@ -1143,7 +1090,7 @@ function queryResourcesByUuid(string $uuid) return $serviceDatabase; } - return $resource; + return null; } function generateTagDeployWebhook($tag_name) { diff --git a/tests/Feature/QueryDatabaseByUuidWithinTeamTest.php b/tests/Feature/QueryDatabaseByUuidWithinTeamTest.php new file mode 100644 index 000000000..db7eb16b2 --- /dev/null +++ b/tests/Feature/QueryDatabaseByUuidWithinTeamTest.php @@ -0,0 +1,70 @@ +teamA = Team::factory()->create(); + $this->teamB = Team::factory()->create(); + + $this->serverA = Server::factory()->create(['team_id' => $this->teamA->id]); + $this->destinationA = StandaloneDocker::where('server_id', $this->serverA->id)->first(); + $this->projectA = Project::factory()->create(['team_id' => $this->teamA->id]); + $this->envA = Environment::factory()->create(['project_id' => $this->projectA->id]); +}); + +test('queryDatabaseByUuidWithinTeam returns database when team owns it', function () { + $database = StandalonePostgresql::create([ + 'name' => 'pg-team-a', + 'image' => 'postgres:15-alpine', + 'postgres_user' => 'postgres', + 'postgres_password' => 'password', + 'postgres_db' => 'postgres', + 'environment_id' => $this->envA->id, + 'destination_id' => $this->destinationA->id, + 'destination_type' => $this->destinationA->getMorphClass(), + ]); + + $found = queryDatabaseByUuidWithinTeam($database->uuid, (string) $this->teamA->id); + + expect($found)->not->toBeNull(); + expect($found->uuid)->toBe($database->uuid); + expect($found)->toBeInstanceOf(StandalonePostgresql::class); +}); + +test('queryDatabaseByUuidWithinTeam returns null when team does not own the database', function () { + $database = StandalonePostgresql::create([ + 'name' => 'pg-team-a', + 'image' => 'postgres:15-alpine', + 'postgres_user' => 'postgres', + 'postgres_password' => 'password', + 'postgres_db' => 'postgres', + 'environment_id' => $this->envA->id, + 'destination_id' => $this->destinationA->id, + 'destination_type' => $this->destinationA->getMorphClass(), + ]); + + $found = queryDatabaseByUuidWithinTeam($database->uuid, (string) $this->teamB->id); + + expect($found)->toBeNull(); +}); + +test('queryDatabaseByUuidWithinTeam returns null for unknown uuid', function () { + $found = queryDatabaseByUuidWithinTeam('does-not-exist', (string) $this->teamA->id); + + expect($found)->toBeNull(); +}); + +test('queryDatabaseByUuidWithinTeam can query every registered standalone database type without error', function () { + foreach (STANDALONE_DATABASE_MODELS as $slug => $modelClass) { + $count = $modelClass::query()->whereUuid('non-existent-uuid')->count(); + expect($count)->toBe(0, "{$modelClass} ({$slug}) failed whereUuid() smoke query"); + } +}); diff --git a/tests/Unit/StandaloneDatabaseRegistryTest.php b/tests/Unit/StandaloneDatabaseRegistryTest.php new file mode 100644 index 000000000..7c56d5f8d --- /dev/null +++ b/tests/Unit/StandaloneDatabaseRegistryTest.php @@ -0,0 +1,45 @@ +not->toBeEmpty(); + + $onDisk = collect($files) + ->map(fn (string $path) => 'App\\Models\\'.basename($path, '.php')) + ->reject(fn (string $class) => $class === StandaloneDocker::class) + ->sort() + ->values() + ->all(); + + $registered = collect(STANDALONE_DATABASE_MODELS)->values()->sort()->values()->all(); + + expect($registered)->toBe( + $onDisk, + 'STANDALONE_DATABASE_MODELS in bootstrap/helpers/constants.php is out of sync with the App\\Models\\Standalone* classes on disk. ' + .'Add the missing model(s) to the registry (and to DATABASE_TYPES) so MCP/API helpers can resolve them.' + ); +}); + +test('STANDALONE_DATABASE_MODELS keys mirror DATABASE_TYPES', function () { + expect(array_keys(STANDALONE_DATABASE_MODELS))->toEqualCanonicalizing(DATABASE_TYPES); +}); + +test('every STANDALONE_DATABASE_MODELS entry is an Eloquent model with whereUuid scope', function () { + foreach (STANDALONE_DATABASE_MODELS as $slug => $modelClass) { + expect(class_exists($modelClass))->toBeTrue("{$slug} maps to non-existent class {$modelClass}"); + expect(is_subclass_of($modelClass, Model::class)) + ->toBeTrue("{$modelClass} is not an Eloquent model"); + expect(method_exists($modelClass, 'team')) + ->toBeTrue("{$modelClass} is missing team() accessor required by queryDatabaseByUuidWithinTeam()"); + } +});