UniqueJobTest.php 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. <?php
  2. namespace Illuminate\Tests\Integration\Queue;
  3. use Exception;
  4. use Illuminate\Bus\Queueable;
  5. use Illuminate\Contracts\Cache\Repository as Cache;
  6. use Illuminate\Contracts\Queue\ShouldBeUnique;
  7. use Illuminate\Contracts\Queue\ShouldBeUniqueUntilProcessing;
  8. use Illuminate\Contracts\Queue\ShouldQueue;
  9. use Illuminate\Database\Schema\Blueprint;
  10. use Illuminate\Foundation\Bus\Dispatchable;
  11. use Illuminate\Queue\InteractsWithQueue;
  12. use Illuminate\Support\Facades\Bus;
  13. use Orchestra\Testbench\TestCase;
  14. class UniqueJobTest extends TestCase
  15. {
  16. protected function getEnvironmentSetUp($app)
  17. {
  18. $app['db']->connection()->getSchemaBuilder()->create('jobs', function (Blueprint $table) {
  19. $table->bigIncrements('id');
  20. $table->string('queue');
  21. $table->longText('payload');
  22. $table->tinyInteger('attempts')->unsigned();
  23. $table->unsignedInteger('reserved_at')->nullable();
  24. $table->unsignedInteger('available_at');
  25. $table->unsignedInteger('created_at');
  26. $table->index(['queue', 'reserved_at']);
  27. });
  28. }
  29. protected function tearDown(): void
  30. {
  31. $this->app['db']->connection()->getSchemaBuilder()->drop('jobs');
  32. parent::tearDown();
  33. }
  34. public function testUniqueJobsAreNotDispatched()
  35. {
  36. Bus::fake();
  37. UniqueTestJob::dispatch();
  38. Bus::assertDispatched(UniqueTestJob::class);
  39. $this->assertFalse(
  40. $this->app->get(Cache::class)->lock($this->getLockKey(UniqueTestJob::class), 10)->get()
  41. );
  42. Bus::fake();
  43. UniqueTestJob::dispatch();
  44. Bus::assertNotDispatched(UniqueTestJob::class);
  45. $this->assertFalse(
  46. $this->app->get(Cache::class)->lock($this->getLockKey(UniqueTestJob::class), 10)->get()
  47. );
  48. }
  49. public function testLockIsReleasedForSuccessfulJobs()
  50. {
  51. UniqueTestJob::$handled = false;
  52. dispatch($job = new UniqueTestJob);
  53. $this->assertTrue($job::$handled);
  54. $this->assertTrue($this->app->get(Cache::class)->lock($this->getLockKey($job), 10)->get());
  55. }
  56. public function testLockIsReleasedForFailedJobs()
  57. {
  58. UniqueTestFailJob::$handled = false;
  59. $this->expectException(Exception::class);
  60. try {
  61. dispatch($job = new UniqueTestFailJob);
  62. } finally {
  63. $this->assertTrue($job::$handled);
  64. $this->assertTrue($this->app->get(Cache::class)->lock($this->getLockKey($job), 10)->get());
  65. }
  66. }
  67. public function testLockIsNotReleasedForJobRetries()
  68. {
  69. UniqueTestRetryJob::$handled = false;
  70. dispatch($job = new UniqueTestRetryJob);
  71. $this->assertFalse($this->app->get(Cache::class)->lock($this->getLockKey($job), 10)->get());
  72. $this->artisan('queue:work', [
  73. 'connection' => 'database',
  74. '--once' => true,
  75. ]);
  76. $this->assertTrue($job::$handled);
  77. $this->assertFalse($this->app->get(Cache::class)->lock($this->getLockKey($job), 10)->get());
  78. UniqueTestRetryJob::$handled = false;
  79. $this->artisan('queue:work', [
  80. 'connection' => 'database',
  81. '--once' => true,
  82. ]);
  83. $this->assertTrue($job::$handled);
  84. $this->assertTrue($this->app->get(Cache::class)->lock($this->getLockKey($job), 10)->get());
  85. }
  86. public function testLockIsNotReleasedForJobReleases()
  87. {
  88. UniqueTestReleasedJob::$handled = false;
  89. dispatch($job = new UniqueTestReleasedJob);
  90. $this->assertFalse($this->app->get(Cache::class)->lock($this->getLockKey($job), 10)->get());
  91. $this->artisan('queue:work', [
  92. 'connection' => 'database',
  93. '--once' => true,
  94. ]);
  95. $this->assertTrue($job::$handled);
  96. $this->assertFalse($this->app->get(Cache::class)->lock($this->getLockKey($job), 10)->get());
  97. UniqueTestReleasedJob::$handled = false;
  98. $this->artisan('queue:work', [
  99. 'connection' => 'database',
  100. '--once' => true,
  101. ]);
  102. $this->assertFalse($job::$handled);
  103. $this->assertTrue($this->app->get(Cache::class)->lock($this->getLockKey($job), 10)->get());
  104. }
  105. public function testLockCanBeReleasedBeforeProcessing()
  106. {
  107. UniqueUntilStartTestJob::$handled = false;
  108. dispatch($job = new UniqueUntilStartTestJob);
  109. $this->assertFalse($this->app->get(Cache::class)->lock($this->getLockKey($job), 10)->get());
  110. $this->artisan('queue:work', [
  111. 'connection' => 'database',
  112. '--once' => true,
  113. ]);
  114. $this->assertTrue($job::$handled);
  115. $this->assertTrue($this->app->get(Cache::class)->lock($this->getLockKey($job), 10)->get());
  116. }
  117. protected function getLockKey($job)
  118. {
  119. return 'laravel_unique_job:'.(is_string($job) ? $job : get_class($job));
  120. }
  121. }
  122. class UniqueTestJob implements ShouldQueue, ShouldBeUnique
  123. {
  124. use InteractsWithQueue, Queueable, Dispatchable;
  125. public static $handled = false;
  126. public function handle()
  127. {
  128. static::$handled = true;
  129. }
  130. }
  131. class UniqueTestFailJob implements ShouldQueue, ShouldBeUnique
  132. {
  133. use InteractsWithQueue, Queueable, Dispatchable;
  134. public $tries = 1;
  135. public static $handled = false;
  136. public function handle()
  137. {
  138. static::$handled = true;
  139. throw new Exception;
  140. }
  141. }
  142. class UniqueTestReleasedJob extends UniqueTestFailJob
  143. {
  144. public $tries = 1;
  145. public $connection = 'database';
  146. public function handle()
  147. {
  148. static::$handled = true;
  149. $this->release();
  150. }
  151. }
  152. class UniqueTestRetryJob extends UniqueTestFailJob
  153. {
  154. public $tries = 2;
  155. public $connection = 'database';
  156. }
  157. class UniqueUntilStartTestJob extends UniqueTestJob implements ShouldBeUniqueUntilProcessing
  158. {
  159. public $tries = 2;
  160. public $connection = 'database';
  161. }