Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/Illuminate/Console/Scheduling/ScheduleRunCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Illuminate\Console\Scheduling;

use Exception;
use Illuminate\Console\Application;
use Illuminate\Console\Command;
use Illuminate\Console\Events\ScheduledTaskFailed;
Expand Down Expand Up @@ -196,6 +197,10 @@ protected function runEvent($event)
round(microtime(true) - $start, 2)
));

if ($event->exitCode != 0 && ! $event->runInBackground) {
throw new Exception("Scheduled command [{$event->command}] failed with exit code [{$event->exitCode}].");
}

$this->eventsRan = true;
} catch (Throwable $e) {
$this->dispatcher->dispatch(new ScheduledTaskFailed($event, $e));
Expand Down
216 changes: 216 additions & 0 deletions tests/Integration/Console/Scheduling/ScheduleRunCommandTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
<?php

namespace Illuminate\Tests\Integration\Console\Scheduling;

use Illuminate\Console\Events\ScheduledTaskFailed;
use Illuminate\Console\Events\ScheduledTaskFinished;
use Illuminate\Console\Events\ScheduledTaskStarting;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Contracts\Container\BindingResolutionException;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Event;
use Orchestra\Testbench\TestCase;

class ScheduleRunCommandTest extends TestCase
{
protected function setUp(): void
{
parent::setUp();
Carbon::setTestNow(Carbon::now());
}

protected function tearDown(): void
{
parent::tearDown();
Carbon::setTestNow();
}

/**
* @throws BindingResolutionException
*/
public function test_failing_command_in_foreground_triggers_event()
{
Event::fake([
ScheduledTaskStarting::class,
ScheduledTaskFinished::class,
ScheduledTaskFailed::class,
]);

// Create a schedule and add the command
$schedule = $this->app->make(Schedule::class);
$task = $schedule->exec('exit 1')
->everyMinute()
->withoutOverlapping();

// Make sure it will run regardless of schedule
$task->when(function () {
return true;
});

// Execute the scheduler
$this->artisan('schedule:run');

// Verify the event sequence
Event::assertDispatched(ScheduledTaskStarting::class);
Event::assertDispatched(ScheduledTaskFinished::class);
Event::assertDispatched(ScheduledTaskFailed::class, function ($event) use ($task) {
return $event->task === $task &&
$event->exception->getMessage() === 'Scheduled command [exit 1] failed with exit code [1].';
});
}

/**
* @throws BindingResolutionException
*/
public function test_failing_command_in_background_does_not_trigger_event()
{
Event::fake([
ScheduledTaskStarting::class,
ScheduledTaskFinished::class,
ScheduledTaskFailed::class,
]);

// Create a schedule and add the command
$schedule = $this->app->make(Schedule::class);
$task = $schedule->exec('exit 1')
->everyMinute()
->runInBackground();

// Make sure it will run regardless of schedule
$task->when(function () {
return true;
});

// Execute the scheduler
$this->artisan('schedule:run');

// Verify the event sequence
Event::assertDispatched(ScheduledTaskStarting::class);
Event::assertDispatched(ScheduledTaskFinished::class);
Event::assertNotDispatched(ScheduledTaskFailed::class);
}

/**
* @throws BindingResolutionException
*/
public function test_successful_command_does_not_trigger_event()
{
Event::fake([
ScheduledTaskStarting::class,
ScheduledTaskFinished::class,
ScheduledTaskFailed::class,
]);

// Create a schedule and add the command
$schedule = $this->app->make(Schedule::class);
$task = $schedule->exec('exit 0')
->everyMinute()
->withoutOverlapping();

// Make sure it will run regardless of schedule
$task->when(function () {
return true;
});

// Execute the scheduler
$this->artisan('schedule:run');

// Verify the event sequence
Event::assertDispatched(ScheduledTaskStarting::class);
Event::assertDispatched(ScheduledTaskFinished::class);
Event::assertNotDispatched(ScheduledTaskFailed::class);
}

/**
* @throws BindingResolutionException
*/
public function test_command_with_no_explicit_return_does_not_trigger_event()
{
Event::fake([
ScheduledTaskStarting::class,
ScheduledTaskFinished::class,
ScheduledTaskFailed::class,
]);

// Create a schedule and add the command that just performs an action without explicit exit
$schedule = $this->app->make(Schedule::class);
$task = $schedule->exec('true')
->everyMinute()
->withoutOverlapping();

// Make sure it will run regardless of schedule
$task->when(function () {
return true;
});

// Execute the scheduler
$this->artisan('schedule:run');

// Verify the event sequence
Event::assertDispatched(ScheduledTaskStarting::class);
Event::assertDispatched(ScheduledTaskFinished::class);
Event::assertNotDispatched(ScheduledTaskFailed::class);
}

/**
* @throws BindingResolutionException
*/
public function test_successful_command_in_background_does_not_trigger_event()
{
Event::fake([
ScheduledTaskStarting::class,
ScheduledTaskFinished::class,
ScheduledTaskFailed::class,
]);

// Create a schedule and add the command
$schedule = $this->app->make(Schedule::class);
$task = $schedule->exec('exit 0')
->everyMinute()
->runInBackground();

// Make sure it will run regardless of schedule
$task->when(function () {
return true;
});

// Execute the scheduler
$this->artisan('schedule:run');

// Verify the event sequence
Event::assertDispatched(ScheduledTaskStarting::class);
Event::assertDispatched(ScheduledTaskFinished::class);
Event::assertNotDispatched(ScheduledTaskFailed::class);
}

/**
* @throws BindingResolutionException
*/
public function test_command_with_no_explicit_return_in_background_does_not_trigger_event()
{
Event::fake([
ScheduledTaskStarting::class,
ScheduledTaskFinished::class,
ScheduledTaskFailed::class,
]);

// Create a schedule and add the command that just performs an action without explicit exit
$schedule = $this->app->make(Schedule::class);
$task = $schedule->exec('true')
->everyMinute()
->runInBackground();

// Make sure it will run regardless of schedule
$task->when(function () {
return true;
});

// Execute the scheduler
$this->artisan('schedule:run');

// Verify the event sequence
Event::assertDispatched(ScheduledTaskStarting::class);
Event::assertDispatched(ScheduledTaskFinished::class);
Event::assertNotDispatched(ScheduledTaskFailed::class);
}
}