mirror of
https://github.com/coollabsio/coolify.git
synced 2025-12-28 05:34:50 +00:00
Add endsWith() checks before appending template paths in serviceParser() to prevent duplicate paths when parse() is called after FQDN updates. This fixes the bug where services like Appwrite realtime would get `/v1/realtime/v1/realtime`. Fixes #7363 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
221 lines
7.0 KiB
PHP
221 lines
7.0 KiB
PHP
<?php
|
|
|
|
/**
|
|
* Feature tests to verify that FQDN updates don't cause path duplication
|
|
* for services with path-based SERVICE_URL/SERVICE_FQDN template variables.
|
|
*
|
|
* This tests the fix for GitHub issue #7363 where Appwrite and MindsDB services
|
|
* had their paths duplicated (e.g., /v1/realtime/v1/realtime) after FQDN updates.
|
|
*
|
|
* IMPORTANT: These tests require database access and must be run inside Docker:
|
|
* docker exec coolify php artisan test --filter ServiceFqdnUpdatePathTest
|
|
*/
|
|
|
|
use App\Models\Server;
|
|
use App\Models\Service;
|
|
use App\Models\ServiceApplication;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
|
|
uses(RefreshDatabase::class);
|
|
|
|
test('Appwrite realtime service does not duplicate path on FQDN update', function () {
|
|
// Create a server
|
|
$server = Server::factory()->create([
|
|
'name' => 'test-server',
|
|
'ip' => '127.0.0.1',
|
|
]);
|
|
|
|
// Load Appwrite template
|
|
$appwriteTemplate = file_get_contents(base_path('templates/compose/appwrite.yaml'));
|
|
|
|
// Create Appwrite service
|
|
$service = Service::factory()->create([
|
|
'server_id' => $server->id,
|
|
'name' => 'appwrite-test',
|
|
'docker_compose_raw' => $appwriteTemplate,
|
|
]);
|
|
|
|
// Create the appwrite-realtime service application
|
|
$serviceApp = ServiceApplication::factory()->create([
|
|
'service_id' => $service->id,
|
|
'name' => 'appwrite-realtime',
|
|
'fqdn' => 'https://test.abc/v1/realtime',
|
|
]);
|
|
|
|
// Parse the service (simulates initial setup)
|
|
$service->parse();
|
|
|
|
// Get environment variable
|
|
$urlVar = $service->environment_variables()
|
|
->where('key', 'SERVICE_URL_APPWRITE')
|
|
->first();
|
|
|
|
// Initial setup should have path once
|
|
expect($urlVar)->not->toBeNull()
|
|
->and($urlVar->value)->not->toContain('/v1/realtime/v1/realtime')
|
|
->and($urlVar->value)->toContain('/v1/realtime');
|
|
|
|
// Simulate user updating FQDN
|
|
$serviceApp->fqdn = 'https://newdomain.com/v1/realtime';
|
|
$serviceApp->save();
|
|
|
|
// Call parse again (this is where the bug occurred)
|
|
$service->parse();
|
|
|
|
// Check that path is not duplicated
|
|
$urlVar = $service->environment_variables()
|
|
->where('key', 'SERVICE_URL_APPWRITE')
|
|
->first();
|
|
|
|
expect($urlVar)->not->toBeNull()
|
|
->and($urlVar->value)->not->toContain('/v1/realtime/v1/realtime')
|
|
->and($urlVar->value)->toContain('/v1/realtime');
|
|
})->skip('Requires database and Appwrite template - run in Docker');
|
|
|
|
test('Appwrite console service does not duplicate /console path', function () {
|
|
$server = Server::factory()->create();
|
|
$appwriteTemplate = file_get_contents(base_path('templates/compose/appwrite.yaml'));
|
|
$service = Service::factory()->create([
|
|
'server_id' => $server->id,
|
|
'docker_compose_raw' => $appwriteTemplate,
|
|
]);
|
|
|
|
$serviceApp = ServiceApplication::factory()->create([
|
|
'service_id' => $service->id,
|
|
'name' => 'appwrite-console',
|
|
'fqdn' => 'https://test.abc/console',
|
|
]);
|
|
|
|
// Parse service
|
|
$service->parse();
|
|
|
|
// Update FQDN
|
|
$serviceApp->fqdn = 'https://newdomain.com/console';
|
|
$serviceApp->save();
|
|
|
|
// Parse again
|
|
$service->parse();
|
|
|
|
// Verify no duplication
|
|
$urlVar = $service->environment_variables()
|
|
->where('key', 'SERVICE_URL_APPWRITE')
|
|
->first();
|
|
|
|
expect($urlVar)->not->toBeNull()
|
|
->and($urlVar->value)->not->toContain('/console/console')
|
|
->and($urlVar->value)->toContain('/console');
|
|
})->skip('Requires database and Appwrite template - run in Docker');
|
|
|
|
test('MindsDB service does not duplicate /api path', function () {
|
|
$server = Server::factory()->create();
|
|
$mindsdbTemplate = file_get_contents(base_path('templates/compose/mindsdb.yaml'));
|
|
$service = Service::factory()->create([
|
|
'server_id' => $server->id,
|
|
'docker_compose_raw' => $mindsdbTemplate,
|
|
]);
|
|
|
|
$serviceApp = ServiceApplication::factory()->create([
|
|
'service_id' => $service->id,
|
|
'name' => 'mindsdb',
|
|
'fqdn' => 'https://test.abc/api',
|
|
]);
|
|
|
|
// Parse service
|
|
$service->parse();
|
|
|
|
// Update FQDN multiple times
|
|
$serviceApp->fqdn = 'https://domain1.com/api';
|
|
$serviceApp->save();
|
|
$service->parse();
|
|
|
|
$serviceApp->fqdn = 'https://domain2.com/api';
|
|
$serviceApp->save();
|
|
$service->parse();
|
|
|
|
// Verify no duplication after multiple updates
|
|
$urlVar = $service->environment_variables()
|
|
->where('key', 'SERVICE_URL_API')
|
|
->orWhere('key', 'LIKE', 'SERVICE_URL_%')
|
|
->first();
|
|
|
|
expect($urlVar)->not->toBeNull()
|
|
->and($urlVar->value)->not->toContain('/api/api');
|
|
})->skip('Requires database and MindsDB template - run in Docker');
|
|
|
|
test('service without path declaration is not affected', function () {
|
|
$server = Server::factory()->create();
|
|
|
|
// Create a simple service without path in template
|
|
$simpleTemplate = <<<'YAML'
|
|
services:
|
|
redis:
|
|
image: redis:7
|
|
environment:
|
|
- SERVICE_FQDN_REDIS
|
|
YAML;
|
|
|
|
$service = Service::factory()->create([
|
|
'server_id' => $server->id,
|
|
'docker_compose_raw' => $simpleTemplate,
|
|
]);
|
|
|
|
$serviceApp = ServiceApplication::factory()->create([
|
|
'service_id' => $service->id,
|
|
'name' => 'redis',
|
|
'fqdn' => 'https://redis.test.abc',
|
|
]);
|
|
|
|
// Parse service
|
|
$service->parse();
|
|
|
|
$fqdnBefore = $service->environment_variables()
|
|
->where('key', 'SERVICE_FQDN_REDIS')
|
|
->first()?->value;
|
|
|
|
// Update FQDN
|
|
$serviceApp->fqdn = 'https://redis.newdomain.com';
|
|
$serviceApp->save();
|
|
|
|
// Parse again
|
|
$service->parse();
|
|
|
|
$fqdnAfter = $service->environment_variables()
|
|
->where('key', 'SERVICE_FQDN_REDIS')
|
|
->first()?->value;
|
|
|
|
// Should work normally without issues
|
|
expect($fqdnAfter)->toBe('redis.newdomain.com')
|
|
->and($fqdnAfter)->not->toContain('//');
|
|
})->skip('Requires database - run in Docker');
|
|
|
|
test('multiple FQDN updates never cause path duplication', function () {
|
|
$server = Server::factory()->create();
|
|
$appwriteTemplate = file_get_contents(base_path('templates/compose/appwrite.yaml'));
|
|
$service = Service::factory()->create([
|
|
'server_id' => $server->id,
|
|
'docker_compose_raw' => $appwriteTemplate,
|
|
]);
|
|
|
|
$serviceApp = ServiceApplication::factory()->create([
|
|
'service_id' => $service->id,
|
|
'name' => 'appwrite-realtime',
|
|
'fqdn' => 'https://test.abc/v1/realtime',
|
|
]);
|
|
|
|
// Update FQDN 10 times and parse each time
|
|
for ($i = 1; $i <= 10; $i++) {
|
|
$serviceApp->fqdn = "https://domain{$i}.com/v1/realtime";
|
|
$serviceApp->save();
|
|
$service->parse();
|
|
|
|
// Check path is never duplicated
|
|
$urlVar = $service->environment_variables()
|
|
->where('key', 'SERVICE_URL_APPWRITE')
|
|
->first();
|
|
|
|
expect($urlVar)->not->toBeNull()
|
|
->and($urlVar->value)->not->toContain('/v1/realtime/v1/realtime')
|
|
->and($urlVar->value)->toContain('/v1/realtime');
|
|
}
|
|
})->skip('Requires database and Appwrite template - run in Docker');
|