mirror of
https://github.com/coollabsio/coolify.git
synced 2026-11-04 18:52:49 +00:00
Co-authored-by: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
136 lines
5.1 KiB
PHP
136 lines
5.1 KiB
PHP
<?php
|
|
|
|
use App\Jobs\ServerConnectionCheckJob;
|
|
use App\Jobs\ServerManagerJob;
|
|
use App\Models\InstanceSettings;
|
|
use App\Models\PrivateKey;
|
|
use App\Models\Server;
|
|
use App\Models\Subscription;
|
|
use App\Models\Team;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Illuminate\Support\Carbon;
|
|
use Illuminate\Support\Facades\Queue;
|
|
|
|
uses(RefreshDatabase::class);
|
|
|
|
beforeEach(function () {
|
|
InstanceSettings::forceCreate(['id' => 0, 'instance_timezone' => 'UTC']);
|
|
$this->rootTeam = Team::factory()->create(['id' => 0]);
|
|
config(['constants.coolify.self_hosted' => false]);
|
|
// Minute 0 is due for the Cloud connection check frequency (*/5).
|
|
Carbon::setTestNow('2026-07-11 12:00:00');
|
|
});
|
|
|
|
afterEach(function () {
|
|
Carbon::setTestNow();
|
|
});
|
|
|
|
function createServerForSelectionTest(Team $team): Server
|
|
{
|
|
$privateKey = PrivateKey::query()->firstWhere('team_id', $team->id)
|
|
?? PrivateKey::factory()->create(['team_id' => $team->id]);
|
|
|
|
return Server::factory()->create(['team_id' => $team->id, 'private_key_id' => $privateKey->id]);
|
|
}
|
|
|
|
/**
|
|
* @return array<int, int> server id => number of connection checks queued for it
|
|
*/
|
|
function connectionChecksPerServer(): array
|
|
{
|
|
Queue::fake();
|
|
(new ServerManagerJob)->handle();
|
|
|
|
return collect(Queue::pushed(ServerConnectionCheckJob::class))
|
|
->countBy(fn (ServerConnectionCheckJob $job) => $job->server->id)
|
|
->sortKeys()
|
|
->all();
|
|
}
|
|
|
|
it('processes each server of a paid team once, also with more than one paid subscription', function () {
|
|
$team = Team::factory()->create();
|
|
Subscription::factory()->count(2)->create(['team_id' => $team->id, 'stripe_invoice_paid' => true]);
|
|
$server = createServerForSelectionTest($team);
|
|
|
|
expect(connectionChecksPerServer())->toBe([$server->id => 1]);
|
|
});
|
|
|
|
it('skips servers of teams without a paid subscription', function () {
|
|
$unpaid = Team::factory()->create();
|
|
Subscription::factory()->create(['team_id' => $unpaid->id, 'stripe_invoice_paid' => false]);
|
|
createServerForSelectionTest($unpaid);
|
|
|
|
$pastDue = Team::factory()->create();
|
|
Subscription::factory()->create(['team_id' => $pastDue->id, 'stripe_invoice_paid' => false, 'stripe_past_due' => true]);
|
|
createServerForSelectionTest($pastDue);
|
|
|
|
createServerForSelectionTest(Team::factory()->create());
|
|
|
|
expect(connectionChecksPerServer())->toBe([]);
|
|
});
|
|
|
|
it('processes a team with one paid and one unpaid subscription', function () {
|
|
$team = Team::factory()->create();
|
|
Subscription::factory()->create(['team_id' => $team->id, 'stripe_invoice_paid' => false]);
|
|
Subscription::factory()->create(['team_id' => $team->id, 'stripe_invoice_paid' => true]);
|
|
$server = createServerForSelectionTest($team);
|
|
|
|
expect(connectionChecksPerServer())->toBe([$server->id => 1]);
|
|
});
|
|
|
|
it('skips soft-deleted servers of a paid team', function () {
|
|
$team = Team::factory()->create();
|
|
Subscription::factory()->create(['team_id' => $team->id, 'stripe_invoice_paid' => true]);
|
|
$kept = createServerForSelectionTest($team);
|
|
createServerForSelectionTest($team)->delete();
|
|
|
|
expect(connectionChecksPerServer())->toBe([$kept->id => 1]);
|
|
});
|
|
|
|
it('processes root team servers once without a subscription', function () {
|
|
$rootServer = createServerForSelectionTest($this->rootTeam);
|
|
|
|
expect(connectionChecksPerServer())->toBe([$rootServer->id => 1]);
|
|
|
|
Subscription::factory()->create(['team_id' => 0, 'stripe_invoice_paid' => true]);
|
|
Carbon::setTestNow('2026-07-11 12:05:00');
|
|
|
|
expect(connectionChecksPerServer())->toBe([$rootServer->id => 1]);
|
|
});
|
|
|
|
it('follows subscription payment changes between runs', function () {
|
|
$team = Team::factory()->create();
|
|
$subscription = Subscription::factory()->create(['team_id' => $team->id, 'stripe_invoice_paid' => false]);
|
|
$server = createServerForSelectionTest($team);
|
|
|
|
expect(connectionChecksPerServer())->toBe([]);
|
|
|
|
$subscription->update(['stripe_invoice_paid' => true]);
|
|
Carbon::setTestNow('2026-07-11 12:05:00');
|
|
expect(connectionChecksPerServer())->toBe([$server->id => 1]);
|
|
|
|
$subscription->update(['stripe_invoice_paid' => false]);
|
|
Carbon::setTestNow('2026-07-11 12:10:00');
|
|
expect(connectionChecksPerServer())->toBe([]);
|
|
});
|
|
|
|
it('processes every server when they span more than one batch', function () {
|
|
$team = Team::factory()->create();
|
|
Subscription::factory()->create(['team_id' => $team->id, 'stripe_invoice_paid' => true]);
|
|
$privateKey = PrivateKey::factory()->create(['team_id' => $team->id]);
|
|
$servers = Server::factory()
|
|
->count(ServerManagerJob::SERVER_BATCH_SIZE + 1)
|
|
->create(['team_id' => $team->id, 'private_key_id' => $privateKey->id]);
|
|
|
|
expect(connectionChecksPerServer())
|
|
->toBe($servers->pluck('id')->sort()->mapWithKeys(fn (int $id) => [$id => 1])->all());
|
|
});
|
|
|
|
it('processes every server in self-hosted mode', function () {
|
|
config(['constants.coolify.self_hosted' => true]);
|
|
Carbon::setTestNow('2026-07-11 12:01:00');
|
|
$server = createServerForSelectionTest(Team::factory()->create());
|
|
|
|
expect(connectionChecksPerServer())->toBe([$server->id => 1]);
|
|
});
|