diff --git a/app/Jobs/Schedule/RunTaskJob.php b/app/Jobs/Schedule/RunTaskJob.php index a7fd8486b..7f206ec8f 100644 --- a/app/Jobs/Schedule/RunTaskJob.php +++ b/app/Jobs/Schedule/RunTaskJob.php @@ -3,7 +3,7 @@ namespace Pterodactyl\Jobs\Schedule; use Exception; -use Carbon\Carbon; +use Cake\Chronos\Chronos; use Pterodactyl\Jobs\Job; use InvalidArgumentException; use Illuminate\Queue\SerializesModels; @@ -158,7 +158,7 @@ class RunTaskJob extends Job implements ShouldQueue $repository = app()->make(ScheduleRepositoryInterface::class); $repository->withoutFreshModel()->update($this->schedule, [ 'is_processing' => false, - 'last_run_at' => Carbon::now()->toDateTimeString(), + 'last_run_at' => Chronos::now()->toDateTimeString(), ]); } diff --git a/app/Services/Schedules/ProcessScheduleService.php b/app/Services/Schedules/ProcessScheduleService.php index c83ad5104..07275950c 100644 --- a/app/Services/Schedules/ProcessScheduleService.php +++ b/app/Services/Schedules/ProcessScheduleService.php @@ -7,36 +7,48 @@ use Cron\CronExpression; use Cake\Chronos\Chronos; use Pterodactyl\Models\Schedule; use Cake\Chronos\ChronosInterface; -use Pterodactyl\Services\Schedules\Tasks\RunTaskService; +use Illuminate\Contracts\Bus\Dispatcher; +use Pterodactyl\Jobs\Schedule\RunTaskJob; +use Pterodactyl\Contracts\Repository\TaskRepositoryInterface; use Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface; class ProcessScheduleService { /** - * @var \Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface + * @var \Illuminate\Contracts\Bus\Dispatcher */ - private $repository; - - /** - * @var \Pterodactyl\Services\Schedules\Tasks\RunTaskService - */ - private $runnerService; + private $dispatcher; /** * @var \DateTimeInterface|null */ private $runTimeOverride; + /** + * @var \Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface + */ + private $scheduleRepository; + + /** + * @var \Pterodactyl\Contracts\Repository\TaskRepositoryInterface + */ + private $taskRepository; + /** * ProcessScheduleService constructor. * - * @param \Pterodactyl\Services\Schedules\Tasks\RunTaskService $runnerService - * @param \Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface $repository + * @param \Illuminate\Contracts\Bus\Dispatcher $dispatcher + * @param \Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface $scheduleRepository + * @param \Pterodactyl\Contracts\Repository\TaskRepositoryInterface $taskRepository */ - public function __construct(RunTaskService $runnerService, ScheduleRepositoryInterface $repository) - { - $this->repository = $repository; - $this->runnerService = $runnerService; + public function __construct( + Dispatcher $dispatcher, + ScheduleRepositoryInterface $scheduleRepository, + TaskRepositoryInterface $taskRepository + ) { + $this->dispatcher = $dispatcher; + $this->scheduleRepository = $scheduleRepository; + $this->taskRepository = $taskRepository; } /** @@ -63,7 +75,10 @@ class ProcessScheduleService */ public function handle(Schedule $schedule) { - $this->repository->loadTasks($schedule); + $this->scheduleRepository->loadTasks($schedule); + + /** @var \Pterodactyl\Models\Task $task */ + $task = $schedule->getRelation('tasks')->where('sequence_id', 1)->first(); $formattedCron = sprintf('%s %s %s * %s', $schedule->cron_minute, @@ -72,13 +87,16 @@ class ProcessScheduleService $schedule->cron_day_of_week ); - $this->repository->update($schedule->id, [ + $this->scheduleRepository->update($schedule->id, [ 'is_processing' => true, 'next_run_at' => $this->getRunAtTime($formattedCron), ]); - $task = $schedule->getRelation('tasks')->where('sequence_id', 1)->first(); - $this->runnerService->handle($task); + $this->taskRepository->update($task->id, ['is_queued' => true]); + + $this->dispatcher->dispatch( + (new RunTaskJob($task->id, $schedule->id))->delay($task->time_offset) + ); } /** diff --git a/app/Services/Schedules/Tasks/RunTaskService.php b/app/Services/Schedules/Tasks/RunTaskService.php deleted file mode 100644 index 5d83db077..000000000 --- a/app/Services/Schedules/Tasks/RunTaskService.php +++ /dev/null @@ -1,53 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Pterodactyl\Services\Schedules\Tasks; - -use Pterodactyl\Models\Task; -use Pterodactyl\Jobs\Schedule\RunTaskJob; -use Illuminate\Foundation\Bus\DispatchesJobs; -use Pterodactyl\Contracts\Repository\TaskRepositoryInterface; - -class RunTaskService -{ - use DispatchesJobs; - - /** - * @var \Pterodactyl\Contracts\Repository\TaskRepositoryInterface - */ - protected $repository; - - /** - * RunTaskService constructor. - * - * @param \Pterodactyl\Contracts\Repository\TaskRepositoryInterface $repository - */ - public function __construct(TaskRepositoryInterface $repository) - { - $this->repository = $repository; - } - - /** - * Push a single task onto the queue. - * - * @param int|\Pterodactyl\Models\Task $task - * - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function handle($task) - { - if (! $task instanceof Task) { - $task = $this->repository->find($task); - } - - $this->repository->update($task->id, ['is_queued' => true]); - $this->dispatch((new RunTaskJob($task->id, $task->schedule_id))->delay($task->time_offset)); - } -} diff --git a/tests/Unit/Jobs/Schedule/RunTaskJobTest.php b/tests/Unit/Jobs/Schedule/RunTaskJobTest.php index 1c6ce81bf..c4d116739 100644 --- a/tests/Unit/Jobs/Schedule/RunTaskJobTest.php +++ b/tests/Unit/Jobs/Schedule/RunTaskJobTest.php @@ -3,8 +3,8 @@ namespace Tests\Unit\Jobs\Schedule; use Mockery as m; -use Carbon\Carbon; use Tests\TestCase; +use Cake\Chronos\Chronos; use Pterodactyl\Models\Task; use Pterodactyl\Models\User; use GuzzleHttp\Psr7\Response; @@ -58,7 +58,7 @@ class RunTaskJobTest extends TestCase { parent::setUp(); Bus::fake(); - Carbon::setTestNow(Carbon::now()); + Chronos::setTestNow(Chronos::now()); $this->commandRepository = m::mock(CommandRepositoryInterface::class); $this->config = m::mock(Repository::class); @@ -94,7 +94,7 @@ class RunTaskJobTest extends TestCase $this->scheduleRepository->shouldReceive('withoutFreshModel->update')->with($schedule->id, [ 'is_processing' => false, - 'last_run_at' => Carbon::now()->toDateTimeString(), + 'last_run_at' => Chronos::now()->toDateTimeString(), ])->once()->andReturnNull(); $this->getJobInstance($task->id, $schedule->id); @@ -124,7 +124,7 @@ class RunTaskJobTest extends TestCase $this->scheduleRepository->shouldReceive('withoutFreshModel->update')->with($schedule->id, [ 'is_processing' => false, - 'last_run_at' => Carbon::now()->toDateTimeString(), + 'last_run_at' => Chronos::now()->toDateTimeString(), ])->once()->andReturnNull(); $this->getJobInstance($task->id, $schedule->id); @@ -202,7 +202,7 @@ class RunTaskJobTest extends TestCase $this->scheduleRepository->shouldReceive('withoutFreshModel->update')->with($schedule->id, [ 'is_processing' => false, - 'last_run_at' => Carbon::now()->toDateTimeString(), + 'last_run_at' => Chronos::now()->toDateTimeString(), ])->once()->andReturn(1); $this->taskRepository->shouldReceive('update')->with($task->id, ['is_queued' => false])->once()->andReturn(1); diff --git a/tests/Unit/Services/Schedules/ProcessScheduleServiceTest.php b/tests/Unit/Services/Schedules/ProcessScheduleServiceTest.php index 5f35187da..febde488e 100644 --- a/tests/Unit/Services/Schedules/ProcessScheduleServiceTest.php +++ b/tests/Unit/Services/Schedules/ProcessScheduleServiceTest.php @@ -8,31 +8,28 @@ use Cron\CronExpression; use Cake\Chronos\Chronos; use Pterodactyl\Models\Task; use Pterodactyl\Models\Schedule; -use Pterodactyl\Services\Schedules\Tasks\RunTaskService; +use Illuminate\Contracts\Bus\Dispatcher; +use Pterodactyl\Jobs\Schedule\RunTaskJob; use Pterodactyl\Services\Schedules\ProcessScheduleService; +use Pterodactyl\Contracts\Repository\TaskRepositoryInterface; use Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface; class ProcessScheduleServiceTest extends TestCase { /** - * @var \Cron\CronExpression|\Mockery\Mock + * @var \Illuminate\Contracts\Bus\Dispatcher|\Mockery\Mock */ - protected $cron; + private $dispatcher; /** * @var \Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface|\Mockery\Mock */ - protected $repository; + private $scheduleRepository; /** - * @var \Pterodactyl\Services\Schedules\Tasks\RunTaskService|\Mockery\Mock + * @var \Pterodactyl\Contracts\Repository\TaskRepositoryInterface|\Mockery\Mock */ - protected $runnerService; - - /** - * @var \Pterodactyl\Services\Schedules\ProcessScheduleService - */ - protected $service; + private $taskRepository; /** * Setup tests. @@ -42,11 +39,9 @@ class ProcessScheduleServiceTest extends TestCase parent::setUp(); Chronos::setTestNow(Chronos::now()); - - $this->repository = m::mock(ScheduleRepositoryInterface::class); - $this->runnerService = m::mock(RunTaskService::class); - - $this->service = new ProcessScheduleService($this->runnerService, $this->repository); + $this->dispatcher = m::mock(Dispatcher::class); + $this->scheduleRepository = m::mock(ScheduleRepositoryInterface::class); + $this->taskRepository = m::mock(TaskRepositoryInterface::class); } /** @@ -59,17 +54,26 @@ class ProcessScheduleServiceTest extends TestCase 'sequence_id' => 1, ])])); - $this->repository->shouldReceive('loadTasks')->with($model)->once()->andReturn($model); + $this->scheduleRepository->shouldReceive('loadTasks')->with($model)->once()->andReturn($model); $formatted = sprintf('%s %s %s * %s', $model->cron_minute, $model->cron_hour, $model->cron_day_of_month, $model->cron_day_of_week); - $this->repository->shouldReceive('update')->with($model->id, [ + $this->scheduleRepository->shouldReceive('update')->with($model->id, [ 'is_processing' => true, 'next_run_at' => Chronos::parse(CronExpression::factory($formatted)->getNextRunDate()->format(Chronos::ATOM)), ]); - $this->runnerService->shouldReceive('handle')->with($task)->once()->andReturnNull(); + $this->taskRepository->shouldReceive('update')->with($task->id, ['is_queued' => true])->once(); - $this->service->handle($model); + $this->dispatcher->shouldReceive('dispatch')->with(m::on(function ($class) use ($model, $task) { + $this->assertInstanceOf(RunTaskJob::class, $class); + $this->assertSame($task->time_offset, $class->delay); + $this->assertSame($task->id, $class->task); + $this->assertSame($model->id, $class->schedule); + + return true; + }))->once(); + + $this->getService()->handle($model); $this->assertTrue(true); } @@ -80,16 +84,30 @@ class ProcessScheduleServiceTest extends TestCase 'sequence_id' => 1, ])])); - $this->repository->shouldReceive('loadTasks')->with($model)->once()->andReturn($model); + $this->scheduleRepository->shouldReceive('loadTasks')->with($model)->once()->andReturn($model); - $this->repository->shouldReceive('update')->with($model->id, [ + $this->scheduleRepository->shouldReceive('update')->with($model->id, [ 'is_processing' => true, 'next_run_at' => Chronos::now()->addSeconds(15), ]); - $this->runnerService->shouldReceive('handle')->with($task)->once()->andReturnNull(); + $this->taskRepository->shouldReceive('update')->with($task->id, ['is_queued' => true])->once(); - $this->service->setRunTimeOverride(Chronos::now()->addSeconds(15))->handle($model); + $this->dispatcher->shouldReceive('dispatch')->with(m::on(function ($class) use ($model, $task) { + $this->assertInstanceOf(RunTaskJob::class, $class); + $this->assertSame($task->time_offset, $class->delay); + $this->assertSame($task->id, $class->task); + $this->assertSame($model->id, $class->schedule); + + return true; + }))->once(); + + $this->getService()->setRunTimeOverride(Chronos::now()->addSeconds(15))->handle($model); $this->assertTrue(true); } + + private function getService(): ProcessScheduleService + { + return new ProcessScheduleService($this->dispatcher, $this->scheduleRepository, $this->taskRepository); + } } diff --git a/tests/Unit/Services/Schedules/Tasks/RunTaskServiceTest.php b/tests/Unit/Services/Schedules/Tasks/RunTaskServiceTest.php deleted file mode 100644 index 3ca7cc1da..000000000 --- a/tests/Unit/Services/Schedules/Tasks/RunTaskServiceTest.php +++ /dev/null @@ -1,90 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Tests\Unit\Services\Schedules\Tasks; - -use Mockery as m; -use Tests\TestCase; -use Pterodactyl\Models\Task; -use Illuminate\Support\Facades\Bus; -use Pterodactyl\Jobs\Schedule\RunTaskJob; -use Pterodactyl\Services\Schedules\Tasks\RunTaskService; -use Pterodactyl\Contracts\Repository\TaskRepositoryInterface; - -class RunTaskServiceTest extends TestCase -{ - /** - * @var \Illuminate\Contracts\Bus\Dispatcher|\Mockery\Mock - */ - protected $dispatcher; - - /** - * @var \Pterodactyl\Contracts\Repository\TaskRepositoryInterface|\Mockery\Mock - */ - protected $repository; - - /** - * @var \Pterodactyl\Services\Schedules\Tasks\RunTaskService - */ - protected $service; - - /** - * Setup tests. - */ - public function setUp() - { - parent::setUp(); - - Bus::fake(); - $this->repository = m::mock(TaskRepositoryInterface::class); - - $this->service = new RunTaskService($this->repository); - } - - /** - * Test that a job is dispatched. - */ - public function testTaskIsDispatched() - { - $task = factory(Task::class)->make(); - - $this->repository->shouldReceive('update')->with($task->id, ['is_queued' => true])->once()->andReturnNull(); - - $this->service->handle($task); - - Bus::assertDispatched(RunTaskJob::class, function ($job) use ($task) { - $this->assertEquals($task->id, $job->task, 'Assert job task matches parent task model.'); - $this->assertEquals($task->schedule_id, $job->schedule, 'Assert job is linked to correct schedule.'); - $this->assertEquals($task->time_offset, $job->delay, 'Assert job delay is set correctly to match task.'); - - return true; - }); - } - - /** - * Test that passing an ID in place of a model works. - */ - public function testIdCanBePassedInPlaceOfModel() - { - $task = factory(Task::class)->make(); - - $this->repository->shouldReceive('find')->with($task->id)->once()->andReturn($task); - $this->repository->shouldReceive('update')->with($task->id, ['is_queued' => true])->once()->andReturnNull(); - - $this->service->handle($task->id); - - Bus::assertDispatched(RunTaskJob::class, function ($job) use ($task) { - $this->assertEquals($task->id, $job->task, 'Assert job task matches parent task model.'); - $this->assertEquals($task->schedule_id, $job->schedule, 'Assert job is linked to correct schedule.'); - $this->assertEquals($task->time_offset, $job->delay, 'Assert job delay is set correctly to match task.'); - - return true; - }); - } -}