diff --git a/src/app/Jobs/CommonJob.php b/src/app/Jobs/CommonJob.php index fa9d0831..ce931614 100644 --- a/src/app/Jobs/CommonJob.php +++ b/src/app/Jobs/CommonJob.php @@ -1,140 +1,144 @@ handle(); * ``` */ abstract class CommonJob implements ShouldQueue { use Dispatchable; use InteractsWithQueue; use Queueable; /** * The failure message. * * @var string */ public $failureMessage; /** * The job deleted state. * * @var bool */ protected $isDeleted = false; /** * The job released state. * * @var bool */ protected $isReleased = false; /** * The number of tries for this Job. * * @var int */ public $tries = 5; /** * Execute the job. * * @return void */ abstract public function handle(); /** * Delete the job from the queue. * * @return void */ public function delete() { // We need this for testing purposes $this->isDeleted = true; // @phpstan-ignore-next-line if ($this->job) { $this->job->delete(); } } /** * Delete the job, call the "failed" method, and raise the failed job event. * * @param \Throwable|null $e An Exception * * @return void */ public function fail($e = null) { // Save the message, for testing purposes $this->failureMessage = $e->getMessage(); // @phpstan-ignore-next-line if ($this->job) { $this->job->fail($e); } } /** * Check if the job has failed * * @return bool */ public function hasFailed(): bool { return $this->failureMessage !== null; } /** * Release the job back into the queue. * * @param int $delay Time in seconds * @return void */ public function release($delay = 0) { // We need this for testing purposes $this->isReleased = true; // @phpstan-ignore-next-line if ($this->job) { $this->job->release($delay); + } else { + // $this->job is only set when the job is dispatched, not if manually executed by calling handle(). + // When manually executed, release() does nothing, and we thus throw an exception. + throw new \Exception("Attempted to release a manually executed job"); } } /** * Determine if the job has been deleted. * * @return bool */ public function isDeleted(): bool { return $this->isDeleted; } /** * Check if the job was released * * @return bool */ public function isReleased(): bool { return $this->isReleased; } } diff --git a/src/tests/Feature/Jobs/Domain/CreateTest.php b/src/tests/Feature/Jobs/Domain/CreateTest.php index 0e5989d8..da341552 100644 --- a/src/tests/Feature/Jobs/Domain/CreateTest.php +++ b/src/tests/Feature/Jobs/Domain/CreateTest.php @@ -1,74 +1,75 @@ deleteTestDomain('domain-create-test.com'); } public function tearDown(): void { $this->deleteTestDomain('domain-create-test.com'); parent::tearDown(); } /** * Test job handle * * @group ldap */ public function testHandle(): void { $domain = $this->getTestDomain( 'domain-create-test.com', [ 'status' => Domain::STATUS_NEW, 'type' => Domain::TYPE_EXTERNAL, ] ); $this->assertFalse($domain->isLdapReady()); // Fake the queue, assert that no jobs were pushed... Queue::fake(); Queue::assertNothingPushed(); $job = new \App\Jobs\Domain\CreateJob($domain->id); $job->handle(); $this->assertTrue($domain->fresh()->isLdapReady()); Queue::assertPushed(\App\Jobs\Domain\VerifyJob::class, 1); Queue::assertPushed( \App\Jobs\Domain\VerifyJob::class, function ($job) use ($domain) { $domainId = TestCase::getObjectProperty($job, 'domainId'); $domainNamespace = TestCase::getObjectProperty($job, 'domainNamespace'); return $domainId === $domain->id && $domainNamespace === $domain->namespace; } ); // Test job releasing on unknown identifier + $this->expectException(\Exception::class); $job = new \App\Jobs\Domain\CreateJob(123); $job->handle(); $this->assertTrue($job->isReleased()); $this->assertFalse($job->hasFailed()); } } diff --git a/src/tests/Feature/Jobs/Group/CreateTest.php b/src/tests/Feature/Jobs/Group/CreateTest.php index 99b87a3b..8b8e3f75 100644 --- a/src/tests/Feature/Jobs/Group/CreateTest.php +++ b/src/tests/Feature/Jobs/Group/CreateTest.php @@ -1,50 +1,51 @@ deleteTestGroup('group@kolab.org'); } public function tearDown(): void { $this->deleteTestGroup('group@kolab.org'); parent::tearDown(); } /** * Test job handle * * @group ldap */ public function testHandle(): void { $group = $this->getTestGroup('group@kolab.org', ['members' => []]); $this->assertFalse($group->isLdapReady()); $job = new \App\Jobs\Group\CreateJob($group->id); $job->handle(); $this->assertTrue($group->fresh()->isLdapReady()); // Test non-existing group ID + $this->expectException(\Exception::class); $job = new \App\Jobs\Group\CreateJob(123); $job->handle(); $this->assertTrue($job->isReleased()); $this->assertFalse($job->hasFailed()); } } diff --git a/src/tests/Feature/Jobs/Resource/CreateTest.php b/src/tests/Feature/Jobs/Resource/CreateTest.php index 496b8228..b7ebfe9e 100644 --- a/src/tests/Feature/Jobs/Resource/CreateTest.php +++ b/src/tests/Feature/Jobs/Resource/CreateTest.php @@ -1,83 +1,84 @@ deleteTestResource('resource-test@' . \config('app.domain')); } public function tearDown(): void { $this->deleteTestResource('resource-test@' . \config('app.domain')); parent::tearDown(); } /** * Test job handle * * @group ldap */ public function testHandle(): void { Queue::fake(); // Test unknown resource + $this->expectException(\Exception::class); $job = new \App\Jobs\Resource\CreateJob(123); $job->handle(); $this->assertTrue($job->isReleased()); $this->assertFalse($job->hasFailed()); $resource = $this->getTestResource('resource-test@' . \config('app.domain')); $this->assertFalse($resource->isLdapReady()); // Test resource creation $job = new \App\Jobs\Resource\CreateJob($resource->id); $job->handle(); $this->assertTrue($resource->fresh()->isLdapReady()); $this->assertFalse($job->hasFailed()); // Test job failures $job = new \App\Jobs\Resource\CreateJob($resource->id); $job->handle(); $this->assertTrue($job->hasFailed()); $this->assertSame("Resource {$resource->id} is already marked as ldap-ready.", $job->failureMessage); $resource->status |= Resource::STATUS_DELETED; $resource->save(); $job = new \App\Jobs\Resource\CreateJob($resource->id); $job->handle(); $this->assertTrue($job->hasFailed()); $this->assertSame("Resource {$resource->id} is marked as deleted.", $job->failureMessage); $resource->status ^= Resource::STATUS_DELETED; $resource->save(); $resource->delete(); $job = new \App\Jobs\Resource\CreateJob($resource->id); $job->handle(); $this->assertTrue($job->hasFailed()); $this->assertSame("Resource {$resource->id} is actually deleted.", $job->failureMessage); // TODO: Test failures on domain sanity checks } } diff --git a/src/tests/Feature/Jobs/SharedFolder/CreateTest.php b/src/tests/Feature/Jobs/SharedFolder/CreateTest.php index f9aca4b5..389f5ab4 100644 --- a/src/tests/Feature/Jobs/SharedFolder/CreateTest.php +++ b/src/tests/Feature/Jobs/SharedFolder/CreateTest.php @@ -1,83 +1,84 @@ deleteTestSharedFolder('folder-test@' . \config('app.domain')); } public function tearDown(): void { $this->deleteTestSharedFolder('folder-test@' . \config('app.domain')); parent::tearDown(); } /** * Test job handle * * @group ldap */ public function testHandle(): void { Queue::fake(); // Test unknown folder + $this->expectException(\Exception::class); $job = new \App\Jobs\SharedFolder\CreateJob(123); $job->handle(); $this->assertTrue($job->isReleased()); $this->assertFalse($job->hasFailed()); $folder = $this->getTestSharedFolder('folder-test@' . \config('app.domain')); $this->assertFalse($folder->isLdapReady()); // Test shared folder creation $job = new \App\Jobs\SharedFolder\CreateJob($folder->id); $job->handle(); $this->assertTrue($folder->fresh()->isLdapReady()); $this->assertFalse($job->hasFailed()); // Test job failures $job = new \App\Jobs\SharedFolder\CreateJob($folder->id); $job->handle(); $this->assertTrue($job->hasFailed()); $this->assertSame("Shared folder {$folder->id} is already marked as ldap-ready.", $job->failureMessage); $folder->status |= SharedFolder::STATUS_DELETED; $folder->save(); $job = new \App\Jobs\SharedFolder\CreateJob($folder->id); $job->handle(); $this->assertTrue($job->hasFailed()); $this->assertSame("Shared folder {$folder->id} is marked as deleted.", $job->failureMessage); $folder->status ^= SharedFolder::STATUS_DELETED; $folder->save(); $folder->delete(); $job = new \App\Jobs\SharedFolder\CreateJob($folder->id); $job->handle(); $this->assertTrue($job->hasFailed()); $this->assertSame("Shared folder {$folder->id} is actually deleted.", $job->failureMessage); // TODO: Test failures on domain sanity checks } } diff --git a/src/tests/Feature/Jobs/User/CreateTest.php b/src/tests/Feature/Jobs/User/CreateTest.php index fcc696e7..5dc8412c 100644 --- a/src/tests/Feature/Jobs/User/CreateTest.php +++ b/src/tests/Feature/Jobs/User/CreateTest.php @@ -1,78 +1,79 @@ deleteTestUser('new-job-user@' . \config('app.domain')); } public function tearDown(): void { $this->deleteTestUser('new-job-user@' . \config('app.domain')); parent::tearDown(); } /** * Test job handle * * @group ldap */ public function testHandle(): void { $user = $this->getTestUser('new-job-user@' . \config('app.domain')); $this->assertFalse($user->isLdapReady()); $job = new \App\Jobs\User\CreateJob($user->id); $job->handle(); $this->assertTrue($user->fresh()->isLdapReady()); $this->assertFalse($job->hasFailed()); // Test job failures $job = new \App\Jobs\User\CreateJob($user->id); $job->handle(); $this->assertTrue($job->hasFailed()); $this->assertSame("User {$user->id} is already marked as ldap-ready.", $job->failureMessage); $user->status |= User::STATUS_DELETED; $user->save(); $job = new \App\Jobs\User\CreateJob($user->id); $job->handle(); $this->assertTrue($job->hasFailed()); $this->assertSame("User {$user->id} is marked as deleted.", $job->failureMessage); $user->status ^= User::STATUS_DELETED; $user->save(); $user->delete(); $job = new \App\Jobs\User\CreateJob($user->id); $job->handle(); $this->assertTrue($job->hasFailed()); $this->assertSame("User {$user->id} is actually deleted.", $job->failureMessage); // TODO: Test failures on domain sanity checks + $this->expectException(\Exception::class); $job = new \App\Jobs\User\CreateJob(123); $job->handle(); $this->assertTrue($job->isReleased()); $this->assertFalse($job->hasFailed()); } }