Files
XC_VM/tests/Unit/ModuleCronRefreshTest.php
T
obscuremindandClaude Opus 5.5 16788f6ba1 fix(modules): write a module's cron lines when it is installed, not at the next restart
A module's cron lines (CronProviderInterface) reach root's crontab
through StartupCommand::installRootCrontab(), which only startup and
status call. A module installed, enabled, disabled or removed from the
panel changed nothing there until the next restart: the anti-abuse
shield, for one, never ran after its install, so its honeypot ports
were never opened.

cron:root_signals, root's every-minute run, now writes the crontab again
when config/modules.php has changed since it was last written from it
(its hash is kept in config/crontab.modules). installRootCrontab() says
whether the crontab holds the list, and a write that failed is tried
again the next minute.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 14:56:12 +00:00

49 lines
1.9 KiB
PHP

<?php
use PHPUnit\Framework\TestCase;
use XcVm\Cli\CronJobs\RootSignalsCronJob;
/**
* root_signals writes root's crontab again when config/modules.php changes,
* so a module installed or enabled from the panel gets its cron lines (the
* anti-abuse shield's, which starts its honeypot) without a restart.
*/
final class ModuleCronRefreshTest extends TestCase {
private string $rDir;
protected function setUp(): void {
$this->rDir = sys_get_temp_dir() . '/xcvm-modcron-' . getmypid() . '/';
@mkdir($this->rDir);
@unlink($this->rDir . 'crontab.modules');
}
protected function tearDown(): void {
@unlink($this->rDir . 'modules.php');
@unlink($this->rDir . 'crontab.modules');
@rmdir($this->rDir);
}
public function testWrittenOnceForEachChangeOfTheModules(): void {
$rWrites = 0;
$rWrite = static function () use (&$rWrites): bool {
$rWrites++;
return true;
};
$this->assertFalse(RootSignalsCronJob::refreshModuleCrons($rWrite, $this->rDir), 'no modules.php: nothing to do');
file_put_contents($this->rDir . 'modules.php', "<?php return ['anti-abuse' => ['installed_version' => '1.0.0']];");
$this->assertTrue(RootSignalsCronJob::refreshModuleCrons($rWrite, $this->rDir), 'a module installed');
$this->assertFalse(RootSignalsCronJob::refreshModuleCrons($rWrite, $this->rDir), 'the next minute: unchanged');
file_put_contents($this->rDir . 'modules.php', "<?php return ['anti-abuse' => ['installed_version' => '1.0.0', 'enabled' => false]];");
$this->assertTrue(RootSignalsCronJob::refreshModuleCrons($rWrite, $this->rDir), 'a module disabled');
$this->assertSame(2, $rWrites);
}
public function testACrontabNotWrittenIsTriedAgain(): void {
file_put_contents($this->rDir . 'modules.php', "<?php return [];");
$this->assertFalse(RootSignalsCronJob::refreshModuleCrons(static fn(): bool => false, $this->rDir));
$this->assertTrue(RootSignalsCronJob::refreshModuleCrons(static fn(): bool => true, $this->rDir), 'the next minute');
}
}