Files
fotospiel-app/tests/Feature/EventControllerTest.php
Codex Agent 8634d16359
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (push) Has been cancelled
tests / ui (push) Has been cancelled
Expand branding controls and logo upload
2026-01-15 08:42:20 +01:00

301 lines
11 KiB
PHP

<?php
namespace Tests\Feature;
use App\Models\Event;
use App\Models\EventPackage;
use App\Models\EventType;
use App\Models\Package;
use App\Models\PackagePurchase;
use App\Models\TenantPackage;
use App\Services\EventJoinTokenService;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Storage;
use Tests\Feature\Tenant\TenantTestCase;
class EventControllerTest extends TenantTestCase
{
public function test_create_event_with_valid_package_succeeds(): void
{
$tenant = $this->tenant;
$eventType = EventType::factory()->create();
$package = Package::factory()->create(['type' => 'endcustomer', 'max_photos' => 100]);
TenantPackage::factory()->create([
'tenant_id' => $tenant->id,
'package_id' => $package->id,
'active' => true,
]);
$purchase = PackagePurchase::factory()->create([
'tenant_id' => $tenant->id,
'package_id' => $package->id,
'type' => 'endcustomer_event',
'metadata' => [],
]);
$response = $this->authenticatedRequest('POST', '/api/v1/tenant/events', [
'name' => 'Test Event',
'slug' => 'test-event',
'event_date' => Carbon::now()->addDays(10)->toDateString(),
'event_type_id' => $eventType->id,
'package_id' => $package->id,
'accepted_waiver' => true,
]);
$response->assertStatus(201);
$this->assertDatabaseHas('events', [
'tenant_id' => $tenant->id,
'name' => json_encode('Test Event'),
'slug' => 'test-event',
'event_type_id' => $eventType->id,
]);
$event = Event::latest()->first();
$this->assertDatabaseHas('event_packages', [
'event_id' => $event->id,
'package_id' => $package->id,
]);
$this->assertDatabaseHas('event_join_tokens', [
'event_id' => $event->id,
]);
$purchase->refresh();
$this->assertNotNull(data_get($purchase->metadata, 'consents.digital_content_waiver_at'));
}
public function test_create_event_without_package_fails(): void
{
$response = $this->authenticatedRequest('POST', '/api/v1/tenant/events', [
'name' => 'Test Event',
'slug' => 'test-event',
'event_date' => Carbon::now()->addDays(10)->toDateString(),
]);
$response->assertStatus(402)
->assertJsonPath('error.code', 'event_limit_missing');
}
public function test_superadmin_can_create_event_without_tenant_package(): void
{
$tenant = $this->tenant;
$eventType = EventType::factory()->create();
$package = Package::factory()->create([
'type' => 'endcustomer',
'slug' => 'pro',
'max_photos' => 100,
]);
$superadmin = \App\Models\User::factory()->create([
'tenant_id' => $tenant->id,
'role' => 'superadmin',
'password' => Hash::make('password'),
'email_verified_at' => now(),
]);
$login = $this->postJson('/api/v1/tenant-auth/login', [
'login' => $superadmin->email,
'password' => 'password',
]);
$login->assertOk();
$token = (string) $login->json('token');
$response = $this->withHeader('Authorization', 'Bearer '.$token)
->postJson('/api/v1/tenant/events', [
'name' => 'Owner Event',
'slug' => 'owner-event',
'event_date' => Carbon::now()->addDays(10)->toDateString(),
'event_type_id' => $eventType->id,
'package_id' => $package->id,
]);
$response->assertStatus(201);
$event = Event::latest()->first();
$this->assertDatabaseHas('events', [
'tenant_id' => $tenant->id,
'slug' => 'owner-event',
]);
$this->assertDatabaseHas('event_packages', [
'event_id' => $event->id,
'package_id' => $package->id,
]);
}
public function test_create_event_requires_waiver_for_endcustomer_package(): void
{
$tenant = $this->tenant;
$eventType = EventType::factory()->create();
$package = Package::factory()->create(['type' => 'endcustomer', 'max_photos' => 100]);
TenantPackage::factory()->create([
'tenant_id' => $tenant->id,
'package_id' => $package->id,
'active' => true,
]);
$response = $this->authenticatedRequest('POST', '/api/v1/tenant/events', [
'name' => 'Test Event',
'slug' => 'test-event',
'event_date' => Carbon::now()->addDays(10)->toDateString(),
'event_type_id' => $eventType->id,
'package_id' => $package->id,
'accepted_waiver' => false,
]);
$response->assertStatus(422)
->assertJsonValidationErrors(['accepted_waiver']);
}
public function test_create_event_with_reseller_package_limits_events(): void
{
$tenant = $this->tenant;
$eventType = EventType::factory()->create();
$package = Package::factory()->create(['type' => 'reseller', 'max_events_per_year' => 1]);
TenantPackage::factory()->create([
'tenant_id' => $tenant->id,
'package_id' => $package->id,
'used_events' => 0,
'active' => true,
'expires_at' => now()->addYear(),
]);
// First event succeeds
$response1 = $this->authenticatedRequest('POST', '/api/v1/tenant/events', [
'name' => 'First Event',
'slug' => 'first-event',
'event_date' => Carbon::now()->addDays(10)->toDateString(),
'event_type_id' => $eventType->id,
'package_id' => $package->id, // Use reseller package for event? Adjust if needed
]);
$response1->assertStatus(201);
// Second event fails due to limit
$response2 = $this->authenticatedRequest('POST', '/api/v1/tenant/events', [
'name' => 'Second Event',
'slug' => 'second-event',
'event_date' => Carbon::now()->addDays(11)->toDateString(),
'event_type_id' => $eventType->id,
'package_id' => $package->id,
]);
$response2->assertStatus(402)
->assertJsonPath('error.code', 'event_limit_exceeded');
}
public function test_update_event_accepts_live_show_settings(): void
{
$eventType = EventType::factory()->create();
$event = Event::factory()->for($this->tenant)->create([
'event_type_id' => $eventType->id,
'name' => 'Live Show Event',
'slug' => 'live-show-settings',
'date' => now()->addDays(5),
]);
$response = $this->authenticatedRequest('PUT', "/api/v1/tenant/events/{$event->slug}", [
'name' => 'Live Show Event',
'event_date' => now()->addDays(5)->toDateString(),
'event_type_id' => $eventType->id,
'settings' => [
'live_show' => [
'moderation_mode' => 'manual',
'retention_window_hours' => 12,
'playback_mode' => 'balanced',
'pace_mode' => 'fixed',
'fixed_interval_seconds' => 9,
'layout_mode' => 'single',
'effect_preset' => 'film_cut',
'effect_intensity' => 60,
'background_mode' => 'blur_last',
],
],
]);
$response->assertOk();
$event->refresh();
$settings = $event->settings;
$this->assertSame('manual', data_get($settings, 'live_show.moderation_mode'));
$this->assertSame(12, data_get($settings, 'live_show.retention_window_hours'));
$this->assertSame('balanced', data_get($settings, 'live_show.playback_mode'));
$this->assertSame('fixed', data_get($settings, 'live_show.pace_mode'));
$this->assertSame(9, data_get($settings, 'live_show.fixed_interval_seconds'));
$this->assertSame('single', data_get($settings, 'live_show.layout_mode'));
$this->assertSame('film_cut', data_get($settings, 'live_show.effect_preset'));
$this->assertSame(60, data_get($settings, 'live_show.effect_intensity'));
$this->assertSame('blur_last', data_get($settings, 'live_show.background_mode'));
}
public function test_update_event_uploads_branding_logo_data_url(): void
{
Storage::fake('public');
$eventType = EventType::factory()->create();
$event = Event::factory()->for($this->tenant)->create([
'event_type_id' => $eventType->id,
'name' => 'Branding Event',
'slug' => 'branding-event',
'date' => now()->addDays(5),
]);
$logoFile = UploadedFile::fake()->image('logo.png', 64, 64);
$logoContents = file_get_contents($logoFile->getRealPath());
$this->assertIsString($logoContents);
$logoDataUrl = 'data:image/png;base64,'.base64_encode($logoContents);
$response = $this->authenticatedRequest('PUT', "/api/v1/tenant/events/{$event->slug}", [
'name' => 'Branding Event',
'event_date' => now()->addDays(5)->toDateString(),
'event_type_id' => $eventType->id,
'settings' => [
'branding' => [
'logo_data_url' => $logoDataUrl,
'logo' => [
'mode' => 'upload',
'value' => $logoDataUrl,
],
],
],
]);
$response->assertOk();
$event->refresh();
$logoPath = (string) data_get($event->settings, 'branding.logo_url');
$this->assertNotEmpty($logoPath);
Storage::disk('public')->assertExists($logoPath);
$this->assertSame($logoPath, data_get($event->settings, 'branding.logo.value'));
$this->assertNull(data_get($event->settings, 'branding.logo_data_url'));
}
public function test_upload_exceeds_package_limit_fails(): void
{
$tenant = $this->tenant;
$event = Event::factory()->create(['tenant_id' => $tenant->id, 'status' => 'published']);
$package = Package::factory()->create(['type' => 'endcustomer', 'max_photos' => 0]); // Limit 0
EventPackage::create([
'event_id' => $event->id,
'package_id' => $package->id,
'purchased_price' => $package->price,
'used_photos' => 0,
]);
Storage::fake('public');
$token = app(EventJoinTokenService::class)->createToken($event);
$response = $this->withHeader('X-Device-Id', 'limit-test')
->post("/api/v1/events/{$token->token}/upload", [
'photo' => UploadedFile::fake()->image('limit.jpg'),
]);
$response->assertStatus(402)
->assertJsonPath('error.code', 'photo_limit_exceeded');
}
}