Replace drifting cron loops with persistent cadence-tracked workers and CI-verifiable Compose wiring.
79 lines
2.6 KiB
PHP
79 lines
2.6 KiB
PHP
<?php
|
|
|
|
use classes\cron_schedule;
|
|
use classes\cron_task_registry;
|
|
|
|
it('discovers module-owned cron task definitions', function (): void {
|
|
$registry = new cron_task_registry(app_path('modules'));
|
|
$definitions = $registry->definitions();
|
|
|
|
expect($definitions)->toHaveCount(23);
|
|
expect(array_keys($definitions))->toContain(
|
|
'system.sync_logs',
|
|
'backups.process_jobs',
|
|
'backups.prune_retention',
|
|
'economic.transfer_queue',
|
|
'dynamicimages.pre_render',
|
|
'weatherapi.preload_department_responses',
|
|
'goals.progress_alerts',
|
|
'account.process_deletion_requests',
|
|
'selfserve.activate_opening_cleaner_relays'
|
|
);
|
|
|
|
$transferQueue = $registry->get('EconomicTransferQueueCron');
|
|
expect($transferQueue)->not->toBeNull();
|
|
expect($transferQueue->id)->toBe('economic.transfer_queue');
|
|
expect($transferQueue->module)->toBe('economic');
|
|
expect($transferQueue->schedule)->toBe(['type' => 'interval', 'seconds' => 30]);
|
|
});
|
|
|
|
it('keeps every discovered cron task in a module cron folder', function (): void {
|
|
$files = glob(app_path('modules/*/cron/tasks.php')) ?: [];
|
|
$modules = array_map(
|
|
static fn(string $file): string => basename(dirname(dirname($file))),
|
|
$files
|
|
);
|
|
|
|
$registry = new cron_task_registry(app_path('modules'));
|
|
foreach ($registry->definitions() as $definition) {
|
|
expect($modules)->toContain($definition->module);
|
|
}
|
|
});
|
|
|
|
it('normalizes and advances interval schedules without tight loops', function (): void {
|
|
expect(cron_schedule::normalize(['type' => 'interval', 'seconds' => 60]))
|
|
->toBe(['type' => 'interval', 'seconds' => 60]);
|
|
|
|
$now = strtotime('2026-07-09 12:10:00');
|
|
$next = cron_schedule::nextRunAt(
|
|
['type' => 'interval', 'seconds' => 300],
|
|
'2026-07-09 12:00:00',
|
|
$now
|
|
);
|
|
|
|
expect($next)->toBe('2026-07-09 12:15:00');
|
|
});
|
|
|
|
it('keeps minute schedules anchored to intended slots when execution finishes late', function (): void {
|
|
$schedule = ['type' => 'interval', 'seconds' => 60];
|
|
|
|
$first = cron_schedule::nextRunAt(
|
|
$schedule,
|
|
'2026-07-27 12:00:00',
|
|
strtotime('2026-07-27 12:00:47')
|
|
);
|
|
$second = cron_schedule::nextRunAt(
|
|
$schedule,
|
|
$first,
|
|
strtotime('2026-07-27 12:01:52')
|
|
);
|
|
|
|
expect($first)->toBe('2026-07-27 12:01:00');
|
|
expect($second)->toBe('2026-07-27 12:02:00');
|
|
});
|
|
|
|
it('rejects unsafe cron intervals', function (): void {
|
|
expect(fn() => cron_schedule::normalize(['type' => 'interval', 'seconds' => 5]))
|
|
->toThrow(InvalidArgumentException::class);
|
|
});
|