Add comprehensive unit tests for scheduler components
This commit is contained in:
@@ -15,6 +15,7 @@ class SchedulerService
|
||||
private CacheInterface $cache;
|
||||
private Container $container;
|
||||
private SettingsInterface $settings;
|
||||
private ?ScheduleJobRegistry $registry = null;
|
||||
|
||||
private const GLOBAL_LOCK_KEY = 'scheduler.global_lock';
|
||||
private const GLOBAL_LOCK_TTL = 300; // 5 minutes
|
||||
@@ -27,6 +28,12 @@ class SchedulerService
|
||||
$this->settings = $settings;
|
||||
}
|
||||
|
||||
// For testing purposes
|
||||
public function setRegistry(ScheduleJobRegistry $registry): void
|
||||
{
|
||||
$this->registry = $registry;
|
||||
}
|
||||
|
||||
public function run(): SchedulerResult
|
||||
{
|
||||
$result = new SchedulerResult();
|
||||
@@ -50,13 +57,16 @@ class SchedulerService
|
||||
$this->updateGlobalLastRun();
|
||||
|
||||
try {
|
||||
$scheduler = new ScheduleJobRegistry($this->container);
|
||||
$scheduler = $this->registry ?: new ScheduleJobRegistry($this->container);
|
||||
|
||||
$configFile = BP_BASE_PATH . '/configs/schedule.php';
|
||||
if (file_exists($configFile)) {
|
||||
require $configFile;
|
||||
} else {
|
||||
$this->logger->warning('Scheduler config file not found: ' . $configFile);
|
||||
// Only load config file if registry was not injected (for production use)
|
||||
if (!$this->registry) {
|
||||
$configFile = BP_BASE_PATH . '/configs/schedule.php';
|
||||
if (file_exists($configFile)) {
|
||||
require $configFile;
|
||||
} else {
|
||||
$this->logger->warning('Scheduler config file not found: ' . $configFile);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($scheduler->getJobs() as $job) {
|
||||
|
||||
@@ -0,0 +1,210 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Unit\Framework\Scheduler;
|
||||
|
||||
use Openguru\OpenCartFramework\Scheduler\Job;
|
||||
use Openguru\OpenCartFramework\Scheduler\TaskInterface;
|
||||
use Tests\TestCase;
|
||||
|
||||
class JobTest extends TestCase
|
||||
{
|
||||
public function testJobCreation()
|
||||
{
|
||||
// Arrange
|
||||
$container = $this->app;
|
||||
|
||||
// Act
|
||||
$job = new Job($container, function() {}, 'TestJob');
|
||||
|
||||
// Assert
|
||||
$this->assertEquals('TestJob', $job->getName());
|
||||
$this->assertEquals(md5('TestJob'), $job->getId());
|
||||
}
|
||||
|
||||
public function testJobWithoutNameUsesClassName()
|
||||
{
|
||||
// Arrange
|
||||
$container = $this->app;
|
||||
|
||||
// Act
|
||||
$job = new Job($container, TestTask::class);
|
||||
|
||||
// Assert
|
||||
$this->assertEquals(TestTask::class, $job->getName());
|
||||
}
|
||||
|
||||
public function testJobWithClosureGetsClosureName()
|
||||
{
|
||||
// Arrange
|
||||
$container = $this->app;
|
||||
|
||||
// Act
|
||||
$job = new Job($container, function() {});
|
||||
|
||||
// Assert
|
||||
$this->assertEquals('Closure', $job->getName());
|
||||
}
|
||||
|
||||
public function testEveryMinuteSchedule()
|
||||
{
|
||||
// Arrange
|
||||
$container = $this->app;
|
||||
$job = new Job($container, function() {});
|
||||
|
||||
// Act
|
||||
$job->everyMinute();
|
||||
|
||||
// Assert
|
||||
$this->assertEquals('* * * * *', $job->getExpression());
|
||||
}
|
||||
|
||||
public function testEveryFiveMinutesSchedule()
|
||||
{
|
||||
// Arrange
|
||||
$container = $this->app;
|
||||
$job = new Job($container, function() {});
|
||||
|
||||
// Act
|
||||
$job->everyFiveMinutes();
|
||||
|
||||
// Assert
|
||||
$this->assertEquals('*/5 * * * *', $job->getExpression());
|
||||
}
|
||||
|
||||
public function testEveryHourSchedule()
|
||||
{
|
||||
// Arrange
|
||||
$container = $this->app;
|
||||
$job = new Job($container, function() {});
|
||||
|
||||
// Act
|
||||
$job->everyHour();
|
||||
|
||||
// Assert
|
||||
$this->assertEquals('0 * * * *', $job->getExpression());
|
||||
}
|
||||
|
||||
public function testDailyAtSchedule()
|
||||
{
|
||||
// Arrange
|
||||
$container = $this->app;
|
||||
$job = new Job($container, function() {});
|
||||
|
||||
// Act
|
||||
$job->dailyAt(9, 30);
|
||||
|
||||
// Assert
|
||||
$this->assertEquals('30 9 * * *', $job->getExpression());
|
||||
}
|
||||
|
||||
public function testAtSchedule()
|
||||
{
|
||||
// Arrange
|
||||
$container = $this->app;
|
||||
$job = new Job($container, function() {});
|
||||
|
||||
// Act
|
||||
$job->at('0 12 * * 1');
|
||||
|
||||
// Assert
|
||||
$this->assertEquals('0 12 * * 1', $job->getExpression());
|
||||
}
|
||||
|
||||
public function testIsDueReturnsTrueForMinuteExpression()
|
||||
{
|
||||
// Arrange
|
||||
$container = $this->app;
|
||||
$job = new Job($container, function() {});
|
||||
$job->everyMinute();
|
||||
|
||||
// Act
|
||||
$isDue = $job->isDue();
|
||||
|
||||
// Assert
|
||||
$this->assertTrue($isDue);
|
||||
}
|
||||
|
||||
public function testIsDueReturnsFalseForFutureExpression()
|
||||
{
|
||||
// Arrange
|
||||
$container = $this->app;
|
||||
$job = new Job($container, function() {});
|
||||
// Set to run at a specific future time
|
||||
$job->at('0 23 * * *'); // 23:00 every day
|
||||
|
||||
// Act
|
||||
$isDue = $job->isDue();
|
||||
|
||||
// Assert - depends on current time, but generally should be false unless it's exactly 23:00
|
||||
// This test is not deterministic, so we'll just check it's boolean
|
||||
$this->assertIsBool($isDue);
|
||||
}
|
||||
|
||||
public function testRunExecutesClosure()
|
||||
{
|
||||
// Arrange
|
||||
$container = $this->app;
|
||||
$executed = false;
|
||||
|
||||
$job = new Job($container, function() use (&$executed) {
|
||||
$executed = true;
|
||||
});
|
||||
|
||||
// Act
|
||||
$job->run();
|
||||
|
||||
// Assert
|
||||
$this->assertTrue($executed);
|
||||
}
|
||||
|
||||
public function testRunExecutesTaskClass()
|
||||
{
|
||||
// Arrange
|
||||
$container = $this->app;
|
||||
|
||||
// Mock the task class binding
|
||||
$container->singleton(TestTask::class, function() {
|
||||
return new TestTask();
|
||||
});
|
||||
|
||||
$job = new Job($container, TestTask::class);
|
||||
|
||||
// Act
|
||||
$job->run();
|
||||
|
||||
// Assert
|
||||
$this->assertTrue(TestTask::$executed);
|
||||
TestTask::reset(); // Reset for other tests
|
||||
}
|
||||
|
||||
public function testRunExecutesTaskInterface()
|
||||
{
|
||||
// Arrange
|
||||
$container = $this->app;
|
||||
$task = new TestTask();
|
||||
$job = new Job($container, $task);
|
||||
|
||||
// Act
|
||||
$job->run();
|
||||
|
||||
// Assert
|
||||
$this->assertTrue(TestTask::$executed);
|
||||
TestTask::reset(); // Reset for other tests
|
||||
}
|
||||
}
|
||||
|
||||
// Test helper class
|
||||
class TestTask implements TaskInterface
|
||||
{
|
||||
public static bool $executed = false;
|
||||
|
||||
public function execute(): void
|
||||
{
|
||||
self::$executed = true;
|
||||
}
|
||||
|
||||
public static function reset(): void
|
||||
{
|
||||
self::$executed = false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Unit\Framework\Scheduler;
|
||||
|
||||
use Openguru\OpenCartFramework\Scheduler\Job;
|
||||
use Openguru\OpenCartFramework\Scheduler\ScheduleJobRegistry;
|
||||
use Tests\TestCase;
|
||||
|
||||
class ScheduleJobRegistryTest extends TestCase
|
||||
{
|
||||
public function testRegistryCreation()
|
||||
{
|
||||
// Arrange & Act
|
||||
$registry = new ScheduleJobRegistry($this->app);
|
||||
|
||||
// Assert
|
||||
$this->assertInstanceOf(ScheduleJobRegistry::class, $registry);
|
||||
$this->assertEmpty($registry->getJobs());
|
||||
}
|
||||
|
||||
public function testAddJobWithoutName()
|
||||
{
|
||||
// Arrange
|
||||
$registry = new ScheduleJobRegistry($this->app);
|
||||
|
||||
// Act
|
||||
$job = $registry->add(function() {});
|
||||
|
||||
// Assert
|
||||
$this->assertInstanceOf(Job::class, $job);
|
||||
$this->assertCount(1, $registry->getJobs());
|
||||
$this->assertEquals('Closure', $job->getName());
|
||||
}
|
||||
|
||||
public function testAddJobWithName()
|
||||
{
|
||||
// Arrange
|
||||
$registry = new ScheduleJobRegistry($this->app);
|
||||
|
||||
// Act
|
||||
$job = $registry->add(function() {}, 'MyCustomJob');
|
||||
|
||||
// Assert
|
||||
$this->assertInstanceOf(Job::class, $job);
|
||||
$this->assertCount(1, $registry->getJobs());
|
||||
$this->assertEquals('MyCustomJob', $job->getName());
|
||||
}
|
||||
|
||||
public function testAddMultipleJobs()
|
||||
{
|
||||
// Arrange
|
||||
$registry = new ScheduleJobRegistry($this->app);
|
||||
|
||||
// Act
|
||||
$job1 = $registry->add(function() {}, 'Job1');
|
||||
$job2 = $registry->add(function() {}, 'Job2');
|
||||
$job3 = $registry->add(TestTask::class, 'Job3');
|
||||
|
||||
// Assert
|
||||
$jobs = $registry->getJobs();
|
||||
$this->assertCount(3, $jobs);
|
||||
$this->assertEquals('Job1', $jobs[0]->getName());
|
||||
$this->assertEquals('Job2', $jobs[1]->getName());
|
||||
$this->assertEquals('Job3', $jobs[2]->getName());
|
||||
}
|
||||
|
||||
public function testGetJobsReturnsArray()
|
||||
{
|
||||
// Arrange
|
||||
$registry = new ScheduleJobRegistry($this->app);
|
||||
$registry->add(function() {}, 'TestJob');
|
||||
|
||||
// Act
|
||||
$jobs = $registry->getJobs();
|
||||
|
||||
// Assert
|
||||
$this->assertIsArray($jobs);
|
||||
$this->assertCount(1, $jobs);
|
||||
$this->assertInstanceOf(Job::class, $jobs[0]);
|
||||
}
|
||||
|
||||
public function testJobSchedulingMethods()
|
||||
{
|
||||
// Arrange
|
||||
$registry = new ScheduleJobRegistry($this->app);
|
||||
|
||||
// Act
|
||||
$job = $registry->add(function() {}, 'TestJob')
|
||||
->everyFiveMinutes();
|
||||
|
||||
// Assert
|
||||
$this->assertEquals('*/5 * * * *', $job->getExpression());
|
||||
// Just check that isDue() returns boolean, don't check the exact value
|
||||
// as it depends on current time when test runs
|
||||
$this->assertIsBool($job->isDue());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Unit\Framework\Scheduler;
|
||||
|
||||
use Openguru\OpenCartFramework\Scheduler\SchedulerResult;
|
||||
use Tests\TestCase;
|
||||
|
||||
class SchedulerResultTest extends TestCase
|
||||
{
|
||||
public function testSchedulerResultInitialization()
|
||||
{
|
||||
// Arrange & Act
|
||||
$result = new SchedulerResult();
|
||||
|
||||
// Assert
|
||||
$this->assertEmpty($result->executed);
|
||||
$this->assertEmpty($result->failed);
|
||||
$this->assertEmpty($result->skipped);
|
||||
}
|
||||
|
||||
public function testAddExecuted()
|
||||
{
|
||||
// Arrange
|
||||
$result = new SchedulerResult();
|
||||
|
||||
// Act
|
||||
$result->addExecuted('TestJob', 1.5);
|
||||
|
||||
// Assert
|
||||
$this->assertCount(1, $result->executed);
|
||||
$this->assertEquals('TestJob', $result->executed[0]['name']);
|
||||
$this->assertEquals(1.5, $result->executed[0]['duration']);
|
||||
}
|
||||
|
||||
public function testAddFailed()
|
||||
{
|
||||
// Arrange
|
||||
$result = new SchedulerResult();
|
||||
|
||||
// Act
|
||||
$result->addFailed('TestJob', 'Database connection failed');
|
||||
|
||||
// Assert
|
||||
$this->assertCount(1, $result->failed);
|
||||
$this->assertEquals('TestJob', $result->failed[0]['name']);
|
||||
$this->assertEquals('Database connection failed', $result->failed[0]['error']);
|
||||
}
|
||||
|
||||
public function testAddSkipped()
|
||||
{
|
||||
// Arrange
|
||||
$result = new SchedulerResult();
|
||||
|
||||
// Act
|
||||
$result->addSkipped('TestJob', 'Not due yet');
|
||||
|
||||
// Assert
|
||||
$this->assertCount(1, $result->skipped);
|
||||
$this->assertEquals('TestJob', $result->skipped[0]['name']);
|
||||
$this->assertEquals('Not due yet', $result->skipped[0]['reason']);
|
||||
}
|
||||
|
||||
public function testMultipleOperations()
|
||||
{
|
||||
// Arrange
|
||||
$result = new SchedulerResult();
|
||||
|
||||
// Act
|
||||
$result->addExecuted('Job1', 0.5);
|
||||
$result->addExecuted('Job2', 1.2);
|
||||
$result->addFailed('Job3', 'Timeout');
|
||||
$result->addSkipped('Job4', 'Already running');
|
||||
$result->addSkipped('Job5', 'Not due');
|
||||
|
||||
// Assert
|
||||
$this->assertCount(2, $result->executed);
|
||||
$this->assertCount(1, $result->failed);
|
||||
$this->assertCount(2, $result->skipped);
|
||||
}
|
||||
|
||||
public function testToArray()
|
||||
{
|
||||
// Arrange
|
||||
$result = new SchedulerResult();
|
||||
$result->addExecuted('Job1', 1.0);
|
||||
$result->addFailed('Job2', 'Error');
|
||||
$result->addSkipped('Job3', 'Skipped');
|
||||
|
||||
// Act
|
||||
$array = $result->toArray();
|
||||
|
||||
// Assert
|
||||
$expected = [
|
||||
'executed' => [['name' => 'Job1', 'duration' => 1.0]],
|
||||
'failed' => [['name' => 'Job2', 'error' => 'Error']],
|
||||
'skipped' => [['name' => 'Job3', 'reason' => 'Skipped']],
|
||||
];
|
||||
$this->assertEquals($expected, $array);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,379 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Unit\Framework\Scheduler;
|
||||
|
||||
use Mockery;
|
||||
use Openguru\OpenCartFramework\Cache\CacheInterface;
|
||||
use Openguru\OpenCartFramework\Config\SettingsInterface;
|
||||
use Openguru\OpenCartFramework\Scheduler\Job;
|
||||
use Openguru\OpenCartFramework\Scheduler\SchedulerService;
|
||||
use Openguru\OpenCartFramework\Scheduler\ScheduleJobRegistry;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Tests\TestCase;
|
||||
|
||||
class SchedulerServiceTest extends TestCase
|
||||
{
|
||||
private SchedulerService $scheduler;
|
||||
private $cacheMock;
|
||||
private $settingsMock;
|
||||
private $loggerMock;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->cacheMock = Mockery::mock(CacheInterface::class);
|
||||
$this->settingsMock = Mockery::mock(SettingsInterface::class);
|
||||
$this->loggerMock = Mockery::mock(LoggerInterface::class);
|
||||
|
||||
$this->scheduler = new SchedulerService(
|
||||
$this->loggerMock,
|
||||
$this->cacheMock,
|
||||
$this->app,
|
||||
$this->settingsMock
|
||||
);
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
Mockery::close();
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function testDisabledModeSkipsExecution()
|
||||
{
|
||||
// Arrange
|
||||
$this->settingsMock->shouldReceive('get')
|
||||
->with('cron.mode', 'disabled')
|
||||
->andReturn('disabled');
|
||||
|
||||
// Act
|
||||
$result = $this->scheduler->run();
|
||||
|
||||
// Assert
|
||||
$this->assertEquals([['name' => 'Global', 'reason' => 'Scheduler is disabled']], $result->skipped);
|
||||
$this->assertEmpty($result->executed);
|
||||
$this->assertEmpty($result->failed);
|
||||
}
|
||||
|
||||
public function testGlobalLockPreventsExecution()
|
||||
{
|
||||
// Arrange
|
||||
$this->settingsMock->shouldReceive('get')
|
||||
->with('cron.mode', 'disabled')
|
||||
->andReturn('system');
|
||||
|
||||
$this->cacheMock->shouldReceive('get')
|
||||
->with('scheduler.global_lock')
|
||||
->andReturn('1'); // Lock is active
|
||||
|
||||
// Act
|
||||
$result = $this->scheduler->run();
|
||||
|
||||
// Assert
|
||||
$this->assertEquals([['name' => 'Global', 'reason' => 'Global scheduler lock active']], $result->skipped);
|
||||
$this->assertEmpty($result->executed);
|
||||
$this->assertEmpty($result->failed);
|
||||
}
|
||||
|
||||
public function testJobLockPreventsExecution()
|
||||
{
|
||||
// Arrange
|
||||
$this->settingsMock->shouldReceive('get')
|
||||
->with('cron.mode', 'disabled')
|
||||
->andReturn('system');
|
||||
|
||||
$this->cacheMock->shouldReceive('get')
|
||||
->with('scheduler.global_lock')
|
||||
->andReturn(null); // No global lock
|
||||
|
||||
$this->cacheMock->shouldReceive('set')
|
||||
->with('scheduler.global_lock', 1, 300)
|
||||
->once();
|
||||
|
||||
$this->cacheMock->shouldReceive('delete')
|
||||
->with('scheduler.global_lock')
|
||||
->once();
|
||||
|
||||
$this->cacheMock->shouldReceive('set')
|
||||
->with('scheduler.global_last_run', Mockery::type('int'))
|
||||
->once();
|
||||
|
||||
// Mock registry and job
|
||||
$registryMock = Mockery::mock(ScheduleJobRegistry::class);
|
||||
$jobMock = Mockery::mock(Job::class);
|
||||
|
||||
$jobMock->shouldReceive('getName')->andReturn('TestJob');
|
||||
$jobMock->shouldReceive('getId')->andReturn('test_job_id');
|
||||
$jobMock->shouldReceive('isDue')->andReturn(true);
|
||||
|
||||
// Job has not run recently (getLastRun returns null)
|
||||
$this->cacheMock->shouldReceive('get')
|
||||
->with('scheduler.last_run.test_job_id')
|
||||
->andReturn(null);
|
||||
|
||||
// Job is locked
|
||||
$this->cacheMock->shouldReceive('get')
|
||||
->with('scheduler.lock.test_job_id')
|
||||
->andReturn('1');
|
||||
|
||||
$registryMock->shouldReceive('getJobs')->andReturn([$jobMock]);
|
||||
|
||||
// Logger should not be called for this test
|
||||
$this->loggerMock->shouldReceive('error')->never();
|
||||
|
||||
// Inject registry for testing
|
||||
$this->scheduler->setRegistry($registryMock);
|
||||
|
||||
// Act
|
||||
$result = $this->scheduler->run();
|
||||
|
||||
// Assert
|
||||
$this->assertEquals([['name' => 'TestJob', 'reason' => 'Job is locked (running)']], $result->skipped);
|
||||
}
|
||||
|
||||
public function testJobRecentlyExecutedPreventsExecution()
|
||||
{
|
||||
// Arrange
|
||||
$this->settingsMock->shouldReceive('get')
|
||||
->with('cron.mode', 'disabled')
|
||||
->andReturn('system');
|
||||
|
||||
$this->cacheMock->shouldReceive('get')
|
||||
->with('scheduler.global_lock')
|
||||
->andReturn(null);
|
||||
|
||||
$this->cacheMock->shouldReceive('set')
|
||||
->with('scheduler.global_lock', 1, 300)
|
||||
->once();
|
||||
|
||||
$this->cacheMock->shouldReceive('delete')
|
||||
->with('scheduler.global_lock')
|
||||
->once();
|
||||
|
||||
$this->cacheMock->shouldReceive('set')
|
||||
->with('scheduler.global_last_run', Mockery::type('int'))
|
||||
->once();
|
||||
|
||||
// Mock registry and job
|
||||
$registryMock = Mockery::mock(ScheduleJobRegistry::class);
|
||||
$jobMock = Mockery::mock(Job::class);
|
||||
|
||||
$jobMock->shouldReceive('getName')->andReturn('TestJob');
|
||||
$jobMock->shouldReceive('getId')->andReturn('test_job_id');
|
||||
$jobMock->shouldReceive('isDue')->andReturn(true);
|
||||
|
||||
$this->cacheMock->shouldReceive('get')
|
||||
->with('scheduler.lock.test_job_id')
|
||||
->andReturn(null); // Not locked
|
||||
|
||||
// Job was recently executed (same minute)
|
||||
$recentTime = time();
|
||||
$this->cacheMock->shouldReceive('get')
|
||||
->with('scheduler.last_run.test_job_id')
|
||||
->andReturn($recentTime);
|
||||
|
||||
$registryMock->shouldReceive('getJobs')->andReturn([$jobMock]);
|
||||
|
||||
// Inject registry for testing
|
||||
$this->scheduler->setRegistry($registryMock);
|
||||
|
||||
// Act
|
||||
$result = $this->scheduler->run();
|
||||
|
||||
// Assert
|
||||
$this->assertEquals([['name' => 'TestJob', 'reason' => 'Already ran recently']], $result->skipped);
|
||||
}
|
||||
|
||||
public function testJobNotDuePreventsExecution()
|
||||
{
|
||||
// Arrange
|
||||
$this->settingsMock->shouldReceive('get')
|
||||
->with('cron.mode', 'disabled')
|
||||
->andReturn('system');
|
||||
|
||||
$this->cacheMock->shouldReceive('get')
|
||||
->with('scheduler.global_lock')
|
||||
->andReturn(null);
|
||||
|
||||
$this->cacheMock->shouldReceive('set')
|
||||
->with('scheduler.global_lock', 1, 300)
|
||||
->once();
|
||||
|
||||
$this->cacheMock->shouldReceive('delete')
|
||||
->with('scheduler.global_lock')
|
||||
->once();
|
||||
|
||||
$this->cacheMock->shouldReceive('set')
|
||||
->with('scheduler.global_last_run', Mockery::type('int'))
|
||||
->once();
|
||||
|
||||
// Mock registry and job
|
||||
$registryMock = Mockery::mock(ScheduleJobRegistry::class);
|
||||
$jobMock = Mockery::mock(Job::class);
|
||||
|
||||
$jobMock->shouldReceive('getName')->andReturn('TestJob');
|
||||
$jobMock->shouldReceive('getId')->andReturn('test_job_id');
|
||||
$jobMock->shouldReceive('isDue')->andReturn(false); // Not due
|
||||
|
||||
$registryMock->shouldReceive('getJobs')->andReturn([$jobMock]);
|
||||
|
||||
// Inject registry for testing
|
||||
$this->scheduler->setRegistry($registryMock);
|
||||
|
||||
// Act
|
||||
$result = $this->scheduler->run();
|
||||
|
||||
// Assert
|
||||
$this->assertEquals([['name' => 'TestJob', 'reason' => 'Not due']], $result->skipped);
|
||||
}
|
||||
|
||||
public function testSuccessfulJobExecution()
|
||||
{
|
||||
// Arrange
|
||||
$this->settingsMock->shouldReceive('get')
|
||||
->with('cron.mode', 'disabled')
|
||||
->andReturn('system');
|
||||
|
||||
$this->cacheMock->shouldReceive('get')
|
||||
->with('scheduler.global_lock')
|
||||
->andReturn(null);
|
||||
|
||||
$this->cacheMock->shouldReceive('set')
|
||||
->with('scheduler.global_lock', 1, 300)
|
||||
->once();
|
||||
|
||||
$this->cacheMock->shouldReceive('delete')
|
||||
->with('scheduler.global_lock')
|
||||
->once();
|
||||
|
||||
$this->cacheMock->shouldReceive('set')
|
||||
->with('scheduler.global_last_run', Mockery::type('int'))
|
||||
->once();
|
||||
|
||||
// Mock registry and job
|
||||
$registryMock = Mockery::mock(ScheduleJobRegistry::class);
|
||||
$jobMock = Mockery::mock(Job::class);
|
||||
|
||||
$jobMock->shouldReceive('getName')->andReturn('TestJob');
|
||||
$jobMock->shouldReceive('getId')->andReturn('test_job_id');
|
||||
$jobMock->shouldReceive('isDue')->andReturn(true);
|
||||
$jobMock->shouldReceive('run')->once();
|
||||
|
||||
$this->cacheMock->shouldReceive('get')
|
||||
->with('scheduler.lock.test_job_id')
|
||||
->andReturn(null);
|
||||
|
||||
$this->cacheMock->shouldReceive('get')
|
||||
->with('scheduler.last_run.test_job_id')
|
||||
->andReturn(null); // Never ran before
|
||||
|
||||
// Lock and unlock operations
|
||||
$this->cacheMock->shouldReceive('set')
|
||||
->with('scheduler.lock.test_job_id', 1, 1800)
|
||||
->once();
|
||||
|
||||
$this->cacheMock->shouldReceive('delete')
|
||||
->with('scheduler.lock.test_job_id')
|
||||
->once();
|
||||
|
||||
$this->cacheMock->shouldReceive('set')
|
||||
->with('scheduler.last_run.test_job_id', Mockery::type('int'))
|
||||
->once();
|
||||
|
||||
$registryMock->shouldReceive('getJobs')->andReturn([$jobMock]);
|
||||
|
||||
$this->loggerMock->shouldReceive('info')
|
||||
->with('Job executed: TestJob', Mockery::type('array'))
|
||||
->once();
|
||||
|
||||
// Inject registry for testing
|
||||
$this->scheduler->setRegistry($registryMock);
|
||||
|
||||
// Act
|
||||
$result = $this->scheduler->run();
|
||||
|
||||
// Assert
|
||||
$this->assertCount(1, $result->executed);
|
||||
$this->assertEquals('TestJob', $result->executed[0]['name']);
|
||||
$this->assertEmpty($result->failed);
|
||||
$this->assertEmpty($result->skipped);
|
||||
}
|
||||
|
||||
public function testJobExecutionFailure()
|
||||
{
|
||||
// Arrange
|
||||
$this->settingsMock->shouldReceive('get')
|
||||
->with('cron.mode', 'disabled')
|
||||
->andReturn('system');
|
||||
|
||||
$this->cacheMock->shouldReceive('get')
|
||||
->with('scheduler.global_lock')
|
||||
->andReturn(null);
|
||||
|
||||
$this->cacheMock->shouldReceive('set')
|
||||
->with('scheduler.global_lock', 1, 300)
|
||||
->once();
|
||||
|
||||
$this->cacheMock->shouldReceive('delete')
|
||||
->with('scheduler.global_lock')
|
||||
->once();
|
||||
|
||||
$this->cacheMock->shouldReceive('set')
|
||||
->with('scheduler.global_last_run', Mockery::type('int'))
|
||||
->once();
|
||||
|
||||
// Mock registry and job
|
||||
$registryMock = Mockery::mock(ScheduleJobRegistry::class);
|
||||
$jobMock = Mockery::mock(Job::class);
|
||||
|
||||
$jobMock->shouldReceive('getName')->andReturn('TestJob');
|
||||
$jobMock->shouldReceive('getId')->andReturn('test_job_id');
|
||||
$jobMock->shouldReceive('isDue')->andReturn(true);
|
||||
$jobMock->shouldReceive('run')->andThrow(new \Exception('Job failed'));
|
||||
|
||||
$this->cacheMock->shouldReceive('get')
|
||||
->with('scheduler.lock.test_job_id')
|
||||
->andReturn(null);
|
||||
|
||||
$this->cacheMock->shouldReceive('get')
|
||||
->with('scheduler.last_run.test_job_id')
|
||||
->andReturn(null);
|
||||
|
||||
// Lock and unlock operations
|
||||
$this->cacheMock->shouldReceive('set')
|
||||
->with('scheduler.lock.test_job_id', 1, 1800)
|
||||
->once();
|
||||
|
||||
$this->cacheMock->shouldReceive('delete')
|
||||
->with('scheduler.lock.test_job_id')
|
||||
->once();
|
||||
|
||||
$this->cacheMock->shouldReceive('set')
|
||||
->with('scheduler.last_failure.test_job_id', Mockery::type('int'))
|
||||
->once();
|
||||
|
||||
$this->cacheMock->shouldReceive('set')
|
||||
->with('scheduler.last_failure_msg.test_job_id', 'Job failed')
|
||||
->once();
|
||||
|
||||
$registryMock->shouldReceive('getJobs')->andReturn([$jobMock]);
|
||||
|
||||
$this->loggerMock->shouldReceive('error')
|
||||
->with('Job failed: TestJob', Mockery::type('array'))
|
||||
->once();
|
||||
|
||||
// Inject registry for testing
|
||||
$this->scheduler->setRegistry($registryMock);
|
||||
|
||||
// Act
|
||||
$result = $this->scheduler->run();
|
||||
|
||||
// Assert
|
||||
$this->assertCount(1, $result->failed);
|
||||
$this->assertEquals('TestJob', $result->failed[0]['name']);
|
||||
$this->assertEquals('Job failed', $result->failed[0]['error']);
|
||||
$this->assertEmpty($result->executed);
|
||||
$this->assertEmpty($result->skipped);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user