refactor(helpers): extract STANDALONE_DATABASE_MODELS registry, add tests

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`.
This commit is contained in:
Andras Bacsai 2026-04-30 14:48:48 +02:00
parent 0f830b31f3
commit 79174b749d
4 changed files with 145 additions and 64 deletions

View file

@ -1,7 +1,26 @@
<?php
use App\Models\StandaloneClickhouse;
use App\Models\StandaloneDragonfly;
use App\Models\StandaloneKeydb;
use App\Models\StandaloneMariadb;
use App\Models\StandaloneMongodb;
use App\Models\StandaloneMysql;
use App\Models\StandalonePostgresql;
use App\Models\StandaloneRedis;
const REDACTED = '<REDACTED>';
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 * * * *',

View file

@ -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)
{

View file

@ -0,0 +1,70 @@
<?php
use App\Models\Environment;
use App\Models\Project;
use App\Models\Server;
use App\Models\StandaloneDocker;
use App\Models\StandalonePostgresql;
use App\Models\Team;
use Illuminate\Foundation\Testing\RefreshDatabase;
uses(RefreshDatabase::class);
beforeEach(function () {
$this->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");
}
});

View file

@ -0,0 +1,45 @@
<?php
use App\Models\StandaloneDocker;
use Illuminate\Database\Eloquent\Model;
/**
* Guards STANDALONE_DATABASE_MODELS against drift.
*
* MCP and API endpoints rely on this registry for team-scoped UUID lookups.
* If a new App\Models\Standalone* model lands without a registry entry, the
* helpers in bootstrap/helpers/shared.php silently fail to resolve it.
*/
test('STANDALONE_DATABASE_MODELS contains every Standalone* model on disk', function () {
$files = glob(dirname(__DIR__, 2).'/app/Models/Standalone*.php');
expect($files)->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()");
}
});