Browse Source

fix(ci): preserve absolute basic-user result paths

Join-Path appended an absolute ResultPath to the repository path,
producing an invalid path and preventing the restricted-token test run.
Use Path.Combine to keep rooted paths intact while retaining repository-
relative paths. Clarify the script parameter help.

The offline process-boundary tests fail with the original expression in
both editions and pass in all four configurations with the fix. They do
not launch a process or modify privileges.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
pull/117/head
Raimund Andree 3 days ago
parent
commit
51dec86ae3
  1. 4
      .github/scripts/Invoke-TestsAsBasicUser.ps1
  2. 81
      Tests/BasicUserRunner.Tests.ps1

4
.github/scripts/Invoke-TestsAsBasicUser.ps1

@ -11,7 +11,7 @@
results through a file of this account, which the restricted token can write. results through a file of this account, which the restricted token can write.
.PARAMETER ResultPath .PARAMETER ResultPath
Specifies the path of the result file in the NUnit format. Specifies an absolute path, or a path relative to the repository, for the result file in the NUnit format.
.PARAMETER Title .PARAMETER Title
Specifies the heading of the test results in the job summary. It can't contain a double quote, a percent sign, or a Specifies the heading of the test results in the job summary. It can't contain a double quote, a percent sign, or a
@ -167,7 +167,7 @@ public static class NTFSSecurityBasicUserProcess
'@ '@
$repositoryPath = (Resolve-Path -LiteralPath (Join-Path -Path $PSScriptRoot -ChildPath '..\..')).ProviderPath $repositoryPath = (Resolve-Path -LiteralPath (Join-Path -Path $PSScriptRoot -ChildPath '..\..')).ProviderPath
$resultFullPath = [IO.Path]::GetFullPath((Join-Path -Path $repositoryPath -ChildPath $ResultPath)) $resultFullPath = [IO.Path]::GetFullPath([IO.Path]::Combine($repositoryPath, $ResultPath))
$resultFolder = Split-Path -Path $resultFullPath -Parent $resultFolder = Split-Path -Path $resultFullPath -Parent
if (-not (Test-Path -LiteralPath $resultFolder)) { if (-not (Test-Path -LiteralPath $resultFolder)) {
New-Item -ItemType Directory -Path $resultFolder | Out-Null New-Item -ItemType Directory -Path $resultFolder | Out-Null

81
Tests/BasicUserRunner.Tests.ps1

@ -0,0 +1,81 @@
<#
Tests the basic-user CI wrapper without creating a process or changing privileges. A fake native process writes
the same result-file boundary as the child; only the Add-Type call of the copied wrapper is mocked.
#>
[Diagnostics.CodeAnalysis.SuppressMessageAttribute(
'PSUseDeclaredVarsMoreThanAssignments', '', Justification = 'Pester shares variables between blocks.'
)]
param ()
BeforeAll {
Import-Module -Name (Join-Path -Path $PSScriptRoot -ChildPath 'TestHelpers.psm1') -Force
$sandbox = New-TestSandbox -Name 'BasicUserWrapper'
$repository = Join-Path -Path $sandbox -ChildPath 'Repository'
$scripts = Join-Path -Path $repository -ChildPath '.github\scripts'
Assert-TestSandboxPath -Sandbox $sandbox -Path $scripts
New-Item -ItemType Directory -Path $scripts -Force | Out-Null
$wrapper = Join-Path -Path $scripts -ChildPath 'Invoke-TestsAsBasicUser.ps1'
Copy-Item -LiteralPath (Join-Path -Path $PSScriptRoot -ChildPath '..\.github\scripts\Invoke-TestsAsBasicUser.ps1') -Destination $wrapper
Add-Type -TypeDefinition @"
using System;
using System.IO;
using System.Text.RegularExpressions;
public static class NTFSSecurityBasicUserProcess
{
public static int Calls;
public static string WorkingDirectory;
public static int Run(string applicationName, string commandLine, string currentDirectory)
{
Calls++;
WorkingDirectory = currentDirectory;
var match = Regex.Match(commandLine, "-ResultPath \"([^\"]+)\"");
if (!match.Success)
throw new InvalidOperationException("The child command has no result path.");
File.WriteAllText(match.Groups[1].Value, "<test-results />");
return 0;
}
}
"@
}
AfterAll {
Remove-TestSandbox -Sandbox $sandbox
}
Describe 'Invoke-TestsAsBasicUser.ps1 result paths' {
BeforeEach {
[NTFSSecurityBasicUserProcess]::Calls = 0
[NTFSSecurityBasicUserProcess]::WorkingDirectory = $null
Mock -CommandName Add-Type -ParameterFilter { $TypeDefinition -like '*class NTFSSecurityBasicUserProcess*' }
}
It 'Should copy the result to an absolute path, also when that path contains spaces' {
$result = Join-Path -Path $sandbox -ChildPath 'Absolute results\Result.xml'
Assert-TestSandboxPath -Sandbox $sandbox -Path $result
& $wrapper -ResultPath $result -Title 'Absolute result path' | Out-Null
Get-Content -LiteralPath $result -Raw | Should -BeExactly '<test-results />'
[NTFSSecurityBasicUserProcess]::Calls | Should -Be 1
[NTFSSecurityBasicUserProcess]::WorkingDirectory | Should -Be $repository
Should -Invoke -CommandName Add-Type -Times 1 -Exactly
}
It 'Should resolve a relative path against the repository, not the caller location' {
$result = Join-Path -Path $repository -ChildPath 'Relative results\Result.xml'
Assert-TestSandboxPath -Sandbox $sandbox -Path $result
Push-Location -LiteralPath $sandbox
try {
& $wrapper -ResultPath 'Relative results\Result.xml' -Title 'Relative result path' | Out-Null
}
finally {
Pop-Location
}
Get-Content -LiteralPath $result -Raw | Should -BeExactly '<test-results />'
[NTFSSecurityBasicUserProcess]::Calls | Should -Be 1
[NTFSSecurityBasicUserProcess]::WorkingDirectory | Should -Be $repository
}
}
Loading…
Cancel
Save