Files
coolify/tests/Unit/DatabaseImportOpenApiTest.php
T
Andras BacsaiandClaude Opus 5.5 8246492bb9 fix(databases): stop and clean up interrupted or stale imports
- Imports store their cleanup data (names and paths, no credentials)
  on the activity. When Coolify fails an import after a restart or as
  stale, it queues a stop of the restore inside the database container
  (only processes of that operation and their children, bottom-up so
  the database server is not affected) and the normal cleanup.
- The cleanup runs once per import, and a stopped CoolifyTask does
  not run again when the queue retries it.
- The stop message stays on the activity: RunRemoteProcess saves the
  process id at start from the stored properties and keeps a stop
  status at the end, and CoolifyTask::failed() keeps the message.
- Regenerate the OpenAPI spec (409 for database start, restart, and
  import, plus earlier API changes that were missing).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 22:18:17 +02:00

97 lines
4.4 KiB
PHP

<?php
test('documents standalone and service database import endpoints', function () {
$document = json_decode((string) file_get_contents(__DIR__.'/../../openapi.json'), true, flags: JSON_THROW_ON_ERROR);
expect($document['paths'])
->toHaveKey('/databases/{uuid}/imports/uploads')
->toHaveKey('/databases/{uuid}/imports')
->toHaveKey('/databases/{uuid}/imports/{activity_id}')
->toHaveKey('/services/{uuid}/databases/{database_uuid}/imports/uploads')
->toHaveKey('/services/{uuid}/databases/{database_uuid}/imports')
->toHaveKey('/services/{uuid}/databases/{database_uuid}/imports/{activity_id}');
foreach ($document['components']['schemas']['DatabaseImportRequest']['oneOf'] as $source) {
expect($source['properties']['replace_existing'])
->toMatchArray(['type' => 'boolean', 'default' => false]);
}
$statusRef = [
'description' => 'Import status',
'content' => [
'application/json' => [
'schema' => [
'$ref' => '#/components/schemas/DatabaseImportStatus',
],
],
],
];
expect($document['paths']['/databases/{uuid}/imports/{activity_id}']['get']['responses']['200'])
->toMatchArray($statusRef)
->and($document['paths']['/services/{uuid}/databases/{database_uuid}/imports/{activity_id}']['get']['responses']['200'])
->toMatchArray($statusRef)
->and($document['components']['schemas'])
->toHaveKey('DatabaseImportStatus');
});
test('documents path parameters for database import endpoints', function () {
$document = json_decode((string) file_get_contents(__DIR__.'/../../openapi.json'), true, flags: JSON_THROW_ON_ERROR);
$operations = [
['/databases/{uuid}/imports/uploads', 'post', ['uuid']],
['/databases/{uuid}/imports', 'post', ['uuid']],
['/databases/{uuid}/imports/{activity_id}', 'get', ['uuid', 'activity_id']],
['/services/{uuid}/databases/{database_uuid}/imports/uploads', 'post', ['uuid', 'database_uuid']],
['/services/{uuid}/databases/{database_uuid}/imports', 'post', ['uuid', 'database_uuid']],
['/services/{uuid}/databases/{database_uuid}/imports/{activity_id}', 'get', ['uuid', 'database_uuid', 'activity_id']],
];
foreach ($operations as [$path, $method, $expectedNames]) {
$parameters = $document['paths'][$path][$method]['parameters'] ?? [];
$pathParameters = collect($parameters)
->filter(fn (array $parameter): bool => ($parameter['in'] ?? null) === 'path')
->map(fn (array $parameter): string => $parameter['name'])
->values()
->all();
expect($pathParameters)->toEqual($expectedNames);
}
});
test('constrains additional properties on each database import source branch', function () {
$document = json_decode((string) file_get_contents(__DIR__.'/../../openapi.json'), true, flags: JSON_THROW_ON_ERROR);
$schema = $document['components']['schemas']['DatabaseImportRequest'];
expect($schema)->not->toHaveKey('additionalProperties');
$expectedProperties = [
['source', 'upload_id', 'dump_all', 'replace_existing'],
['source', 's3_storage_uuid', 'path', 'dump_all', 'replace_existing'],
['source', 'path', 'dump_all', 'replace_existing'],
];
expect($schema['oneOf'])->toHaveCount(count($expectedProperties));
foreach ($schema['oneOf'] as $index => $source) {
expect($source['additionalProperties'])->toBeFalse()
->and($source['properties'])->toHaveKeys($expectedProperties[$index])
->and($source['properties']['replace_existing'])
->toMatchArray(['type' => 'boolean', 'default' => false]);
}
});
test('documents the 409 response of database start, restart, and import endpoints', function () {
$document = json_decode((string) file_get_contents(__DIR__.'/../../openapi.json'), true, flags: JSON_THROW_ON_ERROR);
$inProgress = 'Another start, restart or import of this database is already in progress.';
foreach ([
['/databases/{uuid}/start', 'post'],
['/databases/{uuid}/restart', 'post'],
['/databases/{uuid}/imports', 'post'],
['/services/{uuid}/databases/{database_uuid}/imports', 'post'],
] as [$path, $method]) {
expect($document['paths'][$path][$method]['responses']['409']['description'] ?? null)->toBe($inProgress);
}
});