From 8af71f7231087ccf3c9cbd90eb81953ac1302189 Mon Sep 17 00:00:00 2001 From: Marick van Tuil Date: Wed, 1 Jul 2026 18:46:58 +0200 Subject: [PATCH 1/2] Fix flaky pause/resume queue tests The pause/resume tests polled getQueue() after mutating the queue, but those reads are eventually consistent and could still report the old state well beyond the backoff window, causing random failures. Return the Queue from CloudTasksApi::pause()/resume() and assert on the RPC response, which carries the new state synchronously. This drops the polling helpers and makes the tests deterministic and independent of each other. Also remove the now-stale PHPStan ignore for the QueuePaused/ QueueResumed events, which exist in the supported Laravel versions. --- phpstan.neon | 4 +-- src/CloudTasksApi.php | 5 ++-- src/CloudTasksApiConcrete.php | 9 +++--- src/CloudTasksApiContract.php | 5 ++-- src/CloudTasksApiFake.php | 10 +++++-- tests/CloudTasksApiTest.php | 55 +++-------------------------------- 6 files changed, 24 insertions(+), 64 deletions(-) diff --git a/phpstan.neon b/phpstan.neon index 1211568..579f511 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -6,6 +6,4 @@ parameters: - src level: 9 ignoreErrors: - - "/dispatchAfterCommit with no type specified/" - - message: "#Illuminate\\\\Queue\\\\Events\\\\Queue(Paused|Resumed)#" - identifier: class.notFound \ No newline at end of file + - "/dispatchAfterCommit with no type specified/" \ No newline at end of file diff --git a/src/CloudTasksApi.php b/src/CloudTasksApi.php index d25d666..28a3cd4 100644 --- a/src/CloudTasksApi.php +++ b/src/CloudTasksApi.php @@ -5,6 +5,7 @@ namespace Stackkit\LaravelGoogleCloudTasksQueue; use Google\Cloud\Tasks\V2\Task; +use Google\Cloud\Tasks\V2\Queue; use Illuminate\Support\Facades\Facade; /** @@ -12,8 +13,8 @@ * @method static void deleteTask(string $taskName) * @method static Task getTask(string $taskName) * @method static bool exists(string $taskName) - * @method static void pause(string $queue) - * @method static void resume(string $queue) + * @method static Queue pause(string $queue) + * @method static Queue resume(string $queue) */ class CloudTasksApi extends Facade { diff --git a/src/CloudTasksApiConcrete.php b/src/CloudTasksApiConcrete.php index 639f71b..7a997f7 100644 --- a/src/CloudTasksApiConcrete.php +++ b/src/CloudTasksApiConcrete.php @@ -6,6 +6,7 @@ use Google\Cloud\Tasks\V2\Task; use Google\ApiCore\ApiException; +use Google\Cloud\Tasks\V2\Queue; use Google\Cloud\Tasks\V2\GetTaskRequest; use Google\Cloud\Tasks\V2\CreateTaskRequest; use Google\Cloud\Tasks\V2\DeleteTaskRequest; @@ -68,13 +69,13 @@ public function exists(string $taskName): bool return false; } - public function pause(string $queue): void + public function pause(string $queue): Queue { - $this->client->pauseQueue(PauseQueueRequest::build($queue)); + return $this->client->pauseQueue(PauseQueueRequest::build($queue)); } - public function resume(string $queue): void + public function resume(string $queue): Queue { - $this->client->resumeQueue(ResumeQueueRequest::build($queue)); + return $this->client->resumeQueue(ResumeQueueRequest::build($queue)); } } diff --git a/src/CloudTasksApiContract.php b/src/CloudTasksApiContract.php index fdaa562..32823a2 100644 --- a/src/CloudTasksApiContract.php +++ b/src/CloudTasksApiContract.php @@ -5,6 +5,7 @@ namespace Stackkit\LaravelGoogleCloudTasksQueue; use Google\Cloud\Tasks\V2\Task; +use Google\Cloud\Tasks\V2\Queue; interface CloudTasksApiContract { @@ -16,7 +17,7 @@ public function getTask(string $taskName): Task; public function exists(string $taskName): bool; - public function pause(string $queue): void; + public function pause(string $queue): Queue; - public function resume(string $queue): void; + public function resume(string $queue): Queue; } diff --git a/src/CloudTasksApiFake.php b/src/CloudTasksApiFake.php index 1e99969..d44b0d6 100644 --- a/src/CloudTasksApiFake.php +++ b/src/CloudTasksApiFake.php @@ -7,6 +7,8 @@ use Closure; use PHPUnit\Framework\Assert; use Google\Cloud\Tasks\V2\Task; +use Google\Cloud\Tasks\V2\Queue; +use Google\Cloud\Tasks\V2\Queue\State; class CloudTasksApiFake implements CloudTasksApiContract { @@ -56,14 +58,18 @@ public function exists(string $taskName): bool return false; } - public function pause(string $queue): void + public function pause(string $queue): Queue { $this->pausedQueues[$queue] = true; + + return (new Queue)->setName($queue)->setState(State::PAUSED); } - public function resume(string $queue): void + public function resume(string $queue): Queue { unset($this->pausedQueues[$queue]); + + return (new Queue)->setName($queue)->setState(State::RUNNING); } public function assertTaskDeleted(string $taskName): void diff --git a/tests/CloudTasksApiTest.php b/tests/CloudTasksApiTest.php index 65b9d35..922287d 100644 --- a/tests/CloudTasksApiTest.php +++ b/tests/CloudTasksApiTest.php @@ -11,7 +11,6 @@ use Google\Cloud\Tasks\V2\HttpRequest; use Google\Cloud\Tasks\V2\Queue\State; use PHPUnit\Framework\Attributes\Test; -use Google\Cloud\Tasks\V2\GetQueueRequest; use Google\Cloud\Tasks\V2\Client\CloudTasksClient; use Stackkit\LaravelGoogleCloudTasksQueue\CloudTasksApi; @@ -150,13 +149,11 @@ public function it_can_pause_queues(): void env('CI_CLOUD_TASKS_QUEUE').'-pause' ); - $this->ensureQueueIs($queueName, State::RUNNING); - // Act - CloudTasksApi::pause($queueName); + $queue = CloudTasksApi::pause($queueName); // Assert - $this->assertEquals(State::PAUSED, $this->waitForQueueState($queueName, State::PAUSED)); + $this->assertEquals(State::PAUSED, $queue->getState()); } #[Test] @@ -168,54 +165,10 @@ public function it_can_resume_queues(): void env('CI_CLOUD_TASKS_QUEUE').'-pause' ); - $this->ensureQueueIs($queueName, State::PAUSED); - // Act - CloudTasksApi::resume($queueName); + $queue = CloudTasksApi::resume($queueName); // Assert - $this->assertEquals(State::RUNNING, $this->waitForQueueState($queueName, State::RUNNING)); - } - - private function getQueueState(string $queue): int - { - return $this->client->getQueue(GetQueueRequest::build($queue))->getState(); - } - - private function waitForQueueState(string $queue, int $waitForState): ?int - { - $state = null; - $backoff = [1, 5, 10, 30, 60]; - - foreach ($backoff as $delay) { - $state = $this->getQueueState($queue); - - if ($state === $waitForState) { - return $state; - } - - sleep($delay); - } - - return $this->getQueueState($queue); - } - - private function ensureQueueIs(string $queue, int $desiredState): void - { - $currentState = $this->getQueueState($queue); - - if ($currentState === $desiredState) { - return; - } - - if ($currentState === State::RUNNING && $desiredState === State::PAUSED) { - CloudTasksApi::pause($queue); - } - - if ($currentState === State::PAUSED && $desiredState === State::RUNNING) { - CloudTasksApi::resume($queue); - } - - $this->assertEquals($desiredState, $this->waitForQueueState($queue, $desiredState)); + $this->assertEquals(State::RUNNING, $queue->getState()); } } From e4cb5dc3e0c1a3476ba3e1f89d13b6c8856c7223 Mon Sep 17 00:00:00 2001 From: Marick van Tuil Date: Wed, 1 Jul 2026 18:50:02 +0200 Subject: [PATCH 2/2] Keep QueuePaused/QueueResumed ignore, silence when unmatched These events only exist in newer Laravel versions, and the code guards them with class_exists(). The ignore is required on versions where the classes are absent, but is unmatched where they exist, so set reportUnmatchedIgnoredErrors: false instead of removing it. --- phpstan.neon | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/phpstan.neon b/phpstan.neon index 579f511..ce2a888 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -5,5 +5,11 @@ parameters: paths: - src level: 9 + # QueuePaused/QueueResumed only exist in newer Laravel versions; the code + # guards them with class_exists(), so the ignore is unmatched on versions + # where they do exist. Don't fail when that happens. + reportUnmatchedIgnoredErrors: false ignoreErrors: - - "/dispatchAfterCommit with no type specified/" \ No newline at end of file + - "/dispatchAfterCommit with no type specified/" + - message: "#Illuminate\\\\Queue\\\\Events\\\\Queue(Paused|Resumed)#" + identifier: class.notFound \ No newline at end of file