bd sync: 2026-01-12 17:07:55
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (push) Has been cancelled
tests / ui (push) Has been cancelled

This commit is contained in:
Codex Agent
2026-01-12 17:07:55 +01:00
parent 5afa96251b
commit e69c94ad20
55 changed files with 190 additions and 2974 deletions

View File

@@ -1,100 +0,0 @@
<?php
namespace Tests\Feature\Photobooth;
use App\Models\Event;
use App\Models\EventPhotoboothSetting;
use App\Models\PhotoboothConnectCode;
use PHPUnit\Framework\Attributes\Test;
use Tests\Feature\Tenant\TenantTestCase;
class PhotoboothConnectCodeTest extends TenantTestCase
{
#[Test]
public function it_creates_a_connect_code_for_sparkbooth(): void
{
$event = Event::factory()->for($this->tenant)->create([
'slug' => 'connect-code-event',
]);
EventPhotoboothSetting::factory()
->for($event)
->activeSparkbooth()
->create([
'username' => 'pbconnect',
'password' => 'SECRET12',
]);
$response = $this->authenticatedRequest('POST', "/api/v1/tenant/events/{$event->slug}/photobooth/connect-codes");
$response->assertOk()
->assertJsonPath('data.code', fn ($value) => is_string($value) && strlen($value) === 6)
->assertJsonPath('data.expires_at', fn ($value) => is_string($value) && $value !== '');
$this->assertDatabaseCount('photobooth_connect_codes', 1);
}
#[Test]
public function it_redeems_a_connect_code_and_returns_upload_credentials(): void
{
$event = Event::factory()->for($this->tenant)->create([
'slug' => 'connect-code-redeem',
]);
EventPhotoboothSetting::factory()
->for($event)
->activeSparkbooth()
->create([
'username' => 'pbconnect',
'password' => 'SECRET12',
]);
$codeResponse = $this->authenticatedRequest('POST', "/api/v1/tenant/events/{$event->slug}/photobooth/connect-codes");
$codeResponse->assertOk();
$code = (string) $codeResponse->json('data.code');
$redeem = $this->postJson('/api/v1/photobooth/connect', [
'code' => $code,
]);
$redeem->assertOk()
->assertJsonPath('data.upload_url', fn ($value) => is_string($value) && $value !== '')
->assertJsonPath('data.username', 'pbconnect')
->assertJsonPath('data.password', 'SECRET12');
$this->assertDatabaseHas('photobooth_connect_codes', [
'event_id' => $event->id,
]);
}
#[Test]
public function it_rejects_expired_connect_codes(): void
{
$event = Event::factory()->for($this->tenant)->create([
'slug' => 'connect-code-expired',
]);
EventPhotoboothSetting::factory()
->for($event)
->activeSparkbooth()
->create([
'username' => 'pbconnect',
'password' => 'SECRET12',
]);
$code = '123456';
PhotoboothConnectCode::query()->create([
'event_id' => $event->id,
'code_hash' => hash('sha256', $code),
'expires_at' => now()->subMinute(),
]);
$response = $this->postJson('/api/v1/photobooth/connect', [
'code' => $code,
]);
$response->assertStatus(422);
}
}

View File

@@ -1,26 +0,0 @@
<?php
namespace Tests\Feature\Tenant;
use App\Models\Event;
use App\Models\Photo;
class PhotoModerationControllerTest extends TenantTestCase
{
public function test_tenant_admin_can_approve_photo(): void
{
$event = Event::factory()->for($this->tenant)->create([
'slug' => 'moderation-event',
]);
$photo = Photo::factory()->for($event)->create([
'status' => 'pending',
]);
$response = $this->authenticatedRequest('PATCH', "/api/v1/tenant/events/{$event->slug}/photos/{$photo->id}", [
'status' => 'approved',
]);
$response->assertOk();
$this->assertSame('approved', $photo->refresh()->status);
}
}

View File

@@ -1,46 +0,0 @@
<?php
namespace Tests\Feature\Tenant;
use App\Models\CheckoutSession;
use App\Models\Package;
use Illuminate\Support\Str;
class TenantCheckoutSessionStatusTest extends TenantTestCase
{
public function test_tenant_can_fetch_checkout_session_status(): void
{
$package = Package::factory()->create([
'price' => 129,
]);
$session = CheckoutSession::create([
'id' => (string) Str::uuid(),
'user_id' => $this->tenantUser->id,
'tenant_id' => $this->tenant->id,
'package_id' => $package->id,
'status' => CheckoutSession::STATUS_FAILED,
'provider' => CheckoutSession::PROVIDER_PADDLE,
'provider_metadata' => [
'paddle_checkout_url' => 'https://checkout.paddle.test/checkout/123',
],
'status_history' => [
[
'status' => CheckoutSession::STATUS_FAILED,
'reason' => 'paddle_failed',
'at' => now()->toIso8601String(),
],
],
]);
$response = $this->authenticatedRequest(
'GET',
"/api/v1/tenant/packages/checkout-session/{$session->id}/status"
);
$response->assertOk()
->assertJsonPath('status', CheckoutSession::STATUS_FAILED)
->assertJsonPath('reason', 'paddle_failed')
->assertJsonPath('checkout_url', 'https://checkout.paddle.test/checkout/123');
}
}

View File

@@ -29,10 +29,7 @@ class TenantPaddleCheckoutTest extends TenantTestCase
return $tenant->is($this->tenant)
&& $payloadPackage->is($package)
&& array_key_exists('success_url', $payload)
&& array_key_exists('return_url', $payload)
&& array_key_exists('metadata', $payload)
&& is_array($payload['metadata'])
&& ! empty($payload['metadata']['checkout_session_id']);
&& array_key_exists('return_url', $payload);
})
->andReturn([
'checkout_url' => 'https://checkout.paddle.test/checkout/123',
@@ -45,8 +42,7 @@ class TenantPaddleCheckoutTest extends TenantTestCase
]);
$response->assertOk()
->assertJsonPath('checkout_url', 'https://checkout.paddle.test/checkout/123')
->assertJsonStructure(['checkout_session_id']);
->assertJsonPath('checkout_url', 'https://checkout.paddle.test/checkout/123');
}
public function test_paddle_checkout_requires_paddle_price_id(): void

View File

@@ -1,67 +0,0 @@
<?php
namespace Tests\Unit;
use App\Models\GuestPolicySetting;
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;
use Tests\TestCase;
class RateLimitConfigTest extends TestCase
{
use RefreshDatabase;
public function test_tenant_api_rate_limiter_allows_higher_throughput(): void
{
$request = Request::create('/api/v1/tenant/events', 'GET', [], [], [], [
'REMOTE_ADDR' => '10.0.0.1',
]);
$request->attributes->set('tenant_id', 42);
$limiter = RateLimiter::limiter('tenant-api');
$this->assertNotNull($limiter);
$limit = $limiter($request);
$this->assertInstanceOf(Limit::class, $limit);
$this->assertSame(600, $limit->maxAttempts);
}
public function test_guest_api_rate_limiter_allows_higher_throughput(): void
{
$request = Request::create('/api/v1/events/sample', 'GET', [], [], [], [
'REMOTE_ADDR' => '10.0.0.2',
]);
$limiter = RateLimiter::limiter('guest-api');
$this->assertNotNull($limiter);
$limit = $limiter($request);
$this->assertInstanceOf(Limit::class, $limit);
$this->assertSame(300, $limit->maxAttempts);
}
public function test_guest_policy_defaults_follow_join_token_limits(): void
{
$accessLimit = 300;
$downloadLimit = 120;
config([
'join_tokens.access_limit' => $accessLimit,
'join_tokens.download_limit' => $downloadLimit,
]);
GuestPolicySetting::query()->delete();
GuestPolicySetting::flushCache();
$settings = GuestPolicySetting::current();
$this->assertSame($accessLimit, $settings->join_token_access_limit);
$this->assertSame($downloadLimit, $settings->join_token_download_limit);
}
}

View File

@@ -1,144 +0,0 @@
<?php
namespace Tests\Unit;
use App\Enums\GuestNotificationType;
use App\Events\GuestPhotoUploaded;
use App\Listeners\GuestNotifications\SendPhotoUploadedNotification;
use App\Models\Event;
use App\Models\GuestNotification;
use App\Models\GuestNotificationReceipt;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Carbon;
use Tests\TestCase;
class SendPhotoUploadedNotificationTest extends TestCase
{
use RefreshDatabase;
public function test_it_dedupes_recent_photo_activity_notifications(): void
{
Carbon::setTestNow('2026-01-12 13:48:01');
$event = Event::factory()->create();
$listener = $this->app->make(SendPhotoUploadedNotification::class);
GuestNotification::factory()->create([
'tenant_id' => $event->tenant_id,
'event_id' => $event->id,
'type' => GuestNotificationType::PHOTO_ACTIVITY,
'title' => 'Fotospiel-Test hat gerade ein Foto gemacht 🎉',
'payload' => [
'photo_id' => 123,
'photo_ids' => [123],
'count' => 1,
],
'created_at' => now()->subSeconds(5),
'updated_at' => now()->subSeconds(5),
]);
$listener->handle(new GuestPhotoUploaded(
$event,
123,
'device-123',
'Fotospiel-Test'
));
$notification = GuestNotification::query()
->where('event_id', $event->id)
->where('type', GuestNotificationType::PHOTO_ACTIVITY)
->first();
$this->assertSame(1, GuestNotification::query()
->where('event_id', $event->id)
->where('type', GuestNotificationType::PHOTO_ACTIVITY)
->count());
$this->assertSame(1, (int) ($notification?->payload['count'] ?? 0));
}
public function test_it_groups_recent_photo_activity_notifications(): void
{
Carbon::setTestNow('2026-01-12 13:48:01');
$event = Event::factory()->create();
$listener = $this->app->make(SendPhotoUploadedNotification::class);
GuestNotification::factory()->create([
'tenant_id' => $event->tenant_id,
'event_id' => $event->id,
'type' => GuestNotificationType::PHOTO_ACTIVITY,
'title' => 'Fotospiel-Test hat gerade ein Foto gemacht 🎉',
'payload' => [
'photo_id' => 122,
'photo_ids' => [122],
'count' => 1,
],
'created_at' => now()->subMinutes(5),
'updated_at' => now()->subMinutes(5),
]);
$listener->handle(new GuestPhotoUploaded(
$event,
123,
'device-123',
'Fotospiel-Test'
));
$this->assertSame(1, GuestNotification::query()
->where('event_id', $event->id)
->where('type', GuestNotificationType::PHOTO_ACTIVITY)
->count());
$notification = GuestNotification::query()
->where('event_id', $event->id)
->where('type', GuestNotificationType::PHOTO_ACTIVITY)
->first();
$this->assertSame('Es gibt 2 neue Fotos!', $notification?->title);
$this->assertSame(2, (int) ($notification?->payload['count'] ?? 0));
$this->assertSame(1, GuestNotificationReceipt::query()
->where('guest_identifier', 'device-123')
->where('status', 'read')
->count());
}
public function test_it_creates_notification_outside_group_window(): void
{
Carbon::setTestNow('2026-01-12 13:48:01');
$event = Event::factory()->create();
$listener = $this->app->make(SendPhotoUploadedNotification::class);
GuestNotification::factory()->create([
'tenant_id' => $event->tenant_id,
'event_id' => $event->id,
'type' => GuestNotificationType::PHOTO_ACTIVITY,
'title' => 'Fotospiel-Test hat gerade ein Foto gemacht 🎉',
'payload' => [
'photo_id' => 122,
'photo_ids' => [122],
'count' => 1,
],
'created_at' => now()->subMinutes(20),
'updated_at' => now()->subMinutes(20),
]);
$listener->handle(new GuestPhotoUploaded(
$event,
123,
'device-123',
'Fotospiel-Test'
));
$this->assertSame(2, GuestNotification::query()
->where('event_id', $event->id)
->where('type', GuestNotificationType::PHOTO_ACTIVITY)
->count());
$this->assertSame(1, GuestNotificationReceipt::query()
->where('guest_identifier', 'device-123')
->where('status', 'read')
->count());
}
}