create([ 'event_credits_balance' => 2, 'is_active' => true, 'subscription_expires_at' => now()->addMonths(2), ]); Tenant::factory()->create([ 'event_credits_balance' => 20, 'is_active' => true, 'subscription_expires_at' => now()->addMonths(1), ]); Tenant::factory()->create([ 'event_credits_balance' => 1, 'is_active' => false, 'subscription_expires_at' => now()->subDay(), ]); PurchaseHistory::create([ 'id' => 'ph-1', 'tenant_id' => $lowBalanceTenant->id, 'package_id' => 'starter_pack', 'credits_added' => 5, 'price' => 149.90, 'currency' => 'EUR', 'platform' => 'web', 'transaction_id' => 'txn-1', 'purchased_at' => now()->startOfMonth()->addDay(), 'created_at' => now(), ]); $widget = new CreditAlertsWidget(); $cards = $this->invokeProtectedMethod($widget, 'getCards'); $this->assertCount(3, $cards); $this->assertSame( __('admin.widgets.credit_alerts.low_balance_label'), $cards[0]->getLabel() ); $this->assertSame(1, $cards[0]->getValue()); $this->assertSame( 2, $cards[2]->getValue() ); $this->assertStringContainsString('149.9', (string) $cards[1]->getValue()); } public function test_revenue_trend_widget_compiles_monthly_totals(): void { Carbon::setTestNow(Carbon::create(2025, 10, 20, 12)); $tenant = Tenant::factory()->create(); PurchaseHistory::create([ 'id' => 'cur-1', 'tenant_id' => $tenant->id, 'package_id' => 'pro_pack', 'credits_added' => 10, 'price' => 299.99, 'currency' => 'EUR', 'platform' => 'web', 'transaction_id' => 'txn-cur', 'purchased_at' => now()->copy()->startOfMonth()->addDays(2), 'created_at' => now(), ]); PurchaseHistory::create([ 'id' => 'prev-1', 'tenant_id' => $tenant->id, 'package_id' => 'starter_pack', 'credits_added' => 5, 'price' => 149.90, 'currency' => 'EUR', 'platform' => 'web', 'transaction_id' => 'txn-prev', 'purchased_at' => now()->copy()->subMonth()->startOfMonth()->addDays(4), 'created_at' => now()->subMonth(), ]); $widget = new RevenueTrendWidget(); $data = $this->invokeProtectedMethod($widget, 'getData'); $this->assertArrayHasKey('datasets', $data); $this->assertArrayHasKey('labels', $data); $this->assertCount(12, $data['labels']); $this->assertSame(12, count($data['datasets'][0]['data'])); $lastValue = end($data['datasets'][0]['data']); $prevValue = $data['datasets'][0]['data'][count($data['datasets'][0]['data']) - 2]; $this->assertEquals(299.99, $lastValue); $this->assertEquals(149.90, $prevValue); } /** * @template T * * @param object $object * @param string $method * @return mixed */ private function invokeProtectedMethod(object $object, string $method) { $reflection = new ReflectionClass($object); $reflectedMethod = $reflection->getMethod($method); $reflectedMethod->setAccessible(true); return $reflectedMethod->invoke($object); } }