Files
hub/tests/Feature/Analytics/AnalyticsSecurityTest.php
Jon Leopard 5c1863218f fix: optimize test suite performance with DatabaseTransactions
This commit completes the PR #53 integration by optimizing the test suite:

Performance Improvements:
- Migrated 25 test files from RefreshDatabase to DatabaseTransactions
- Tests now run in 12.69s parallel (previously 30s+)
- Increased PostgreSQL max_locks_per_transaction to 256 for parallel testing

Test Infrastructure Changes:
- Disabled broadcasting in tests (set to null) to avoid Reverb connectivity issues
- Reverted 5 integration tests to RefreshDatabase (CheckoutFlowTest + 4 Service tests)
  that require full schema recreation due to complex fixtures

PR #53 Integration Fixes:
- Added Product.inStock() scope for inventory queries
- Fixed ProductFactory to create InventoryItem records instead of using removed columns
- Added Department.products() relationship
- Fixed FulfillmentWorkOrderController view variables
- Fixed orders migration location_id foreign key constraint
- Created seller-layout component wrapper

All 146 tests now pass with optimal performance.
2025-11-17 20:52:50 -07:00

221 lines
6.8 KiB
PHP

<?php
namespace Tests\Feature\Analytics;
use App\Models\Analytics\AnalyticsEvent;
use App\Models\Analytics\BuyerEngagementScore;
use App\Models\Analytics\ProductView;
use App\Models\Business;
use App\Models\User;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Tests\TestCase;
class AnalyticsSecurityTest extends TestCase
{
use DatabaseTransactions;
protected User $sellerUser1;
protected User $sellerUser2;
protected Business $business1;
protected Business $business2;
protected function setUp(): void
{
parent::setUp();
// Create two separate businesses and users
$this->business1 = Business::factory()->create(['name' => 'Business 1']);
$this->business2 = Business::factory()->create(['name' => 'Business 2']);
$this->sellerUser1 = User::factory()->create(['user_type' => 'seller']);
$this->sellerUser2 = User::factory()->create(['user_type' => 'seller']);
// Attach users to their businesses
$this->sellerUser1->businesses()->attach($this->business1->id, [
'is_primary' => true,
'permissions' => ['analytics.overview', 'analytics.products'],
]);
$this->sellerUser2->businesses()->attach($this->business2->id, [
'is_primary' => true,
'permissions' => ['analytics.overview'],
]);
}
/** @test */
public function analytics_events_are_scoped_to_business()
{
// Create events for both businesses
$event1 = AnalyticsEvent::create([
'business_id' => $this->business1->id,
'event_type' => 'product_view',
'event_category' => 'product',
'event_action' => 'view',
]);
$event2 = AnalyticsEvent::create([
'business_id' => $this->business2->id,
'event_type' => 'product_view',
'event_category' => 'product',
'event_action' => 'view',
]);
// Login as business 1 user
$this->actingAs($this->sellerUser1);
// Should only see business 1 events
$events = AnalyticsEvent::all();
$this->assertCount(1, $events);
$this->assertEquals($this->business1->id, $events->first()->business_id);
// Login as business 2 user
$this->actingAs($this->sellerUser2);
// Should only see business 2 events
$events = AnalyticsEvent::all();
$this->assertCount(1, $events);
$this->assertEquals($this->business2->id, $events->first()->business_id);
}
/** @test */
public function product_views_are_scoped_to_business()
{
// Create product views for both businesses
ProductView::create([
'business_id' => $this->business1->id,
'product_id' => 1,
'viewed_at' => now(),
]);
ProductView::create([
'business_id' => $this->business2->id,
'product_id' => 2,
'viewed_at' => now(),
]);
// Login as business 1 user
$this->actingAs($this->sellerUser1);
$this->assertCount(1, ProductView::all());
// Login as business 2 user
$this->actingAs($this->sellerUser2);
$this->assertCount(1, ProductView::all());
}
/** @test */
public function buyer_engagement_scores_are_scoped_to_business()
{
// Create scores for both businesses
BuyerEngagementScore::create([
'business_id' => $this->business1->id,
'buyer_business_id' => 100,
'total_score' => 85,
]);
BuyerEngagementScore::create([
'business_id' => $this->business2->id,
'buyer_business_id' => 200,
'total_score' => 75,
]);
// Login as business 1 user
$this->actingAs($this->sellerUser1);
$scores = BuyerEngagementScore::all();
$this->assertCount(1, $scores);
$this->assertEquals(100, $scores->first()->buyer_business_id);
// Login as business 2 user
$this->actingAs($this->sellerUser2);
$scores = BuyerEngagementScore::all();
$this->assertCount(1, $scores);
$this->assertEquals(200, $scores->first()->buyer_business_id);
}
/** @test */
public function users_cannot_access_analytics_without_permission()
{
// User 2 doesn't have analytics.products permission
$this->actingAs($this->sellerUser2);
$response = $this->get(route('seller.business.analytics.products', $this->business2->slug));
$response->assertStatus(403);
}
/** @test */
public function users_can_access_analytics_with_permission()
{
// User 1 has analytics.products permission
$this->actingAs($this->sellerUser1);
$response = $this->get(route('seller.business.analytics.products', $this->business1->slug));
$response->assertStatus(200);
}
/** @test */
public function users_cannot_access_other_business_analytics()
{
$this->actingAs($this->sellerUser1);
// Try to access business 2's analytics
$response = $this->get(route('seller.business.analytics.index', $this->business2->slug));
$response->assertStatus(403);
}
/** @test */
public function for_business_scope_removes_global_scope()
{
// Create events for both businesses
AnalyticsEvent::create([
'business_id' => $this->business1->id,
'event_type' => 'test1',
'event_category' => 'test',
'event_action' => 'test',
]);
AnalyticsEvent::create([
'business_id' => $this->business2->id,
'event_type' => 'test2',
'event_category' => 'test',
'event_action' => 'test',
]);
// Login as business 1 user
$this->actingAs($this->sellerUser1);
// Using forBusiness scope should allow access to specific business data
$events = AnalyticsEvent::forBusiness($this->business1->id)->get();
$this->assertCount(1, $events);
$this->assertEquals($this->business1->id, $events->first()->business_id);
}
/** @test */
public function unauthenticated_users_cannot_access_analytics()
{
$response = $this->get(route('seller.business.analytics.index', $this->business1->slug));
$response->assertRedirect(route('login'));
}
/** @test */
public function analytics_models_auto_set_business_id_on_creation()
{
$this->actingAs($this->sellerUser1);
// Create event without specifying business_id
$event = AnalyticsEvent::create([
'event_type' => 'test',
'event_category' => 'test',
'event_action' => 'test',
]);
// Should auto-set to current user's business
$this->assertEquals($this->business1->id, $event->business_id);
}
}