Skip to content

Commit 251b9d8

Browse files
committed
Update tests that fail jobs multiple times
1 parent 6932b21 commit 251b9d8

File tree

4 files changed

+97
-41
lines changed

4 files changed

+97
-41
lines changed

src/CloudTasksJob.php

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,13 @@
99

1010
class CloudTasksJob extends LaravelJob implements JobContract
1111
{
12+
/**
13+
* The Cloud Tasks raw job payload (request payload).
14+
*
15+
* @var array
16+
*/
1217
private array $job;
13-
private ?int $attempts;
18+
1419
private ?int $maxTries;
1520
public ?int $retryUntil = null;
1621

@@ -29,6 +34,11 @@ public function __construct(array $job, CloudTasksQueue $cloudTasksQueue)
2934
$this->queue = $command['queue'] ?? config('queue.connections.' .config('queue.default') . '.queue');
3035
}
3136

37+
public function job()
38+
{
39+
return $this->job;
40+
}
41+
3242
public function getJobId(): string
3343
{
3444
return $this->job['uuid'];
@@ -46,12 +56,12 @@ public function getRawBody(): string
4656

4757
public function attempts(): ?int
4858
{
49-
return $this->attempts;
59+
return $this->job['internal']['attempts'];
5060
}
5161

5262
public function setAttempts(int $attempts): void
5363
{
54-
$this->attempts = $attempts;
64+
$this->job['internal']['attempts'] = $attempts;
5565
}
5666

5767
public function setMaxTries(int $maxTries): void

tests/CloudTasksDashboardTest.php

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -258,8 +258,7 @@ public function when_a_job_is_dispatched_it_will_be_added_to_the_dashboard()
258258
'status' => 'queued',
259259
'name' => SimpleJob::class,
260260
]);
261-
$payload = \Safe\json_decode($task->getMetadata()['payload'], true);
262-
$this->assertSame($payload, $job->payloadAsArray);
261+
$this->assertSame($task->getMetadata()['payload'], $job->payload);
263262
}
264263

265264
/**
@@ -396,9 +395,9 @@ public function when_a_job_fails_it_will_be_updated_in_the_dashboard()
396395
);
397396

398397
$job = $this->dispatch(new FailingJob());
399-
$job->run();
400-
$job->run();
401-
$job->run();
398+
$releasedJob = $job->runAndGetReleasedJob();
399+
$releasedJob = $releasedJob->runAndGetReleasedJob();
400+
$releasedJob->run();
402401

403402
// Assert
404403
$task = StackkitCloudTask::firstOrFail();

tests/TaskHandlerTest.php

Lines changed: 49 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use Google\Protobuf\Duration;
88
use Illuminate\Queue\Events\JobProcessed;
99
use Illuminate\Queue\Events\JobProcessing;
10+
use Illuminate\Queue\Events\JobReleasedAfterException;
1011
use Illuminate\Support\Facades\Event;
1112
use Illuminate\Support\Facades\Log;
1213
use Illuminate\Validation\ValidationException;
@@ -239,13 +240,13 @@ public function after_max_attempts_it_will_log_to_failed_table()
239240
// Act & Assert
240241
$this->assertDatabaseCount('failed_jobs', 0);
241242

242-
$job->run();
243+
$releasedJob = $job->runAndGetReleasedJob();
243244
$this->assertDatabaseCount('failed_jobs', 0);
244245

245-
$job->run();
246+
$releasedJob = $releasedJob->runAndGetReleasedJob();
246247
$this->assertDatabaseCount('failed_jobs', 0);
247248

248-
$job->run();
249+
$releasedJob->run();
249250
$this->assertDatabaseCount('failed_jobs', 1);
250251
}
251252

@@ -264,17 +265,17 @@ public function after_max_attempts_it_will_delete_the_task()
264265
$job = $this->dispatch(new FailingJob());
265266

266267
// Act & Assert
267-
$job->run();
268+
$releasedJob = $job->runAndGetReleasedJob();
268269
CloudTasksApi::assertDeletedTaskCount(1);
269270
CloudTasksApi::assertTaskDeleted($job->task->getName());
270271
$this->assertDatabaseCount('failed_jobs', 0);
271272

272-
$job->run();
273+
$releasedJob = $releasedJob->runAndGetReleasedJob();
273274
CloudTasksApi::assertDeletedTaskCount(2);
274275
CloudTasksApi::assertTaskDeleted($job->task->getName());
275276
$this->assertDatabaseCount('failed_jobs', 0);
276277

277-
$job->run();
278+
$releasedJob->run();
278279
CloudTasksApi::assertDeletedTaskCount(3);
279280
CloudTasksApi::assertTaskDeleted($job->task->getName());
280281
$this->assertDatabaseCount('failed_jobs', 1);
@@ -294,7 +295,7 @@ public function after_max_retry_until_it_will_log_to_failed_table_and_delete_the
294295
$job = $this->dispatch(new FailingJob());
295296

296297
// Act
297-
$job->run();
298+
$releasedJob = $job->runAndGetReleasedJob();
298299

299300
// Assert
300301
CloudTasksApi::assertDeletedTaskCount(1);
@@ -303,7 +304,7 @@ public function after_max_retry_until_it_will_log_to_failed_table_and_delete_the
303304

304305
// Act
305306
CloudTasksApi::partialMock()->shouldReceive('getRetryUntilTimestamp')->andReturn(1);
306-
$job->run();
307+
$releasedJob->run();
307308

308309
// Assert
309310
CloudTasksApi::assertDeletedTaskCount(2);
@@ -353,15 +354,15 @@ public function test_max_attempts_in_combination_with_retry_until()
353354
$job = $this->dispatch(new FailingJob());
354355

355356
// Act & Assert
356-
$job->run();
357-
$job->run();
357+
$releasedJob = $job->runAndGetReleasedJob();
358+
$releasedJob = $releasedJob->runAndGetReleasedJob();
358359

359360
# After 2 attempts both Laravel versions should report the same: 2 errors and 0 failures.
360-
$task = StackkitCloudTask::whereTaskUuid($job->payloadAsArray['uuid'])->firstOrFail();
361+
$task = StackkitCloudTask::whereTaskUuid($job->payloadAsArray('uuid'))->firstOrFail();
361362
$this->assertEquals(2, $task->getNumberOfAttempts());
362363
$this->assertEquals('error', $task->status);
363364

364-
$job->run();
365+
$releasedJob->run();
365366

366367
# Max attempts was reached
367368
# Laravel 5, 6, 7: fail because max attempts was reached
@@ -375,7 +376,7 @@ public function test_max_attempts_in_combination_with_retry_until()
375376
}
376377

377378
CloudTasksApi::shouldReceive('getRetryUntilTimestamp')->andReturn(time() - 1);
378-
$job->run();
379+
$releasedJob->run();
379380

380381
$this->assertEquals('failed', $task->fresh()->status);
381382
}
@@ -400,19 +401,23 @@ public function it_can_handle_encrypted_jobs()
400401
// Assert
401402
$this->assertStringContainsString(
402403
'O:26:"Tests\Support\EncryptedJob"',
403-
decrypt($job->payloadAsArray['data']['command']),
404+
decrypt($job->payloadAsArray('data.command')),
404405
);
405406

406407
Log::assertLogged('EncryptedJob:success');
407408
}
408409

410+
/**
411+
* @test
412+
*/
409413
public function failing_jobs_are_released()
410414
{
411415
// Arrange
412416
OpenIdVerificator::fake();
413417
CloudTasksApi::partialMock()->shouldReceive('getRetryConfig')->andReturn(
414418
(new RetryConfig())->setMaxAttempts(3)
415419
);
420+
Event::fake([JobReleasedAfterException::class]);
416421

417422
// Act
418423
$job = $this->dispatch(new FailingJob());
@@ -426,5 +431,35 @@ public function failing_jobs_are_released()
426431
CloudTasksApi::assertDeletedTaskCount(1);
427432
CloudTasksApi::assertCreatedTaskCount(2);
428433
CloudTasksApi::assertTaskDeleted($job->task->getName());
434+
Event::assertDispatched(JobReleasedAfterException::class, function ($event) {
435+
return $event->job->attempts() === 1;
436+
});
437+
}
438+
439+
/**
440+
* @test
441+
*/
442+
public function attempts_are_tracked_internally()
443+
{
444+
// Arrange
445+
CloudTasksApi::fake();
446+
OpenIdVerificator::fake();
447+
Event::fake([JobReleasedAfterException::class]);
448+
449+
// Act & Assert
450+
$job = $this->dispatch(new FailingJob());
451+
$job->run();
452+
$releasedJob = null;
453+
454+
Event::assertDispatched(JobReleasedAfterException::class, function ($event) use (&$releasedJob) {
455+
$releasedJob = $event->job->getRawBody();
456+
return $event->job->attempts() === 1;
457+
});
458+
459+
$this->runFromPayload($releasedJob);
460+
461+
Event::assertDispatched(JobReleasedAfterException::class, function ($event) {
462+
return $event->job->attempts() === 2;
463+
});
429464
}
430465
}

tests/TestCase.php

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@
99
use Google\Cloud\Tasks\V2\RetryConfig;
1010
use Google\Cloud\Tasks\V2\Task;
1111
use Illuminate\Foundation\Testing\DatabaseTransactions;
12+
use Illuminate\Queue\Events\JobReleasedAfterException;
1213
use Illuminate\Support\Facades\DB;
1314
use Google\Cloud\Tasks\V2\CloudTasksClient;
1415
use Illuminate\Support\Facades\Event;
1516
use Mockery;
17+
use Stackkit\LaravelGoogleCloudTasksQueue\CloudTasksJob;
1618
use Stackkit\LaravelGoogleCloudTasksQueue\TaskCreated;
1719
use Stackkit\LaravelGoogleCloudTasksQueue\TaskHandler;
1820

@@ -25,13 +27,22 @@ class TestCase extends \Orchestra\Testbench\TestCase
2527
*/
2628
public $client;
2729

30+
public string $releasedJobPayload;
31+
2832
protected function setUp(): void
2933
{
3034
parent::setUp();
3135

3236
$this->withFactories(__DIR__ . '/../factories');
3337

3438
$this->defaultHeaders['Authorization'] = 'Bearer ' . encrypt(time() + 10);
39+
40+
Event::listen(
41+
JobReleasedAfterException::class,
42+
function (JobReleasedAfterException $event) {
43+
$this->releasedJobPayload = $event->job->getRawBody();
44+
}
45+
);
3546
}
3647

3748
/**
@@ -129,58 +140,59 @@ public function dispatch($job)
129140

130141
dispatch($job);
131142

132-
return new class($payload, $payloadAsArray, $task) {
143+
return new class($payload, $task, $this) {
133144
public string $payload;
134-
public array $payloadAsArray;
135145
public Task $task;
146+
public TestCase $testCase;
136147

137-
public function __construct(string $payload, array $payloadAsArray, Task $task)
148+
public function __construct(string $payload, Task $task, TestCase $testCase)
138149
{
139150
$this->payload = $payload;
140-
$this->payloadAsArray = $payloadAsArray;
141151
$this->task = $task;
152+
$this->testCase = $testCase;
142153
}
143154

144155
public function run(): void
145156
{
146157
rescue(function (): void {
147158
app(TaskHandler::class)->handle($this->payload);
148159
});
149-
150-
$this->payload = $this->incrementAttempts($this->payload);
151160
}
152161

153162
public function runWithoutExceptionHandler(): void
154163
{
155164
app(TaskHandler::class)->handle($this->payload);
156-
157-
$this->payload = $this->incrementAttempts($this->payload);
158165
}
159166

160-
private function incrementAttempts(string $payload): string
167+
public function runAndGetReleasedJob(): self
161168
{
162-
$decoded = \Safe\json_decode($payload, true);
169+
rescue(function (): void {
170+
app(TaskHandler::class)->handle($this->payload);
171+
});
172+
173+
return new self(
174+
$this->testCase->releasedJobPayload,
175+
$this->task,
176+
$this->testCase
177+
);
178+
}
163179

164-
$decoded['internal']['attempts'] ??= 0;
165-
$decoded['internal']['attempts']++;
180+
public function payloadAsArray(string $key = '')
181+
{
182+
$decoded = json_decode($this->payload, true);
166183

167-
return json_encode($decoded);
184+
return data_get($decoded, $key ?: null);
168185
}
169186
};
170187
}
171188

172-
public function runFromPayload(array $payload): void
189+
public function runFromPayload(string $payload): void
173190
{
174191
rescue(function () use ($payload) {
175192
app(TaskHandler::class)->handle($payload);
176193
});
177194
}
178195

179-
public function dispatchAndRun($job): void
180-
{
181-
$this->runFromPayload($this->dispatch($job));
182-
}
183-
184196
public function assertTaskDeleted(string $taskId): void
185197
{
186198
try {

0 commit comments

Comments
 (0)