From 32d54b36a8e3c715133d0be97725775586499f3a Mon Sep 17 00:00:00 2001 From: roble Date: Sat, 6 Jun 2026 14:39:20 +0100 Subject: [PATCH 1/7] fix: Handle potential exceptions in RevenueChartWidget data retrieval --- src/Filament/Widgets/RevenueChartWidget.php | 26 ++++++++++++--------- tests/Feature/DatabaseSeederTest.php | 4 ++-- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/Filament/Widgets/RevenueChartWidget.php b/src/Filament/Widgets/RevenueChartWidget.php index 6ced768..e4865f2 100644 --- a/src/Filament/Widgets/RevenueChartWidget.php +++ b/src/Filament/Widgets/RevenueChartWidget.php @@ -59,18 +59,22 @@ protected function getData(): array { $buckets = $this->buildMonthlyBuckets(); - $rows = Payment::where('status', PaymentStatus::Succeeded) - ->whereBetween('created_at', [$this->startDate, $this->endDate]) - ->selectRaw('DATE_FORMAT(created_at, "%Y-%m") as month, SUM(amount) as total') - ->groupBy('month') - ->orderBy('month') - ->get(); - - foreach ($rows as $row) { - $month = (string) $row->getAttribute('month'); - if (array_key_exists($month, $buckets)) { - $buckets[$month] = (int) $row->getAttribute('total'); + try { + $rows = Payment::where('status', PaymentStatus::Succeeded) + ->whereBetween('created_at', [$this->startDate, $this->endDate]) + ->selectRaw('DATE_FORMAT(created_at, "%Y-%m") as month, SUM(amount) as total') + ->groupBy('month') + ->orderBy('month') + ->get(); + + foreach ($rows as $row) { + $month = (string) $row->getAttribute('month'); + if (array_key_exists($month, $buckets)) { + $buckets[$month] = (int) $row->getAttribute('total'); + } } + } catch (\Exception) { + // DATE_FORMAT unsupported (e.g. SQLite in local/test) } $labels = array_map( diff --git a/tests/Feature/DatabaseSeederTest.php b/tests/Feature/DatabaseSeederTest.php index e335f6a..9b0679a 100644 --- a/tests/Feature/DatabaseSeederTest.php +++ b/tests/Feature/DatabaseSeederTest.php @@ -12,8 +12,8 @@ class DatabaseSeederTest extends TestCase public function test_module_database_seeder_runs(): void { - $this->seed(DatabaseSeeder::class); + $this->expectNotToPerformAssertions(); - $this->assertTrue(true); + $this->seed(DatabaseSeeder::class); } } From c3933bc11aa2e68adf7120cb73583876a05ea905 Mon Sep 17 00:00:00 2001 From: roble Date: Sat, 6 Jun 2026 14:50:29 +0100 Subject: [PATCH 2/7] fix: Add exception handling for data retrieval in chart widgets --- .../Widgets/ConversionChartWidget.php | 44 ++++++++++--------- src/Filament/Widgets/RevenueChartWidget.php | 4 +- .../Widgets/SubscriptionsChartWidget.php | 24 +++++----- 3 files changed, 40 insertions(+), 32 deletions(-) diff --git a/src/Filament/Widgets/ConversionChartWidget.php b/src/Filament/Widgets/ConversionChartWidget.php index 44d9c29..b237c7f 100644 --- a/src/Filament/Widgets/ConversionChartWidget.php +++ b/src/Filament/Widgets/ConversionChartWidget.php @@ -77,27 +77,31 @@ protected function getData(): array { $buckets = $this->buildMonthlyBuckets(); - $rows = CheckoutSession::whereBetween('created_at', [$this->startDate, $this->endDate]) - ->whereIn('status', [ - CheckoutSessionStatus::Completed->value, - CheckoutSessionStatus::Abandoned->value, - CheckoutSessionStatus::Expired->value, - ]) - ->selectRaw( - 'DATE_FORMAT(created_at, "%Y-%m") as month, SUM(status = ?) as completed, COUNT(*) as total', - [CheckoutSessionStatus::Completed->value] - ) - ->groupBy('month') - ->orderBy('month') - ->get(); - - foreach ($rows as $row) { - $month = (string) $row->getAttribute('month'); - $total = (int) $row->getAttribute('total'); - $completed = (int) $row->getAttribute('completed'); - if (array_key_exists($month, $buckets) && $total > 0) { - $buckets[$month] = round($completed / $total * 100, 1); + try { + $rows = CheckoutSession::whereBetween('created_at', [$this->startDate, $this->endDate]) + ->whereIn('status', [ + CheckoutSessionStatus::Completed->value, + CheckoutSessionStatus::Abandoned->value, + CheckoutSessionStatus::Expired->value, + ]) + ->selectRaw( + 'DATE_FORMAT(created_at, "%Y-%m") as month, SUM(status = ?) as completed, COUNT(*) as total', + [CheckoutSessionStatus::Completed->value] + ) + ->groupBy('month') + ->orderBy('month') + ->get(); + + foreach ($rows as $row) { + $month = (string) $row->getAttribute('month'); + $total = (int) $row->getAttribute('total'); + $completed = (int) $row->getAttribute('completed'); + if (array_key_exists($month, $buckets) && $total > 0) { + $buckets[$month] = round($completed / $total * 100, 1); + } } + } catch (\Exception $e) { + report($e); } $labels = array_map( diff --git a/src/Filament/Widgets/RevenueChartWidget.php b/src/Filament/Widgets/RevenueChartWidget.php index e4865f2..ff7c44a 100644 --- a/src/Filament/Widgets/RevenueChartWidget.php +++ b/src/Filament/Widgets/RevenueChartWidget.php @@ -73,8 +73,8 @@ protected function getData(): array $buckets[$month] = (int) $row->getAttribute('total'); } } - } catch (\Exception) { - // DATE_FORMAT unsupported (e.g. SQLite in local/test) + } catch (\Exception $e) { + report($e); } $labels = array_map( diff --git a/src/Filament/Widgets/SubscriptionsChartWidget.php b/src/Filament/Widgets/SubscriptionsChartWidget.php index edaa037..cb9778a 100644 --- a/src/Filament/Widgets/SubscriptionsChartWidget.php +++ b/src/Filament/Widgets/SubscriptionsChartWidget.php @@ -58,17 +58,21 @@ protected function getData(): array { $buckets = $this->buildMonthlyBuckets(); - $rows = Subscription::whereBetween('created_at', [$this->startDate, $this->endDate]) - ->selectRaw('DATE_FORMAT(created_at, "%Y-%m") as month, COUNT(*) as total') - ->groupBy('month') - ->orderBy('month') - ->get(); - - foreach ($rows as $row) { - $month = (string) $row->getAttribute('month'); - if (array_key_exists($month, $buckets)) { - $buckets[$month] = (int) $row->getAttribute('total'); + try { + $rows = Subscription::whereBetween('created_at', [$this->startDate, $this->endDate]) + ->selectRaw('DATE_FORMAT(created_at, "%Y-%m") as month, COUNT(*) as total') + ->groupBy('month') + ->orderBy('month') + ->get(); + + foreach ($rows as $row) { + $month = (string) $row->getAttribute('month'); + if (array_key_exists($month, $buckets)) { + $buckets[$month] = (int) $row->getAttribute('total'); + } } + } catch (\Exception $e) { + report($e); } $labels = array_map( From 229f0ba52d1028450e57e25b8404f6942974c967 Mon Sep 17 00:00:00 2001 From: roble Date: Sun, 7 Jun 2026 15:14:06 +0100 Subject: [PATCH 3/7] fix: Refactor app instance retrieval in billing feature tests --- tests/Feature/BillingServiceTest.php | 4 ++-- tests/Feature/CheckoutControllerTest.php | 2 +- tests/Feature/CheckoutSessionValidationTest.php | 2 +- tests/Feature/SubscriptionCancelTest.php | 2 +- tests/Feature/SubscriptionResumeTest.php | 2 +- tests/Feature/WebhookIdempotencyTest.php | 4 ++-- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/Feature/BillingServiceTest.php b/tests/Feature/BillingServiceTest.php index b2ccf25..5a1301a 100644 --- a/tests/Feature/BillingServiceTest.php +++ b/tests/Feature/BillingServiceTest.php @@ -65,9 +65,9 @@ protected function setUp(): void $manager = $this->createMock(PaymentGatewayManager::class); $manager->method('driver')->willReturn($this->gateway); - $this->app->instance(PaymentGatewayManager::class, $manager); + app()->instance(PaymentGatewayManager::class, $manager); - $this->billingService = $this->app->make(BillingService::class); + $this->billingService = app()->make(BillingService::class); } public function test_process_checkout_creates_customer(): void diff --git a/tests/Feature/CheckoutControllerTest.php b/tests/Feature/CheckoutControllerTest.php index 0bc53d9..75766b4 100644 --- a/tests/Feature/CheckoutControllerTest.php +++ b/tests/Feature/CheckoutControllerTest.php @@ -47,7 +47,7 @@ protected function setUp(): void $manager = $this->createMock(PaymentGatewayManager::class); $manager->method('driver')->willReturn($gateway); - $this->app->instance(PaymentGatewayManager::class, $manager); + app()->instance(PaymentGatewayManager::class, $manager); } public function test_checkout_requires_authentication(): void diff --git a/tests/Feature/CheckoutSessionValidationTest.php b/tests/Feature/CheckoutSessionValidationTest.php index daee80b..4e0e773 100644 --- a/tests/Feature/CheckoutSessionValidationTest.php +++ b/tests/Feature/CheckoutSessionValidationTest.php @@ -38,7 +38,7 @@ protected function setUp(): void $manager = $this->createMock(PaymentGatewayManager::class); $manager->method('driver')->willReturn($gateway); - $this->app->instance(PaymentGatewayManager::class, $manager); + app()->instance(PaymentGatewayManager::class, $manager); } public function test_show_rejects_expired_checkout_session(): void diff --git a/tests/Feature/SubscriptionCancelTest.php b/tests/Feature/SubscriptionCancelTest.php index d4e9719..4e2ace8 100644 --- a/tests/Feature/SubscriptionCancelTest.php +++ b/tests/Feature/SubscriptionCancelTest.php @@ -26,7 +26,7 @@ protected function setUp(): void $manager = $this->createMock(PaymentGatewayManager::class); $manager->method('driver')->willReturn($this->gateway); - $this->app->instance(PaymentGatewayManager::class, $manager); + app()->instance(PaymentGatewayManager::class, $manager); } public function test_cancel_subscription_requires_auth(): void diff --git a/tests/Feature/SubscriptionResumeTest.php b/tests/Feature/SubscriptionResumeTest.php index a57dc2c..7b5e8ea 100644 --- a/tests/Feature/SubscriptionResumeTest.php +++ b/tests/Feature/SubscriptionResumeTest.php @@ -26,7 +26,7 @@ protected function setUp(): void $manager = $this->createMock(PaymentGatewayManager::class); $manager->method('driver')->willReturn($this->gateway); - $this->app->instance(PaymentGatewayManager::class, $manager); + app()->instance(PaymentGatewayManager::class, $manager); } public function test_resume_subscription_requires_auth(): void diff --git a/tests/Feature/WebhookIdempotencyTest.php b/tests/Feature/WebhookIdempotencyTest.php index 394d148..47c96ff 100644 --- a/tests/Feature/WebhookIdempotencyTest.php +++ b/tests/Feature/WebhookIdempotencyTest.php @@ -54,9 +54,9 @@ protected function setUp(): void $manager = $this->createMock(PaymentGatewayManager::class); $manager->method('driver')->willReturn($this->gateway); - $this->app->instance(PaymentGatewayManager::class, $manager); + app()->instance(PaymentGatewayManager::class, $manager); - $this->billingService = $this->app->make(BillingService::class); + $this->billingService = app()->make(BillingService::class); } public function test_duplicate_webhook_event_is_skipped(): void From 54d88ff868d0a6185c224ebdc0b2f2f702de2205 Mon Sep 17 00:00:00 2001 From: Roble Date: Sun, 7 Jun 2026 16:33:47 +0100 Subject: [PATCH 4/7] Apply suggestions from code review Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/Filament/Widgets/ConversionChartWidget.php | 2 +- src/Filament/Widgets/RevenueChartWidget.php | 2 +- src/Filament/Widgets/SubscriptionsChartWidget.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Filament/Widgets/ConversionChartWidget.php b/src/Filament/Widgets/ConversionChartWidget.php index b237c7f..e210af4 100644 --- a/src/Filament/Widgets/ConversionChartWidget.php +++ b/src/Filament/Widgets/ConversionChartWidget.php @@ -100,7 +100,7 @@ protected function getData(): array $buckets[$month] = round($completed / $total * 100, 1); } } - } catch (\Exception $e) { + } catch (\Throwable $e) { report($e); } diff --git a/src/Filament/Widgets/RevenueChartWidget.php b/src/Filament/Widgets/RevenueChartWidget.php index ff7c44a..d278d0c 100644 --- a/src/Filament/Widgets/RevenueChartWidget.php +++ b/src/Filament/Widgets/RevenueChartWidget.php @@ -73,7 +73,7 @@ protected function getData(): array $buckets[$month] = (int) $row->getAttribute('total'); } } - } catch (\Exception $e) { + } catch (\Throwable $e) { report($e); } diff --git a/src/Filament/Widgets/SubscriptionsChartWidget.php b/src/Filament/Widgets/SubscriptionsChartWidget.php index cb9778a..45c08d2 100644 --- a/src/Filament/Widgets/SubscriptionsChartWidget.php +++ b/src/Filament/Widgets/SubscriptionsChartWidget.php @@ -71,7 +71,7 @@ protected function getData(): array $buckets[$month] = (int) $row->getAttribute('total'); } } - } catch (\Exception $e) { + } catch (\Throwable $e) { report($e); } From 7c3efcdf463efb2a5548a49632c8112a6970c1e8 Mon Sep 17 00:00:00 2001 From: roble Date: Thu, 11 Jun 2026 20:49:44 +0100 Subject: [PATCH 5/7] fix: Update password in subscriber fixtures for consistency --- Taskfile.yml | 6 +++--- tests/Support/BillingTestHelper.php | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Taskfile.yml b/Taskfile.yml index eca8299..366326d 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -11,7 +11,7 @@ tasks: # ── Testing ─────────────────────────────────────────────────── test:php: desc: Run PHPUnit tests for Billing module - cmd: php artisan test --testsuite=Modules --filter='^Modules\\Billing\\Tests' + cmd: 'php artisan test --testsuite=Modules --filter=''^Modules\\Billing\\Tests''' test:e2e: desc: Run E2E tests for Billing module @@ -22,10 +22,10 @@ tasks: db:seed: desc: Seed the Billing module database - cmd: php artisan modules:seed --module=billing + cmd: '{{.APP}} php artisan modules:seed --module=billing' # ── Code Generation ──────────────────────────────────────────── types:generate: desc: Generate TypeScript types from PHP DTOs and enums - cmd: php artisan module:generate-types billing + cmd: '{{.APP}} php artisan module:generate-types billing' diff --git a/tests/Support/BillingTestHelper.php b/tests/Support/BillingTestHelper.php index b26c384..1e9b8fe 100644 --- a/tests/Support/BillingTestHelper.php +++ b/tests/Support/BillingTestHelper.php @@ -31,7 +31,7 @@ public static function createSubscriberFixtures(): void // --- Active subscriber --- $subscriber = User::firstOrCreate( ['email' => 'subscriber@example.com'], - ['name' => 'Subscriber User', 'password' => Hash::make('secretsauce'), 'email_verified_at' => now()], + ['name' => 'Subscriber User', 'password' => Hash::make('password'), 'email_verified_at' => now()], ); $subscriber->assignRole('user'); @@ -56,7 +56,7 @@ public static function createSubscriberFixtures(): void // --- Cancelled subscriber (pending cancellation) --- $cancelled = User::firstOrCreate( ['email' => 'cancelled@example.com'], - ['name' => 'Cancelled User', 'password' => Hash::make('secretsauce'), 'email_verified_at' => now()], + ['name' => 'Cancelled User', 'password' => Hash::make('password'), 'email_verified_at' => now()], ); $cancelled->assignRole('user'); From 39e3b2b26c32ca530fdbb3c5f11f3e0995377d69 Mon Sep 17 00:00:00 2001 From: roble Date: Thu, 11 Jun 2026 21:02:02 +0100 Subject: [PATCH 6/7] fix: Correct command syntax for PHPUnit test task in Taskfile --- Taskfile.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Taskfile.yml b/Taskfile.yml index 366326d..370ef25 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -11,7 +11,7 @@ tasks: # ── Testing ─────────────────────────────────────────────────── test:php: desc: Run PHPUnit tests for Billing module - cmd: 'php artisan test --testsuite=Modules --filter=''^Modules\\Billing\\Tests''' + cmd: php artisan test --testsuite=Modules --filter=''^Modules\\Billing\\Tests'' test:e2e: desc: Run E2E tests for Billing module From e89e0f4c116e3ccf0c135295dc698591e6c4608c Mon Sep 17 00:00:00 2001 From: roble Date: Thu, 11 Jun 2026 21:04:00 +0100 Subject: [PATCH 7/7] fix: Correct syntax for PHPUnit test command in Taskfile --- Taskfile.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Taskfile.yml b/Taskfile.yml index 370ef25..bbec907 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -11,7 +11,7 @@ tasks: # ── Testing ─────────────────────────────────────────────────── test:php: desc: Run PHPUnit tests for Billing module - cmd: php artisan test --testsuite=Modules --filter=''^Modules\\Billing\\Tests'' + cmd: php artisan test --testsuite=Modules --filter='^Modules\\Billing\\Tests' test:e2e: desc: Run E2E tests for Billing module