diff --git a/app/Models/Application.php b/app/Models/Application.php index c284528f1..a68c1d54a 100644 --- a/app/Models/Application.php +++ b/app/Models/Application.php @@ -4,6 +4,7 @@ namespace App\Models; use App\Enums\ApplicationDeploymentStatus; use Illuminate\Database\Eloquent\Casts\Attribute; +use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Process\InvokedProcess; @@ -104,7 +105,7 @@ use Visus\Cuid2\Cuid2; class Application extends BaseModel { - use SoftDeletes; + use HasFactory, SoftDeletes; private static $parserVersion = '4'; diff --git a/app/Models/Server.php b/app/Models/Server.php index 83b91b254..e0a66c58b 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -11,6 +11,7 @@ use App\Notifications\Server\Reachable; use App\Notifications\Server\Unreachable; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Casts\Attribute; +use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Support\Carbon; use Illuminate\Support\Collection; @@ -48,7 +49,7 @@ use Symfony\Component\Yaml\Yaml; class Server extends BaseModel { - use SchemalessAttributesTrait, SoftDeletes; + use HasFactory, SchemalessAttributesTrait, SoftDeletes; public static $batch_counter = 0; diff --git a/config/database.php b/config/database.php index f48a68082..6f4acbfd2 100644 --- a/config/database.php +++ b/config/database.php @@ -49,6 +49,22 @@ return [ 'search_path' => 'public', 'sslmode' => 'prefer', ], + + 'testing' => [ + 'driver' => 'pgsql', + 'url' => env('DATABASE_TEST_URL'), + 'host' => env('DB_TEST_HOST', 'postgres'), + 'port' => env('DB_TEST_PORT', '5432'), + 'database' => env('DB_TEST_DATABASE', 'coolify_test'), + 'username' => env('DB_TEST_USERNAME', 'coolify'), + 'password' => env('DB_TEST_PASSWORD', 'password'), + 'charset' => 'utf8', + 'prefix' => '', + 'prefix_indexes' => true, + 'search_path' => 'public', + 'sslmode' => 'prefer', + ], + ], /* diff --git a/database/factories/ApplicationFactory.php b/database/factories/ApplicationFactory.php new file mode 100644 index 000000000..ded507c56 --- /dev/null +++ b/database/factories/ApplicationFactory.php @@ -0,0 +1,22 @@ + fake()->unique()->name(), + 'destination_id' => 1, + 'git_repository' => fake()->url(), + 'git_branch' => fake()->word(), + 'build_pack' => 'nixpacks', + 'ports_exposes' => '3000', + 'environment_id' => 1, + 'destination_id' => 1, + ]; + } +} diff --git a/database/factories/ServerFactory.php b/database/factories/ServerFactory.php new file mode 100644 index 000000000..29546bf56 --- /dev/null +++ b/database/factories/ServerFactory.php @@ -0,0 +1,17 @@ + fake()->unique()->name(), + 'ip' => fake()->unique()->ipv4(), + 'private_key_id' => 1, + ]; + } +} diff --git a/phpunit.xml b/phpunit.xml index 45cb69439..f1c2be92d 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -13,8 +13,8 @@ - - + + diff --git a/tests/Feature/ExecuteContainerCommandTest.php b/tests/Feature/ExecuteContainerCommandTest.php new file mode 100644 index 000000000..6d485fe65 --- /dev/null +++ b/tests/Feature/ExecuteContainerCommandTest.php @@ -0,0 +1,57 @@ +shouldSetUpDatabase()) { + $this->setUpTestDatabase(); + } + // Create test data + $this->user = User::factory()->create(); + $this->team = $this->user->teams()->first(); + $this->server = Server::factory()->create(['team_id' => $this->team->id]); + $this->application = Application::factory()->create(); + + // Login the user + $this->actingAs($this->user); + } + + protected function tearDown(): void + { + if ($this->shouldSetUpDatabase()) { + $this->tearDownTestDatabase(); + } + parent::tearDown(); + } + + private function shouldSetUpDatabase(): bool + { + return in_array($this->name(), [ + 'it_allows_valid_container_access', + 'it_prevents_cross_server_container_access', + ]); + } +} diff --git a/tests/Traits/HandlesTestDatabase.php b/tests/Traits/HandlesTestDatabase.php new file mode 100644 index 000000000..adb577e7c --- /dev/null +++ b/tests/Traits/HandlesTestDatabase.php @@ -0,0 +1,78 @@ +createTestDatabase($database); + + // Run migrations + Artisan::call('migrate:fresh', [ + '--database' => 'testing', + '--seed' => false, + ]); + } catch (\Exception $e) { + $this->tearDownTestDatabase(); + throw $e; + } + } + + protected function tearDownTestDatabase(): void + { + try { + // Drop test database + $database = config('database.connections.testing.database'); + $this->dropTestDatabase($database); + } catch (\Exception $e) { + // Log error but don't throw + error_log('Failed to tear down test database: '.$e->getMessage()); + } + } + + protected function createTestDatabase($database) + { + try { + // Connect to postgres database to create/drop test database + config(['database.connections.pgsql.database' => 'postgres']); + DB::purge('pgsql'); + DB::reconnect('pgsql'); + + // Drop if exists and create new database + DB::connection('pgsql')->statement("DROP DATABASE IF EXISTS $database WITH (FORCE);"); + DB::connection('pgsql')->statement("CREATE DATABASE $database;"); + + // Switch back to testing connection + DB::disconnect('pgsql'); + DB::reconnect('testing'); + } catch (\Exception $e) { + $this->tearDownTestDatabase(); + throw new \Exception('Could not create test database: '.$e->getMessage()); + } + } + + protected function dropTestDatabase($database) + { + try { + // Connect to postgres database to drop test database + config(['database.connections.pgsql.database' => 'postgres']); + DB::purge('pgsql'); + DB::reconnect('pgsql'); + + // Drop the test database + DB::connection('pgsql')->statement("DROP DATABASE IF EXISTS $database WITH (FORCE);"); + + DB::disconnect('pgsql'); + } catch (\Exception $e) { + // Log error but don't throw + error_log('Failed to drop test database: '.$e->getMessage()); + } + } +}