Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
26 changes: 26 additions & 0 deletions src/queue/src/DeferredQueue.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

namespace Hypervel\Queue;

use DateInterval;
use DateTimeInterface;
use Hypervel\Coordinator\Timer;
use Hypervel\Engine\Coroutine;
use Throwable;

Expand All @@ -16,6 +19,18 @@ class DeferredQueue extends SyncQueue
*/
protected $exceptionCallback;

/**
* Create a new deferred queue instance.
*/
public function __construct(
protected ?bool $dispatchAfterCommit = false,
protected ?Timer $timer = null
) {
if (! $this->timer) {
$this->timer = new Timer;
}
}

/**
* Push a new job onto the queue.
*/
Expand All @@ -35,6 +50,17 @@ public function push(object|string $job, mixed $data = '', ?string $queue = null
return null;
}

/**
* Push a new job onto the queue after (n) seconds.
*/
public function later(DateInterval|DateTimeInterface|int $delay, object|string $job, mixed $data = '', ?string $queue = null): mixed
{
return $this->timer->after(
(float) $this->secondsUntil($delay),
fn () => $this->deferJob($job, $data, $queue)
);
}

/**
* Set the exception callback for the deferred queue.
*/
Expand Down
100 changes: 100 additions & 0 deletions tests/Queue/QueueDeferredQueueTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,18 @@

namespace Hypervel\Tests\Queue;

use DateInterval;
use Exception;
use Hypervel\Container\Container;
use Hypervel\Contracts\Events\Dispatcher;
use Hypervel\Contracts\Queue\QueueableEntity;
use Hypervel\Contracts\Queue\ShouldQueueAfterCommit;
use Hypervel\Coordinator\Timer;
use Hypervel\Database\DatabaseTransactionsManager;
use Hypervel\Queue\DeferredQueue;
use Hypervel\Queue\InteractsWithQueue;
use Hypervel\Queue\Jobs\SyncJob;
use Hypervel\Support\Carbon;
use Hypervel\Tests\TestCase;
use Mockery as m;

Expand Down Expand Up @@ -90,6 +93,79 @@ public function testItAddsATransactionCallbackForInterfaceBasedAfterCommitJobs()
run(fn () => $deferred->push(new DeferredQueueAfterCommitInterfaceJob));
}

public function testLaterSchedulesJobWithDelay()
{
$timer = m::mock(Timer::class);
$timer->shouldReceive('after')
->once()
->with(5.0, m::type('Closure'))
->andReturnUsing(function ($delay, $callback) {
$callback();
return 1;
});

$deferred = new DeferredQueue(timer: $timer);
$deferred->setConnectionName('deferred');
$deferred->setContainer($this->getContainer());

unset($_SERVER['__deferred.later.test']);

run(fn () => $deferred->later(5, DeferredQueueLaterTestHandler::class, ['foo' => 'bar']));

$this->assertInstanceOf(SyncJob::class, $_SERVER['__deferred.later.test'][0]);
$this->assertEquals(['foo' => 'bar'], $_SERVER['__deferred.later.test'][1]);
}

public function testLaterWithDateInterval()
{
$timer = m::mock(Timer::class);
$timer->shouldReceive('after')
->once()
->with(10.0, m::type('Closure'))
->andReturnUsing(function ($delay, $callback) {
$callback();
return 1;
});

$deferred = new DeferredQueue(timer: $timer);
$deferred->setConnectionName('deferred');
$deferred->setContainer($this->getContainer());

unset($_SERVER['__deferred.later.interval.test']);

run(fn () => $deferred->later(new DateInterval('PT10S'), DeferredQueueLaterIntervalTestHandler::class, ['baz' => 'qux']));

$this->assertInstanceOf(SyncJob::class, $_SERVER['__deferred.later.interval.test'][0]);
$this->assertEquals(['baz' => 'qux'], $_SERVER['__deferred.later.interval.test'][1]);
}

public function testLaterWithDateTime()
{
Carbon::setTestNow('2024-01-01 12:00:00');

$timer = m::mock(Timer::class);
$timer->shouldReceive('after')
->once()
->with(15.0, m::type('Closure'))
->andReturnUsing(function ($delay, $callback) {
$callback();
return 1;
});

$deferred = new DeferredQueue(timer: $timer);
$deferred->setConnectionName('deferred');
$deferred->setContainer($this->getContainer());

unset($_SERVER['__deferred.later.datetime.test']);

run(fn () => $deferred->later(Carbon::parse('2024-01-01 12:00:15'), DeferredQueueLaterDateTimeTestHandler::class, ['test' => 'data']));

$this->assertInstanceOf(SyncJob::class, $_SERVER['__deferred.later.datetime.test'][0]);
$this->assertEquals(['test' => 'data'], $_SERVER['__deferred.later.datetime.test'][1]);

Carbon::setTestNow();
}

protected function getContainer(): Container
{
return new Container;
Expand Down Expand Up @@ -154,3 +230,27 @@ public function handle()
{
}
}

class DeferredQueueLaterTestHandler
{
public function fire(SyncJob $job, mixed $data): void
{
$_SERVER['__deferred.later.test'] = func_get_args();
}
}

class DeferredQueueLaterIntervalTestHandler
{
public function fire(SyncJob $job, mixed $data): void
{
$_SERVER['__deferred.later.interval.test'] = func_get_args();
}
}

class DeferredQueueLaterDateTimeTestHandler
{
public function fire(SyncJob $job, mixed $data): void
{
$_SERVER['__deferred.later.datetime.test'] = func_get_args();
}
}