tests/Plugins/PluginSteps.Tests.ps1

<#
    Integration Test: Plugin Steps (brief W08)
    Registers a no-op plug-in compiled on the fly (signed .NET Framework assembly), with one step and one image, then tests
    Get-XrmPluginSteps, Disable-XrmPluginStep, Enable-XrmPluginStep and Remove-XrmPluginAssembly.
    The test never modifies Microsoft steps: the platform refuses it.
#>

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

$suffix = Get-Random -Minimum 10000 -Maximum 99999;
$assemblyName = "PdoTest.Plugins$suffix";
$source = @"
using System;
using Microsoft.Xrm.Sdk;
namespace PdoTest
{
    public class NoOpPlugin : IPlugin
    {
        public void Execute(IServiceProvider serviceProvider) { }
    }
}
"@
;

# ============================================================
# Setup: assembly, plug-in type, step, image
# ============================================================
Write-Section "Setup - register a test plug-in";

$assemblyPath = New-TestPluginAssembly -AssemblyName $assemblyName -Source $source;
if (-not $assemblyPath) {
    Write-Host " [SKIP] .NET Framework compiler not available: plug-in tests skipped" -ForegroundColor Yellow;
    Write-TestSummary;
    return;
}
$Global:XrmClient | Upsert-XrmAssembly -AssemblyPath $assemblyPath;
$assembly = $Global:XrmClient | Get-XrmRecord -LogicalName "pluginassembly" -AttributeName "name" -Value $assemblyName -Columns "name";
Assert-Test "Assembly registered" { $null -ne $assembly };

$typeId = $Global:XrmClient | Add-XrmRecord -Record (New-XrmEntity -LogicalName "plugintype" -Attributes @{
        typename = "PdoTest.NoOpPlugin"; name = "PdoTest.NoOpPlugin"; friendlyname = "NoOpPlugin"; pluginassemblyid = $assembly.Reference
    });
$message = $Global:XrmClient | Get-XrmRecord -LogicalName "sdkmessage" -AttributeName "name" -Value "Create" -Columns "name";
$filterQuery = New-XrmQueryExpression -LogicalName "sdkmessagefilter" -Columns "primaryobjecttypecode";
$filterQuery = $filterQuery | Add-XrmQueryCondition -Field "sdkmessageid" -Condition Equal -Values $message.Id;
$filterQuery = $filterQuery | Add-XrmQueryCondition -Field "primaryobjecttypecode" -Condition Equal -Values "letter";
$filter = @($Global:XrmClient | Get-XrmMultipleRecords -Query $filterQuery)[0];
$stepId = $Global:XrmClient | Add-XrmRecord -Record (New-XrmEntity -LogicalName "sdkmessageprocessingstep" -Attributes @{
        name                = "PdoTest NoOp: Create of letter";
        eventhandler        = (New-XrmEntityReference -LogicalName "plugintype" -Id $typeId);
        sdkmessageid        = $message.Reference;
        sdkmessagefilterid  = $filter.Reference;
        stage               = (New-XrmOptionSetValue -Value 40);
        mode                = (New-XrmOptionSetValue -Value 0);
        rank                = 1;
        supporteddeployment = (New-XrmOptionSetValue -Value 0);
    });
$imageId = $Global:XrmClient | Add-XrmRecord -Record (New-XrmEntity -LogicalName "sdkmessageprocessingstepimage" -Attributes @{
        sdkmessageprocessingstepid = (New-XrmEntityReference -LogicalName "sdkmessageprocessingstep" -Id $stepId);
        imagetype                  = (New-XrmOptionSetValue -Value 1);
        messagepropertyname        = "Id";
        entityalias                = "post";
        name                       = "post";
        attributes                 = "subject";
    });
Assert-Test "Plug-in type, step and image registered" { $typeId -ne [Guid]::Empty -and $stepId -ne [Guid]::Empty -and $imageId -ne [Guid]::Empty };

# ============================================================
# Get-XrmPluginSteps
# ============================================================
Write-Section "Get-XrmPluginSteps";

$byAssembly = @(Get-XrmPluginSteps -XrmClient $Global:XrmClient -AssemblyName $assemblyName);
Assert-Test "-AssemblyName: the test step, with its assembly, type, message and table" {
    $byAssembly.Count -eq 1 -and $byAssembly[0].Id -eq $stepId -and $byAssembly[0].AssemblyName -eq $assemblyName -and $byAssembly[0].PluginTypeName -eq "PdoTest.NoOpPlugin" -and $byAssembly[0].MessageName -eq "Create" -and $byAssembly[0].EntityLogicalName -eq "letter";
};

$byTable = @(Get-XrmPluginSteps -XrmClient $Global:XrmClient -EntityLogicalName "letter" -MessageName "Create" -ActiveOnly -CustomOnly);
Assert-Test "-EntityLogicalName -MessageName -ActiveOnly -CustomOnly: includes the test step, every row matches" {
    @($byTable | Where-Object { $_.Id -eq $stepId }).Count -eq 1 -and @($byTable | Where-Object { $_.EntityLogicalName -ne "letter" -or $_.MessageName -ne "Create" -or $_.statecode_Value.Value -ne 0 }).Count -eq 0;
};

$custom = @(Get-XrmPluginSteps -XrmClient $Global:XrmClient -CustomOnly);
Assert-Test "-CustomOnly: no Microsoft assembly, no hidden step" {
    @($custom | Where-Object { $_.AssemblyName -like "Microsoft.*" -or $_.Record["ishidden"].Value }).Count -eq 0 -and @($custom | Where-Object { $_.Id -eq $stepId }).Count -eq 1;
};

# ============================================================
# Disable-XrmPluginStep / Enable-XrmPluginStep
# ============================================================
Write-Section "Disable-XrmPluginStep / Enable-XrmPluginStep";

$stepRef = $byAssembly[0].Reference;
$Global:XrmClient | Disable-XrmPluginStep -PluginStepReference $stepRef | Out-Null;
$inactive = @(Get-XrmPluginSteps -XrmClient $Global:XrmClient -AssemblyName $assemblyName -ActiveOnly);
Assert-Test "Disable-XrmPluginStep: step disabled, excluded by -ActiveOnly" { $inactive.Count -eq 0 };

$Global:XrmClient | Enable-XrmPluginStep -PluginStepReference $stepRef | Out-Null;
$active = @(Get-XrmPluginSteps -XrmClient $Global:XrmClient -AssemblyName $assemblyName -ActiveOnly);
Assert-Test "Enable-XrmPluginStep: step enabled again" { $active.Count -eq 1 };

# ============================================================
# Remove-XrmPluginAssembly
# ============================================================
Write-Section "Remove-XrmPluginAssembly";

$managedAssembly = @($Global:XrmClient | Get-XrmMultipleRecords -Query (New-XrmQueryExpression -LogicalName "pluginassembly" -Columns "name" -TopCount 1 | Add-XrmQueryCondition -Field "ismanaged" -Condition Equal -Values $true))[0];
$managedError = $null;
try { Remove-XrmPluginAssembly -XrmClient $Global:XrmClient -Name $managedAssembly.name -Force -WhatIf | Out-Null; } catch { $managedError = $_.Exception.Message; }
Assert-Test "Managed assembly: refused before any deletion" { $managedError -like "*is managed*" };

$stepsError = $null;
try { Remove-XrmPluginAssembly -XrmClient $Global:XrmClient -Name $assemblyName | Out-Null; } catch { $stepsError = $_.Exception.Message; }
Assert-Test "Without -Force: refused, the step is kept" { $stepsError -like "*1 step*-Force*" -and $null -ne ($Global:XrmClient | Get-XrmRecord -LogicalName "sdkmessageprocessingstep" -Id $stepId -IfExists) };

$removed = Remove-XrmPluginAssembly -XrmClient $Global:XrmClient -Name $assemblyName -Force;
Assert-Test "-Force: step, type and assembly deleted" {
    $removed.StepsRemoved -eq 1 -and $removed.TypesRemoved -eq 1 -and
    $null -eq ($Global:XrmClient | Get-XrmRecord -LogicalName "sdkmessageprocessingstep" -Id $stepId -IfExists) -and
    $null -eq ($Global:XrmClient | Get-XrmRecord -LogicalName "sdkmessageprocessingstepimage" -Id $imageId -IfExists) -and
    $null -eq ($Global:XrmClient | Get-XrmRecord -LogicalName "plugintype" -Id $typeId -IfExists) -and
    $null -eq ($Global:XrmClient | Get-XrmRecord -LogicalName "pluginassembly" -Id $assembly.Id -IfExists);
};

$missingError = $null;
try { Remove-XrmPluginAssembly -XrmClient $Global:XrmClient -Name $assemblyName | Out-Null; } catch { $missingError = $_.Exception.Message; }
Assert-Test "Missing assembly: error" { $missingError -like "*not found*" };

# Safety net when an assertion above failed
if ($Global:XrmClient | Get-XrmRecord -LogicalName "pluginassembly" -Id $assembly.Id -IfExists) {
    try { Remove-XrmPluginAssembly -XrmClient $Global:XrmClient -Name $assemblyName -Force | Out-Null; } catch { Write-Host " Cleanup failed: $($_.Exception.Message)" -ForegroundColor Red; }
}
Remove-Item -Path (Split-Path $assemblyPath -Parent) -Recurse -Force -ErrorAction SilentlyContinue;

Write-TestSummary;