evaluator = $this->app->make(PackageLimitEvaluator::class); } public function test_assess_event_creation_returns_null_when_allowance_available(): void { $tenant = Tenant::factory()->create(); $package = Package::factory()->reseller()->create(['max_events_per_year' => 5]); TenantPackage::factory()->for($tenant)->for($package)->create([ 'used_events' => 1, 'expires_at' => now()->addMonth(), 'active' => true, ]); $violation = $this->evaluator->assessEventCreation($tenant, null); $this->assertNull($violation); } public function test_assess_event_creation_returns_package_violation_when_quota_reached(): void { $package = Package::factory()->reseller()->create([ 'max_events_per_year' => 1, ]); $tenant = Tenant::factory()->create(); TenantPackage::factory()->create([ 'tenant_id' => $tenant->id, 'package_id' => $package->id, 'used_events' => 1, 'expires_at' => now()->addMonth(), 'active' => true, ]); $tenant->refresh(); $violation = $this->evaluator->assessEventCreation($tenant, null); $this->assertNotNull($violation); $this->assertSame('event_limit_exceeded', $violation['code']); $this->assertSame('events', $violation['meta']['scope']); $this->assertSame(0, $violation['meta']['remaining']); } public function test_assess_photo_upload_returns_violation_when_photo_limit_reached(): void { $package = Package::factory()->endcustomer()->create([ 'max_photos' => 5, ]); $tenant = Tenant::factory()->create(); $event = Event::factory() ->for($tenant) ->create(); EventPackage::create([ 'event_id' => $event->id, 'package_id' => $package->id, 'purchased_price' => $package->price, 'purchased_at' => now(), 'used_photos' => 5, 'used_guests' => 0, 'gallery_expires_at' => now()->addDays(14), ]); $violation = $this->evaluator->assessPhotoUpload($tenant->fresh(), $event->id); $this->assertNotNull($violation); $this->assertSame('photo_limit_exceeded', $violation['code']); $this->assertSame('photos', $violation['meta']['scope']); $this->assertSame(0, $violation['meta']['remaining']); } public function test_assess_photo_upload_returns_null_when_below_limit(): void { $package = Package::factory()->endcustomer()->create([ 'max_photos' => 10, ]); $tenant = Tenant::factory()->create(); $event = Event::factory() ->for($tenant) ->create(); EventPackage::create([ 'event_id' => $event->id, 'package_id' => $package->id, 'purchased_price' => $package->price, 'purchased_at' => now(), 'used_photos' => 4, 'used_guests' => 0, 'gallery_expires_at' => now()->addDays(14), ]); $violation = $this->evaluator->assessPhotoUpload($tenant->fresh(), $event->id); $this->assertNull($violation); } public function test_summarize_event_package_returns_expected_structure(): void { Config::set('package-limits.photo_thresholds', [0.5, 0.9]); Config::set('package-limits.guest_thresholds', [0.5]); Config::set('package-limits.gallery_warning_days', [7, 1]); $package = Package::factory()->endcustomer()->create([ 'max_photos' => 10, 'max_guests' => 20, 'gallery_days' => 7, ]); $tenant = Tenant::factory()->create(); $event = Event::factory()->for($tenant)->create(); $eventPackage = EventPackage::create([ 'event_id' => $event->id, 'package_id' => $package->id, 'purchased_price' => $package->price, 'purchased_at' => now()->subDay(), 'used_photos' => 5, 'used_guests' => 10, 'gallery_expires_at' => now()->addDays(3), ])->fresh(['package']); $summary = $this->evaluator->summarizeEventPackage($eventPackage); $this->assertSame(10, $summary['photos']['limit']); $this->assertSame(5, $summary['photos']['used']); $this->assertSame('warning', $summary['photos']['state']); $this->assertSame(20, $summary['guests']['limit']); $this->assertSame(10, $summary['guests']['used']); $this->assertSame('warning', $summary['guests']['state']); $this->assertSame('warning', $summary['gallery']['state']); $this->assertTrue($summary['can_upload_photos']); $this->assertTrue($summary['can_add_guests']); } public function test_guest_limits_apply_grace_buffer_before_blocking(): void { Config::set('package-limits.guest_grace', 10); $package = Package::factory()->endcustomer()->create([ 'max_guests' => 5, ]); $tenant = Tenant::factory()->create(); $event = Event::factory()->for($tenant)->create(); $eventPackage = EventPackage::create([ 'event_id' => $event->id, 'package_id' => $package->id, 'purchased_price' => $package->price, 'purchased_at' => now(), 'used_photos' => 0, 'used_guests' => 5, 'gallery_expires_at' => now()->addDays(7), ])->fresh(['package']); $summary = $this->evaluator->summarizeEventPackage($eventPackage); $this->assertSame('warning', $summary['guests']['state']); $eventPackage->update(['used_guests' => 15]); $summary = $this->evaluator->summarizeEventPackage($eventPackage->fresh(['package'])); $this->assertSame('limit_reached', $summary['guests']['state']); } public function test_assess_photo_upload_respects_extra_limits(): void { $package = Package::factory()->endcustomer()->create([ 'max_photos' => 5, ]); $tenant = Tenant::factory()->create(); $event = Event::factory() ->for($tenant) ->create(); $eventPackage = EventPackage::create([ 'event_id' => $event->id, 'package_id' => $package->id, 'purchased_price' => $package->price, 'purchased_at' => now(), 'used_photos' => 5, 'used_guests' => 0, 'gallery_expires_at' => now()->addDays(14), 'extra_photos' => 5, ])->fresh(['package']); $violation = $this->evaluator->assessPhotoUpload($tenant->fresh(), $event->id); $this->assertNull($violation, 'Upload should be allowed within extra photo allowance'); $eventPackage->update(['used_photos' => 10]); $violation = $this->evaluator->assessPhotoUpload($tenant->fresh(), $event->id); $this->assertNotNull($violation, 'Upload should be blocked after exceeding base + extras'); $this->assertSame('photo_limit_exceeded', $violation['code']); $this->assertSame(0, $violation['meta']['remaining']); } }