tests/Client/XrmClientPropagation.Tests.ps1

<#
    Static Test: XrmClient propagation
    Every call from a module function to another module function that takes -XrmClient must pass the caller's client:
    otherwise it falls back to $Global:XrmClient, which is empty in a parallel runspace and wrong when a script uses two connections.
    Accepted forms: -XrmClient $x, $XrmClient | Verb-Xrm..., or splatting a hashtable that holds an XrmClient key.
    Not checked: argument completers, and functions that have no client of their own (no XrmClient parameter or variable).
#>

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

Write-Section "Parse module sources";

$srcPath = Join-Path $PSScriptRoot "..\..\src" | Resolve-Path;
$files = Get-ChildItem -Path $srcPath -Recurse -Include "*.ps1";
$fileAsts = @{};
$functionsWithClient = @{};
foreach ($file in $files) {
    $ast = [System.Management.Automation.Language.Parser]::ParseFile($file.FullName, [ref]$null, [ref]$null);
    $fileAsts[$file.FullName] = $ast;
    $ast.FindAll({ $args[0] -is [System.Management.Automation.Language.FunctionDefinitionAst] }, $true) | ForEach-Object {
        $paramBlock = $_.Body.ParamBlock;
        if ($paramBlock -and ($paramBlock.Parameters | Where-Object { $_.Name.VariablePath.UserPath -eq "XrmClient" })) {
            $functionsWithClient[$_.Name] = $true;
        }
    };
}
Assert-Test "Module functions with an XrmClient parameter found ($($functionsWithClient.Count))" {
    $functionsWithClient.Count -gt 100;
};

Write-Section "Check internal calls";

$violations = [System.Collections.Generic.List[string]]::new();
foreach ($filePath in $fileAsts.Keys) {
    $commands = $fileAsts[$filePath].FindAll({ $args[0] -is [System.Management.Automation.Language.CommandAst] }, $true);
    foreach ($command in $commands) {
        $name = $command.GetCommandName();
        if (-not $name -or -not $functionsWithClient.ContainsKey($name)) {
            continue;
        }

        # Enclosing function, and skip completers / attributes
        $parent = $command.Parent;
        $enclosingFunction = $null;
        $skip = $false;
        while ($parent) {
            if ($parent -is [System.Management.Automation.Language.AttributeAst]) { $skip = $true; break; }
            if ($parent -is [System.Management.Automation.Language.CommandAst] -and $parent.GetCommandName() -eq "Register-ArgumentCompleter") { $skip = $true; break; }
            if ($parent -is [System.Management.Automation.Language.FunctionDefinitionAst]) { $enclosingFunction = $parent; break; }
            $parent = $parent.Parent;
        }
        if ($skip -or -not $enclosingFunction) {
            continue;
        }
        $functionText = $enclosingFunction.Extent.Text;
        if ($functionText -notmatch '\$XrmClient\b') {
            continue;
        }

        $bound = $command.CommandElements | Where-Object { $_ -is [System.Management.Automation.Language.CommandParameterAst] -and $_.ParameterName -eq "XrmClient" };
        if ($bound) {
            continue;
        }

        $splats = $command.CommandElements | Where-Object { $_ -is [System.Management.Automation.Language.VariableExpressionAst] -and $_.Splatted };
        if ($splats -and $functionText -match '["'']?XrmClient["'']?\s*=\s*\$XrmClient') {
            continue;
        }

        if ($command.Parent -is [System.Management.Automation.Language.PipelineAst]) {
            $index = $command.Parent.PipelineElements.IndexOf($command);
            if ($index -gt 0 -and $command.Parent.PipelineElements[$index - 1].Extent.Text -match '^\$\w*XrmClient$') {
                continue;
            }
        }

        $relativePath = $filePath.Substring($srcPath.Path.Length + 1);
        $violations.Add("$($relativePath):$($command.Extent.StartLineNumber) $name");
    }
}

$violations | ForEach-Object { Write-Host " missing -XrmClient: $_" -ForegroundColor Yellow; };
Assert-Test "Every internal call passes the caller's XrmClient ($($violations.Count) missing)" {
    $violations.Count -eq 0;
};

Write-Section "Runtime: explicit client with an empty global client";

$client = $Global:XrmClient;
$userId = ($client | Get-XrmWhoAmI);
$Global:XrmClient = $null;
try {
    $user = $client | Get-XrmUser -UserId $userId -Columns "fullname";
    Assert-Test "Get-XrmUser" { $null -ne $user -and $user.Id -eq $userId };

    $roles = @($client | Get-XrmRoles -OnlyRoots -Columns "name");
    Assert-Test "Get-XrmRoles -OnlyRoots" { $roles.Count -gt 0 };

    $privilege = $client | New-XrmRolePrivilege -PrivilegeName "prvReadAccount" -Depth Global;
    Assert-Test "New-XrmRolePrivilege -PrivilegeName" { $null -ne $privilege -and $privilege.PrivilegeId -ne [Guid]::Empty };
}
finally {
    $Global:XrmClient = $client;
}

Write-TestSummary;