seed(RolePermissionSeeder::class); $this->user = User::factory() ->active() ->has(Employee::factory()) ->create(); $this->ownerRole = Role::where('name', 'Owner')->first(); $this->user->roles()->attach($this->ownerRole->id); // Create test outlets $this->outlets = Outlet::factory()->count(3)->create(); }); function mountBottleAuditComponent(User $user, Outlet $outlet, Bottle $bottle) { return Livewire::actingAs($user)->test(\App\Livewire\Studio\Catalog\Bottle\Audit::class, ['outlet' => $outlet, 'bottle' => $bottle]); } /* |-------------------------------------------------------------------------- | Audit Component Rendering & Mount |-------------------------------------------------------------------------- */ it('bottle audit component initializes correctly', function () { $bottle = Bottle::factory()->create(['name' => 'Test Bottle']); $outlet = $this->outlets->first(); $component = mountBottleAuditComponent($this->user, $outlet, $bottle); expect($component->label)->toBe('Test Bottle di '.$outlet->name); expect($component->activityLogs)->toBeInstanceOf(\Illuminate\Database\Eloquent\Collection::class); }); /* |-------------------------------------------------------------------------- | Activity Logs Display Tests |-------------------------------------------------------------------------- */ it('displays bottle activity logs correctly', function () { $bottle = Bottle::factory()->create(); $outlet = $this->outlets->first(); // Create some activity logs with expected properties Activity::create([ 'log_name' => 'default', 'description' => 'Stock updated', 'subject_type' => get_class($bottle), 'subject_id' => $bottle->id, 'causer_type' => get_class($this->user), 'causer_id' => $this->user->id, 'properties' => [ 'outlet_id' => $outlet->id, 'previous_stock' => 5, 'quantity_change' => 10, 'new_stock' => 15, ], 'event' => AuditStockStatus::INCREASED->value, ]); Activity::create([ 'log_name' => 'default', 'description' => 'Stock decreased', 'subject_type' => get_class($bottle), 'subject_id' => $bottle->id, 'causer_type' => get_class($this->user), 'causer_id' => $this->user->id, 'properties' => [ 'outlet_id' => $outlet->id, 'previous_stock' => 15, 'quantity_change' => -5, 'new_stock' => 10, ], 'event' => AuditStockStatus::DECREASED->value, ]); $component = mountBottleAuditComponent($this->user, $outlet, $bottle); expect($component->activityLogs)->toHaveCount(2); expect($component->activityLogs->first()->event['label'])->toBe(AuditStockStatus::INCREASED->label()); expect($component->activityLogs->first()->event['color'])->toBe(AuditStockStatus::INCREASED->color()); expect($component->activityLogs->last()->event['label'])->toBe(AuditStockStatus::DECREASED->label()); expect($component->activityLogs->last()->event['color'])->toBe(AuditStockStatus::DECREASED->color()); }); it('filters bottle activity logs by outlet', function () { $bottle = Bottle::factory()->create(); $outlet1 = $this->outlets->first(); $outlet2 = $this->outlets->last(); // Create activity for outlet1 Activity::create([ 'log_name' => 'default', 'description' => 'Stock updated outlet1', 'subject_type' => get_class($bottle), 'subject_id' => $bottle->id, 'causer_type' => get_class($this->user), 'causer_id' => $this->user->id, 'properties' => [ 'outlet_id' => $outlet1->id, 'previous_stock' => 5, 'quantity_change' => 10, 'new_stock' => 15, ], 'event' => AuditStockStatus::UPDATED->value, ]); // Create activity for outlet2 Activity::create([ 'log_name' => 'default', 'description' => 'Stock increased outlet2', 'subject_type' => get_class($bottle), 'subject_id' => $bottle->id, 'causer_type' => get_class($this->user), 'causer_id' => $this->user->id, 'properties' => [ 'outlet_id' => $outlet2->id, 'previous_stock' => 0, 'quantity_change' => 20, 'new_stock' => 20, ], 'event' => AuditStockStatus::INCREASED->value, ]); $component1 = mountBottleAuditComponent($this->user, $outlet1, $bottle); $component2 = mountBottleAuditComponent($this->user, $outlet2, $bottle); expect($component1->activityLogs)->toHaveCount(1); expect($component1->activityLogs->first()->description)->toBe('Stock updated outlet1'); expect($component2->activityLogs)->toHaveCount(1); expect($component2->activityLogs->first()->description)->toBe('Stock increased outlet2'); }); it('orders bottle activity logs by latest first', function () { $bottle = Bottle::factory()->create(); $outlet = $this->outlets->first(); // Create activities with different timestamps $activity1 = Activity::create([ 'log_name' => 'default', 'description' => 'first activity', 'subject_type' => get_class($bottle), 'subject_id' => $bottle->id, 'causer_type' => get_class($this->user), 'causer_id' => $this->user->id, 'properties' => [ 'outlet_id' => $outlet->id, 'previous_stock' => 5, 'quantity_change' => 5, 'new_stock' => 10, ], 'event' => AuditStockStatus::UPDATED->value, 'created_at' => now()->subMinutes(5), ]); $activity2 = Activity::create([ 'log_name' => 'default', 'description' => 'second activity', 'subject_type' => get_class($bottle), 'subject_id' => $bottle->id, 'causer_type' => get_class($this->user), 'causer_id' => $this->user->id, 'properties' => [ 'outlet_id' => $outlet->id, 'previous_stock' => 10, 'quantity_change' => 10, 'new_stock' => 20, ], 'event' => AuditStockStatus::INCREASED->value, 'created_at' => now(), ]); $component = mountBottleAuditComponent($this->user, $outlet, $bottle); expect($component->activityLogs)->toHaveCount(2); expect($component->activityLogs->first()->description)->toBe('second activity'); expect($component->activityLogs->last()->description)->toBe('first activity'); }); /* |-------------------------------------------------------------------------- | Activity Logs Content Tests |-------------------------------------------------------------------------- */ it('maps bottle activity events to correct labels and colors', function () { $bottle = Bottle::factory()->create(); $outlet = $this->outlets->first(); // Test INCREASED event Activity::create([ 'log_name' => 'default', 'description' => 'Stock increased', 'subject_type' => get_class($bottle), 'subject_id' => $bottle->id, 'causer_type' => get_class($this->user), 'causer_id' => $this->user->id, 'properties' => [ 'outlet_id' => $outlet->id, 'previous_stock' => 10, 'quantity_change' => 5, 'new_stock' => 15, ], 'event' => AuditStockStatus::INCREASED->value, ]); // Test DECREASED event Activity::create([ 'log_name' => 'default', 'description' => 'Stock decreased', 'subject_type' => get_class($bottle), 'subject_id' => $bottle->id, 'causer_type' => get_class($this->user), 'causer_id' => $this->user->id, 'properties' => [ 'outlet_id' => $outlet->id, 'previous_stock' => 15, 'quantity_change' => -3, 'new_stock' => 12, ], 'event' => AuditStockStatus::DECREASED->value, ]); // Test UPDATED event Activity::create([ 'log_name' => 'default', 'description' => 'Stock updated', 'subject_type' => get_class($bottle), 'subject_id' => $bottle->id, 'causer_type' => get_class($this->user), 'causer_id' => $this->user->id, 'properties' => [ 'outlet_id' => $outlet->id, 'previous_stock' => 12, 'quantity_change' => 0, 'new_stock' => 12, ], 'event' => AuditStockStatus::UPDATED->value, ]); $component = mountBottleAuditComponent($this->user, $outlet, $bottle); expect($component->activityLogs)->toHaveCount(3); // Check INCREASED event mapping $increasedLog = collect($component->activityLogs)->firstWhere('event.label', AuditStockStatus::INCREASED->label()); expect($increasedLog['event']['color'])->toBe(AuditStockStatus::INCREASED->color()); // Check DECREASED event mapping $decreasedLog = collect($component->activityLogs)->firstWhere('event.label', AuditStockStatus::DECREASED->label()); expect($decreasedLog['event']['color'])->toBe(AuditStockStatus::DECREASED->color()); // Check UPDATED event mapping $updatedLog = collect($component->activityLogs)->firstWhere('event.label', AuditStockStatus::UPDATED->label()); expect($updatedLog['event']['color'])->toBe(AuditStockStatus::UPDATED->color()); }); /* |-------------------------------------------------------------------------- | Empty State Tests |-------------------------------------------------------------------------- */ it('handles empty bottle activity logs in audit component', function () { $bottle = Bottle::factory()->create(); $outlet = $this->outlets->first(); $component = mountBottleAuditComponent($this->user, $outlet, $bottle); expect($component->activityLogs)->toBeInstanceOf(\Illuminate\Database\Eloquent\Collection::class); expect($component->activityLogs)->toHaveCount(0); }); it('handles bottle with no activities in specific outlet', function () { $bottle = Bottle::factory()->create(); $outlet1 = $this->outlets->first(); $outlet2 = $this->outlets->last(); // Create activity only for outlet2 Activity::create([ 'log_name' => 'default', 'description' => 'Stock updated', 'subject_type' => get_class($bottle), 'subject_id' => $bottle->id, 'causer_type' => get_class($this->user), 'causer_id' => $this->user->id, 'properties' => [ 'outlet_id' => $outlet2->id, 'previous_stock' => 5, 'quantity_change' => 5, 'new_stock' => 10, ], 'event' => AuditStockStatus::UPDATED->value, ]); $component1 = mountBottleAuditComponent($this->user, $outlet1, $bottle); $component2 = mountBottleAuditComponent($this->user, $outlet2, $bottle); expect($component1->activityLogs)->toHaveCount(0); expect($component2->activityLogs)->toHaveCount(1); }); /* |-------------------------------------------------------------------------- | Integration Tests |-------------------------------------------------------------------------- */ it('displays real bottle activity log data correctly', function () { $bottle = Bottle::factory()->create(['name' => 'Integration Bottle']); $outlet = $this->outlets->first(); $activity = Activity::create([ 'log_name' => 'default', 'description' => 'Stock adjustment for integration test', 'subject_type' => get_class($bottle), 'subject_id' => $bottle->id, 'causer_type' => get_class($this->user), 'causer_id' => $this->user->id, 'properties' => [ 'outlet_id' => $outlet->id, 'previous_stock' => 100, 'quantity_change' => 25, 'new_stock' => 125, ], 'event' => AuditStockStatus::INCREASED->value, ]); $component = mountBottleAuditComponent($this->user, $outlet, $bottle); expect($component->activityLogs)->toHaveCount(1); $log = $component->activityLogs->first(); expect($log->description)->toBe('Stock adjustment for integration test'); expect($log->properties['previous_stock'])->toBe(100); expect($log->properties['quantity_change'])->toBe(25); expect($log->properties['new_stock'])->toBe(125); expect($log->event['label'])->toBe(AuditStockStatus::INCREASED->label()); expect($log->event['color'])->toBe(AuditStockStatus::INCREASED->color()); }); it('handles multiple bottle activities across different outlets', function () { $bottle = Bottle::factory()->create(); $outlet1 = $this->outlets->first(); $outlet2 = $this->outlets->last(); // Create activities for both outlets Activity::create([ 'log_name' => 'default', 'description' => 'Activity in outlet 1', 'subject_type' => get_class($bottle), 'subject_id' => $bottle->id, 'causer_type' => get_class($this->user), 'causer_id' => $this->user->id, 'properties' => [ 'outlet_id' => $outlet1->id, 'previous_stock' => 10, 'quantity_change' => 5, 'new_stock' => 15, ], 'event' => AuditStockStatus::INCREASED->value, ]); Activity::create([ 'log_name' => 'default', 'description' => 'Activity in outlet 2', 'subject_type' => get_class($bottle), 'subject_id' => $bottle->id, 'causer_type' => get_class($this->user), 'causer_id' => $this->user->id, 'properties' => [ 'outlet_id' => $outlet2->id, 'previous_stock' => 20, 'quantity_change' => -3, 'new_stock' => 17, ], 'event' => AuditStockStatus::DECREASED->value, ]); $component1 = mountBottleAuditComponent($this->user, $outlet1, $bottle); $component2 = mountBottleAuditComponent($this->user, $outlet2, $bottle); expect($component1->activityLogs)->toHaveCount(1); expect($component1->activityLogs->first()->description)->toBe('Activity in outlet 1'); expect($component2->activityLogs)->toHaveCount(1); expect($component2->activityLogs->first()->description)->toBe('Activity in outlet 2'); }); /* |-------------------------------------------------------------------------- | Edge Cases and Error Handling |-------------------------------------------------------------------------- */ it('handles bottle activity logs with missing properties gracefully', function () { $bottle = Bottle::factory()->create(); $outlet = $this->outlets->first(); // Create activity with incomplete properties (shouldn't happen in real usage) Activity::create([ 'log_name' => 'default', 'description' => 'Incomplete activity', 'subject_type' => get_class($bottle), 'subject_id' => $bottle->id, 'causer_type' => get_class($this->user), 'causer_id' => $this->user->id, 'properties' => [ 'outlet_id' => $outlet->id, 'previous_stock' => 0, // Add required properties 'quantity_change' => 0, 'new_stock' => 0, ], 'event' => AuditStockStatus::UPDATED->value, ]); $component = mountBottleAuditComponent($this->user, $outlet, $bottle); expect($component->activityLogs)->toHaveCount(1); // Component should render correctly with all required properties }); it('handles bottle activities with null causer', function () { $bottle = Bottle::factory()->create(); $outlet = $this->outlets->first(); Activity::create([ 'log_name' => 'default', 'description' => 'Activity with no causer', 'subject_type' => get_class($bottle), 'subject_id' => $bottle->id, 'causer_type' => null, 'causer_id' => null, 'properties' => [ 'outlet_id' => $outlet->id, 'previous_stock' => 5, 'quantity_change' => 5, 'new_stock' => 10, ], 'event' => AuditStockStatus::INCREASED->value, ]); $component = mountBottleAuditComponent($this->user, $outlet, $bottle); expect($component->activityLogs)->toHaveCount(1); expect($component->activityLogs->first()->causer)->toBeNull(); });