Path: blob/1.0-develop/tests/Integration/Services/Schedules/ProcessScheduleServiceTest.php
7460 views
<?php12namespace Pterodactyl\Tests\Integration\Services\Schedules;34use Exception;5use Carbon\CarbonImmutable;6use Pterodactyl\Models\Task;7use Pterodactyl\Models\Schedule;8use Illuminate\Support\Facades\Bus;9use Illuminate\Contracts\Bus\Dispatcher;10use Pterodactyl\Jobs\Schedule\RunTaskJob;11use Pterodactyl\Exceptions\DisplayException;12use Pterodactyl\Tests\Integration\IntegrationTestCase;13use Pterodactyl\Services\Schedules\ProcessScheduleService;1415class ProcessScheduleServiceTest extends IntegrationTestCase16{17/**18* Test that a schedule with no tasks registered returns an error.19*/20public function testScheduleWithNoTasksReturnsException()21{22$server = $this->createServerModel();23$schedule = Schedule::factory()->create(['server_id' => $server->id]);2425$this->expectException(DisplayException::class);26$this->expectExceptionMessage('Cannot process schedule for task execution: no tasks are registered.');2728$this->getService()->handle($schedule);29}3031/**32* Test that an error during the schedule update is not persisted to the database.33*/34public function testErrorDuringScheduleDataUpdateDoesNotPersistChanges()35{36$server = $this->createServerModel();3738/** @var Schedule $schedule */39$schedule = Schedule::factory()->create([40'server_id' => $server->id,41'cron_minute' => 'hodor', // this will break the getNextRunDate() function.42]);4344/** @var Task $task */45$task = Task::factory()->create(['schedule_id' => $schedule->id, 'sequence_id' => 1]);4647$this->expectException(\InvalidArgumentException::class);4849$this->getService()->handle($schedule);5051$this->assertDatabaseMissing('schedules', ['id' => $schedule->id, 'is_processing' => true]);52$this->assertDatabaseMissing('tasks', ['id' => $task->id, 'is_queued' => true]);53}5455/**56* Test that a job is dispatched as expected using the initial delay.57*/58#[\PHPUnit\Framework\Attributes\DataProvider('dispatchNowDataProvider')]59public function testJobCanBeDispatchedWithExpectedInitialDelay(bool $now)60{61Bus::fake();6263$server = $this->createServerModel();6465/** @var Schedule $schedule */66$schedule = Schedule::factory()->create(['server_id' => $server->id]);6768/** @var Task $task */69$task = Task::factory()->create(['schedule_id' => $schedule->id, 'time_offset' => 10, 'sequence_id' => 1]);7071$this->getService()->handle($schedule, $now);7273Bus::assertDispatched(RunTaskJob::class, function ($job) use ($now, $task) {74$this->assertInstanceOf(RunTaskJob::class, $job);75$this->assertSame($task->id, $job->task->id);76// Jobs using dispatchNow should not have a delay associated with them.77$this->assertSame($now ? null : 10, $job->delay);7879return true;80});8182$this->assertDatabaseHas('schedules', ['id' => $schedule->id, 'is_processing' => true]);83$this->assertDatabaseHas('tasks', ['id' => $task->id, 'is_queued' => true]);84}8586/**87* Test that even if a schedule's task sequence gets messed up the first task based on88* the ascending order of tasks is used.89*90* @see https://github.com/pterodactyl/panel/issues/253491*/92public function testFirstSequenceTaskIsFound()93{94Bus::fake();9596$server = $this->createServerModel();97/** @var Schedule $schedule */98$schedule = Schedule::factory()->create(['server_id' => $server->id]);99100/** @var Task $task */101$task2 = Task::factory()->create(['schedule_id' => $schedule->id, 'sequence_id' => 4]);102$task = Task::factory()->create(['schedule_id' => $schedule->id, 'sequence_id' => 2]);103$task3 = Task::factory()->create(['schedule_id' => $schedule->id, 'sequence_id' => 3]);104105$this->getService()->handle($schedule);106107Bus::assertDispatched(RunTaskJob::class, function (RunTaskJob $job) use ($task) {108return $task->id === $job->task->id;109});110111$this->assertDatabaseHas('schedules', ['id' => $schedule->id, 'is_processing' => true]);112$this->assertDatabaseHas('tasks', ['id' => $task->id, 'is_queued' => true]);113$this->assertDatabaseHas('tasks', ['id' => $task2->id, 'is_queued' => false]);114$this->assertDatabaseHas('tasks', ['id' => $task3->id, 'is_queued' => false]);115}116117/**118* Tests that a task's processing state is reset correctly if using "dispatchNow" and there is119* an exception encountered while running it.120*121* @see https://github.com/pterodactyl/panel/issues/2550122*/123public function testTaskDispatchedNowIsResetProperlyIfErrorIsEncountered()124{125$this->swap(Dispatcher::class, $dispatcher = \Mockery::mock(Dispatcher::class));126127$server = $this->createServerModel();128/** @var Schedule $schedule */129$schedule = Schedule::factory()->create(['server_id' => $server->id, 'last_run_at' => null]);130/** @var Task $task */131$task = Task::factory()->create(['schedule_id' => $schedule->id, 'sequence_id' => 1]);132133$dispatcher->expects('dispatchNow')->andThrows(new \Exception('Test thrown exception'));134135$this->expectException(\Exception::class);136$this->expectExceptionMessage('Test thrown exception');137138$this->getService()->handle($schedule, true);139140$this->assertDatabaseHas('schedules', [141'id' => $schedule->id,142'is_processing' => false,143'last_run_at' => CarbonImmutable::now()->toAtomString(),144]);145146$this->assertDatabaseHas('tasks', ['id' => $task->id, 'is_queued' => false]);147}148149public static function dispatchNowDataProvider(): array150{151return [[true], [false]];152}153154private function getService(): ProcessScheduleService155{156return $this->app->make(ProcessScheduleService::class);157}158}159160161