mirror of
https://github.com/coollabsio/coolify.git
synced 2026-10-29 10:04:36 +08:00
187 lines
6.8 KiB
PHP
187 lines
6.8 KiB
PHP
<?php
|
|
|
|
use App\Livewire\Project\Shared\GetLogs;
|
|
use App\Models\Application;
|
|
use App\Models\Environment;
|
|
use App\Models\PrivateKey;
|
|
use App\Models\Project;
|
|
use App\Models\Server;
|
|
use App\Models\StandaloneDocker;
|
|
use App\Models\Team;
|
|
use App\Models\User;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Illuminate\Process\PendingProcess;
|
|
use Illuminate\Support\Facades\Process;
|
|
use Livewire\Livewire;
|
|
|
|
uses(RefreshDatabase::class);
|
|
|
|
beforeEach(function () {
|
|
$this->user = User::factory()->create();
|
|
$this->team = Team::factory()->create();
|
|
$this->user->teams()->attach($this->team, ['role' => 'owner']);
|
|
|
|
$privateKey = PrivateKey::factory()->create(['team_id' => $this->team->id]);
|
|
$this->server = functionalLogServer($this->team->id, $privateKey->id, 'Frankfurt');
|
|
$this->edge = functionalLogServer($this->team->id, $privateKey->id, 'Helsinki');
|
|
$this->destination = StandaloneDocker::where('server_id', $this->server->id)->firstOrFail();
|
|
$edgeDestination = StandaloneDocker::where('server_id', $this->edge->id)->firstOrFail();
|
|
$project = Project::factory()->create(['team_id' => $this->team->id]);
|
|
$environment = Environment::factory()->create(['project_id' => $project->id]);
|
|
|
|
$this->application = Application::factory()->create([
|
|
'environment_id' => $environment->id,
|
|
'destination_id' => $this->destination->id,
|
|
'destination_type' => $this->destination->getMorphClass(),
|
|
'build_pack' => 'dockerfile',
|
|
]);
|
|
$this->application->additional_networks()->attach($edgeDestination->id, ['server_id' => $this->edge->id]);
|
|
|
|
$this->actingAs($this->user);
|
|
session(['currentTeam' => $this->team]);
|
|
});
|
|
|
|
function functionalLogServer(int $teamId, int $privateKeyId, string $name): Server
|
|
{
|
|
$server = Server::factory()->create([
|
|
'team_id' => $teamId,
|
|
'private_key_id' => $privateKeyId,
|
|
'name' => $name,
|
|
]);
|
|
$server->settings->fill([
|
|
'is_reachable' => true,
|
|
'is_usable' => true,
|
|
'force_disabled' => false,
|
|
])->save();
|
|
|
|
return Server::with('settings')->findOrFail($server->id);
|
|
}
|
|
|
|
function mergedLogSources(): array
|
|
{
|
|
return [
|
|
['server' => test()->server->uuid, 'container' => 'web-frankfurt', 'label' => 'Frankfurt'],
|
|
['server' => test()->edge->uuid, 'container' => 'web-helsinki', 'label' => 'Helsinki'],
|
|
];
|
|
}
|
|
|
|
it('rejects a foreign server or an invalid container name before reading logs', function () {
|
|
$otherTeam = Team::factory()->create();
|
|
$foreign = functionalLogServer($otherTeam->id, $this->server->private_key_id, 'Foreign');
|
|
$unrelated = functionalLogServer($this->team->id, $this->server->private_key_id, 'Unrelated');
|
|
|
|
$rejected = [
|
|
[
|
|
['server' => $this->server->uuid, 'container' => 'web-frankfurt', 'label' => 'Frankfurt'],
|
|
['server' => $foreign->uuid, 'container' => 'web-foreign', 'label' => 'Foreign'],
|
|
],
|
|
[
|
|
['server' => $unrelated->uuid, 'container' => 'web', 'label' => 'Unrelated'],
|
|
],
|
|
[
|
|
['server' => $this->server->uuid, 'container' => 'web-frankfurt', 'label' => 'Frankfurt'],
|
|
['server' => $this->edge->uuid, 'container' => 'bad name', 'label' => 'Bad'],
|
|
],
|
|
];
|
|
|
|
foreach ($rejected as $sources) {
|
|
Process::fake();
|
|
|
|
Livewire::test(GetLogs::class, [
|
|
'server' => $this->server,
|
|
'resource' => $this->application,
|
|
'container' => 'web-frankfurt',
|
|
'sources' => $sources,
|
|
])
|
|
->call('getLogs')
|
|
->assertReturned('Unauthorized.');
|
|
|
|
Process::assertNothingRan();
|
|
}
|
|
|
|
$this->edge->updateQuietly(['team_id' => $otherTeam->id]);
|
|
Process::fake();
|
|
|
|
Livewire::test(GetLogs::class, [
|
|
'server' => $this->server,
|
|
'resource' => $this->application,
|
|
'container' => 'web-helsinki',
|
|
'sources' => [
|
|
['server' => $this->edge->uuid, 'container' => 'web-helsinki', 'label' => 'Helsinki'],
|
|
],
|
|
])
|
|
->call('getLogs')
|
|
->assertReturned('Unauthorized.');
|
|
|
|
Process::assertNothingRan();
|
|
});
|
|
|
|
it('merges container logs by timestamp and tags each line with its source', function () {
|
|
Process::fake(function (PendingProcess $process) {
|
|
$command = is_array(remoteProcessScript($process)) ? implode(' ', remoteProcessScript($process)) : remoteProcessScript($process);
|
|
if (str_contains($command, 'web-helsinki')) {
|
|
return Process::result(output: "2026-09-28T10:00:02Z second\n2026-09-28T10:00:01Z first\n");
|
|
}
|
|
if (str_contains($command, 'web-frankfurt')) {
|
|
return Process::result(output: "2026-09-28T10:00:02Z frankfurt\n");
|
|
}
|
|
|
|
return Process::result();
|
|
});
|
|
|
|
$component = Livewire::test(GetLogs::class, [
|
|
'server' => $this->server,
|
|
'resource' => $this->application,
|
|
'container' => 'web-frankfurt',
|
|
'sources' => mergedLogSources(),
|
|
'showTimeStamps' => true,
|
|
]);
|
|
|
|
$component->call('getLogs')->assertReturned(implode("\n", [
|
|
"Helsinki\t2026-09-28T10:00:01Z first",
|
|
"Frankfurt\t2026-09-28T10:00:02Z frankfurt",
|
|
"Helsinki\t2026-09-28T10:00:02Z second",
|
|
]));
|
|
|
|
$component->call('downloadAllLogs')->assertReturned(implode("\n", [
|
|
'Helsinki | first',
|
|
'Frankfurt | frankfurt',
|
|
'Helsinki | second',
|
|
]));
|
|
|
|
$component->set('showTimeStamps', true)
|
|
->call('downloadAllLogs')
|
|
->assertReturned(implode("\n", [
|
|
'Helsinki | 2026-09-28T10:00:01Z first',
|
|
'Frankfurt | 2026-09-28T10:00:02Z frankfurt',
|
|
'Helsinki | 2026-09-28T10:00:02Z second',
|
|
]));
|
|
});
|
|
|
|
it('asks each source only for lines after its own timestamp', function () {
|
|
$commands = [];
|
|
Process::fake(function (PendingProcess $process) use (&$commands) {
|
|
$command = is_array(remoteProcessScript($process)) ? implode(' ', remoteProcessScript($process)) : remoteProcessScript($process);
|
|
$commands[] = $command;
|
|
|
|
return Process::result(output: "2026-09-28T10:00:03Z line\n");
|
|
});
|
|
|
|
Livewire::test(GetLogs::class, [
|
|
'server' => $this->server,
|
|
'resource' => $this->application,
|
|
'container' => 'web-frankfurt',
|
|
'sources' => mergedLogSources(),
|
|
])->call('getLogs', [
|
|
'Frankfurt' => '2026-09-28T10:00:01.123456789Z',
|
|
'Helsinki' => 'not-a-timestamp',
|
|
]);
|
|
|
|
$frankfurt = collect($commands)->first(fn (string $command): bool => str_contains($command, 'web-frankfurt'));
|
|
$helsinki = collect($commands)->first(fn (string $command): bool => str_contains($command, 'web-helsinki'));
|
|
|
|
expect($frankfurt)->toContain('--since 2026-09-28T10:00:01.123456789Z')
|
|
->and($helsinki)->toContain('docker logs -n ')
|
|
->and($helsinki)->not->toContain('--since');
|
|
});
|