diff --git a/src/IdLE.Core/Public/Invoke-IdleProviderMethod.ps1 b/src/IdLE.Core/Public/Invoke-IdleProviderMethod.ps1 index 11540f7c..1dbd3947 100644 --- a/src/IdLE.Core/Public/Invoke-IdleProviderMethod.ps1 +++ b/src/IdLE.Core/Public/Invoke-IdleProviderMethod.ps1 @@ -66,13 +66,21 @@ function Invoke-IdleProviderMethod { [string] $MethodName, [Parameter(Mandatory)] + [AllowNull()] [AllowEmptyCollection()] [object[]] $MethodArguments ) # Auth session acquisition (optional, data-only) $authSession = $null - + + # [AllowNull()] on $MethodArguments is required so PowerShell's parameter binder accepts + # [object[]] arrays that contain $null elements (e.g., a $null attribute value to clear). + # However, the array reference itself must never be null - that would indicate a caller bug. + if ($null -eq $MethodArguments) { + throw "MethodArguments must not be null. Pass an empty array if no arguments are needed." + } + # Validate AuthSessionOptions early (regardless of broker availability) if ($With.ContainsKey('AuthSessionOptions')) { $sessionOptions = $With.AuthSessionOptions diff --git a/tests/Steps/Invoke-IdleStepEnsureAttributes.Tests.ps1 b/tests/Steps/Invoke-IdleStepEnsureAttributes.Tests.ps1 index 50ec784d..636b7aba 100644 --- a/tests/Steps/Invoke-IdleStepEnsureAttributes.Tests.ps1 +++ b/tests/Steps/Invoke-IdleStepEnsureAttributes.Tests.ps1 @@ -322,6 +322,38 @@ Describe 'Invoke-IdleStepEnsureAttributes (built-in step)' { $attributeNames | Should -Contain 'Title' $attributeNames | Should -Contain 'Office' } + + It 'passes $null attribute values without error (regression: MethodArguments binding)' { + # When attributes contain $null values (e.g., to unset/clear an attribute), + # Invoke-IdleProviderMethod must not fail with "Cannot bind argument to parameter + # 'MethodArguments' because it is null." - caused by PS binding an [object[]] + # containing $null elements to a [Mandatory] parameter without [AllowNull()]. + $step = [pscustomobject]@{ + Name = 'Unset phone attributes' + Type = 'IdLE.Step.EnsureAttributes' + With = @{ + Provider = 'Identity' + IdentityKey = 'user@contoso.com' + Attributes = @{ + mobile = $null + telephoneNumber = $null + } + } + } + + $handler = 'IdLE.Steps.Common\Invoke-IdleStepEnsureAttributes' + { & $handler -Context $script:Context -Step $step } | Should -Not -Throw + + # Both attributes should have been called with $null value + $callsByName = @{} + $script:FakeProviderLegacy.CallLog | ForEach-Object { + $callsByName[$_.Name] = $_ + } + $callsByName.Keys | Should -Contain 'mobile' + $callsByName.Keys | Should -Contain 'telephoneNumber' + $callsByName['mobile'].Value | Should -BeNullOrEmpty + $callsByName['telephoneNumber'].Value | Should -BeNullOrEmpty + } } Context 'StepResult shape' {