verschieben des sofortigen verzichts auf das Widerrrufsrecht zum Anlegen des Events

This commit is contained in:
Codex Agent
2025-12-22 13:11:16 +01:00
parent 84234bfb8e
commit c947e638eb
29 changed files with 877 additions and 374 deletions

View File

@@ -0,0 +1,39 @@
<?php
namespace Tests\Feature\Api\Tenant;
use Illuminate\Support\Facades\Http;
use Tests\Feature\Tenant\TenantTestCase;
class BillingPortalTest extends TenantTestCase
{
public function test_tenant_can_create_paddle_portal_session(): void
{
Http::fake([
'*paddle.com/customers' => Http::response([
'data' => ['id' => 'cus_123'],
], 200),
'*paddle.com/customer-portal-sessions' => Http::response([
'data' => [
'urls' => [
'general' => [
'overview' => 'https://portal.example/overview',
],
],
],
], 200),
]);
$this->tenant->forceFill(['paddle_customer_id' => null])->save();
$response = $this->authenticatedRequest('POST', '/api/v1/tenant/billing/portal');
$response->assertOk();
$response->assertJsonPath('url', 'https://portal.example/overview');
$this->assertDatabaseHas('tenants', [
'id' => $this->tenant->id,
'paddle_customer_id' => 'cus_123',
]);
}
}

View File

@@ -36,7 +36,6 @@ class CheckoutFreeActivationTest extends TestCase
$response = $this->postJson(route('checkout.free-activate'), [
'package_id' => $package->id,
'accepted_terms' => true,
'accepted_waiver' => true,
'locale' => 'de',
]);
@@ -63,7 +62,7 @@ class CheckoutFreeActivationTest extends TestCase
]);
}
public function test_free_checkout_requires_waiver_when_package_activates_immediately(): void
public function test_free_checkout_does_not_require_waiver_before_first_use(): void
{
$tenant = Tenant::factory()->create();
$user = User::factory()->for($tenant)->create();
@@ -76,14 +75,13 @@ class CheckoutFreeActivationTest extends TestCase
$response = $this->postJson(route('checkout.free-activate'), [
'package_id' => $package->id,
'accepted_terms' => true,
'accepted_waiver' => false,
'locale' => 'de',
]);
$response->assertStatus(422)
->assertJsonValidationErrors(['accepted_waiver']);
$response->assertOk()
->assertJsonPath('status', 'completed');
$this->assertDatabaseMissing('package_purchases', [
$this->assertDatabaseHas('package_purchases', [
'tenant_id' => $tenant->id,
'package_id' => $package->id,
]);

View File

@@ -4,40 +4,51 @@ namespace Tests\Feature;
use App\Models\Event;
use App\Models\EventPackage;
use App\Models\EventType;
use App\Models\Package;
use App\Models\Tenant;
use App\Models\PackagePurchase;
use App\Models\TenantPackage;
use App\Models\User;
use App\Services\EventJoinTokenService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Storage;
use Tests\TestCase;
use Tests\Feature\Tenant\TenantTestCase;
class EventControllerTest extends TestCase
class EventControllerTest extends TenantTestCase
{
use RefreshDatabase;
public function test_create_event_with_valid_package_succeeds(): void
{
$tenant = Tenant::factory()->create();
$user = User::factory()->create(['tenant_id' => $tenant->id]);
$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->actingAs($user)
->postJson('/api/v1/tenant/events', [
'name' => 'Test Event',
'slug' => 'test-event',
'date' => '2025-10-01',
'package_id' => $package->id,
]);
$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' => 'Test Event',
'name' => json_encode('Test Event'),
'slug' => 'test-event',
'event_type_id' => $eventType->id,
]);
$event = Event::latest()->first();
@@ -50,35 +61,50 @@ class EventControllerTest extends TestCase
'event_id' => $event->id,
]);
$this->assertDatabaseHas('package_purchases', [
'event_id' => $event->id,
'package_id' => $package->id,
'type' => 'endcustomer_event',
'provider' => 'manual',
'provider_id' => 'manual',
]);
$purchase->refresh();
$this->assertNotNull(data_get($purchase->metadata, 'consents.digital_content_waiver_at'));
}
public function test_create_event_without_package_fails(): void
{
$tenant = Tenant::factory()->create();
$user = User::factory()->create(['tenant_id' => $tenant->id]);
$response = $this->authenticatedRequest('POST', '/api/v1/tenant/events', [
'name' => 'Test Event',
'slug' => 'test-event',
'event_date' => Carbon::now()->addDays(10)->toDateString(),
]);
$response = $this->actingAs($user)
->postJson('/api/v1/tenant/events', [
'name' => 'Test Event',
'slug' => 'test-event',
'date' => '2025-10-01',
]);
$response->assertStatus(402)
->assertJsonPath('error.code', 'event_limit_missing');
}
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(['package_id']);
->assertJsonValidationErrors(['accepted_waiver']);
}
public function test_create_event_with_reseller_package_limits_events(): void
{
$tenant = Tenant::factory()->create();
$user = User::factory()->create(['tenant_id' => $tenant->id]);
$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,
@@ -89,37 +115,38 @@ class EventControllerTest extends TestCase
]);
// First event succeeds
$response1 = $this->actingAs($user)
->postJson('/api/v1/tenant/events', [
'name' => 'First Event',
'slug' => 'first-event',
'date' => '2025-10-01',
'package_id' => $package->id, // Use reseller package for event? Adjust if needed
]);
$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->actingAs($user)
->postJson('/api/v1/tenant/events', [
'name' => 'Second Event',
'slug' => 'second-event',
'date' => '2025-10-02',
'package_id' => $package->id,
]);
$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)
->assertJson(['error' => 'No available package for event creation']);
->assertJsonPath('error.code', 'event_limit_exceeded');
}
public function test_upload_exceeds_package_limit_fails(): void
{
$tenant = Tenant::factory()->create();
$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::factory()->create([
EventPackage::create([
'event_id' => $event->id,
'package_id' => $package->id,
'purchased_price' => $package->price,
'used_photos' => 0,
]);

View File

@@ -81,7 +81,6 @@ class PaddleCheckoutControllerTest extends TestCase
'package_id' => $package->id,
'coupon_code' => 'SAVE15',
'accepted_terms' => true,
'accepted_waiver' => true,
]);
$response->assertOk()

View File

@@ -64,6 +64,7 @@ class EventManagementTest extends TenantTestCase
'event_type_id' => $eventType->id,
'event_date' => Carbon::now()->addDays(10)->toDateString(),
'status' => 'draft',
'accepted_waiver' => true,
];
$response = $this->authenticatedRequest('POST', '/api/v1/tenant/events', $payload);

View File

@@ -164,11 +164,6 @@ async function acceptCheckoutTerms(page: import('@playwright/test').Page) {
const termsCheckbox = page.locator('#checkout-terms-hero');
await expect(termsCheckbox).toBeVisible();
await termsCheckbox.click();
const waiverCheckbox = page.locator('#checkout-waiver-hero');
if (await waiverCheckbox.isVisible()) {
await waiverCheckbox.click();
}
}
declare global {

View File

@@ -44,11 +44,6 @@ test.describe('Paddle sandbox full flow (staging)', () => {
await expect(termsCheckbox).toBeVisible();
await termsCheckbox.click();
const waiverCheckbox = page.locator('#checkout-waiver-hero');
if (await waiverCheckbox.isVisible()) {
await waiverCheckbox.click();
}
const checkoutCta = page.getByRole('button', { name: /Weiter mit Paddle|Continue with Paddle/i }).first();
await expect(checkoutCta).toBeVisible({ timeout: 20000 });

View File

@@ -103,11 +103,6 @@ test.describe('Standard package checkout with Paddle completion', () => {
await expect(termsCheckbox).toBeVisible();
await termsCheckbox.click();
const waiverCheckbox = page.locator('#checkout-waiver-hero');
if (await waiverCheckbox.isVisible()) {
await waiverCheckbox.click();
}
await page.getByRole('button', { name: /Weiter mit Paddle|Continue with Paddle/i }).first().click();
let checkoutMode: 'inline' | 'hosted' | null = null;