Modules/businessdev.ALbuild.Apps/Private/Invoke-BcNativeContainerTest.ps1
|
function Invoke-BcNativeContainerTest { <# .SYNOPSIS Runs ALbuild's built-in AL test runner inside a container. .DESCRIPTION Internal helper behind Invoke-BcContainerTest. Resolves the credentials and the set of test apps (from a project folder, honouring pipeline.config, or from explicit extension ids), copies the in-container runner payload into the container, and invokes it to produce the JUnit result file at -ResultPathInContainer. The caller copies the file out and parses it. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '', Justification = 'The containerPassword pipeline variable arrives as plain text and must be turned into a PSCredential to open the in-container client session.')] [CmdletBinding()] param( [Parameter(Mandatory)] [string] $Name, [Parameter(Mandatory)] [string] $ResultPathInContainer, [string] $ProjectFolder, [string[]] $ExtensionId, [pscredential] $Credential, [string] $TestSuite = 'DEFAULT', [int] $TestRunnerCodeunitId, [switch] $DisableIsolation, [string] $Tenant = 'default', [string] $CompanyName = '', [string] $Culture = 'en-US', [string] $Auth = 'NavUserPassword', [switch] $AzureDevOps, [string] $DockerExecutable = 'docker' ) # --- Resolve credentials -------------------------------------------------------------------- if (-not $Credential -and $Auth -ne 'Windows') { if ($env:containerUsername -and $env:containerPassword) { $secure = ConvertTo-SecureString -String $env:containerPassword -AsPlainText -Force $Credential = New-Object System.Management.Automation.PSCredential -ArgumentList $env:containerUsername, $secure } else { throw "Credentials are required for '$Auth' authentication. Pass -Credential or set the containerUsername/containerPassword environment variables." } } $userName = '' $password = '' if ($Credential) { $userName = $Credential.UserName $password = $Credential.GetNetworkCredential().Password } # --- Build the list of test apps to run ---------------------------------------------------- # Per-app runner codeunit and suite come from the project config (albuild.json); the cmdlet # parameters act as global overrides: -TestRunnerCodeunitId forces an id for every app, and # -DisableIsolation switches the default runner for apps that do not pin one. $testApps = [System.Collections.Generic.List[object]]::new() if ($ExtensionId) { $runnerId = if ($PSBoundParameters.ContainsKey('TestRunnerCodeunitId')) { $TestRunnerCodeunitId } elseif ($DisableIsolation) { 130451 } else { 130450 } foreach ($id in $ExtensionId) { $testApps.Add([PSCustomObject]@{ ExtensionId = $id; AppName = ''; TestRunnerCodeunitId = $runnerId; TestSuite = $TestSuite }) } } else { if (-not $ProjectFolder) { $ProjectFolder = (Get-Location).Path } $discovered = @(Get-BcTestAppFolder -ProjectFolder $ProjectFolder) if ($discovered.Count -eq 0) { throw "No AL test apps were found under '$ProjectFolder'." } foreach ($app in $discovered) { $cfg = $app.ProjectConfig if ($PSBoundParameters.ContainsKey('TestRunnerCodeunitId')) { $runnerId = $TestRunnerCodeunitId } elseif ($cfg.TestRunnerCodeunitId -gt 0) { $runnerId = $cfg.TestRunnerCodeunitId } elseif ($DisableIsolation) { $runnerId = 130451 } else { $runnerId = $cfg.EffectiveTestRunnerCodeunitId() } $suite = if ($PSBoundParameters.ContainsKey('TestSuite')) { $TestSuite } else { $cfg.TestSuite } Write-ALbuildLog "Test app '$($app.Name)' ($($app.Id)) -> suite '$suite', runner codeunit $runnerId" $testApps.Add([PSCustomObject]@{ ExtensionId = $app.Id; AppName = $app.Name; TestRunnerCodeunitId = $runnerId; TestSuite = $suite }) } } # --- Stage the in-container runner payload -------------------------------------------------- $resourceFolder = Join-Path -Path $script:ModuleRoot -ChildPath 'Resources/TestRunner' $clientContextScript = Join-Path -Path $resourceFolder -ChildPath 'BcTestClientContext.ps1' $runnerScript = Join-Path -Path $resourceFolder -ChildPath 'Invoke-BcAlTestRun.ps1' foreach ($payload in @($clientContextScript, $runnerScript)) { if (-not (Test-Path -LiteralPath $payload)) { throw "Test runner payload '$payload' is missing from the module." } } $resultDirInContainer = Split-Path -Path $ResultPathInContainer -Parent Invoke-BcContainerCommand -ContainerName $Name -DockerExecutable $DockerExecutable ` -Variables @{ ResultDir = $resultDirInContainer } -ScriptBlock { New-Item -ItemType Directory -Path $ResultDir -Force | Out-Null } | Out-Null # Both runner scripts are shared into C:\run\my (same folder), so the runner finds its sibling. [void](Copy-BcFileToContainer -Name $Name -DockerExecutable $DockerExecutable -Source $clientContextScript) $runnerInContainer = Copy-BcFileToContainer -Name $Name -DockerExecutable $DockerExecutable -Source $runnerScript $containerRunnerDir = Split-Path -Path $runnerInContainer -Parent # --- Run the tests in the container --------------------------------------------------------- $azureDevOpsMode = if ($AzureDevOps) { 'warning' } else { 'no' } $output = Invoke-BcContainerCommand -ContainerName $Name -DockerExecutable $DockerExecutable -StreamOutput -Variables @{ RunnerDir = $containerRunnerDir TestApps = @($testApps) TestSuite = $TestSuite JUnitPath = $ResultPathInContainer Tenant = $Tenant CompanyName = $CompanyName Auth = $Auth RunnerUser = $userName RunnerPass = $password Culture = $Culture AzureDevOps = $azureDevOpsMode } -ScriptBlock { $runner = Join-Path $RunnerDir 'Invoke-BcAlTestRun.ps1' & $runner -TestApps @($TestApps) -TestSuite $TestSuite -JUnitResultFileName $JUnitPath ` -Tenant $Tenant -CompanyName $CompanyName -Auth $Auth -UserName $RunnerUser -Password $RunnerPass ` -Culture $Culture -AzureDevOps $AzureDevOps } $null = $output # the runner output already streamed live via -StreamOutput } |