tests/Client/ScriptBlockScope.Tests.ps1

<#
    Static Test: scriptblock scope
    A scriptblock passed to a command (ForEach-ObjectWithProgress, Invoke-XrmQueryPagesInternal, Watch-XrmAsynchOperation...)
    runs in its own scope: there, $PSBoundParameters is the scriptblock one, so a test such as
    $PSBoundParameters.ContainsKey("TopCount") is always false. Read it before the scriptblock.
#>

. "$PSScriptRoot\..\_TestConfig.ps1";

Write-Section "PSBoundParameters inside passed scriptblocks";

$srcPath = Join-Path $PSScriptRoot "..\..\src" | Resolve-Path;
$violations = [System.Collections.Generic.List[string]]::new();
foreach ($file in Get-ChildItem -Path $srcPath -Recurse -Include "*.ps1") {
    $ast = [System.Management.Automation.Language.Parser]::ParseFile($file.FullName, [ref]$null, [ref]$null);
    $blocks = $ast.FindAll({
            $args[0] -is [System.Management.Automation.Language.ScriptBlockExpressionAst] -and
            $args[0].Parent -is [System.Management.Automation.Language.CommandAst] -and
            $args[0].Parent.GetCommandName() -ne "Register-ArgumentCompleter"
        }, $true);
    foreach ($block in $blocks) {
        $block.FindAll({ $args[0] -is [System.Management.Automation.Language.VariableExpressionAst] -and $args[0].VariablePath.UserPath -eq "PSBoundParameters" }, $true) | ForEach-Object {
            $violations.Add("$($file.FullName.Substring($srcPath.Path.Length + 1)):$($_.Extent.StartLineNumber) in a $($block.Parent.GetCommandName()) scriptblock");
        };
    }
}

$violations | ForEach-Object { Write-Host " $_" -ForegroundColor Yellow; };
Assert-Test "No `$PSBoundParameters read inside a passed scriptblock ($($violations.Count) found)" {
    $violations.Count -eq 0;
};

Write-TestSummary;