create(); WorkspaceMembership::factory()->create([ 'workspace_id' => (int) $tenant->workspace_id, 'user_id' => (int) $member->getKey(), 'role' => 'readonly', ]); /** @var TenantMembershipManager $manager */ $manager = app(TenantMembershipManager::class); $membership = $manager->grantScope( tenant: $tenant, actor: $owner, member: $member, source: 'manual', ); $manager->removeMember( tenant: $tenant, actor: $owner, membership: $membership, ); $logs = AuditLog::query() ->where('managed_environment_id', $tenant->id) ->whereIn('action', [ AuditActionId::ManagedEnvironmentAccessScopeGrant->value, AuditActionId::ManagedEnvironmentAccessScopeRemove->value, ]) ->get() ->keyBy('action'); expect($logs)->toHaveCount(2); $grantLog = $logs->get(AuditActionId::ManagedEnvironmentAccessScopeGrant->value); $removeLog = $logs->get(AuditActionId::ManagedEnvironmentAccessScopeRemove->value); expect($grantLog)->not->toBeNull(); expect($removeLog)->not->toBeNull(); expect($grantLog->status)->toBe('success'); expect($removeLog->status)->toBe('success'); expect($grantLog->metadata) ->toHaveKey('member_user_id', $member->id) ->toHaveKey('workspace_role', 'readonly') ->toHaveKey('source', 'manual') ->not->toHaveKey('member_email') ->not->toHaveKey('member_name'); expect($removeLog->metadata) ->toHaveKey('member_user_id', $member->id) ->not->toHaveKey('member_email') ->not->toHaveKey('member_name'); }); it('rejects managed-environment role-change attempts without writing role-change audit truth', function () { [$owner, $tenant] = createUserWithTenant(role: 'owner'); $membership = ManagedEnvironmentMembership::query() ->where('managed_environment_id', $tenant->id) ->where('user_id', $owner->id) ->firstOrFail(); /** @var TenantMembershipManager $manager */ $manager = app(TenantMembershipManager::class); expect(fn () => $manager->changeRole( tenant: $tenant, actor: $owner, membership: $membership, newRole: 'manager', ))->toThrow(DomainException::class); expect(AuditLog::query() ->where('managed_environment_id', $tenant->id) ->where('action', AuditActionId::TenantMembershipRoleChange->value) ->exists())->toBeFalse(); });