fixed event join token handling in the event admin. created new seeders with new tenants and package purchases. added new playwright test scenarios.

This commit is contained in:
Codex Agent
2025-10-26 14:44:47 +01:00
parent 6290a3a448
commit ecf5a23b28
59 changed files with 3900 additions and 691 deletions

View File

@@ -0,0 +1,115 @@
<?php
namespace Tests\Feature\Tenant;
use App\Http\Controllers\Api\Tenant\DashboardController;
use App\Models\Event;
use App\Models\EventType;
use App\Models\Package;
use App\Models\Photo;
use App\Models\Task;
use App\Models\Tenant;
use App\Models\TenantPackage;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Http\Request;
use Illuminate\Support\Arr;
use Tests\TestCase;
class DashboardSummaryTest extends TestCase
{
use RefreshDatabase;
public function test_it_returns_dashboard_metrics_for_tenant(): void
{
app()->setLocale('de');
$tenant = Tenant::factory()->create([
'event_credits_balance' => 5,
]);
$eventType = EventType::factory()->create();
$eventWithTasks = Event::factory()->create([
'tenant_id' => $tenant->id,
'event_type_id' => $eventType->id,
'status' => 'published',
'is_active' => true,
'date' => now()->addDays(3),
]);
$eventWithoutTasks = Event::factory()->create([
'tenant_id' => $tenant->id,
'event_type_id' => $eventType->id,
'status' => 'draft',
'is_active' => false,
'date' => now()->addDays(10),
]);
$task = Task::factory()->create([
'tenant_id' => $tenant->id,
'is_completed' => true,
]);
$eventWithTasks->tasks()->attach($task->id);
Photo::factory()->create([
'event_id' => $eventWithTasks->id,
'tenant_id' => $tenant->id,
'created_at' => now()->subDays(2),
]);
Photo::factory()->create([
'event_id' => $eventWithTasks->id,
'tenant_id' => $tenant->id,
'created_at' => now()->subDays(8),
]);
$package = Package::factory()
->reseller()
->create([
'name' => 'Standard',
'name_translations' => ['de' => 'Standard', 'en' => 'Standard'],
'price' => 59,
]);
$activePackage = TenantPackage::factory()->create([
'tenant_id' => $tenant->id,
'package_id' => $package->id,
'expires_at' => now()->addMonth(),
'used_events' => 1,
'active' => true,
]);
$controller = new DashboardController;
$request = Request::create('/api/v1/tenant/dashboard', 'GET');
$request->attributes->set('decoded_token', ['tenant_id' => $tenant->id]);
$response = $controller($request);
$this->assertSame(200, $response->getStatusCode());
$payload = $response->getData(true);
$this->assertSame(1, Arr::get($payload, 'active_events'));
$this->assertSame(1, Arr::get($payload, 'new_photos'));
$this->assertSame(50, Arr::get($payload, 'task_progress'));
$this->assertSame(5, Arr::get($payload, 'credit_balance'));
$this->assertSame(2, Arr::get($payload, 'upcoming_events'));
$activePackagePayload = Arr::get($payload, 'active_package');
$this->assertIsArray($activePackagePayload);
$this->assertSame('Standard', Arr::get($activePackagePayload, 'name'));
$this->assertSame($activePackage->remaining_events, Arr::get($activePackagePayload, 'remaining_events'));
$this->assertSame(
$activePackage->expires_at->toIso8601String(),
Arr::get($payload, 'active_package.expires_at')
);
$this->assertSame(
$tenant->event_credits_balance,
Arr::get($payload, 'credit_balance')
);
}
}

View File

@@ -0,0 +1,138 @@
<?php
namespace Tests\Feature\Tenant;
use App\Models\Event;
use App\Models\EventPackage;
use App\Models\Package;
use App\Models\Tenant;
use Illuminate\Support\Carbon;
class EventListTest extends TenantTestCase
{
public function test_index_returns_events_without_packages(): void
{
$event = Event::factory()
->for($this->tenant)
->create([
'tenant_id' => $this->tenant->id,
'name' => 'Package-less Event',
'slug' => 'package-less-event',
]);
$response = $this->authenticatedRequest('GET', '/api/v1/tenant/events');
$response->assertOk();
$payload = $response->json('data');
$this->assertNotEmpty($payload, 'Expected at least one event in the response');
$matchingEvent = collect($payload)->firstWhere('id', $event->id);
$this->assertNotNull($matchingEvent, 'Created event should be present in response payload');
$this->assertNull($matchingEvent['package'], 'Events without package should return null package data');
}
public function test_index_includes_package_details_when_available(): void
{
$package = Package::factory()->create([
'type' => 'endcustomer',
'name' => 'Standard',
'name_translations' => [
'de' => 'Standard',
'en' => 'Standard',
],
'price' => 59,
'gallery_days' => 45,
]);
$event = Event::factory()
->for($this->tenant)
->create([
'tenant_id' => $this->tenant->id,
'name' => 'Packaged Event',
'slug' => 'packaged-event',
]);
EventPackage::create([
'event_id' => $event->id,
'package_id' => $package->id,
'purchased_price' => $package->price,
'purchased_at' => Carbon::now()->subDay(),
'gallery_expires_at' => Carbon::now()->addDays($package->gallery_days ?? 30),
'used_photos' => 0,
'used_guests' => 0,
]);
$response = $this->authenticatedRequest('GET', '/api/v1/tenant/events');
$response->assertOk();
$payload = collect($response->json('data'));
$matchingEvent = $payload->firstWhere('id', $event->id);
$this->assertNotNull($matchingEvent, 'Packaged event should be present in response payload');
$this->assertIsArray($matchingEvent['package']);
$this->assertSame($package->id, $matchingEvent['package']['id']);
$this->assertSame('Standard', $matchingEvent['package']['name']);
$this->assertSame('59.00', $matchingEvent['package']['price']);
}
public function test_index_scopes_events_to_authenticated_tenant(): void
{
$foreignTenant = Tenant::factory()->create();
Event::factory()->for($foreignTenant)->create([
'name' => 'Foreign Event',
'slug' => 'foreign-event',
]);
$ownEvent = Event::factory()->for($this->tenant)->create([
'name' => 'Own Event',
'slug' => 'own-event',
]);
$response = $this->authenticatedRequest('GET', '/api/v1/tenant/events');
$response->assertOk();
$payload = collect($response->json('data'));
$this->assertTrue($payload->pluck('id')->contains($ownEvent->id), 'Authenticated tenant should see own event.');
$this->assertFalse($payload->pluck('slug')->contains('foreign-event'), 'Events from other tenants must be filtered out.');
}
public function test_index_handles_event_package_without_package_model(): void
{
$event = Event::factory()->for($this->tenant)->create([
'name' => 'Legacy Event',
'slug' => 'legacy-event',
]);
$package = Package::factory()->create([
'type' => 'endcustomer',
'price' => 49,
]);
EventPackage::create([
'event_id' => $event->id,
'package_id' => $package->id,
'purchased_price' => $package->price,
'purchased_at' => Carbon::now()->subDays(5),
'gallery_expires_at' => Carbon::now()->addDays(15),
'used_photos' => 10,
'used_guests' => 25,
]);
$package->delete();
$response = $this->authenticatedRequest('GET', '/api/v1/tenant/events');
$response->assertOk();
$matchingEvent = collect($response->json('data'))->firstWhere('id', $event->id);
$this->assertNotNull($matchingEvent, 'Event should still be returned even if package record is missing.');
$this->assertNull($matchingEvent['package'], 'Package payload should be null when relation cannot be resolved.');
}
}

View File

@@ -0,0 +1,86 @@
<?php
namespace Tests\Feature\Tenant;
use App\Models\Event;
use App\Models\EventType;
use App\Models\Package;
use App\Models\TenantPackage;
use Illuminate\Support\Carbon;
class EventManagementTest extends TenantTestCase
{
public function test_event_types_endpoint_returns_translated_types(): void
{
$types = EventType::factory()->count(2)->create([
'name' => [
'de' => 'Feier',
'en' => 'Celebration',
],
'icon' => 'party',
]);
$response = $this->authenticatedRequest('GET', '/api/v1/tenant/event-types');
$response->assertOk();
$payload = $response->json('data');
$this->assertCount(2, $payload);
$first = collect($payload)->firstWhere('id', $types->first()->id);
$this->assertNotNull($first, 'Expected event type to be present');
$this->assertSame('Feier', $first['name']);
$this->assertArrayHasKey('slug', $first);
$this->assertArrayHasKey('name_translations', $first);
$this->assertArrayHasKey('icon', $first);
$this->assertArrayHasKey('settings', $first);
}
public function test_event_can_be_created_with_event_type_and_date(): void
{
$eventType = EventType::factory()->create([
'name' => [
'de' => 'Hochzeit',
'en' => 'Wedding',
],
'icon' => 'ring',
]);
$package = Package::factory()->endcustomer()->create([
'price' => 79.90,
]);
TenantPackage::factory()
->for($this->tenant)
->for($package)
->create([
'used_events' => 0,
'active' => true,
]);
$this->tenant->update([
'event_credits_balance' => 1,
]);
$payload = [
'name' => 'Launch Event',
'slug' => 'launch-event',
'event_type_id' => $eventType->id,
'event_date' => Carbon::now()->addDays(10)->toDateString(),
'status' => 'draft',
];
$response = $this->authenticatedRequest('POST', '/api/v1/tenant/events', $payload);
$response->assertCreated();
$response->assertJsonPath('data.slug', 'launch-event');
$response->assertJsonPath('data.event_type_id', $eventType->id);
$this->assertDatabaseHas(Event::class, [
'slug' => 'launch-event',
'event_type_id' => $eventType->id,
'tenant_id' => $this->tenant->id,
]);
}
}