queue)->toBe('search') ->and((new IndexUserJob(22))->queue)->toBe('search'); Queue::fake(); IndexUserJob::dispatch(22); Queue::assertPushedOn('search', IndexUserJob::class); }); it('collapses duplicate index jobs with unique ids', function () { $artwork = new IndexArtworkJob(15); $user = new IndexUserJob(22); expect($artwork)->toBeInstanceOf(ShouldBeUniqueUntilProcessing::class) ->and($artwork->uniqueId())->toBe('15') ->and($artwork->uniqueFor)->toBe(120) ->and($user)->toBeInstanceOf(ShouldBeUniqueUntilProcessing::class) ->and($user->uniqueId())->toBe('22') ->and($user->uniqueFor)->toBe(60); }); it('uniques ranking scope rebuilds so hourly fan-out cannot stack the same scope', function () { $job = new RankBuildScopeListsJob('category', 44); expect($job)->toBeInstanceOf(ShouldBeUnique::class) ->and($job->uniqueId())->toBe('category:44') ->and($job->uniqueFor)->toBe(21600) ->and($job->uniqueFor)->toBeGreaterThan(360) ->and((new RankBuildScopeListsJob('content_type', 3))->uniqueId())->toBe('content_type:3') ->and((new RankBuildScopeListsJob('category', 44))->uniqueId()) ->not->toBe((new RankBuildScopeListsJob('category', 45))->uniqueId()); }); it('does not enqueue the same ranking scope again after 360s while the unique lock remains', function () { Queue::fake(); $this->freezeTime(); RankBuildScopeListsJob::dispatch('category', 88); $this->travel(361)->seconds(); RankBuildScopeListsJob::dispatch('category', 88); RankBuildScopeListsJob::dispatch('category', 89); Queue::assertPushed(RankBuildScopeListsJob::class, 2); Queue::assertPushed(RankBuildScopeListsJob::class, fn (RankBuildScopeListsJob $job): bool => $job->uniqueId() === 'category:88'); Queue::assertPushed(RankBuildScopeListsJob::class, fn (RankBuildScopeListsJob $job): bool => $job->uniqueId() === 'category:89'); });