create(); $this->actingAs($user) ->get(route('filament.admin.resources.policies.index', filamentTenantRouteParams($unauthorizedTenant))) ->assertNotFound(); }); test('tenant portfolio tenant view returns 404 for non-member tenant record', function () { $user = User::factory()->create(); $this->actingAs($user); $authorizedTenant = Tenant::factory()->create(['tenant_id' => 'tenant-portfolio-authorized-view']); $unauthorizedTenant = Tenant::factory()->create(['tenant_id' => 'tenant-portfolio-unauthorized-view']); $user->tenants()->syncWithoutDetaching([ $authorizedTenant->getKey() => ['role' => 'owner'], ]); $this->get(route('filament.admin.resources.tenants.view', array_merge( filamentTenantRouteParams($unauthorizedTenant), ['record' => $unauthorizedTenant], )))->assertNotFound(); }); test('tenant portfolio tenant edit returns 404 for non-member tenant record', function () { $user = User::factory()->create(); $this->actingAs($user); $authorizedTenant = Tenant::factory()->create(['tenant_id' => 'tenant-portfolio-authorized-edit']); $unauthorizedTenant = Tenant::factory()->create(['tenant_id' => 'tenant-portfolio-unauthorized-edit']); $user->tenants()->syncWithoutDetaching([ $authorizedTenant->getKey() => ['role' => 'owner'], ]); $this->get(route('filament.admin.resources.tenants.edit', array_merge( filamentTenantRouteParams($unauthorizedTenant), ['record' => $unauthorizedTenant], )))->assertNotFound(); }); test('tenant portfolio lists only tenants the user can access', function () { $user = User::factory()->create(); $this->actingAs($user); $authorizedTenant = Tenant::factory()->create([ 'tenant_id' => 'tenant-portfolio-authorized', 'name' => 'Authorized Tenant', ]); $unauthorizedTenant = Tenant::factory()->create([ 'tenant_id' => 'tenant-portfolio-unauthorized', 'name' => 'Unauthorized Tenant', ]); $user->tenants()->syncWithoutDetaching([ $authorizedTenant->getKey() => ['role' => 'owner'], ]); $this->get(route('filament.admin.resources.tenants.index', filamentTenantRouteParams($authorizedTenant))) ->assertOk() ->assertSee($authorizedTenant->name) ->assertDontSee($unauthorizedTenant->name); }); test('tenant portfolio bulk sync dispatches one job per eligible tenant', function () { Bus::fake(); $user = User::factory()->create(); $this->actingAs($user); $tenantA = Tenant::factory()->create(['tenant_id' => 'tenant-bulk-a']); $tenantB = Tenant::factory()->create(['tenant_id' => 'tenant-bulk-b']); $user->tenants()->syncWithoutDetaching([ $tenantA->getKey() => ['role' => 'owner'], $tenantB->getKey() => ['role' => 'operator'], ]); Filament::setTenant($tenantA, true); Livewire::test(ListTenants::class) ->assertTableBulkActionVisible('syncSelected') ->callTableBulkAction('syncSelected', collect([$tenantA, $tenantB])); Bus::assertDispatchedTimes(BulkTenantSyncJob::class, 1); $this->assertDatabaseHas('operation_runs', [ 'tenant_id' => $tenantA->id, 'user_id' => $user->id, 'type' => 'tenant.sync', 'status' => 'queued', ]); }); test('tenant portfolio bulk sync is disabled for readonly users', function () { Bus::fake(); $user = User::factory()->create(); $this->actingAs($user); $tenant = Tenant::factory()->create(['tenant_id' => 'tenant-bulk-readonly']); $user->tenants()->syncWithoutDetaching([ $tenant->getKey() => ['role' => 'readonly'], ]); Filament::setTenant($tenant, true); $livewire = Livewire::actingAs($user) ->test(ListTenants::class) ->selectTableRecords([$tenant]) ->assertTableBulkActionVisible('syncSelected') ->assertTableBulkActionDisabled('syncSelected'); $actions = $livewire->parseNestedTableBulkActions('syncSelected'); $livewire->assertActionExists($actions, fn ($action): bool => $action->getTooltip() === UiTooltips::insufficientPermission()); $livewire->callTableBulkAction('syncSelected', collect([$tenant])); Bus::assertNotDispatched(BulkTenantSyncJob::class); }); test('tenant portfolio bulk sync is disabled when selection includes unauthorized tenants', function () { Bus::fake(); $user = User::factory()->create(); $this->actingAs($user); $tenantA = Tenant::factory()->create(['tenant_id' => 'tenant-bulk-mixed-a']); $tenantB = Tenant::factory()->create(['tenant_id' => 'tenant-bulk-mixed-b']); $user->tenants()->syncWithoutDetaching([ $tenantA->getKey() => ['role' => 'owner'], $tenantB->getKey() => ['role' => 'readonly'], ]); Filament::setTenant($tenantA, true); $livewire = Livewire::actingAs($user) ->test(ListTenants::class) ->selectTableRecords([$tenantA, $tenantB]) ->assertTableBulkActionVisible('syncSelected') ->assertTableBulkActionDisabled('syncSelected'); $actions = $livewire->parseNestedTableBulkActions('syncSelected'); $livewire->assertActionExists($actions, fn ($action): bool => $action->getTooltip() === UiTooltips::insufficientPermission()); $livewire->callTableBulkAction('syncSelected', collect([$tenantA, $tenantB])); Bus::assertNotDispatched(BulkTenantSyncJob::class); }); test('tenant set event updates user tenant preference last used timestamp', function () { [$user, $tenant] = createUserWithTenant(); TenantSet::dispatch($tenant, $user); $this->assertDatabaseHas('user_tenant_preferences', [ 'user_id' => $user->id, 'tenant_id' => $tenant->id, ]); });