Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
pterodactyl
GitHub Repository: pterodactyl/panel
Path: blob/1.0-develop/tests/Integration/Services/Schedules/ProcessScheduleServiceTest.php
7460 views
1
<?php
2
3
namespace Pterodactyl\Tests\Integration\Services\Schedules;
4
5
use Exception;
6
use Carbon\CarbonImmutable;
7
use Pterodactyl\Models\Task;
8
use Pterodactyl\Models\Schedule;
9
use Illuminate\Support\Facades\Bus;
10
use Illuminate\Contracts\Bus\Dispatcher;
11
use Pterodactyl\Jobs\Schedule\RunTaskJob;
12
use Pterodactyl\Exceptions\DisplayException;
13
use Pterodactyl\Tests\Integration\IntegrationTestCase;
14
use Pterodactyl\Services\Schedules\ProcessScheduleService;
15
16
class ProcessScheduleServiceTest extends IntegrationTestCase
17
{
18
/**
19
* Test that a schedule with no tasks registered returns an error.
20
*/
21
public function testScheduleWithNoTasksReturnsException()
22
{
23
$server = $this->createServerModel();
24
$schedule = Schedule::factory()->create(['server_id' => $server->id]);
25
26
$this->expectException(DisplayException::class);
27
$this->expectExceptionMessage('Cannot process schedule for task execution: no tasks are registered.');
28
29
$this->getService()->handle($schedule);
30
}
31
32
/**
33
* Test that an error during the schedule update is not persisted to the database.
34
*/
35
public function testErrorDuringScheduleDataUpdateDoesNotPersistChanges()
36
{
37
$server = $this->createServerModel();
38
39
/** @var Schedule $schedule */
40
$schedule = Schedule::factory()->create([
41
'server_id' => $server->id,
42
'cron_minute' => 'hodor', // this will break the getNextRunDate() function.
43
]);
44
45
/** @var Task $task */
46
$task = Task::factory()->create(['schedule_id' => $schedule->id, 'sequence_id' => 1]);
47
48
$this->expectException(\InvalidArgumentException::class);
49
50
$this->getService()->handle($schedule);
51
52
$this->assertDatabaseMissing('schedules', ['id' => $schedule->id, 'is_processing' => true]);
53
$this->assertDatabaseMissing('tasks', ['id' => $task->id, 'is_queued' => true]);
54
}
55
56
/**
57
* Test that a job is dispatched as expected using the initial delay.
58
*/
59
#[\PHPUnit\Framework\Attributes\DataProvider('dispatchNowDataProvider')]
60
public function testJobCanBeDispatchedWithExpectedInitialDelay(bool $now)
61
{
62
Bus::fake();
63
64
$server = $this->createServerModel();
65
66
/** @var Schedule $schedule */
67
$schedule = Schedule::factory()->create(['server_id' => $server->id]);
68
69
/** @var Task $task */
70
$task = Task::factory()->create(['schedule_id' => $schedule->id, 'time_offset' => 10, 'sequence_id' => 1]);
71
72
$this->getService()->handle($schedule, $now);
73
74
Bus::assertDispatched(RunTaskJob::class, function ($job) use ($now, $task) {
75
$this->assertInstanceOf(RunTaskJob::class, $job);
76
$this->assertSame($task->id, $job->task->id);
77
// Jobs using dispatchNow should not have a delay associated with them.
78
$this->assertSame($now ? null : 10, $job->delay);
79
80
return true;
81
});
82
83
$this->assertDatabaseHas('schedules', ['id' => $schedule->id, 'is_processing' => true]);
84
$this->assertDatabaseHas('tasks', ['id' => $task->id, 'is_queued' => true]);
85
}
86
87
/**
88
* Test that even if a schedule's task sequence gets messed up the first task based on
89
* the ascending order of tasks is used.
90
*
91
* @see https://github.com/pterodactyl/panel/issues/2534
92
*/
93
public function testFirstSequenceTaskIsFound()
94
{
95
Bus::fake();
96
97
$server = $this->createServerModel();
98
/** @var Schedule $schedule */
99
$schedule = Schedule::factory()->create(['server_id' => $server->id]);
100
101
/** @var Task $task */
102
$task2 = Task::factory()->create(['schedule_id' => $schedule->id, 'sequence_id' => 4]);
103
$task = Task::factory()->create(['schedule_id' => $schedule->id, 'sequence_id' => 2]);
104
$task3 = Task::factory()->create(['schedule_id' => $schedule->id, 'sequence_id' => 3]);
105
106
$this->getService()->handle($schedule);
107
108
Bus::assertDispatched(RunTaskJob::class, function (RunTaskJob $job) use ($task) {
109
return $task->id === $job->task->id;
110
});
111
112
$this->assertDatabaseHas('schedules', ['id' => $schedule->id, 'is_processing' => true]);
113
$this->assertDatabaseHas('tasks', ['id' => $task->id, 'is_queued' => true]);
114
$this->assertDatabaseHas('tasks', ['id' => $task2->id, 'is_queued' => false]);
115
$this->assertDatabaseHas('tasks', ['id' => $task3->id, 'is_queued' => false]);
116
}
117
118
/**
119
* Tests that a task's processing state is reset correctly if using "dispatchNow" and there is
120
* an exception encountered while running it.
121
*
122
* @see https://github.com/pterodactyl/panel/issues/2550
123
*/
124
public function testTaskDispatchedNowIsResetProperlyIfErrorIsEncountered()
125
{
126
$this->swap(Dispatcher::class, $dispatcher = \Mockery::mock(Dispatcher::class));
127
128
$server = $this->createServerModel();
129
/** @var Schedule $schedule */
130
$schedule = Schedule::factory()->create(['server_id' => $server->id, 'last_run_at' => null]);
131
/** @var Task $task */
132
$task = Task::factory()->create(['schedule_id' => $schedule->id, 'sequence_id' => 1]);
133
134
$dispatcher->expects('dispatchNow')->andThrows(new \Exception('Test thrown exception'));
135
136
$this->expectException(\Exception::class);
137
$this->expectExceptionMessage('Test thrown exception');
138
139
$this->getService()->handle($schedule, true);
140
141
$this->assertDatabaseHas('schedules', [
142
'id' => $schedule->id,
143
'is_processing' => false,
144
'last_run_at' => CarbonImmutable::now()->toAtomString(),
145
]);
146
147
$this->assertDatabaseHas('tasks', ['id' => $task->id, 'is_queued' => false]);
148
}
149
150
public static function dispatchNowDataProvider(): array
151
{
152
return [[true], [false]];
153
}
154
155
private function getService(): ProcessScheduleService
156
{
157
return $this->app->make(ProcessScheduleService::class);
158
}
159
}
160
161