mirror of
https://github.com/coollabsio/coolify.git
synced 2026-11-04 10:42:23 +00:00
Deployment options get the same status dot and Stopped caption as services instead of colored badges, so the listbox no longer needs labelClass.
251 lines
10 KiB
PHP
251 lines
10 KiB
PHP
<?php
|
|
|
|
use App\Support\RuntimeLogTargets;
|
|
|
|
function runtimeLogServer(string $uuid, string $name, array $containers): array
|
|
{
|
|
return [
|
|
'uuid' => $uuid,
|
|
'name' => $name,
|
|
'containers' => $containers,
|
|
];
|
|
}
|
|
|
|
function runtimeLogContainer(string $name, string $labels, string $state = 'running', string $id = ''): array
|
|
{
|
|
return [
|
|
'ID' => $id !== '' ? $id : $name,
|
|
'Names' => $name,
|
|
'State' => $state,
|
|
'Labels' => $labels,
|
|
];
|
|
}
|
|
|
|
function runtimeLogTargets(array $servers, string $deployment = '0', string $service = '', bool $groupDeployments = true): array
|
|
{
|
|
return RuntimeLogTargets::select($servers, $deployment, $service, $groupDeployments);
|
|
}
|
|
|
|
it('orders production ahead of newer previews and uses the top bar pills', function () {
|
|
$targets = runtimeLogTargets([
|
|
runtimeLogServer('primary', 'Primary', [
|
|
runtimeLogContainer('app-pr-7', 'coolify.pullRequestId=7,com.docker.compose.service=web', 'exited'),
|
|
runtimeLogContainer('app', 'coolify.pullRequestId=0,com.docker.compose.service=web'),
|
|
runtimeLogContainer('app-pr-42', 'coolify.pullRequestId=42,com.docker.compose.service=web'),
|
|
]),
|
|
]);
|
|
|
|
expect($targets['deployments'])->toBe([
|
|
['value' => '0', 'label' => 'Production', 'dot' => 'success'],
|
|
['value' => '42', 'label' => 'PR #42', 'dot' => 'success'],
|
|
['value' => '7', 'label' => 'PR #7', 'dot' => 'error', 'state' => 'Stopped'],
|
|
])
|
|
->and($targets['deployment'])->toBe('0')
|
|
->and($targets['service'])->toBe('')
|
|
->and($targets['sources'][0]['container'])->toBe('app')
|
|
->and($targets)->not->toHaveKey('servers');
|
|
});
|
|
|
|
it('treats a missing pull request label as production', function () {
|
|
$targets = runtimeLogTargets([
|
|
runtimeLogServer('primary', 'Primary', [
|
|
runtimeLogContainer('app', 'com.docker.compose.service=web'),
|
|
runtimeLogContainer('app-pr-3', 'coolify.pullRequestId=3,com.docker.compose.service=web'),
|
|
]),
|
|
]);
|
|
|
|
expect($targets['deployments'][0]['label'])->toBe('Production')
|
|
->and($targets['sources'][0]['container'])->toBe('app');
|
|
});
|
|
|
|
it('uses the preview from the query when that deployment exists', function () {
|
|
$targets = runtimeLogTargets([
|
|
runtimeLogServer('primary', 'Primary', [
|
|
runtimeLogContainer('app', 'coolify.pullRequestId=0,com.docker.compose.service=web'),
|
|
runtimeLogContainer('app-pr-12', 'coolify.pullRequestId=12,com.docker.compose.service=web'),
|
|
]),
|
|
], '12');
|
|
|
|
expect($targets['deployment'])->toBe('12')
|
|
->and($targets['sources'][0]['container'])->toBe('app-pr-12');
|
|
});
|
|
|
|
it('falls back to production when the requested preview is gone', function () {
|
|
$targets = runtimeLogTargets([
|
|
runtimeLogServer('primary', 'Primary', [
|
|
runtimeLogContainer('app', 'coolify.pullRequestId=0,com.docker.compose.service=web'),
|
|
runtimeLogContainer('app-pr-7', 'coolify.pullRequestId=7,com.docker.compose.service=web'),
|
|
]),
|
|
], '99');
|
|
|
|
expect($targets['deployment'])->toBe('0')
|
|
->and($targets['sources'][0]['container'])->toBe('app');
|
|
});
|
|
|
|
it('falls back to the newest preview when production has no container', function () {
|
|
$targets = runtimeLogTargets([
|
|
runtimeLogServer('primary', 'Primary', [
|
|
runtimeLogContainer('app-pr-3', 'coolify.pullRequestId=3,com.docker.compose.service=web'),
|
|
runtimeLogContainer('app-pr-12', 'coolify.pullRequestId=12,com.docker.compose.service=web'),
|
|
]),
|
|
]);
|
|
|
|
expect($targets['deployment'])->toBe('12')
|
|
->and($targets['sources'][0]['container'])->toBe('app-pr-12');
|
|
});
|
|
|
|
it('names services from the compose label, then coolify.name, then the container headline', function () {
|
|
$targets = runtimeLogTargets([
|
|
runtimeLogServer('primary', 'Primary', [
|
|
runtimeLogContainer('compose-name', 'com.docker.compose.service=api,coolify.name=ignored'),
|
|
runtimeLogContainer('labeled-name', 'coolify.name=worker'),
|
|
runtimeLogContainer('plain-container-1', ''),
|
|
]),
|
|
], '0', '', false);
|
|
|
|
expect(array_column($targets['services'], 'label'))->toBe(['api', 'Plain Container', 'worker'])
|
|
->and($targets['service'])->toBe('api')
|
|
->and($targets['sources'][0]['container'])->toBe('compose-name');
|
|
});
|
|
|
|
it('gives each service one option and colors it from the container states', function (array $states, string $dot, ?string $description, ?string $stateLabel) {
|
|
$containers = [];
|
|
foreach ($states as $index => $state) {
|
|
$containers[] = runtimeLogContainer('web-'.$index, 'com.docker.compose.service=web', $state);
|
|
}
|
|
|
|
$option = runtimeLogTargets([
|
|
runtimeLogServer('primary', 'Primary', $containers),
|
|
])['services'][0];
|
|
|
|
expect($option['label'])->toBe('web')
|
|
->and($option['value'])->toBe('web')
|
|
->and($option['dot'])->toBe($dot)
|
|
->and($option['description'] ?? null)->toBe($description)
|
|
->and($option['state'] ?? null)->toBe($stateLabel);
|
|
})->with([
|
|
'running' => [['running'], 'success', null, null],
|
|
'starting' => [['starting'], 'warning', null, 'Starting'],
|
|
'restarting' => [['running', 'restarting'], 'warning', '2 containers', 'Restarting'],
|
|
'mixed' => [['running', 'exited'], 'warning', '2 containers', 'Degraded'],
|
|
'stopped' => [['exited', 'dead'], 'error', '2 containers', 'Stopped'],
|
|
]);
|
|
|
|
it('defaults to the first running service and keeps the requested service key', function () {
|
|
$servers = [
|
|
runtimeLogServer('primary', 'Primary', [
|
|
runtimeLogContainer('api', 'com.docker.compose.service=api', 'exited'),
|
|
runtimeLogContainer('web-old', 'com.docker.compose.service=web', 'exited'),
|
|
runtimeLogContainer('web-new', 'com.docker.compose.service=web', 'running'),
|
|
runtimeLogContainer('worker', 'com.docker.compose.service=worker', 'running'),
|
|
]),
|
|
];
|
|
|
|
$targets = runtimeLogTargets($servers);
|
|
|
|
expect(array_column($targets['services'], 'label'))->toBe(['api', 'web', 'worker'])
|
|
->and($targets['services'][0]['dot'])->toBe('error')
|
|
->and($targets['services'][0]['state'])->toBe('Stopped')
|
|
->and($targets['services'][1]['dot'])->toBe('warning')
|
|
->and($targets['services'][1]['state'])->toBe('Degraded')
|
|
->and($targets['services'][1]['description'])->toBe('2 containers')
|
|
->and($targets['services'][2]['state'] ?? null)->toBeNull()
|
|
->and($targets['service'])->toBe('web')
|
|
->and(array_column($targets['sources'], 'container'))->toBe(['web-new', 'web-old']);
|
|
|
|
$requested = runtimeLogTargets($servers, '0', 'worker');
|
|
|
|
expect($requested['service'])->toBe('worker')
|
|
->and($requested['sources'][0]['container'])->toBe('worker');
|
|
});
|
|
|
|
it('merges a service across servers and a rollout duplicate', function () {
|
|
$targets = runtimeLogTargets([
|
|
runtimeLogServer('primary', 'Frankfurt', [
|
|
runtimeLogContainer('web-new', 'com.docker.compose.service=web', 'running'),
|
|
runtimeLogContainer('web-old', 'com.docker.compose.service=web', 'exited'),
|
|
runtimeLogContainer('api', 'com.docker.compose.service=api', 'running'),
|
|
]),
|
|
runtimeLogServer('edge', 'Helsinki', [
|
|
runtimeLogContainer('web-edge', 'com.docker.compose.service=web', 'running'),
|
|
]),
|
|
], '0', 'web');
|
|
|
|
expect($targets)->not->toHaveKey('servers')
|
|
->and($targets['sources'])->toBe([
|
|
['server' => 'primary', 'container' => 'web-new', 'label' => 'Frankfurt web-new'],
|
|
['server' => 'primary', 'container' => 'web-old', 'label' => 'Frankfurt web-old'],
|
|
['server' => 'edge', 'container' => 'web-edge', 'label' => 'Helsinki'],
|
|
])
|
|
->and($targets['streamKey'])->toBe('primary:web-new|primary:web-old|edge:web-edge');
|
|
});
|
|
|
|
it('opens a preview that exists on only one server', function () {
|
|
$targets = runtimeLogTargets([
|
|
runtimeLogServer('primary', 'Primary', [
|
|
runtimeLogContainer('primary-app', 'coolify.pullRequestId=0'),
|
|
]),
|
|
runtimeLogServer('edge', 'Edge', [
|
|
runtimeLogContainer('edge-pr-8', 'coolify.pullRequestId=8'),
|
|
]),
|
|
], '8');
|
|
|
|
expect($targets['deployment'])->toBe('8')
|
|
->and($targets['sources'])->toBe([
|
|
['server' => 'edge', 'container' => 'edge-pr-8', 'label' => 'edge-pr-8'],
|
|
]);
|
|
});
|
|
|
|
it('does not split one-click services into preview deployments', function () {
|
|
$targets = runtimeLogTargets([
|
|
runtimeLogServer('primary', 'Primary', [
|
|
runtimeLogContainer('app-service', 'coolify.pullRequestId=4,com.docker.compose.service=app'),
|
|
runtimeLogContainer('db-service', 'coolify.pullRequestId=4,com.docker.compose.service=db'),
|
|
]),
|
|
], '4', '', false);
|
|
|
|
expect($targets['deployments'])->toBe([])
|
|
->and($targets['deployment'])->toBe('0')
|
|
->and(array_column($targets['services'], 'label'))->toBe(['app', 'db']);
|
|
});
|
|
|
|
it('reads labels given as an array', function () {
|
|
$targets = runtimeLogTargets([
|
|
runtimeLogServer('primary', 'Primary', [
|
|
[
|
|
'ID' => 'app-id',
|
|
'Names' => 'app',
|
|
'State' => 'running',
|
|
'Labels' => [
|
|
'coolify.pullRequestId' => '0',
|
|
'com.docker.compose.service' => 'web',
|
|
],
|
|
],
|
|
]),
|
|
]);
|
|
|
|
expect($targets['sources'][0]['container'])->toBe('app')
|
|
->and($targets['services'][0]['label'])->toBe('web');
|
|
});
|
|
|
|
it('drops containers whose names cannot be passed to docker logs', function () {
|
|
$targets = runtimeLogTargets([
|
|
runtimeLogServer('primary', 'Primary', [
|
|
runtimeLogContainer('valid-name', 'com.docker.compose.service=web'),
|
|
runtimeLogContainer('bad name', 'com.docker.compose.service=api'),
|
|
]),
|
|
]);
|
|
|
|
expect(array_column($targets['services'], 'value'))->toBe(['web']);
|
|
});
|
|
|
|
it('returns an empty selection when nothing is running', function () {
|
|
$targets = runtimeLogTargets([
|
|
runtimeLogServer('primary', 'Primary', []),
|
|
], '12', 'web');
|
|
|
|
expect($targets['sources'])->toBe([])
|
|
->and($targets['services'])->toBe([])
|
|
->and($targets['streamKey'])->toBe('');
|
|
});
|