create(); $tenant = ManagedEnvironment::factory()->active()->create([ 'workspace_id' => (int) $workspace->getKey(), ]); $owner = User::factory()->create(); $member = User::factory()->create(['name' => 'Scoped Member']); WorkspaceMembership::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'user_id' => (int) $owner->getKey(), 'role' => 'owner', ]); WorkspaceMembership::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'user_id' => (int) $member->getKey(), 'role' => 'readonly', ]); Livewire::actingAs($owner) ->test(TenantMembershipsRelationManager::class, [ 'ownerRecord' => $tenant, 'pageClass' => ManageTenantMemberships::class, ]) ->callTableAction('add_member', null, [ 'user_id' => (int) $member->getKey(), 'role' => 'owner', ]); $scope = ManagedEnvironmentMembership::query() ->where('managed_environment_id', (int) $tenant->getKey()) ->where('user_id', (int) $member->getKey()) ->first(); expect($scope)->not->toBeNull() ->and($scope?->role)->toBe('readonly') ->and(AuditLog::query()->where('action', AuditActionId::ManagedEnvironmentAccessScopeGrant->value)->exists())->toBeTrue(); }); it('keeps explicit scope removal destructive and audit logged', function (): void { $workspace = Workspace::factory()->create(); $tenant = ManagedEnvironment::factory()->active()->create([ 'workspace_id' => (int) $workspace->getKey(), ]); $owner = User::factory()->create(); $member = User::factory()->create(); WorkspaceMembership::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'user_id' => (int) $owner->getKey(), 'role' => 'owner', ]); WorkspaceMembership::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'user_id' => (int) $member->getKey(), 'role' => 'operator', ]); $scope = ManagedEnvironmentMembership::query()->create([ 'managed_environment_id' => (int) $tenant->getKey(), 'user_id' => (int) $member->getKey(), 'role' => 'operator', 'source' => 'manual', ]); Livewire::actingAs($owner) ->test(TenantMembershipsRelationManager::class, [ 'ownerRecord' => $tenant, 'pageClass' => ManageTenantMemberships::class, ]) ->assertTableActionExists('remove', fn (Action $action): bool => $action->isConfirmationRequired(), $scope) ->callTableAction('remove', $scope); expect(ManagedEnvironmentMembership::query()->whereKey($scope->getKey())->exists())->toBeFalse() ->and(AuditLog::query()->where('action', AuditActionId::ManagedEnvironmentAccessScopeRemove->value)->exists())->toBeTrue(); }); it('rejects direct role changes on managed-environment access scopes', function (): void { $workspace = Workspace::factory()->create(); $tenant = ManagedEnvironment::factory()->active()->create([ 'workspace_id' => (int) $workspace->getKey(), ]); $owner = User::factory()->create(); $member = User::factory()->create(); WorkspaceMembership::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'user_id' => (int) $owner->getKey(), 'role' => 'owner', ]); WorkspaceMembership::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'user_id' => (int) $member->getKey(), 'role' => 'operator', ]); $scope = ManagedEnvironmentMembership::query()->create([ 'managed_environment_id' => (int) $tenant->getKey(), 'user_id' => (int) $member->getKey(), 'role' => 'operator', 'source' => 'manual', ]); expect(fn () => app(TenantMembershipManager::class)->changeRole($tenant, $owner, $scope, 'owner')) ->toThrow(DomainException::class, 'Managed-environment access scopes do not manage roles. Change the workspace role instead.'); });