From 28950b54db1fd68a1414bddff012bbc756c9a1f7 Mon Sep 17 00:00:00 2001 From: Richard McDaniel Date: Sat, 22 Mar 2025 22:12:43 +0000 Subject: [PATCH 01/12] Improve test coverage --- tests/bootstrap.php | 1 - tests/classAliases.php | 7 ------- 2 files changed, 8 deletions(-) delete mode 100644 tests/classAliases.php diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 00d2f112..25b78bcc 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -3,7 +3,6 @@ declare(strict_types=1); require_once __DIR__ . '/../vendor/autoload.php'; -require_once __DIR__ . '/classAliases.php'; use PHPUnit\Event\Facade; use Tests\TestSuiteSubscriber; diff --git a/tests/classAliases.php b/tests/classAliases.php deleted file mode 100644 index aa4ea679..00000000 --- a/tests/classAliases.php +++ /dev/null @@ -1,7 +0,0 @@ - Date: Sat, 22 Mar 2025 22:18:18 +0000 Subject: [PATCH 02/12] Improve test coverage --- tests/classAliases.php | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 tests/classAliases.php diff --git a/tests/classAliases.php b/tests/classAliases.php new file mode 100644 index 00000000..aa4ea679 --- /dev/null +++ b/tests/classAliases.php @@ -0,0 +1,7 @@ + Date: Sat, 22 Mar 2025 23:01:27 +0000 Subject: [PATCH 03/12] Improve test coverage --- .../WithoutOverlappingMiddlewareTest.php | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/tests/Unit/Middleware/WithoutOverlappingMiddlewareTest.php b/tests/Unit/Middleware/WithoutOverlappingMiddlewareTest.php index c0502a2b..7fe65714 100644 --- a/tests/Unit/Middleware/WithoutOverlappingMiddlewareTest.php +++ b/tests/Unit/Middleware/WithoutOverlappingMiddlewareTest.php @@ -4,6 +4,8 @@ namespace Tests\Unit\Middleware; +use Illuminate\Contracts\Cache\Lock; +use Illuminate\Contracts\Cache\Repository; use Illuminate\Support\Facades\Cache; use Mockery\MockInterface; use Tests\Fixtures\TestActivity; @@ -111,4 +113,60 @@ public function testAllowsMultipleActivityInstances(): void $this->assertNull(Cache::get($middleware1->getWorkflowSemaphoreKey())); $this->assertSame(0, count(Cache::get($middleware1->getActivitySemaphoreKey()))); } + + public function testUnknownTypeDoesNotCallNext(): void + { + $this->app->make('cache') + ->store() + ->clear(); + + $job = $this->mock(TestActivity::class, static function (MockInterface $mock) { + $mock->shouldReceive('release') + ->once(); + }); + + $middleware = new WithoutOverlappingMiddleware(1, 999); + + $middleware->handle($job, function ($job) { + $this->fail('Should not call next when type is unknown'); + }); + + $this->assertNull(Cache::get($middleware->getWorkflowSemaphoreKey())); + $this->assertNull(Cache::get($middleware->getActivitySemaphoreKey())); + } + + public function testReleaseWhenCompareAndSetFails(): void + { + $this->app->make('cache') + ->store() + ->clear(); + + $job = $this->mock(TestWorkflow::class, static function (MockInterface $mock) { + $mock->shouldReceive('release') + ->once(); + }); + + $lock = $this->mock(Lock::class, static function (MockInterface $mock) { + $mock->shouldReceive('get') + ->once() + ->andReturn(false); + }); + + $cache = $this->mock(Repository::class, static function (MockInterface $mock) use ($lock) { + $mock->shouldReceive('lock') + ->once() + ->andReturn($lock); + $mock->shouldReceive('get') + ->andReturn([]); + }); + + $middleware = new WithoutOverlappingMiddleware(1, WithoutOverlappingMiddleware::WORKFLOW); + + $middleware->handle($job, function ($job) { + $this->fail('Should not call next when lock is not acquired'); + }); + + $this->assertNull(Cache::get($middleware->getWorkflowSemaphoreKey())); + $this->assertNull(Cache::get($middleware->getActivitySemaphoreKey())); + } } From cba0e3f12ae9baf6b69c7ce008438bacc24c4816 Mon Sep 17 00:00:00 2001 From: Richard McDaniel Date: Sat, 22 Mar 2025 23:11:04 +0000 Subject: [PATCH 04/12] Improve test coverage --- tests/Unit/Serializers/SerializeTest.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/Unit/Serializers/SerializeTest.php b/tests/Unit/Serializers/SerializeTest.php index 3f35e541..e12786a6 100644 --- a/tests/Unit/Serializers/SerializeTest.php +++ b/tests/Unit/Serializers/SerializeTest.php @@ -87,4 +87,11 @@ private function testSerializeUnserialize($data, $serializer, $unserializer): vo $this->assertSame($data, $unserialized); } } + + public function testSerializableReturnsFalseForClosure(): void + { + $this->assertFalse(Serializer::serializable(function () { + return 'test'; + })); + } } From 61152f20da2d5c54ca6d573dd6eb19ce1dabfe35 Mon Sep 17 00:00:00 2001 From: Richard McDaniel Date: Sat, 22 Mar 2025 23:22:43 +0000 Subject: [PATCH 05/12] Improve test coverage --- tests/Unit/Serializers/SerializeTest.php | 14 ++--- .../Traits/MonitorQueueConnectionTest.php | 54 +++++++++++++++++++ 2 files changed, 61 insertions(+), 7 deletions(-) create mode 100644 tests/Unit/Traits/MonitorQueueConnectionTest.php diff --git a/tests/Unit/Serializers/SerializeTest.php b/tests/Unit/Serializers/SerializeTest.php index e12786a6..399e0386 100644 --- a/tests/Unit/Serializers/SerializeTest.php +++ b/tests/Unit/Serializers/SerializeTest.php @@ -58,6 +58,13 @@ public static function dataProvider(): array ]; } + public function testSerializableReturnsFalseForClosure(): void + { + $this->assertFalse(Serializer::serializable(static function () { + return 'test'; + })); + } + private function testSerializeUnserialize($data, $serializer, $unserializer): void { config([ @@ -87,11 +94,4 @@ private function testSerializeUnserialize($data, $serializer, $unserializer): vo $this->assertSame($data, $unserialized); } } - - public function testSerializableReturnsFalseForClosure(): void - { - $this->assertFalse(Serializer::serializable(function () { - return 'test'; - })); - } } diff --git a/tests/Unit/Traits/MonitorQueueConnectionTest.php b/tests/Unit/Traits/MonitorQueueConnectionTest.php new file mode 100644 index 00000000..e3b915e4 --- /dev/null +++ b/tests/Unit/Traits/MonitorQueueConnectionTest.php @@ -0,0 +1,54 @@ +makeAnonymousTraitInstance(); + + $this->assertSame(config('queue.default'), $instance->viaConnection()); + } + + public function testReturnsDefaultQueue(): void + { + $instance = $this->makeAnonymousTraitInstance(); + + $this->assertSame('default', $instance->viaQueue()); + } + + public function testReturnsCustomConnection(): void + { + config([ + 'workflows.monitor_connection' => 'custom_connection', + ]); + + $instance = $this->makeAnonymousTraitInstance(); + + $this->assertSame('custom_connection', $instance->viaConnection()); + } + + public function testReturnsCustomQueue(): void + { + config([ + 'workflows.monitor_queue' => 'custom_queue', + ]); + + $instance = $this->makeAnonymousTraitInstance(); + + $this->assertSame('custom_queue', $instance->viaQueue()); + } + + private function makeAnonymousTraitInstance(): object + { + return new class() { + use MonitorQueueConnection; + }; + } +} From 3783ee13bd321c7baedac613b959dca379cfba56 Mon Sep 17 00:00:00 2001 From: Richard McDaniel Date: Sat, 22 Mar 2025 23:35:38 +0000 Subject: [PATCH 06/12] Improve test coverage --- .../Traits/MonitorQueueConnectionTest.php | 10 ++++++ tests/Unit/migrations/MigrationsTest.php | 33 +++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 tests/Unit/migrations/MigrationsTest.php diff --git a/tests/Unit/Traits/MonitorQueueConnectionTest.php b/tests/Unit/Traits/MonitorQueueConnectionTest.php index e3b915e4..db525578 100644 --- a/tests/Unit/Traits/MonitorQueueConnectionTest.php +++ b/tests/Unit/Traits/MonitorQueueConnectionTest.php @@ -11,6 +11,10 @@ class MonitorQueueConnectionTest extends TestCase { public function testReturnsDefaultConnection(): void { + config([ + 'queue.default' => 'sync', + ]); + $instance = $this->makeAnonymousTraitInstance(); $this->assertSame(config('queue.default'), $instance->viaConnection()); @@ -18,6 +22,10 @@ public function testReturnsDefaultConnection(): void public function testReturnsDefaultQueue(): void { + config([ + 'queue.default' => 'sync', + ]); + $instance = $this->makeAnonymousTraitInstance(); $this->assertSame('default', $instance->viaQueue()); @@ -26,6 +34,7 @@ public function testReturnsDefaultQueue(): void public function testReturnsCustomConnection(): void { config([ + 'queue.default' => 'sync', 'workflows.monitor_connection' => 'custom_connection', ]); @@ -37,6 +46,7 @@ public function testReturnsCustomConnection(): void public function testReturnsCustomQueue(): void { config([ + 'queue.default' => 'sync', 'workflows.monitor_queue' => 'custom_queue', ]); diff --git a/tests/Unit/migrations/MigrationsTest.php b/tests/Unit/migrations/MigrationsTest.php new file mode 100644 index 00000000..ae24cc0b --- /dev/null +++ b/tests/Unit/migrations/MigrationsTest.php @@ -0,0 +1,33 @@ +assertTrue(Schema::hasTable('workflows')); + $this->assertTrue(Schema::hasTable('workflow_logs')); + $this->assertTrue(Schema::hasTable('workflow_signals')); + $this->assertTrue(Schema::hasTable('workflow_timers')); + $this->assertTrue(Schema::hasTable('workflow_exceptions')); + $this->assertTrue(Schema::hasTable('workflow_relationships')); + + $this->artisan('migrate:reset', [ + '--path' => dirname(__DIR__, 3) . '/src/migrations', + '--realpath' => true, + ])->run(); + + $this->assertFalse(Schema::hasTable('workflows')); + $this->assertFalse(Schema::hasTable('workflow_logs')); + $this->assertFalse(Schema::hasTable('workflow_signals')); + $this->assertFalse(Schema::hasTable('workflow_timers')); + $this->assertFalse(Schema::hasTable('workflow_exceptions')); + $this->assertFalse(Schema::hasTable('workflow_relationships')); + } +} From 1bb49dbe5884c0f492e3a26769a5a67fd8036b7f Mon Sep 17 00:00:00 2001 From: Richard McDaniel Date: Sat, 22 Mar 2025 23:47:54 +0000 Subject: [PATCH 07/12] Improve test coverage --- tests/Unit/Traits/MonitorQueueConnectionTest.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/Unit/Traits/MonitorQueueConnectionTest.php b/tests/Unit/Traits/MonitorQueueConnectionTest.php index db525578..f3756d02 100644 --- a/tests/Unit/Traits/MonitorQueueConnectionTest.php +++ b/tests/Unit/Traits/MonitorQueueConnectionTest.php @@ -13,6 +13,7 @@ public function testReturnsDefaultConnection(): void { config([ 'queue.default' => 'sync', + 'workflows.monitor_connection' => config('queue.default'), ]); $instance = $this->makeAnonymousTraitInstance(); @@ -24,6 +25,7 @@ public function testReturnsDefaultQueue(): void { config([ 'queue.default' => 'sync', + 'workflows.monitor_connection' => config('queue.default'), ]); $instance = $this->makeAnonymousTraitInstance(); @@ -47,6 +49,7 @@ public function testReturnsCustomQueue(): void { config([ 'queue.default' => 'sync', + 'workflows.monitor_connection' => config('queue.default'), 'workflows.monitor_queue' => 'custom_queue', ]); From 9d67832c51ad3a44df1ed4d34be69ab71142f90e Mon Sep 17 00:00:00 2001 From: Richard McDaniel Date: Sun, 23 Mar 2025 00:09:20 +0000 Subject: [PATCH 08/12] Improve test coverage --- tests/Unit/ActivityTest.php | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/Unit/ActivityTest.php b/tests/Unit/ActivityTest.php index e0ffd3eb..f4a63e2e 100644 --- a/tests/Unit/ActivityTest.php +++ b/tests/Unit/ActivityTest.php @@ -6,11 +6,13 @@ use BadMethodCallException; use Exception; +use Tests\Fixtures\NonRetryableTestExceptionActivity; use Tests\Fixtures\TestExceptionActivity; use Tests\Fixtures\TestInvalidActivity; use Tests\Fixtures\TestOtherActivity; use Tests\Fixtures\TestWorkflow; use Tests\TestCase; +use Workflow\Exceptions\NonRetryableException; use Workflow\Models\StoredWorkflow; use Workflow\Serializers\Serializer; use Workflow\States\WorkflowCreatedStatus; @@ -64,6 +66,24 @@ public function testExceptionActivity(): void $this->assertSame(WorkflowFailedStatus::class, $workflow->status()); } + public function testNonRetryableExceptionActivity(): void + { + $this->expectException(NonRetryableException::class); + + $workflow = WorkflowStub::load(WorkflowStub::make(TestWorkflow::class)->id()); + $activity = new NonRetryableTestExceptionActivity(0, now()->toDateTimeString(), StoredWorkflow::findOrFail( + $workflow->id() + )); + + $activity->handle(); + + $workflow->fresh(); + + $this->assertSame(1, $workflow->exceptions()->count()); + $this->assertSame(0, $workflow->logs()->count()); + $this->assertSame(WorkflowFailedStatus::class, $workflow->status()); + } + public function testFailedActivity(): void { $workflow = WorkflowStub::load(WorkflowStub::make(TestWorkflow::class)->id()); From 442fc4e3f34a9623f8c7275f49dc60675bf1dc9f Mon Sep 17 00:00:00 2001 From: Richard McDaniel Date: Sun, 23 Mar 2025 00:49:34 +0000 Subject: [PATCH 09/12] Improve test coverage --- tests/Unit/ExceptionTest.php | 46 ++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 tests/Unit/ExceptionTest.php diff --git a/tests/Unit/ExceptionTest.php b/tests/Unit/ExceptionTest.php new file mode 100644 index 00000000..dac9eefd --- /dev/null +++ b/tests/Unit/ExceptionTest.php @@ -0,0 +1,46 @@ +toDateTimeString(), new StoredWorkflow(), new \Exception( + 'Test exception' + )); + + $middleware = collect($exception->middleware()) + ->map(static fn ($middleware) => is_object($middleware) ? get_class($middleware) : $middleware) + ->values(); + + $this->assertCount(1, $middleware); + $this->assertSame([WithoutOverlappingMiddleware::class], $middleware->all()); + } + + public function testExceptionWorkflowRunning(): void + { + $workflow = WorkflowStub::load(WorkflowStub::make(TestWorkflow::class)->id()); + $storedWorkflow = StoredWorkflow::findOrFail($workflow->id()); + $storedWorkflow->update([ + 'arguments' => Serializer::serialize([]), + 'status' => WorkflowRunningStatus::$name, + ]); + + $exception = new Exception(0, now()->toDateTimeString(), $storedWorkflow, new \Exception('Test exception')); + $exception->handle(); + + $this->assertSame(WorkflowRunningStatus::class, $workflow->status()); + } +} From ff6e4462c0072d53983bcf492021f5509be3546d Mon Sep 17 00:00:00 2001 From: Richard McDaniel Date: Sun, 23 Mar 2025 00:57:27 +0000 Subject: [PATCH 10/12] Improve test coverage --- tests/Unit/SignalTest.php | 44 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 tests/Unit/SignalTest.php diff --git a/tests/Unit/SignalTest.php b/tests/Unit/SignalTest.php new file mode 100644 index 00000000..6470acc9 --- /dev/null +++ b/tests/Unit/SignalTest.php @@ -0,0 +1,44 @@ +middleware()) + ->map(static fn ($middleware) => is_object($middleware) ? get_class($middleware) : $middleware) + ->values(); + + $this->assertCount(1, $middleware); + $this->assertSame([WithoutOverlappingMiddleware::class], $middleware->all()); + } + + public function testSignalWorkflowRunning(): void + { + $workflow = WorkflowStub::load(WorkflowStub::make(TestWorkflow::class)->id()); + $storedWorkflow = StoredWorkflow::findOrFail($workflow->id()); + $storedWorkflow->update([ + 'arguments' => Serializer::serialize([]), + 'status' => WorkflowRunningStatus::$name, + ]); + + $signal = new Signal($storedWorkflow); + $signal->handle(); + + $this->assertSame(WorkflowRunningStatus::class, $workflow->status()); + } +} From ffe49ed4cb35931858ad6dbede3ac47aa87c65d4 Mon Sep 17 00:00:00 2001 From: Richard McDaniel Date: Sun, 23 Mar 2025 01:21:47 +0000 Subject: [PATCH 11/12] Improve test coverage --- tests/Unit/WorkflowTest.php | 56 +++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/tests/Unit/WorkflowTest.php b/tests/Unit/WorkflowTest.php index 08eeaced..ce08623a 100644 --- a/tests/Unit/WorkflowTest.php +++ b/tests/Unit/WorkflowTest.php @@ -5,21 +5,77 @@ namespace Tests\Unit; use Illuminate\Support\Carbon; +use Illuminate\Support\Facades\Event; use Tests\Fixtures\TestActivity; use Tests\Fixtures\TestChildWorkflow; use Tests\Fixtures\TestOtherActivity; use Tests\Fixtures\TestParentWorkflow; use Tests\Fixtures\TestWorkflow; use Tests\TestCase; +use Workflow\Events\WorkflowFailed; use Workflow\Exception; use Workflow\Models\StoredWorkflow; use Workflow\Serializers\Serializer; use Workflow\States\WorkflowCompletedStatus; +use Workflow\States\WorkflowFailedStatus; use Workflow\States\WorkflowPendingStatus; +use Workflow\Workflow; use Workflow\WorkflowStub; final class WorkflowTest extends TestCase { + public function testFailed(): void + { + Event::fake(); + + $stub = WorkflowStub::load(WorkflowStub::make(TestWorkflow::class)->id()); + $storedWorkflow = StoredWorkflow::findOrFail($stub->id()); + $storedWorkflow->update([ + 'arguments' => Serializer::serialize([]), + 'status' => WorkflowPendingStatus::class, + ]); + + $workflow = new Workflow($storedWorkflow); + + $workflow->failed(new \Exception('Test exception')); + + $this->assertSame(WorkflowFailedStatus::class, $stub->status()); + $this->assertSame( + 'Test exception', + Serializer::unserialize($stub->exceptions()->first()->exception)['message'] + ); + + Event::assertDispatched(WorkflowFailed::class, static function ($event) use ($stub) { + return $event->workflowId === $stub->id(); + }); + } + + public function testFailedTwice(): void + { + Event::fake(); + + $stub = WorkflowStub::load(WorkflowStub::make(TestWorkflow::class)->id()); + $storedWorkflow = StoredWorkflow::findOrFail($stub->id()); + $storedWorkflow->update([ + 'arguments' => Serializer::serialize([]), + 'status' => WorkflowFailedStatus::class, + ]); + + $workflow = new Workflow($storedWorkflow); + + $workflow->failed(new \Exception('Test exception')); + + $this->assertSame(WorkflowFailedStatus::class, $stub->status()); + $this->assertSame( + 'Test exception', + Serializer::unserialize($stub->exceptions()->first()->exception)['message'] + ); + + Event::assertNotDispatched(WorkflowFailed::class, static function ($event) use ($stub) { + return $event->workflowId === $stub->id(); + }); + } + public function testException(): void { $exception = new \Exception('test'); From 3f3c573872341de0b6253498cf82758b096701e9 Mon Sep 17 00:00:00 2001 From: Richard McDaniel Date: Sun, 23 Mar 2025 01:29:33 +0000 Subject: [PATCH 12/12] Improve test coverage --- tests/Unit/WorkflowTest.php | 52 +++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/tests/Unit/WorkflowTest.php b/tests/Unit/WorkflowTest.php index ce08623a..04d09982 100644 --- a/tests/Unit/WorkflowTest.php +++ b/tests/Unit/WorkflowTest.php @@ -28,6 +28,13 @@ public function testFailed(): void { Event::fake(); + $parentWorkflow = WorkflowStub::load(WorkflowStub::make(TestWorkflow::class)->id()); + $storedParentWorkflow = StoredWorkflow::findOrFail($parentWorkflow->id()); + $storedParentWorkflow->update([ + 'arguments' => Serializer::serialize([]), + 'status' => WorkflowPendingStatus::$name, + ]); + $stub = WorkflowStub::load(WorkflowStub::make(TestWorkflow::class)->id()); $storedWorkflow = StoredWorkflow::findOrFail($stub->id()); $storedWorkflow->update([ @@ -35,6 +42,12 @@ public function testFailed(): void 'status' => WorkflowPendingStatus::class, ]); + $storedWorkflow->parents() + ->attach($storedParentWorkflow, [ + 'parent_index' => 0, + 'parent_now' => now(), + ]); + $workflow = new Workflow($storedWorkflow); $workflow->failed(new \Exception('Test exception')); @@ -76,6 +89,45 @@ public function testFailedTwice(): void }); } + public function testFailedWithParentFailed(): void + { + Event::fake(); + + $parentWorkflow = WorkflowStub::load(WorkflowStub::make(TestWorkflow::class)->id()); + $storedParentWorkflow = StoredWorkflow::findOrFail($parentWorkflow->id()); + $storedParentWorkflow->update([ + 'arguments' => Serializer::serialize([]), + 'status' => WorkflowFailedStatus::$name, + ]); + + $stub = WorkflowStub::load(WorkflowStub::make(TestWorkflow::class)->id()); + $storedWorkflow = StoredWorkflow::findOrFail($stub->id()); + $storedWorkflow->update([ + 'arguments' => Serializer::serialize([]), + 'status' => WorkflowPendingStatus::class, + ]); + + $storedWorkflow->parents() + ->attach($storedParentWorkflow, [ + 'parent_index' => 0, + 'parent_now' => now(), + ]); + + $workflow = new Workflow($storedWorkflow); + + $workflow->failed(new \Exception('Test exception')); + + $this->assertSame(WorkflowFailedStatus::class, $stub->status()); + $this->assertSame( + 'Test exception', + Serializer::unserialize($stub->exceptions()->first()->exception)['message'] + ); + + Event::assertDispatched(WorkflowFailed::class, static function ($event) use ($stub) { + return $event->workflowId === $stub->id(); + }); + } + public function testException(): void { $exception = new \Exception('test');