create(); [$user, $tenantA] = createUserWithTenant($tenantA, role: 'owner'); $tenantB = Tenant::factory()->create([ 'status' => 'active', 'workspace_id' => (int) $tenantA->workspace_id, ]); $user->tenants()->syncWithoutDetaching([ $tenantB->getKey() => ['role' => 'owner'], ]); OperationRun::factory()->create([ 'tenant_id' => (int) $tenantA->getKey(), 'workspace_id' => (int) $tenantA->workspace_id, 'type' => 'policy.sync', 'status' => 'queued', 'outcome' => 'pending', ]); OperationRun::factory()->create([ 'tenant_id' => (int) $tenantB->getKey(), 'workspace_id' => (int) $tenantB->workspace_id, 'type' => 'inventory.sync', 'status' => 'queued', 'outcome' => 'pending', ]); Filament::setTenant($tenantA, true); $this->actingAs($user) ->withSession([WorkspaceContext::SESSION_KEY => (int) $tenantA->workspace_id]) ->get('/admin/operations') ->assertOk() ->assertSee('Policy sync') ->assertDontSee('Inventory sync'); }); test('operation run view is not accessible cross-workspace', function (): void { $workspaceA = Workspace::factory()->create(); $workspaceB = Workspace::factory()->create(); $user = User::factory()->create(); WorkspaceMembership::factory()->create([ 'workspace_id' => (int) $workspaceA->getKey(), 'user_id' => (int) $user->getKey(), 'role' => 'owner', ]); $tenantB = Tenant::factory()->create([ 'status' => 'active', 'workspace_id' => (int) $workspaceB->getKey(), ]); $runB = OperationRun::factory()->create([ 'tenant_id' => (int) $tenantB->getKey(), 'workspace_id' => (int) $workspaceB->getKey(), 'type' => 'inventory.sync', 'status' => 'queued', 'outcome' => 'pending', ]); $this->actingAs($user) ->withSession([WorkspaceContext::SESSION_KEY => (int) $workspaceA->getKey()]) ->get(route('admin.operations.view', ['run' => (int) $runB->getKey()])) ->assertNotFound(); }); test('readonly users can view operation runs in their workspace', function (): void { $tenant = Tenant::factory()->create(); $run = OperationRun::factory()->create([ 'tenant_id' => (int) $tenant->getKey(), 'workspace_id' => (int) $tenant->workspace_id, 'type' => 'drift.generate', 'status' => 'queued', 'outcome' => 'pending', ]); [$user, $tenant] = createUserWithTenant($tenant, role: 'readonly'); Filament::setTenant(null, true); $this->actingAs($user) ->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id]) ->get('/admin/operations') ->assertOk() ->assertSee('Drift generation'); $this->actingAs($user) ->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id]) ->get(route('admin.operations.view', ['run' => (int) $run->getKey()])) ->assertOk() ->assertSee('Operation run'); });