forgetCachedPermissions(); Role::create(['name' => 'Retailer']); Role::create(['name' => 'Manufacturer']); Role::create(['name' => 'Customer']); Role::create(['name' => 'Admin']); }); function createPartnerWithHub(): array { $hub = Hub::factory()->create(); $partner = Partner::factory()->setupCompleted()->create(['hub_id' => $hub->id]); $user = User::factory()->create(['partner_id' => $partner->id]); return [$user, $partner, $hub]; } function createProductForPartner(Partner $partner, array $overrides = []): Product { $category = Category::factory()->create(); $product = Product::factory()->create(array_merge([ 'partner_id' => $partner->id, 'product_type' => ProductType::SmartOrder, 'status' => ProductStatus::Pending, 'price_type' => PriceType::Fixed, 'name' => 'Test Produkt', 'description_short' => 'Kurzbeschreibung', 'description_long' => 'Langbeschreibung', 'b2in_article_number' => 'B2IN-000001', ], $overrides)); $product->categories()->attach($category->id); $product->variants()->create([ 'is_master_variant' => true, 'sku' => 'EDIT-SKU-001', 'selling_price' => 125000, 'purchase_price' => 68000, 'msrp' => 149900, 'availability_status' => 'in_stock', 'delivery_time_text' => '4-6 Wochen', 'currency' => 'EUR', 'variant_weight_g' => 45000, 'is_active' => true, ]); return $product; } // --- Access Tests --- test('owner can access product edit page', function () { [$user, $partner] = createPartnerWithHub(); $user->assignRole('Manufacturer'); $product = createProductForPartner($partner); $this->actingAs($user) ->get(route('products.edit.standard', $product)) ->assertSuccessful(); }); test('admin can access any product edit page', function () { [$user, $partner] = createPartnerWithHub(); $user->assignRole('Admin'); $otherPartner = Partner::factory()->setupCompleted()->create(); $product = createProductForPartner($otherPartner); $this->actingAs($user) ->get(route('products.edit.standard', $product)) ->assertSuccessful(); }); test('other partner cannot access product edit page', function () { [$user] = createPartnerWithHub(); $user->assignRole('Manufacturer'); $otherPartner = Partner::factory()->setupCompleted()->create(); $product = createProductForPartner($otherPartner); $this->actingAs($user) ->get(route('products.edit.standard', $product)) ->assertForbidden(); }); test('customer cannot access product edit page', function () { $customer = User::factory()->create(); $customer->assignRole('Customer'); $partner = Partner::factory()->setupCompleted()->create(); $product = createProductForPartner($partner); $this->actingAs($customer) ->get(route('products.edit.standard', $product)) ->assertForbidden(); }); // --- Mount Pre-Fill Tests --- test('edit page pre-fills product data', function () { [$user, $partner] = createPartnerWithHub(); $user->assignRole('Manufacturer'); $product = createProductForPartner($partner, [ 'name' => 'Vorhandenes Sofa', 'description_short' => 'Ein tolles Sofa.', 'country_of_origin' => 'DE', 'main_material' => 'Buche', 'assembly_service' => true, 'service_radius_km' => 50, 'warranty_months' => 24, ]); $this->actingAs($user); Volt::test('products.form-standard', ['product' => $product]) ->assertSet('name', 'Vorhandenes Sofa') ->assertSet('descriptionShort', 'Ein tolles Sofa.') ->assertSet('countryOfOrigin', 'DE') ->assertSet('mainMaterial', 'Buche') ->assertSet('assemblyService', true) ->assertSet('serviceRadiusKm', 50) ->assertSet('warrantyMonths', 24); }); test('edit page pre-fills variant data', function () { [$user, $partner] = createPartnerWithHub(); $user->assignRole('Manufacturer'); $product = createProductForPartner($partner); $this->actingAs($user); Volt::test('products.form-standard', ['product' => $product]) ->assertSet('sku', 'EDIT-SKU-001') ->assertSet('sellingPrice', 1250.00) ->assertSet('purchasePrice', 680.00) ->assertSet('msrp', 1499.00) ->assertSet('currency', 'EUR'); }); // --- Save / Update Tests --- test('edit saves updated product fields', function () { Storage::fake('public'); [$user, $partner] = createPartnerWithHub(); $user->assignRole('Manufacturer'); $product = createProductForPartner($partner); $this->actingAs($user); Volt::test('products.form-standard', ['product' => $product]) ->set('name', 'Aktualisiertes Sofa') ->set('descriptionShort', 'Neue Kurzbeschreibung.') ->set('mainMaterial', 'Eiche massiv') ->set('warrantyMonths', 36) ->call('save') ->assertHasNoErrors(); $product->refresh(); expect($product->name)->toBe('Aktualisiertes Sofa'); expect($product->description_short)->toBe('Neue Kurzbeschreibung.'); expect($product->main_material)->toBe('Eiche massiv'); expect($product->warranty_months)->toBe(36); }); test('edit saves updated variant data', function () { Storage::fake('public'); [$user, $partner] = createPartnerWithHub(); $user->assignRole('Manufacturer'); $product = createProductForPartner($partner); $this->actingAs($user); Volt::test('products.form-standard', ['product' => $product]) ->set('sku', 'UPDATED-SKU') ->set('sellingPrice', 1500.00) ->set('currency', 'CHF') ->call('save') ->assertHasNoErrors(); $variant = $product->variants()->where('is_master_variant', true)->first(); $variant->refresh(); expect($variant->sku)->toBe('UPDATED-SKU'); expect($variant->selling_price)->toBe(150000); expect($variant->currency)->toBe('CHF'); }); test('edit re-submits to pending when status is active', function () { Storage::fake('public'); [$user, $partner] = createPartnerWithHub(); $user->assignRole('Manufacturer'); $product = createProductForPartner($partner, ['status' => ProductStatus::Draft]); $this->actingAs($user); Volt::test('products.form-standard', ['product' => $product]) ->set('status', 'active') ->call('save') ->assertHasNoErrors(); $product->refresh(); expect($product->status)->toBe(ProductStatus::Pending); }); test('edit keeps draft status when saving as draft', function () { Storage::fake('public'); [$user, $partner] = createPartnerWithHub(); $user->assignRole('Manufacturer'); $product = createProductForPartner($partner, ['status' => ProductStatus::Pending]); $this->actingAs($user); Volt::test('products.form-standard', ['product' => $product]) ->set('status', 'draft') ->call('save') ->assertHasNoErrors(); $product->refresh(); expect($product->status)->toBe(ProductStatus::Draft); }); test('edit with correction status re-submits to pending', function () { Storage::fake('public'); [$user, $partner] = createPartnerWithHub(); $user->assignRole('Manufacturer'); $product = createProductForPartner($partner, [ 'status' => ProductStatus::Correction, 'curation_notes' => 'Bitte Bilder verbessern.', ]); $this->actingAs($user); Volt::test('products.form-standard', ['product' => $product]) ->assertSet('status', 'active') ->call('save') ->assertHasNoErrors(); $product->refresh(); expect($product->status)->toBe(ProductStatus::Pending); }); // --- Activity Logging Tests --- test('edit creates activity log entry on save', function () { Storage::fake('public'); [$user, $partner] = createPartnerWithHub(); $user->assignRole('Manufacturer'); $product = createProductForPartner($partner); $this->actingAs($user); Volt::test('products.form-standard', ['product' => $product]) ->set('name', 'Updated Name') ->call('save') ->assertHasNoErrors(); $activity = ProductActivity::where('product_id', $product->id)->first(); expect($activity)->not->toBeNull(); expect($activity->action)->toBe('updated'); expect($activity->user_id)->toBe($user->id); }); test('create creates activity log entry on save', function () { Storage::fake('public'); [$user] = createPartnerWithHub(); $user->assignRole('Manufacturer'); $category = Category::factory()->create(); $this->actingAs($user); Volt::test('products.form-standard') ->set('name', 'Activity-Log Produkt') ->set('descriptionShort', 'Produkt mit Activity.') ->set('categoryId', $category->id) ->set('priceType', 'fixed') ->set('status', 'active') ->call('save') ->assertHasNoErrors(); $product = Product::where('name', 'Activity-Log Produkt')->first(); $activity = ProductActivity::where('product_id', $product->id)->first(); expect($activity)->not->toBeNull(); expect($activity->action)->toBe('created'); expect($activity->user_id)->toBe($user->id); }); // --- Validation Tests --- test('edit requires name', function () { [$user, $partner] = createPartnerWithHub(); $user->assignRole('Manufacturer'); $product = createProductForPartner($partner); $this->actingAs($user); Volt::test('products.form-standard', ['product' => $product]) ->set('name', '') ->call('save') ->assertHasErrors(['name' => 'required']); }); test('edit requires short description', function () { [$user, $partner] = createPartnerWithHub(); $user->assignRole('Manufacturer'); $product = createProductForPartner($partner); $this->actingAs($user); Volt::test('products.form-standard', ['product' => $product]) ->set('descriptionShort', '') ->call('save') ->assertHasErrors(['descriptionShort' => 'required']); }); test('edit allows own sku without unique error', function () { Storage::fake('public'); [$user, $partner] = createPartnerWithHub(); $user->assignRole('Manufacturer'); $product = createProductForPartner($partner); $this->actingAs($user); // Keeping the same SKU should not trigger unique error Volt::test('products.form-standard', ['product' => $product]) ->assertSet('sku', 'EDIT-SKU-001') ->call('save') ->assertHasNoErrors(); }); test('edit rejects duplicate sku from other product', function () { [$user, $partner] = createPartnerWithHub(); $user->assignRole('Manufacturer'); // Create first product with SKU $product1 = createProductForPartner($partner); // Create second product with different SKU $product2 = Product::factory()->create([ 'partner_id' => $partner->id, 'product_type' => ProductType::SmartOrder, 'status' => ProductStatus::Pending, 'price_type' => PriceType::Fixed, 'description_short' => 'Kurz', 'b2in_article_number' => 'B2IN-000002', ]); $product2->categories()->attach(Category::factory()->create()->id); $product2->variants()->create([ 'is_master_variant' => true, 'sku' => 'OTHER-SKU', 'selling_price' => 50000, 'is_active' => true, ]); $this->actingAs($user); // Try to change product2's SKU to product1's SKU Volt::test('products.form-standard', ['product' => $product2]) ->set('sku', 'EDIT-SKU-001') ->call('save') ->assertHasErrors(['sku' => 'unique']); }); // --- Existing Media Tests --- test('edit shows existing media', function () { [$user, $partner] = createPartnerWithHub(); $user->assignRole('Manufacturer'); $product = createProductForPartner($partner); // Add media $product->media()->create([ 'file_path' => 'products/1/test.jpg', 'type' => 'image', 'alt_text' => 'Test Image', 'order_column' => 1, ]); $this->actingAs($user); Volt::test('products.form-standard', ['product' => $product]) ->assertSet('existingMedia', fn ($value) => count($value) === 1 && $value[0]['alt_text'] === 'Test Image'); }); test('edit can remove existing media', function () { Storage::fake('public'); [$user, $partner] = createPartnerWithHub(); $user->assignRole('Manufacturer'); $product = createProductForPartner($partner); // Create a fake file Storage::disk('public')->put('products/1/test.jpg', 'fake-image-content'); $media = $product->media()->create([ 'file_path' => 'products/1/test.jpg', 'type' => 'image', 'alt_text' => 'Test Image', 'order_column' => 1, ]); $this->actingAs($user); Volt::test('products.form-standard', ['product' => $product]) ->assertSet('existingMedia', fn ($value) => count($value) === 1) ->call('removeExistingMedia', $media->id) ->assertSet('existingMedia', fn ($value) => count($value) === 0); expect($product->media()->count())->toBe(0); Storage::disk('public')->assertMissing('products/1/test.jpg'); }); test('edit can reorder existing media via drag and drop', function () { [$user, $partner] = createPartnerWithHub(); $user->assignRole('Manufacturer'); $product = createProductForPartner($partner); $media1 = $product->media()->create([ 'file_path' => 'products/1/first.jpg', 'type' => 'image', 'alt_text' => 'First Image', 'order_column' => 1, ]); $media2 = $product->media()->create([ 'file_path' => 'products/1/second.jpg', 'type' => 'image', 'alt_text' => 'Second Image', 'order_column' => 2, ]); $media3 = $product->media()->create([ 'file_path' => 'products/1/third.jpg', 'type' => 'image', 'alt_text' => 'Third Image', 'order_column' => 3, ]); $this->actingAs($user); Volt::test('products.form-standard', ['product' => $product]) ->assertSet('existingMedia', fn ($value) => count($value) === 3 && $value[0]['id'] === $media1->id) ->call('updateMediaOrder', [$media3->id, $media1->id, $media2->id]) ->assertSet('existingMedia', fn ($value) => $value[0]['id'] === $media3->id && $value[1]['id'] === $media1->id && $value[2]['id'] === $media2->id ); expect($media3->fresh()->order_column)->toBe(1); expect($media1->fresh()->order_column)->toBe(2); expect($media2->fresh()->order_column)->toBe(3); }); test('edit existing media is loaded sorted by order column', function () { [$user, $partner] = createPartnerWithHub(); $user->assignRole('Manufacturer'); $product = createProductForPartner($partner); $media2 = $product->media()->create([ 'file_path' => 'products/1/second.jpg', 'type' => 'image', 'alt_text' => 'Second', 'order_column' => 2, ]); $media1 = $product->media()->create([ 'file_path' => 'products/1/first.jpg', 'type' => 'image', 'alt_text' => 'First', 'order_column' => 1, ]); $this->actingAs($user); Volt::test('products.form-standard', ['product' => $product]) ->assertSet('existingMedia', fn ($value) => $value[0]['id'] === $media1->id && $value[1]['id'] === $media2->id); }); test('edit media order includes order_column in existing media', function () { [$user, $partner] = createPartnerWithHub(); $user->assignRole('Manufacturer'); $product = createProductForPartner($partner); $product->media()->create([ 'file_path' => 'products/1/test.jpg', 'type' => 'image', 'alt_text' => 'Test', 'order_column' => 5, ]); $this->actingAs($user); Volt::test('products.form-standard', ['product' => $product]) ->assertSet('existingMedia', fn ($value) => isset($value[0]['order_column']) && $value[0]['order_column'] === 5); }); // --- Wood Origins Tests --- test('edit pre-fills wood origins from product', function () { [$user, $partner] = createPartnerWithHub(); $user->assignRole('Manufacturer'); $product = createProductForPartner($partner); $product->woodOrigins()->create([ 'wood_species' => 'Quercus robur', 'origin_country' => 'PL', 'origin_region' => 'Masowien', 'harvest_year' => 2024, 'sustainability_certificate' => 'FSC', ]); $this->actingAs($user); Volt::test('products.form-standard', ['product' => $product]) ->assertSet('woodOrigins', fn ($value) => count($value) === 1 && $value[0]['wood_species'] === 'Quercus robur' && $value[0]['origin_country'] === 'PL' ); }); test('edit saves updated wood origins', function () { Storage::fake('public'); [$user, $partner] = createPartnerWithHub(); $user->assignRole('Manufacturer'); $product = createProductForPartner($partner); $product->woodOrigins()->create([ 'wood_species' => 'Old Species', 'origin_country' => 'DE', ]); $this->actingAs($user); Volt::test('products.form-standard', ['product' => $product]) ->set('woodOrigins', [ [ 'wood_species' => 'New Species', 'origin_country' => 'AT', 'origin_region' => 'Tirol', 'harvest_year' => 2025, 'forest_operator' => 'Forstbetrieb', 'sustainability_certificate' => 'PEFC', 'eudr_reference_id' => 'EUDR-2025-AT', ], ]) ->call('save') ->assertHasNoErrors(); $product->refresh(); $origins = $product->woodOrigins; expect($origins)->toHaveCount(1); expect($origins->first()->wood_species)->toBe('New Species'); expect($origins->first()->origin_country)->toBe('AT'); expect($origins->first()->origin_region)->toBe('Tirol'); }); // --- Activity Log Display --- test('edit shows activity history in zuordnung tab', function () { [$user, $partner] = createPartnerWithHub(); $user->assignRole('Manufacturer'); $product = createProductForPartner($partner); $product->activities()->create([ 'user_id' => $user->id, 'action' => 'created', 'note' => null, ]); $this->actingAs($user); Volt::test('products.form-standard', ['product' => $product]) ->set('activeTab', 'zuordnung') ->assertSeeText('created'); }); // --- Archive / Sold from Edit Form --- test('edit can archive a product', function () { [$user, $partner] = createPartnerWithHub(); $user->assignRole('Manufacturer'); $product = createProductForPartner($partner, ['status' => ProductStatus::Active]); $this->actingAs($user); Volt::test('products.form-standard', ['product' => $product]) ->call('archiveProduct') ->assertRedirect(route('products.index')); $product->refresh(); expect($product->status)->toBe(ProductStatus::Archived); }); test('edit can mark a product as sold', function () { [$user, $partner] = createPartnerWithHub(); $user->assignRole('Manufacturer'); $product = createProductForPartner($partner, ['status' => ProductStatus::Active]); $this->actingAs($user); Volt::test('products.form-standard', ['product' => $product]) ->call('markAsSold') ->assertRedirect(route('products.index')); $product->refresh(); expect($product->status)->toBe(ProductStatus::Sold); });