diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml
index 40f2d20..d3ab8aa 100644
--- a/.github/workflows/run-tests.yml
+++ b/.github/workflows/run-tests.yml
@@ -67,17 +67,6 @@ jobs:
- { queue: 'github-actions-laravel10-php83', laravel: '10.*', php: '8.3', 'testbench': '8.*'}
- { queue: 'github-actions-laravel10-php82', laravel: '10.*', php: '8.2', 'testbench': '8.*'}
- { queue: 'github-actions-laravel10-php81', laravel: '10.*', php: '8.1', 'testbench': '8.*'}
- - { queue: 'github-actions-laravel9-php83', laravel: '9.*', php: '8.3', 'testbench': '7.*'}
- - { queue: 'github-actions-laravel9-php82', laravel: '9.*', php: '8.2', 'testbench': '7.*'}
- - { queue: 'github-actions-laravel9-php81', laravel: '9.*', php: '8.1', 'testbench': '7.*'}
- - { queue: 'github-actions-laravel9-php80', laravel: '9.*', php: '8.0', 'testbench': '7.*'}
- - { queue: 'github-actions-laravel8-php81', laravel: '8.*', php: '8.1', 'testbench': '6.*'}
- - { queue: 'github-actions-laravel8-php80', laravel: '8.*', php: '8.0', 'testbench': '6.*'}
- - { queue: 'github-actions-laravel8-php74', laravel: '8.*', php: '7.4', 'testbench': '6.*'}
- - { queue: 'github-actions-laravel7-php80', laravel: '7.*', php: '8.0', 'testbench': '5.*' }
- - { queue: 'github-actions-laravel7-php74', laravel: '7.*', php: '7.4', 'testbench': '5.*' }
- - { queue: 'github-actions-laravel6-php80', laravel: '6.*', php: '8.0', 'testbench': '4.*' }
- - { queue: 'github-actions-laravel6-php74', laravel: '6.*', php: '7.4', 'testbench': '4.*' }
name: PHP ${{ matrix.payload.php }} - Laravel ${{ matrix.payload.laravel }} - DB ${{ matrix.db }}
diff --git a/README.md b/README.md
index 23b072e..9f8196f 100644
--- a/README.md
+++ b/README.md
@@ -21,7 +21,7 @@ This package allows Google Cloud Tasks to be used as the queue driver.
- This package requires Laravel 6 or higher and supports MySQL 8 and PostgreSQL 14. Might support older database versions too, but package hasn't been tested for it.
+ This package requires Laravel 10 or higher and supports MySQL 8 and PostgreSQL 15. Might support older database versions too, but package hasn't been tested for it.
Please check the [Laravel support policy](https://laravel.com/docs/master/releases#support-policy) table for supported Laravel and PHP versions.
@@ -85,8 +85,7 @@ With Cloud Tasks, this is not the case. Instead, Cloud Tasks will schedule the j
#### Good to know
-- The "Min backoff" and "Max backoff" options in Cloud Tasks are ignored. This is intentional: Laravel has its own backoff feature (which is more powerful than what Cloud Tasks offers) and therefore I have chosen that over the Cloud Tasks one.
-- Similarly to the backoff feature, I have also chosen to let the package do job retries the 'Laravel way'. In Cloud Tasks, when a task throws an exception, Cloud Tasks will decide for itself when to retry the task (based on the backoff values). It will also manage its own state and knows how many times a task has been retried. This is different from Laravel. In typical Laravel queues, when a job throws an exception, the job is deleted and released back onto the queue. In order to support Laravel's backoff feature, this package must behave the same way about job retries.
+- Backoff, retries, max tries, retryUntil are all handled by Laravel. All these options are ignored in the Cloud Tasks configuration.
diff --git a/composer.json b/composer.json
index cf4f0ba..dfb337b 100644
--- a/composer.json
+++ b/composer.json
@@ -15,10 +15,11 @@
"thecodingmachine/safe": "^1.0|^2.0"
},
"require-dev": {
- "orchestra/testbench": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0",
+ "orchestra/testbench": "^8.0",
"nunomaduro/larastan": "^1.0 || ^2.0",
"thecodingmachine/phpstan-safe-rule": "^1.2",
- "laravel/legacy-factories": "^1.3"
+ "laravel/legacy-factories": "^1.3",
+ "laravel/pint": "^1.13"
},
"autoload": {
"psr-4": {
@@ -44,22 +45,6 @@
"l10": [
"composer require laravel/framework:10.* orchestra/testbench:8.* --no-interaction --no-update",
"composer update --prefer-stable --prefer-dist --no-interaction"
- ],
- "l9": [
- "composer require laravel/framework:9.* orchestra/testbench:7.* --no-interaction --no-update",
- "composer update --prefer-stable --prefer-dist --no-interaction"
- ],
- "l8": [
- "composer require laravel/framework:8.* orchestra/testbench:6.* --no-interaction --no-update",
- "composer update --prefer-stable --prefer-dist --no-interaction"
- ],
- "l7": [
- "composer require laravel/framework:7.* orchestra/testbench:5.* --no-interaction --no-update",
- "composer update --prefer-stable --prefer-dist --no-interaction"
- ],
- "l6": [
- "composer require laravel/framework:6.* orchestra/testbench:4.* --no-interaction --no-update",
- "composer update --prefer-stable --prefer-dist --no-interaction"
]
}
}
diff --git a/config/cloud-tasks.php b/config/cloud-tasks.php
index c8cbdca..1885df2 100644
--- a/config/cloud-tasks.php
+++ b/config/cloud-tasks.php
@@ -5,6 +5,6 @@
return [
'dashboard' => [
'enabled' => env('STACKKIT_CLOUD_TASKS_DASHBOARD_ENABLED', false),
- 'password' => env('STACKKIT_CLOUD_TASKS_DASHBOARD_PASSWORD', 'MyPassword1!')
+ 'password' => env('STACKKIT_CLOUD_TASKS_DASHBOARD_PASSWORD', 'MyPassword1!'),
],
];
diff --git a/factories/StackkitCloudTaskFactory.php b/factories/StackkitCloudTaskFactory.php
index ac0991e..926ce94 100644
--- a/factories/StackkitCloudTaskFactory.php
+++ b/factories/StackkitCloudTaskFactory.php
@@ -6,9 +6,9 @@
/** @var \Illuminate\Database\Eloquent\Factory $factory */
+use Faker\Generator as Faker;
use Illuminate\Support\Str;
use Stackkit\LaravelGoogleCloudTasksQueue\StackkitCloudTask;
-use Faker\Generator as Faker;
$factory->define(StackkitCloudTask::class, function (Faker $faker) {
return [
diff --git a/migrations/2021_10_16_171140_create_stackkit_cloud_tasks_table.php b/migrations/2021_10_16_171140_create_stackkit_cloud_tasks_table.php
index 2455a5a..a78f17c 100644
--- a/migrations/2021_10_16_171140_create_stackkit_cloud_tasks_table.php
+++ b/migrations/2021_10_16_171140_create_stackkit_cloud_tasks_table.php
@@ -1,5 +1,7 @@
bearerToken();
- if (!$token) {
+ if (! $token) {
return false;
}
@@ -27,14 +28,12 @@ public static function check($request)
return $expireTimestamp > Carbon::now()->timestamp;
} catch (Throwable $e) {
- return false;
+ return false;
}
}
/**
* Determine if the dashboard is enabled.
- *
- * @return bool
*/
public static function dashboardEnabled(): bool
{
@@ -43,8 +42,6 @@ public static function dashboardEnabled(): bool
/**
* Determine if the dashboard is disabled.
- *
- * @return bool
*/
public static function dashboardDisabled(): bool
{
diff --git a/src/CloudTasksApi.php b/src/CloudTasksApi.php
index c113bf4..e0c04a2 100644
--- a/src/CloudTasksApi.php
+++ b/src/CloudTasksApi.php
@@ -4,16 +4,13 @@
namespace Stackkit\LaravelGoogleCloudTasksQueue;
-use Google\Cloud\Tasks\V2\RetryConfig;
use Google\Cloud\Tasks\V2\Task;
use Illuminate\Support\Facades\Facade;
/**
- * @method static RetryConfig getRetryConfig(string $queueName)
* @method static Task createTask(string $queueName, Task $task)
* @method static void deleteTask(string $taskName)
* @method static Task getTask(string $taskName)
- * @method static int|null getRetryUntilTimestamp(Task $task)
*/
class CloudTasksApi extends Facade
{
diff --git a/src/CloudTasksApiConcrete.php b/src/CloudTasksApiConcrete.php
index d63b8ed..4e09517 100644
--- a/src/CloudTasksApiConcrete.php
+++ b/src/CloudTasksApiConcrete.php
@@ -4,18 +4,13 @@
namespace Stackkit\LaravelGoogleCloudTasksQueue;
-use Exception;
-use Google\Cloud\Tasks\V2\Attempt;
use Google\Cloud\Tasks\V2\CloudTasksClient;
-use Google\Cloud\Tasks\V2\RetryConfig;
use Google\Cloud\Tasks\V2\Task;
-use Google\Protobuf\Duration;
-use Google\Protobuf\Timestamp;
class CloudTasksApiConcrete implements CloudTasksApiContract
{
/**
- * @var CloudTasksClient $client
+ * @var CloudTasksClient
*/
private $client;
@@ -24,17 +19,6 @@ public function __construct(CloudTasksClient $client)
$this->client = $client;
}
- public function getRetryConfig(string $queueName): RetryConfig
- {
- $retryConfig = $this->client->getQueue($queueName)->getRetryConfig();
-
- if (! $retryConfig instanceof RetryConfig) {
- throw new Exception('Queue does not have a retry config.');
- }
-
- return $retryConfig;
- }
-
public function createTask(string $queueName, Task $task): Task
{
return $this->client->createTask($queueName, $task);
@@ -49,30 +33,4 @@ public function getTask(string $taskName): Task
{
return $this->client->getTask($taskName);
}
-
- public function getRetryUntilTimestamp(Task $task): ?int
- {
- $attempt = $task->getFirstAttempt();
-
- if (!$attempt instanceof Attempt) {
- return null;
- }
-
- $queueName = implode('/', array_slice(explode('/', $task->getName()), 0, 6));
-
- $retryConfig = $this->getRetryConfig($queueName);
-
- $maxRetryDuration = $retryConfig->getMaxRetryDuration();
- $dispatchTime = $attempt->getDispatchTime();
-
- if (! $maxRetryDuration instanceof Duration || ! $dispatchTime instanceof Timestamp) {
- return null;
- }
-
- $maxDurationInSeconds = (int) $maxRetryDuration->getSeconds();
-
- $firstAttemptTimestamp = $dispatchTime->toDateTime()->getTimestamp();
-
- return $firstAttemptTimestamp + $maxDurationInSeconds;
- }
}
diff --git a/src/CloudTasksApiContract.php b/src/CloudTasksApiContract.php
index aa0880b..8e8aca3 100644
--- a/src/CloudTasksApiContract.php
+++ b/src/CloudTasksApiContract.php
@@ -4,14 +4,13 @@
namespace Stackkit\LaravelGoogleCloudTasksQueue;
-use Google\Cloud\Tasks\V2\RetryConfig;
use Google\Cloud\Tasks\V2\Task;
interface CloudTasksApiContract
{
- public function getRetryConfig(string $queueName): RetryConfig;
public function createTask(string $queueName, Task $task): Task;
+
public function deleteTask(string $taskName): void;
+
public function getTask(string $taskName): Task;
- public function getRetryUntilTimestamp(Task $task): ?int;
}
diff --git a/src/CloudTasksApiController.php b/src/CloudTasksApiController.php
index 1d3a771..e3762f2 100644
--- a/src/CloudTasksApiController.php
+++ b/src/CloudTasksApiController.php
@@ -1,14 +1,17 @@
utc()->format('H:i') . '\' THEN \'this_minute\'
- WHEN ' . $groupBy['this_hour'] . ' = \'' . now()->utc()->format('H') . '\' THEN \'this_hour\'
+ WHEN '.$groupBy['this_minute'].' = \''.now()->utc()->format('H:i').'\' THEN \'this_minute\'
+ WHEN '.$groupBy['this_hour'].' = \''.now()->utc()->format('H').'\' THEN \'this_hour\'
ELSE \'today\'
END AS time_preset
- ')
+ '),
]
)
->groupBy(
@@ -74,7 +77,7 @@ public function dashboard(): array
]
)
->get()
- ->map(fn($row) => StatRow::createFromObject($row))
+ ->map(fn ($row) => StatRow::createFromObject($row))
->toArray();
$response = [
@@ -156,11 +159,10 @@ public function tasks()
$maxId = $tasks->max('id');
- return $tasks->map(function (StackkitCloudTask $task) use ($maxId)
- {
+ return $tasks->map(function (StackkitCloudTask $task) use ($maxId) {
return [
'uuid' => $task->task_uuid,
- 'id' => str_pad((string) $task->id, strlen($maxId), '0', STR_PAD_LEFT),
+ 'id' => str_pad((string) $task->id, strlen((string) $maxId), '0', STR_PAD_LEFT),
'name' => $task->name,
'status' => $task->status,
'attempts' => $task->getNumberOfAttempts(),
diff --git a/src/CloudTasksApiFake.php b/src/CloudTasksApiFake.php
index f1af5da..1614efe 100644
--- a/src/CloudTasksApiFake.php
+++ b/src/CloudTasksApiFake.php
@@ -5,28 +5,14 @@
namespace Stackkit\LaravelGoogleCloudTasksQueue;
use Closure;
-use Google\Cloud\Tasks\V2\RetryConfig;
use Google\Cloud\Tasks\V2\Task;
-use Google\Protobuf\Duration;
-use Illuminate\Support\Arr;
-use Illuminate\Support\Str;
use PHPUnit\Framework\Assert;
class CloudTasksApiFake implements CloudTasksApiContract
{
public array $createdTasks = [];
- public array $deletedTasks = [];
-
- public function getRetryConfig(string $queueName): RetryConfig
- {
- $retryConfig = new RetryConfig();
- $retryConfig
- ->setMinBackoff((new Duration(['seconds' => 0])))
- ->setMaxBackoff((new Duration(['seconds' => 0])));
-
- return $retryConfig;
- }
+ public array $deletedTasks = [];
public function createTask(string $queueName, Task $task): Task
{
@@ -46,17 +32,11 @@ public function getTask(string $taskName): Task
->setName($taskName);
}
-
- public function getRetryUntilTimestamp(Task $task): ?int
- {
- return null;
- }
-
public function assertTaskDeleted(string $taskName): void
{
Assert::assertTrue(
in_array($taskName, $this->deletedTasks),
- 'The task [' . $taskName . '] should have been deleted but it is not.'
+ 'The task ['.$taskName.'] should have been deleted but it is not.'
);
}
@@ -64,7 +44,7 @@ public function assertTaskNotDeleted(string $taskName): void
{
Assert::assertTrue(
! in_array($taskName, $this->deletedTasks),
- 'The task [' . $taskName . '] should not have been deleted but it was.'
+ 'The task ['.$taskName.'] should not have been deleted but it was.'
);
}
diff --git a/src/CloudTasksConnector.php b/src/CloudTasksConnector.php
index db81cd6..0eec81b 100644
--- a/src/CloudTasksConnector.php
+++ b/src/CloudTasksConnector.php
@@ -1,5 +1,7 @@
job = $job;
$this->container = Container::getInstance();
$this->cloudTasksQueue = $cloudTasksQueue;
-
+
$command = TaskHandler::getCommandProperties($job['data']['command']);
- $this->queue = $command['queue'] ?? config('queue.connections.' .config('queue.default') . '.queue');
+ $this->queue = $command['queue'] ?? config('queue.connections.'.config('queue.default').'.queue');
}
public function job()
@@ -66,41 +63,11 @@ public function setAttempts(int $attempts): void
$this->job['internal']['attempts'] = $attempts;
}
- public function setMaxTries(int $maxTries): void
- {
- if ($maxTries === -1) {
- $maxTries = 0;
- }
-
- $this->maxTries = $maxTries;
- }
-
- public function maxTries(): ?int
- {
- return $this->maxTries;
- }
-
public function setQueue(string $queue): void
{
$this->queue = $queue;
}
- public function setRetryUntil(?int $retryUntil): void
- {
- $this->retryUntil = $retryUntil;
- }
-
- public function retryUntil(): ?int
- {
- return $this->retryUntil;
- }
-
- // timeoutAt was renamed to retryUntil in 8.x but we still support this.
- public function timeoutAt(): ?int
- {
- return $this->retryUntil;
- }
-
public function delete(): void
{
// Laravel automatically calls delete() after a job is processed successfully. However, this is
@@ -137,16 +104,6 @@ public function release($delay = 0)
$properties = TaskHandler::getCommandProperties($this->job['data']['command']);
$connection = $properties['connection'] ?? config('queue.default');
- // The package uses the JobReleasedAfterException provided by Laravel to grab
- // the payload of the released job in tests to easily run and test a released
- // job. Because the event is only accessible in Laravel 9.x, we create an
- // identical event to hook into for Laravel versions older than 9.x
- if (version_compare(app()->version(), '9.0.0', '<')) {
- if (data_get($this->job, 'internal.errored')) {
- app('events')->dispatch(new JobReleasedAfterException($connection, $this));
- }
- }
-
if (! data_get($this->job, 'internal.errored')) {
app('events')->dispatch(new JobReleased($connection, $this, $delay));
}
diff --git a/src/CloudTasksQueue.php b/src/CloudTasksQueue.php
index b07242b..f09222b 100644
--- a/src/CloudTasksQueue.php
+++ b/src/CloudTasksQueue.php
@@ -1,5 +1,7 @@
withUuid($payload);
-
// Since 3.x tasks are released back onto the queue after an exception has
// been thrown. This means we lose the native [X-CloudTasks-TaskRetryCount] header
// value and need to manually set and update the number of times a task has been attempted.
@@ -161,7 +138,7 @@ protected function pushToCloudTasks($queue, $payload, $delay = 0)
// The deadline for requests sent to the app. If the app does not respond by
// this deadline then the request is cancelled and the attempt is marked as
// a failure. Cloud Tasks will retry the task according to the RetryConfig.
- if (!empty($this->config['dispatch_deadline'])) {
+ if (! empty($this->config['dispatch_deadline'])) {
$task->setDispatchDeadline(new Duration(['seconds' => $this->config['dispatch_deadline']]));
}
@@ -183,15 +160,6 @@ protected function pushToCloudTasks($queue, $payload, $delay = 0)
return $payload['uuid'];
}
- private function withUuid(array $payload): array
- {
- if (!isset($payload['uuid'])) {
- $payload['uuid'] = (string) Str::uuid();
- }
-
- return $payload;
- }
-
private function taskName(string $queueName, array $payload): string
{
$displayName = $this->sanitizeTaskName($payload['displayName']);
@@ -200,7 +168,7 @@ private function taskName(string $queueName, array $payload): string
$this->config['project'],
$this->config['location'],
$queueName,
- $displayName . '-' . $payload['uuid'] . '-' . Carbon::now()->getTimeStampMs(),
+ $displayName.'-'.$payload['uuid'].'-'.Carbon::now()->getTimeStampMs(),
);
}
@@ -217,7 +185,7 @@ private function sanitizeTaskName(string $taskName)
private function withAttempts(array $payload): array
{
- if (!isset($payload['internal']['attempts'])) {
+ if (! isset($payload['internal']['attempts'])) {
$payload['internal']['attempts'] = 0;
}
diff --git a/src/CloudTasksServiceProvider.php b/src/CloudTasksServiceProvider.php
index d22a281..88e4f8e 100644
--- a/src/CloudTasksServiceProvider.php
+++ b/src/CloudTasksServiceProvider.php
@@ -1,5 +1,7 @@
publishes([
- __DIR__ . '/../config/cloud-tasks.php' => config_path('cloud-tasks.php'),
+ __DIR__.'/../config/cloud-tasks.php' => config_path('cloud-tasks.php'),
], ['cloud-tasks']);
- $this->mergeConfigFrom(__DIR__ . '/../config/cloud-tasks.php', 'cloud-tasks');
+ $this->mergeConfigFrom(__DIR__.'/../config/cloud-tasks.php', 'cloud-tasks');
}
private function registerViews(): void
@@ -65,7 +68,7 @@ private function registerViews(): void
// return;
}
- $this->loadViewsFrom(__DIR__ . '/../views', 'cloud-tasks');
+ $this->loadViewsFrom(__DIR__.'/../views', 'cloud-tasks');
}
private function registerAssets(): void
@@ -75,7 +78,7 @@ private function registerAssets(): void
}
$this->publishes([
- __DIR__ . '/../dashboard/dist' => public_path('vendor/cloud-tasks'),
+ __DIR__.'/../dashboard/dist' => public_path('vendor/cloud-tasks'),
], ['cloud-tasks']);
}
@@ -86,7 +89,7 @@ private function registerMigrations(): void
}
$this->loadMigrationsFrom([
- __DIR__ . '/../migrations',
+ __DIR__.'/../migrations',
]);
}
@@ -139,7 +142,7 @@ private function registerDashboard(): void
});
$events->listen(JobFailed::class, function (JobFailed $event) {
- if (!$event->job instanceof CloudTasksJob) {
+ if (! $event->job instanceof CloudTasksJob) {
return;
}
@@ -152,7 +155,7 @@ private function registerDashboard(): void
});
$events->listen(JobProcessing::class, function (JobProcessing $event) {
- if (!$event->job instanceof CloudTasksJob) {
+ if (! $event->job instanceof CloudTasksJob) {
return;
}
@@ -162,7 +165,7 @@ private function registerDashboard(): void
});
$events->listen(JobProcessed::class, function (JobProcessed $event) {
- if (!$event->job instanceof CloudTasksJob) {
+ if (! $event->job instanceof CloudTasksJob) {
return;
}
@@ -174,7 +177,7 @@ private function registerDashboard(): void
});
$events->listen(JobExceptionOccurred::class, function (JobExceptionOccurred $event) {
- if (!$event->job instanceof CloudTasksJob) {
+ if (! $event->job instanceof CloudTasksJob) {
return;
}
@@ -186,7 +189,7 @@ private function registerDashboard(): void
});
$events->listen(JobFailed::class, function ($event) {
- if (!$event->job instanceof CloudTasksJob) {
+ if (! $event->job instanceof CloudTasksJob) {
return;
}
@@ -196,7 +199,7 @@ private function registerDashboard(): void
});
$events->listen(JobReleased::class, function (JobReleased $event) {
- if (!$event->job instanceof CloudTasksJob) {
+ if (! $event->job instanceof CloudTasksJob) {
return;
}
diff --git a/src/Config.php b/src/Config.php
index 422ce44..cb60dc5 100644
--- a/src/Config.php
+++ b/src/Config.php
@@ -1,5 +1,7 @@
$uuid,
'name' => $this->getTaskName($task),
'queue' => $queue,
- 'payload' => $this->getTaskBody($task),
+ 'payload' => $this->getTaskBody($task),
'status' => $status,
'metadata' => $metadata->toJson(),
'created_at' => now()->utc(),
diff --git a/src/Entities/StatRow.php b/src/Entities/StatRow.php
index a92d18a..406d982 100644
--- a/src/Entities/StatRow.php
+++ b/src/Entities/StatRow.php
@@ -1,11 +1,15 @@
job = $job;
- $this->connectionName = $connectionName;
- }
-}
diff --git a/src/Events/TaskCreated.php b/src/Events/TaskCreated.php
index a05f415..9ab76aa 100644
--- a/src/Events/TaskCreated.php
+++ b/src/Events/TaskCreated.php
@@ -9,6 +9,7 @@
class TaskCreated
{
public string $queue;
+
public Task $task;
public function task(Task $task): self
diff --git a/src/LogFake.php b/src/LogFake.php
index e4e86ff..c9a3476 100644
--- a/src/LogFake.php
+++ b/src/LogFake.php
@@ -5,7 +5,6 @@
namespace Stackkit\LaravelGoogleCloudTasksQueue;
use PHPUnit\Framework\Assert as PHPUnit;
-use Psr\Log\LoggerInterface;
class LogFake
{
@@ -52,7 +51,7 @@ public function debug(string $message, array $context = []): void
}
/**
- * @param string $level
+ * @param string $level
*/
public function log($level, string $message, array $context = []): void
{
@@ -66,14 +65,14 @@ public function channel(): self
public function assertLogged(string $message): void
{
- PHPUnit::assertTrue(in_array($message, $this->loggedMessages), 'The message [' . $message . '] was not logged.');
+ PHPUnit::assertTrue(in_array($message, $this->loggedMessages), 'The message ['.$message.'] was not logged.');
}
public function assertNotLogged(string $message): void
{
PHPUnit::assertTrue(
! in_array($message, $this->loggedMessages),
- 'The message [' . $message . '] was logged.'
+ 'The message ['.$message.'] was logged.'
);
}
}
diff --git a/src/OpenIdVerificatorConcrete.php b/src/OpenIdVerificatorConcrete.php
index a3c1c4a..8e5541c 100644
--- a/src/OpenIdVerificatorConcrete.php
+++ b/src/OpenIdVerificatorConcrete.php
@@ -11,7 +11,7 @@ class OpenIdVerificatorConcrete extends Facade
{
public function verify(?string $token, array $config): void
{
- if (!$token) {
+ if (! $token) {
throw new CloudTasksException('Missing [Authorization] header');
}
diff --git a/src/OpenIdVerificatorFake.php b/src/OpenIdVerificatorFake.php
index 79cedb6..5666e76 100644
--- a/src/OpenIdVerificatorFake.php
+++ b/src/OpenIdVerificatorFake.php
@@ -10,7 +10,7 @@ class OpenIdVerificatorFake
{
public function verify(?string $token, array $config): void
{
- if (!$token) {
+ if (! $token) {
return;
}
@@ -19,7 +19,7 @@ public function verify(?string $token, array $config): void
[
'audience' => Config::getAudience($config),
'throwException' => true,
- 'certsLocation' => __DIR__ . '/../tests/Support/self-signed-public-key-as-jwk.json',
+ 'certsLocation' => __DIR__.'/../tests/Support/self-signed-public-key-as-jwk.json',
]
);
}
diff --git a/src/StackkitCloudTask.php b/src/StackkitCloudTask.php
index 4af02f1..2d83bae 100644
--- a/src/StackkitCloudTask.php
+++ b/src/StackkitCloudTask.php
@@ -1,14 +1,18 @@
$builder
+ * @param Builder $builder
* @return Builder
*/
public function scopeNewestFirst(Builder $builder): Builder
@@ -40,7 +44,7 @@ public function scopeNewestFirst(Builder $builder): Builder
}
/**
- * @param Builder $builder
+ * @param Builder $builder
* @return Builder
*/
public function scopeFailed(Builder $builder): Builder
@@ -69,7 +73,7 @@ public function getNumberOfAttempts(): int
}
/**
- * @param mixed $value
+ * @param mixed $value
*/
public function setMetadata(string $key, $value): void
{
@@ -101,6 +105,7 @@ public function getEvents(): array
return collect($events)->map(function ($event) {
/** @var array $event */
$event['diff'] = Carbon::parse($event['datetime'])->diffForHumans();
+
return $event;
})->toArray();
}
diff --git a/src/TaskHandler.php b/src/TaskHandler.php
index 434e8f8..365a6ce 100644
--- a/src/TaskHandler.php
+++ b/src/TaskHandler.php
@@ -1,19 +1,17 @@
client = $client;
@@ -57,8 +50,8 @@ public function handle(?string $task = null): void
}
/**
- * @param string|array|null $task
- * @return array
+ * @param string|array|null $task
+ *
* @throws JsonException
*/
private function captureTask($task): array
@@ -99,7 +92,7 @@ private function loadQueueConnectionConfiguration(array $task): void
{
$command = self::getCommandProperties($task['data']['command']);
$connection = $command['connection'] ?? config('queue.default');
- $baseConfig = config('queue.connections.' . $connection);
+ $baseConfig = config('queue.connections.'.$connection);
$config = (new CloudTasksConnector())->connect($baseConfig)->config;
// The connection name from the config may not be the actual connection name
@@ -117,8 +110,6 @@ private function handleTask(array $task): void
{
$job = new CloudTasksJob($task, $this->queue);
- $this->loadQueueRetryConfig($job);
-
$taskName = request()->header('X-Cloudtasks-Taskname');
$fullTaskName = $this->client->taskName(
$this->config['project'],
@@ -137,39 +128,11 @@ private function handleTask(array $task): void
throw $e;
}
- // If the task has a [X-CloudTasks-TaskRetryCount] header higher than 0, then
- // we know the job was created using an earlier version of the package. This
- // job does not have the attempts tracked internally yet.
- $taskRetryCountHeader = request()->header('X-CloudTasks-TaskRetryCount');
- if ($taskRetryCountHeader && (int) $taskRetryCountHeader > 0) {
- $job->setAttempts((int) $taskRetryCountHeader);
- } else {
- $job->setAttempts($task['internal']['attempts']);
- }
-
- $job->setMaxTries($this->retryConfig->getMaxAttempts());
-
- // If the job is being attempted again we also check if a
- // max retry duration has been set. If that duration
- // has passed, it should stop trying altogether.
- if ($job->attempts() > 0) {
- $job->setRetryUntil(CloudTasksApi::getRetryUntilTimestamp($apiTask));
- }
-
$job->setAttempts($job->attempts() + 1);
app('queue.worker')->process($this->config['connection'], $job, $this->getWorkerOptions());
}
- private function loadQueueRetryConfig(CloudTasksJob $job): void
- {
- $queue = $job->getQueue() ?: $this->config['queue'];
-
- $queueName = $this->client->queueName($this->config['project'], $this->config['location'], $queue);
-
- $this->retryConfig = CloudTasksApi::getRetryConfig($queueName);
- }
-
public static function getCommandProperties(string $command): array
{
if (Str::startsWith($command, 'O:')) {
@@ -187,9 +150,9 @@ public function getWorkerOptions(): WorkerOptions
{
$options = new WorkerOptions();
- $prop = version_compare(app()->version(), '8.0.0', '<') ? 'delay' : 'backoff';
-
- $options->$prop = $this->config['backoff'] ?? 0;
+ if (isset($this->config['backoff'])) {
+ $options->backoff = $this->config['backoff'];
+ }
return $options;
}
diff --git a/src/TaskMetadata.php b/src/TaskMetadata.php
index 963bd37..2451a39 100644
--- a/src/TaskMetadata.php
+++ b/src/TaskMetadata.php
@@ -1,5 +1,7 @@
fail('Missing [' . $env . '] environment variable.');
+ if (! env($env)) {
+ $this->fail('Missing ['.$env.'] environment variable.');
}
}
@@ -42,26 +41,6 @@ protected function setUp(): void
}
- /**
- * @test
- */
- public function test_get_retry_config()
- {
- // Act
- $retryConfig = CloudTasksApi::getRetryConfig(
- $this->client->queueName(
- env('CI_CLOUD_TASKS_PROJECT_ID'),
- env('CI_CLOUD_TASKS_LOCATION'),
- env('CI_CLOUD_TASKS_QUEUE')
- )
- );
-
- // Assert
- $this->assertInstanceOf(RetryConfig::class, $retryConfig);
- $this->assertEquals(2, $retryConfig->getMaxAttempts());
- $this->assertEquals(5, $retryConfig->getMaxRetryDuration()->getSeconds());
- }
-
/**
* @test
*/
@@ -88,7 +67,7 @@ public function test_create_task()
// Assert
$this->assertMatchesRegularExpression(
- '/projects\/' . env('CI_CLOUD_TASKS_PROJECT_ID') . '\/locations\/' . env('CI_CLOUD_TASKS_LOCATION') . '\/queues\/' . env('CI_CLOUD_TASKS_QUEUE') . '\/tasks\/\d+$/',
+ '/projects\/'.env('CI_CLOUD_TASKS_PROJECT_ID').'\/locations\/'.env('CI_CLOUD_TASKS_LOCATION').'\/queues\/'.env('CI_CLOUD_TASKS_QUEUE').'\/tasks\/\d+$/',
$taskName
);
}
@@ -147,44 +126,4 @@ public function test_delete_task()
$this->expectExceptionMessage('NOT_FOUND');
CloudTasksApi::getTask($task->getName());
}
-
- /**
- * @test
- */
- public function test_get_retry_until_timestamp()
- {
- // Arrange
- $httpRequest = new HttpRequest();
- $httpRequest->setHttpMethod(HttpMethod::GET);
- $httpRequest->setUrl('https://httpstat.us/500');
-
- $cloudTask = new Task();
- $cloudTask->setHttpRequest($httpRequest);
-
- $createdTask = CloudTasksApi::createTask(
- $this->client->queueName(
- env('CI_CLOUD_TASKS_PROJECT_ID'),
- env('CI_CLOUD_TASKS_LOCATION'),
- env('CI_CLOUD_TASKS_CUSTOM_QUEUE', env('CI_CLOUD_TASKS_QUEUE'))
- ),
- $cloudTask,
- );
-
- $secondsSlept = 0;
- while ($createdTask->getFirstAttempt() === null) {
- $createdTask = CloudTasksApi::getTask($createdTask->getName());
- sleep(1);
- $secondsSlept += 1;
-
- if ($secondsSlept >= 180) {
- $this->fail('Task took too long to get executed.');
- }
- }
-
- // The queue max retry duration is 5 seconds. The max retry until timestamp is calculated from the
- // first attempt, so we expect it to be [timestamp first attempt] + 5 seconds.
- $expected = $createdTask->getFirstAttempt()->getDispatchTime()->getSeconds() + 5;
- $actual = CloudTasksApi::getRetryUntilTimestamp($createdTask);
- $this->assertSame($expected, $actual);
- }
}
diff --git a/tests/CloudTasksDashboardTest.php b/tests/CloudTasksDashboardTest.php
index 6cb7b99..0279941 100644
--- a/tests/CloudTasksDashboardTest.php
+++ b/tests/CloudTasksDashboardTest.php
@@ -1,8 +1,9 @@
create(['created_at' => now()->setTime(15,4, 59)]);
- factory(StackkitCloudTask::class)->create(['created_at' => now()->setTime(16,5, 0)]);
- factory(StackkitCloudTask::class)->create(['created_at' => now()->setTime(16,5, 59)]);
- factory(StackkitCloudTask::class)->create(['created_at' => now()->setTime(16,6, 0)]);
+ factory(StackkitCloudTask::class)->create(['created_at' => now()->setTime(15, 4, 59)]);
+ factory(StackkitCloudTask::class)->create(['created_at' => now()->setTime(16, 5, 0)]);
+ factory(StackkitCloudTask::class)->create(['created_at' => now()->setTime(16, 5, 59)]);
+ factory(StackkitCloudTask::class)->create(['created_at' => now()->setTime(16, 6, 0)]);
// Act
$response = $this->getJson('/cloud-tasks-api/tasks?time=16:05');
@@ -137,9 +139,9 @@ public function it_can_filter_tasks_created_at_exact_time()
public function it_can_filter_tasks_created_at_exact_hour()
{
// Arrange
- factory(StackkitCloudTask::class)->create(['created_at' => now()->setTime(15,59, 59)]);
- factory(StackkitCloudTask::class)->create(['created_at' => now()->setTime(16,5, 59)]);
- factory(StackkitCloudTask::class)->create(['created_at' => now()->setTime(16,32, 32)]);
+ factory(StackkitCloudTask::class)->create(['created_at' => now()->setTime(15, 59, 59)]);
+ factory(StackkitCloudTask::class)->create(['created_at' => now()->setTime(16, 5, 59)]);
+ factory(StackkitCloudTask::class)->create(['created_at' => now()->setTime(16, 32, 32)]);
// Act
$response = $this->getJson('/cloud-tasks-api/tasks?hour=16');
@@ -228,7 +230,7 @@ public function it_returns_info_about_a_specific_task()
$task = factory(StackkitCloudTask::class)->create();
// Act
- $response = $this->getJson('/cloud-tasks-api/task/' . $task->task_uuid);
+ $response = $this->getJson('/cloud-tasks-api/task/'.$task->task_uuid);
// Assert
$this->assertEquals($task->id, $response['id']);
@@ -309,7 +311,7 @@ public function when_a_job_is_scheduled_it_will_be_added_as_such()
public function when_a_job_is_running_it_will_be_updated_in_the_dashboard()
{
// Arrange
- \Illuminate\Support\Carbon::setTestNow(now());
+ Carbon::setTestNow(now());
CloudTasksApi::fake();
OpenIdVerificator::fake();
@@ -335,7 +337,7 @@ public function when_a_job_is_running_it_will_be_updated_in_the_dashboard()
public function when_a_job_is_successful_it_will_be_updated_in_the_dashboard()
{
// Arrange
- \Illuminate\Support\Carbon::setTestNow(now());
+ Carbon::setTestNow(now());
CloudTasksApi::fake();
OpenIdVerificator::fake();
@@ -361,7 +363,7 @@ public function when_a_job_is_successful_it_will_be_updated_in_the_dashboard()
public function when_a_job_errors_it_will_be_updated_in_the_dashboard()
{
// Arrange
- \Illuminate\Support\Carbon::setTestNow(now());
+ Carbon::setTestNow(now());
CloudTasksApi::fake();
OpenIdVerificator::fake();
@@ -388,14 +390,11 @@ public function when_a_job_errors_it_will_be_updated_in_the_dashboard()
public function when_a_job_fails_it_will_be_updated_in_the_dashboard()
{
// Arrange
- \Illuminate\Support\Carbon::setTestNow(now());
+ Carbon::setTestNow(now());
CloudTasksApi::fake();
OpenIdVerificator::fake();
- CloudTasksApi::partialMock()->shouldReceive('getRetryConfig')->andReturn(
- (new RetryConfig())->setMaxAttempts(3)
- );
- $job = $this->dispatch(new FailingJob());
+ $job = $this->dispatch(new FailingJobWithMaxTries());
$releasedJob = $job->runAndGetReleasedJob();
$releasedJob = $releasedJob->runAndGetReleasedJob();
$releasedJob->run();
@@ -420,12 +419,9 @@ public function when_a_job_fails_it_will_be_updated_in_the_dashboard()
public function when_a_job_is_released_it_will_be_updated_in_the_dashboard()
{
// Arrange
- \Illuminate\Support\Carbon::setTestNow(now());
+ Carbon::setTestNow(now());
CloudTasksApi::fake();
OpenIdVerificator::fake();
- CloudTasksApi::partialMock()->shouldReceive('getRetryConfig')->andReturn(
- (new RetryConfig())->setMaxAttempts(3)
- );
$this->dispatch(new JobThatWillBeReleased())->run();
@@ -451,12 +447,9 @@ public function when_a_job_is_released_it_will_be_updated_in_the_dashboard()
public function job_release_delay_is_added_to_the_metadata()
{
// Arrange
- \Illuminate\Support\Carbon::setTestNow(now());
+ Carbon::setTestNow(now());
CloudTasksApi::fake();
OpenIdVerificator::fake();
- CloudTasksApi::partialMock()->shouldReceive('getRetryConfig')->andReturn(
- (new RetryConfig())->setMaxAttempts(3)
- );
$this->dispatch(new JobThatWillBeReleased(15))->run();
@@ -487,17 +480,11 @@ public function test_publish()
// Act & Assert
$expectedPublishBase = dirname(__DIR__);
- if (version_compare(app()->version(), '9.0.0', '>=')) {
- $this->artisan('vendor:publish --tag=cloud-tasks --force')
- ->expectsOutputToContain('Publishing [cloud-tasks] assets.')
- ->expectsOutputToContain('Copying file [' . $expectedPublishBase . '/config/cloud-tasks.php] to [config/cloud-tasks.php]')
- ->expectsOutputToContain('Copying directory [' . $expectedPublishBase . '/dashboard/dist] to [public/vendor/cloud-tasks]');
- } else {
- $this->artisan('vendor:publish --tag=cloud-tasks --force')
- ->expectsOutput('Copied File [' . $expectedPublishBase . '/config/cloud-tasks.php] To [/config/cloud-tasks.php]')
- ->expectsOutput('Copied Directory [' . $expectedPublishBase . '/dashboard/dist] To [/public/vendor/cloud-tasks]')
- ->expectsOutput('Publishing complete.');
- }
+ $this->artisan('vendor:publish --tag=cloud-tasks --force')
+ ->expectsOutputToContain('Publishing [cloud-tasks] assets.')
+ ->expectsOutputToContain('Copying file ['.$expectedPublishBase.'/config/cloud-tasks.php] to [config/cloud-tasks.php]')
+ ->expectsOutputToContain('Copying directory ['.$expectedPublishBase.'/dashboard/dist] to [public/vendor/cloud-tasks]');
+
}
/**
@@ -521,7 +508,7 @@ public function when_dashboard_is_enabled_it_adds_the_necessary_routes()
*/
public function when_dashboard_is_enabled_it_adds_the_necessary_migrations()
{
- $this->assertTrue(in_array(dirname(__DIR__) . '/src/../migrations', app('migrator')->paths()));
+ $this->assertTrue(in_array(dirname(__DIR__).'/src/../migrations', app('migrator')->paths()));
}
/**
@@ -569,7 +556,7 @@ public function dashboard_is_password_protected()
public function can_enter_with_token()
{
// Arrange
- $this->defaultHeaders['Authorization'] = 'Bearer ' . encrypt(time() + 10);
+ $this->defaultHeaders['Authorization'] = 'Bearer '.encrypt(time() + 10);
// Act
$response = $this->getJson('/cloud-tasks-api/dashboard');
@@ -584,7 +571,7 @@ public function can_enter_with_token()
public function token_can_expire()
{
// Arrange
- $this->defaultHeaders['Authorization'] = 'Bearer ' . encrypt(Carbon::create(2020, 5, 15, 15, 15, 15)->timestamp);
+ $this->defaultHeaders['Authorization'] = 'Bearer '.encrypt(Carbon::create(2020, 5, 15, 15, 15, 15)->timestamp);
// Act & Assert
Carbon::setTestNow(Carbon::create(2020, 5, 15, 15, 15, 14));
diff --git a/tests/ConfigHandlerTest.php b/tests/ConfigHandlerTest.php
index 523f6a4..63c256f 100644
--- a/tests/ConfigHandlerTest.php
+++ b/tests/ConfigHandlerTest.php
@@ -1,5 +1,7 @@
version(), '8.0.0', '<')) {
- $this->markTestSkipped('Not supported by Laravel 7.x and below.');
- }
-
// Arrange
CloudTasksApi::fake();
Event::fake();
@@ -202,10 +198,6 @@ public function it_can_dispatch_after_commit_inline()
*/
public function it_can_dispatch_after_commit_through_config()
{
- if (version_compare(app()->version(), '8.0.0', '<')) {
- $this->markTestSkipped('Not supported by Laravel 7.x and below.');
- }
-
// Arrange
CloudTasksApi::fake();
Event::fake();
@@ -233,7 +225,7 @@ public function jobs_can_be_released()
CloudTasksApi::fake();
OpenIdVerificator::fake();
Event::fake([
- $this->getJobReleasedAfterExceptionEvent(),
+ JobReleasedAfterException::class,
JobReleased::class,
]);
@@ -241,16 +233,18 @@ public function jobs_can_be_released()
$this->dispatch(new JobThatWillBeReleased())->run();
// Assert
- Event::assertNotDispatched($this->getJobReleasedAfterExceptionEvent());
+ Event::assertNotDispatched(JobReleasedAfterException::class);
CloudTasksApi::assertDeletedTaskCount(0); // it returned 200 OK so we dont delete it, but Google does
$releasedJob = null;
Event::assertDispatched(JobReleased::class, function (JobReleased $event) use (&$releasedJob) {
$releasedJob = $event->job;
+
return true;
});
CloudTasksApi::assertTaskCreated(function (Task $task) {
$body = $task->getHttpRequest()->getBody();
$decoded = json_decode($body, true);
+
return $decoded['data']['commandName'] === 'Tests\\Support\\JobThatWillBeReleased'
&& $decoded['internal']['attempts'] === 1;
});
@@ -261,6 +255,7 @@ public function jobs_can_be_released()
CloudTasksApi::assertTaskCreated(function (Task $task) {
$body = $task->getHttpRequest()->getBody();
$decoded = json_decode($body, true);
+
return $decoded['data']['commandName'] === 'Tests\\Support\\JobThatWillBeReleased'
&& $decoded['internal']['attempts'] === 2;
});
@@ -275,7 +270,7 @@ public function jobs_can_be_released_with_a_delay()
CloudTasksApi::fake();
OpenIdVerificator::fake();
Event::fake([
- $this->getJobReleasedAfterExceptionEvent(),
+ JobReleasedAfterException::class,
JobReleased::class,
]);
Carbon::setTestNow(now()->addDay());
@@ -302,7 +297,7 @@ public function test_default_backoff()
// Arrange
CloudTasksApi::fake();
OpenIdVerificator::fake();
- Event::fake($this->getJobReleasedAfterExceptionEvent());
+ Event::fake(JobReleasedAfterException::class);
// Act
$this->dispatch(new FailingJob())->run();
@@ -321,7 +316,7 @@ public function test_backoff_from_queue_config()
$this->setConfigValue('backoff', 123);
CloudTasksApi::fake();
OpenIdVerificator::fake();
- Event::fake($this->getJobReleasedAfterExceptionEvent());
+ Event::fake(JobReleasedAfterException::class);
// Act
$this->dispatch(new FailingJob())->run();
@@ -340,12 +335,11 @@ public function test_backoff_from_job()
Carbon::setTestNow(now()->addDay());
CloudTasksApi::fake();
OpenIdVerificator::fake();
- Event::fake($this->getJobReleasedAfterExceptionEvent());
+ Event::fake(JobReleasedAfterException::class);
// Act
$failingJob = new FailingJob();
- $prop = version_compare(app()->version(), '8.0.0', '<') ? 'delay' : 'backoff';
- $failingJob->$prop = 123;
+ $failingJob->backoff = 123;
$this->dispatch($failingJob)->run();
// Assert
@@ -358,10 +352,6 @@ public function test_backoff_from_job()
/** @test */
public function test_exponential_backoff_from_job_method()
{
- if (version_compare(app()->version(), '8.0.0', '<')) {
- $this->markTestSkipped('Not supported by Laravel 7.x and below.');
- }
-
// Arrange
Carbon::setTestNow(now()->addDay());
CloudTasksApi::fake();
@@ -393,15 +383,14 @@ public function test_failing_method_on_job()
{
// Arrange
CloudTasksApi::fake();
- CloudTasksApi::partialMock()->shouldReceive('getRetryConfig')->andReturn(
- (new RetryConfig())->setMaxAttempts(1)
- );
-
OpenIdVerificator::fake();
Log::swap(new LogFake());
// Act
- $this->dispatch(new FailingJob())->run();
+ $this->dispatch(new FailingJob())
+ ->runAndGetReleasedJob()
+ ->runAndGetReleasedJob()
+ ->runAndGetReleasedJob();
// Assert
Log::assertLogged('FailingJob:failed');
@@ -417,10 +406,10 @@ public function test_queue_before_and_after_hooks()
// Act
Queue::before(function (JobProcessing $event) {
- logger('Queue::before:' . $event->job->payload()['data']['commandName']);
+ logger('Queue::before:'.$event->job->payload()['data']['commandName']);
});
Queue::after(function (JobProcessed $event) {
- logger('Queue::after:' . $event->job->payload()['data']['commandName']);
+ logger('Queue::after:'.$event->job->payload()['data']['commandName']);
});
$this->dispatch(new SimpleJob())->run();
@@ -495,7 +484,7 @@ public function it_adds_a_task_name_based_on_the_display_name()
CloudTasksApi::assertTaskCreated(function (Task $task, string $queueName): bool {
$uuid = \Safe\json_decode($task->getHttpRequest()->getBody(), true)['uuid'];
- return $task->getName() === 'projects/my-test-project/locations/europe-west6/queues/barbequeue/tasks/Tests-Support-SimpleJob-' . $uuid . '-1685649757000';
+ return $task->getName() === 'projects/my-test-project/locations/europe-west6/queues/barbequeue/tasks/Tests-Support-SimpleJob-'.$uuid.'-1685649757000';
});
}
}
diff --git a/tests/Support/EncryptedJob.php b/tests/Support/EncryptedJob.php
index 8f8e4ff..86b5290 100644
--- a/tests/Support/EncryptedJob.php
+++ b/tests/Support/EncryptedJob.php
@@ -1,5 +1,7 @@
addMinutes(5);
+ }
+}
diff --git a/tests/Support/FailingJobWithRetryUntil.php b/tests/Support/FailingJobWithRetryUntil.php
new file mode 100644
index 0000000..46a1a8c
--- /dev/null
+++ b/tests/Support/FailingJobWithRetryUntil.php
@@ -0,0 +1,15 @@
+addMinutes(5);
+ }
+}
diff --git a/tests/Support/JobThatWillBeReleased.php b/tests/Support/JobThatWillBeReleased.php
index bbd2d98..1192545 100644
--- a/tests/Support/JobThatWillBeReleased.php
+++ b/tests/Support/JobThatWillBeReleased.php
@@ -1,5 +1,7 @@
user->name);
+ logger('UserJob:'.$this->user->name);
}
}
diff --git a/tests/TaskHandlerTest.php b/tests/TaskHandlerTest.php
index b100da6..07345cf 100644
--- a/tests/TaskHandlerTest.php
+++ b/tests/TaskHandlerTest.php
@@ -1,13 +1,14 @@
shouldReceive('getRetryConfig')->andReturn(
- (new RetryConfig())->setMaxAttempts(3)
- );
- $job = $this->dispatch(new FailingJob());
+ $job = $this->dispatch(new FailingJobWithMaxTries());
// Act & Assert
$this->assertDatabaseCount('failed_jobs', 0);
@@ -275,10 +279,6 @@ public function after_max_attempts_it_will_delete_the_task()
// Arrange
OpenIdVerificator::fake();
- CloudTasksApi::partialMock()->shouldReceive('getRetryConfig')->andReturn(
- (new RetryConfig())->setMaxAttempts(3)
- );
-
$job = $this->dispatch(new FailingJob());
// Act & Assert
@@ -300,16 +300,18 @@ public function after_max_attempts_it_will_delete_the_task()
/**
* @test
+ *
+ * @testWith [{"now": "2020-01-01 00:00:00", "try_at": "2020-01-01 00:00:00", "should_fail": false}]
+ * [{"now": "2020-01-01 00:00:00", "try_at": "2020-01-01 00:04:59", "should_fail": false}]
+ * [{"now": "2020-01-01 00:00:00", "try_at": "2020-01-01 00:05:00", "should_fail": true}]
*/
- public function after_max_retry_until_it_will_log_to_failed_table_and_delete_the_task()
+ public function after_max_retry_until_it_will_log_to_failed_table_and_delete_the_task(array $args)
{
// Arrange
+ $this->travelTo($args['now']);
+
OpenIdVerificator::fake();
- CloudTasksApi::partialMock()->shouldReceive('getRetryConfig')->andReturn(
- (new RetryConfig())->setMaxRetryDuration(new Duration(['seconds' => 30]))
- );
- CloudTasksApi::partialMock()->shouldReceive('getRetryUntilTimestamp')->andReturn(1);
- $job = $this->dispatch(new FailingJob());
+ $job = $this->dispatch(new FailingJobWithRetryUntil());
// Act
$releasedJob = $job->runAndGetReleasedJob();
@@ -320,13 +322,11 @@ public function after_max_retry_until_it_will_log_to_failed_table_and_delete_the
$this->assertDatabaseCount('failed_jobs', 0);
// Act
- CloudTasksApi::partialMock()->shouldReceive('getRetryUntilTimestamp')->andReturn(1);
+ $this->travelTo($args['try_at']);
$releasedJob->run();
// Assert
- CloudTasksApi::assertDeletedTaskCount(2);
- CloudTasksApi::assertTaskDeleted($job->task->getName());
- $this->assertDatabaseCount('failed_jobs', 1);
+ $this->assertDatabaseCount('failed_jobs', $args['should_fail'] ? 1 : 0);
}
/**
@@ -336,10 +336,6 @@ public function test_unlimited_max_attempts()
{
// Arrange
OpenIdVerificator::fake();
- CloudTasksApi::partialMock()->shouldReceive('getRetryConfig')->andReturn(
- // -1 is a valid option in Cloud Tasks to indicate there is no max.
- (new RetryConfig())->setMaxAttempts(-1)
- );
// Act
$job = $this->dispatch(new FailingJob());
@@ -356,46 +352,30 @@ public function test_unlimited_max_attempts()
*/
public function test_max_attempts_in_combination_with_retry_until()
{
- // Laravel 5, 6, 7: check both max_attempts and retry_until before failing a job.
- // Laravel 8+: if retry_until, only check that
-
// Arrange
OpenIdVerificator::fake();
- CloudTasksApi::partialMock()->shouldReceive('getRetryConfig')->andReturn(
- (new RetryConfig())
- ->setMaxAttempts(3)
- ->setMaxRetryDuration(new Duration(['seconds' => 3]))
- );
- CloudTasksApi::partialMock()->shouldReceive('getRetryUntilTimestamp')->andReturn(time() + 10)->byDefault();
-
- $job = $this->dispatch(new FailingJob());
- // Act & Assert
- $releasedJob = $job->runAndGetReleasedJob();
- $releasedJob = $releasedJob->runAndGetReleasedJob();
+ $this->travelTo('2020-01-01 00:00:00');
- # After 2 attempts both Laravel versions should report the same: 2 errors and 0 failures.
- $task = StackkitCloudTask::whereTaskUuid($job->payloadAsArray('uuid'))->firstOrFail();
- $this->assertEquals(2, $task->getNumberOfAttempts());
- $this->assertEquals('error', $task->status);
+ $job = $this->dispatch(new FailingJobWithMaxTriesAndRetryUntil());
- $releasedJob->run();
+ // When retryUntil is specified, the maxAttempts is ignored.
- # Max attempts was reached
- # Laravel 5, 6, 7: fail because max attempts was reached
- # Laravel 8+: don't fail because retryUntil has not yet passed.
+ // Act & Assert
- if (version_compare(app()->version(), '8.0.0', '<')) {
- $this->assertEquals('failed', $task->fresh()->status);
- return;
- } else {
- $this->assertEquals('error', $task->fresh()->status);
+ // The max attempts is 3, but the retryUntil is set to 5 minutes from now.
+ // So when we attempt the job 10 times, it should still not fail.
+ foreach (range(1, 10) as $attempt) {
+ $job = $job->runAndGetReleasedJob();
+ CloudTasksApi::assertDeletedTaskCount($attempt);
+ CloudTasksApi::assertTaskDeleted($job->task->getName());
+ $this->assertDatabaseCount('failed_jobs', 0);
}
- CloudTasksApi::shouldReceive('getRetryUntilTimestamp')->andReturn(time() - 1);
- $releasedJob->run();
-
- $this->assertEquals('failed', $task->fresh()->status);
+ // Now we travel to 5 minutes from now, and the job should fail.
+ $this->travelTo('2020-01-01 00:05:00');
+ $job->run();
+ $this->assertDatabaseCount('failed_jobs', 1);
}
/**
@@ -403,10 +383,6 @@ public function test_max_attempts_in_combination_with_retry_until()
*/
public function it_can_handle_encrypted_jobs()
{
- if (version_compare(app()->version(), '8.0.0', '<')) {
- $this->markTestSkipped('Not supported by Laravel 7.x and below.');
- }
-
// Arrange
OpenIdVerificator::fake();
Log::swap(new LogFake());
@@ -431,10 +407,7 @@ public function failing_jobs_are_released()
{
// Arrange
OpenIdVerificator::fake();
- CloudTasksApi::partialMock()->shouldReceive('getRetryConfig')->andReturn(
- (new RetryConfig())->setMaxAttempts(3)
- );
- Event::fake($this->getJobReleasedAfterExceptionEvent());
+ Event::fake(JobReleasedAfterException::class);
// Act
$job = $this->dispatch(new FailingJob());
@@ -448,7 +421,7 @@ public function failing_jobs_are_released()
CloudTasksApi::assertDeletedTaskCount(1);
CloudTasksApi::assertCreatedTaskCount(2);
CloudTasksApi::assertTaskDeleted($job->task->getName());
- Event::assertDispatched($this->getJobReleasedAfterExceptionEvent(), function ($event) {
+ Event::assertDispatched(JobReleasedAfterException::class, function ($event) {
return $event->job->attempts() === 1;
});
}
@@ -460,44 +433,26 @@ public function attempts_are_tracked_internally()
{
// Arrange
OpenIdVerificator::fake();
- Event::fake($this->getJobReleasedAfterExceptionEvent());
+ Event::fake(JobReleasedAfterException::class);
// Act & Assert
$job = $this->dispatch(new FailingJob());
$job->run();
$releasedJob = null;
- Event::assertDispatched($this->getJobReleasedAfterExceptionEvent(), function ($event) use (&$releasedJob) {
+ Event::assertDispatched(JobReleasedAfterException::class, function ($event) use (&$releasedJob) {
$releasedJob = $event->job->getRawBody();
+
return $event->job->attempts() === 1;
});
$this->runFromPayload($releasedJob);
- Event::assertDispatched($this->getJobReleasedAfterExceptionEvent(), function ($event) {
+ Event::assertDispatched(JobReleasedAfterException::class, function ($event) {
return $event->job->attempts() === 2;
});
}
- /**
- * @test
- */
- public function attempts_are_copied_from_x_header()
- {
- // Arrange
- OpenIdVerificator::fake();
- Event::fake($this->getJobReleasedAfterExceptionEvent());
-
- // Act & Assert
- $job = $this->dispatch(new FailingJob());
- request()->headers->set('X-CloudTasks-TaskRetryCount', 6);
- $job->run();
-
- Event::assertDispatched($this->getJobReleasedAfterExceptionEvent(), function ($event) {
- return $event->job->attempts() === 7;
- });
- }
-
/**
* @test
*/
@@ -505,7 +460,7 @@ public function retried_jobs_get_a_new_name()
{
// Arrange
OpenIdVerificator::fake();
- Event::fake($this->getJobReleasedAfterExceptionEvent());
+ Event::fake(JobReleasedAfterException::class);
CloudTasksApi::fake();
// Act & Assert
@@ -519,10 +474,12 @@ public function retried_jobs_get_a_new_name()
CloudTasksApi::assertCreatedTaskCount(2);
CloudTasksApi::assertTaskCreated(function (Task $task): bool {
[$timestamp] = array_reverse(explode('-', $task->getName()));
+
return $timestamp === '1685035628000';
});
CloudTasksApi::assertTaskCreated(function (Task $task): bool {
[$timestamp] = array_reverse(explode('-', $task->getName()));
+
return $timestamp === '1685035629000';
});
}
diff --git a/tests/TestCase.php b/tests/TestCase.php
index bbdd1e1..117fb09 100644
--- a/tests/TestCase.php
+++ b/tests/TestCase.php
@@ -1,5 +1,7 @@
withFactories(__DIR__ . '/../factories');
+ $this->withFactories(__DIR__.'/../factories');
- $this->defaultHeaders['Authorization'] = 'Bearer ' . encrypt(time() + 10);
+ $this->defaultHeaders['Authorization'] = 'Bearer '.encrypt(time() + 10);
Event::listen(
- $this->getJobReleasedAfterExceptionEvent(),
+ JobReleasedAfterException::class,
function ($event) {
$this->releasedJobPayload = $event->job->getRawBody();
}
@@ -48,8 +49,7 @@ function ($event) {
* In a normal app environment these would be added to the 'providers' array in
* the config/app.php file.
*
- * @param \Illuminate\Foundation\Application $app
- *
+ * @param \Illuminate\Foundation\Application $app
* @return array
*/
protected function getPackageProviders($app)
@@ -66,14 +66,14 @@ protected function getPackageProviders($app)
*/
protected function defineDatabaseMigrations()
{
- $this->loadMigrationsFrom(__DIR__ . '/../migrations');
- $this->loadMigrationsFrom(__DIR__ . '/../vendor/orchestra/testbench-core/laravel/migrations');
+ $this->loadMigrationsFrom(__DIR__.'/../migrations');
+ $this->loadMigrationsFrom(__DIR__.'/../vendor/orchestra/testbench-core/laravel/migrations');
}
/**
* Define environment setup.
*
- * @param \Illuminate\Foundation\Application $app
+ * @param \Illuminate\Foundation\Application $app
* @return void
*/
protected function getEnvironmentSetUp($app)
@@ -85,13 +85,13 @@ protected function getEnvironmentSetUp($app)
$app['config']->set('database.default', 'testbench');
$port = env('DB_DRIVER') === 'mysql' ? 3307 : 5432;
$app['config']->set('database.connections.testbench', [
- 'driver' => env('DB_DRIVER', 'mysql'),
+ 'driver' => env('DB_DRIVER', 'mysql'),
'host' => '127.0.0.1',
'port' => $port,
'database' => 'cloudtasks',
'username' => 'cloudtasks',
'password' => 'cloudtasks',
- 'prefix' => '',
+ 'prefix' => '',
]);
$app['config']->set('cache.default', 'file');
@@ -120,7 +120,7 @@ protected function getEnvironmentSetUp($app)
protected function setConfigValue($key, $value)
{
- $this->app['config']->set('queue.connections.my-cloudtasks-connection.' . $key, $value);
+ $this->app['config']->set('queue.connections.my-cloudtasks-connection.'.$key, $value);
}
public function dispatch($job)
@@ -140,9 +140,12 @@ public function dispatch($job)
dispatch($job);
- return new class($payload, $task, $this) {
+ return new class($payload, $task, $this)
+ {
public string $payload;
+
public Task $task;
+
public TestCase $testCase;
public function __construct(string $payload, Task $task, TestCase $testCase)
@@ -211,7 +214,7 @@ public function assertTaskExists(string $taskId): void
$this->assertInstanceOf(Task::class, $task);
} catch (ApiException $e) {
- $this->fail('Task [' . $taskId . '] should exist but it does not (or something else went wrong).');
+ $this->fail('Task ['.$taskId.'] should exist but it does not (or something else went wrong).');
}
}
@@ -227,25 +230,15 @@ protected function addIdTokenToHeader(?Closure $closure = null): void
$base = $closure($base);
}
- $privateKey = file_get_contents(__DIR__ . '/../tests/Support/self-signed-private-key.txt');
+ $privateKey = file_get_contents(__DIR__.'/../tests/Support/self-signed-private-key.txt');
$token = JWT::encode($base, $privateKey, 'RS256', 'abc123');
- request()->headers->set('Authorization', 'Bearer ' . $token);
+ request()->headers->set('Authorization', 'Bearer '.$token);
}
protected function assertDatabaseCount($table, int $count, $connection = null)
{
$this->assertEquals($count, DB::connection($connection)->table($table)->count());
}
-
- public function getJobReleasedAfterExceptionEvent(): string
- {
- // The JobReleasedAfterException event is not available in Laravel versions
- // below 9.x so instead for those versions we throw our own event which
- // is identical to the Laravel one.
- return version_compare(app()->version(), '9.0.0', '<')
- ? PackageJobReleasedAfterException::class
- : JobReleasedAfterException::class;
- }
}