Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
152 changes: 151 additions & 1 deletion powershell-runtime/tests/helpers/AssertionHelpers.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -446,4 +446,154 @@ Describe "Assert-FileExists" {
{ Assert-FileExists -Path $unreadableFile -ShouldBeFile } | Should -Not -Throw
}
}
}
}

Describe "Assert-PathEquals" {
Context "When paths are identical" {
It "Should pass for identical forward slash paths" {
{ Assert-PathEquals -Actual "/var/task/handler.ps1" -Expected "/var/task/handler.ps1" } | Should -Not -Throw
}

It "Should pass for identical backslash paths" {
{ Assert-PathEquals -Actual "C:\var\task\handler.ps1" -Expected "C:\var\task\handler.ps1" } | Should -Not -Throw
}

It "Should pass for identical relative paths" {
{ Assert-PathEquals -Actual "lib/utilities.ps1" -Expected "lib/utilities.ps1" } | Should -Not -Throw
}

It "Should pass for identical single file names" {
{ Assert-PathEquals -Actual "handler.ps1" -Expected "handler.ps1" } | Should -Not -Throw
}
}

Context "When paths differ only by separators" {
It "Should pass when actual uses backslashes and expected uses forward slashes" {
{ Assert-PathEquals -Actual "C:\var\task\handler.ps1" -Expected "C:/var/task/handler.ps1" } | Should -Not -Throw
}

It "Should pass when actual uses forward slashes and expected uses backslashes" {
{ Assert-PathEquals -Actual "/var/task/handler.ps1" -Expected "\var\task\handler.ps1" } | Should -Not -Throw
}

It "Should pass for mixed separators in both paths" {
{ Assert-PathEquals -Actual "C:\var/task\handler.ps1" -Expected "C:/var\task/handler.ps1" } | Should -Not -Throw
}

It "Should pass for nested directory paths with different separators" {
{ Assert-PathEquals -Actual "lib\modules\aws\tools\handler.ps1" -Expected "lib/modules/aws/tools/handler.ps1" } | Should -Not -Throw
}
}

Context "When paths are genuinely different" {
It "Should throw for different file names" {
{ Assert-PathEquals -Actual "/var/task/handler.ps1" -Expected "/var/task/different.ps1" } | Should -Throw -ExpectedMessage "*Expected path '/var/task/different.ps1' but got '/var/task/handler.ps1'*"
}

It "Should throw for different directory paths" {
{ Assert-PathEquals -Actual "/var/task/handler.ps1" -Expected "/var/runtime/handler.ps1" } | Should -Throw -ExpectedMessage "*Expected path '/var/runtime/handler.ps1' but got '/var/task/handler.ps1'*"
}

It "Should throw for different number of path segments" {
{ Assert-PathEquals -Actual "/var/task/lib/handler.ps1" -Expected "/var/task/handler.ps1" } | Should -Throw -ExpectedMessage "*Expected path '/var/task/handler.ps1' but got '/var/task/lib/handler.ps1'*"
}

It "Should throw for completely different paths" {
{ Assert-PathEquals -Actual "C:\Windows\System32\file.txt" -Expected "/usr/local/bin/script.sh" } | Should -Throw -ExpectedMessage "*Expected path '/usr/local/bin/script.sh' but got 'C:\Windows\System32\file.txt'*"
}
}

Context "When using custom Because parameter" {
It "Should include custom reason in error message" {
{ Assert-PathEquals -Actual "/var/task/wrong.ps1" -Expected "/var/task/handler.ps1" -Because "the script file path should match the handler configuration" } | Should -Throw -ExpectedMessage "*because the script file path should match the handler configuration*"
}

It "Should include custom reason with normalized paths" {
{ Assert-PathEquals -Actual "C:\var\task\wrong.ps1" -Expected "/var/task/handler.ps1" -Because "cross-platform paths should be normalized" } | Should -Throw -ExpectedMessage "*because cross-platform paths should be normalized*"
}
}

Context "When handling edge cases" {
It "Should handle PowerShell parameter validation for empty strings" {
# PowerShell parameter validation prevents empty strings for mandatory parameters
{ Assert-PathEquals -Actual "" -Expected "" } | Should -Throw -ExpectedMessage "*Cannot bind argument to parameter*"
}

It "Should handle PowerShell parameter validation for empty actual path" {
{ Assert-PathEquals -Actual "" -Expected "/var/task/handler.ps1" } | Should -Throw -ExpectedMessage "*Cannot bind argument to parameter*"
}

It "Should handle paths with trailing separators" {
{ Assert-PathEquals -Actual "/var/task/" -Expected "/var/task" } | Should -Throw -ExpectedMessage "*Expected path '/var/task' but got '/var/task/'*"
}

It "Should handle paths with multiple consecutive separators" {
{ Assert-PathEquals -Actual "/var//task/handler.ps1" -Expected "/var/task/handler.ps1" } | Should -Throw -ExpectedMessage "*Expected path '/var/task/handler.ps1' but got '/var//task/handler.ps1'*"
}

It "Should handle paths with dots" {
{ Assert-PathEquals -Actual "/var/task/./handler.ps1" -Expected "/var/task/handler.ps1" } | Should -Throw -ExpectedMessage "*Expected path '/var/task/handler.ps1' but got '/var/task/./handler.ps1'*"
}

It "Should handle paths with parent directory references" {
{ Assert-PathEquals -Actual "/var/task/../task/handler.ps1" -Expected "/var/task/handler.ps1" } | Should -Throw -ExpectedMessage "*Expected path '/var/task/handler.ps1' but got '/var/task/../task/handler.ps1'*"
}
}

Context "When testing real-world Lambda scenarios" {
It "Should pass for typical Lambda task paths with different separators" {
{ Assert-PathEquals -Actual "/var\task\handler.ps1" -Expected "/var/task/handler.ps1" } | Should -Not -Throw
}

It "Should pass for module paths in subdirectories" {
{ Assert-PathEquals -Actual "/var\task\lib\utilities.ps1" -Expected "/var/task/lib/utilities.ps1" } | Should -Not -Throw
}

It "Should pass for PowerShell module manifest paths" {
{ Assert-PathEquals -Actual "/opt\powershell\modules\MyModule\MyModule.psd1" -Expected "/opt/powershell/modules/MyModule/MyModule.psd1" } | Should -Not -Throw
}

It "Should pass for Windows-style Lambda paths normalized to Linux" {
{ Assert-PathEquals -Actual "C:\lambda\task\function.ps1" -Expected "C:/lambda/task/function.ps1" } | Should -Not -Throw
}

It "Should handle layer paths correctly" {
{ Assert-PathEquals -Actual "/opt\powershell\modules\AWS.Tools.Common\AWS.Tools.Common.psd1" -Expected "/opt/powershell/modules/AWS.Tools.Common/AWS.Tools.Common.psd1" } | Should -Not -Throw
}
}

Context "When error messages show normalization details" {
It "Should show normalized paths in error message when normalization occurred" {
{ Assert-PathEquals -Actual "C:\var\task\wrong.ps1" -Expected "/var/task/handler.ps1" } | Should -Throw -ExpectedMessage "*normalized: '/var/task/handler.ps1' vs 'C:/var/task/wrong.ps1'*"
}

It "Should not show normalization details when no normalization was needed" {
{ Assert-PathEquals -Actual "/var/task/wrong.ps1" -Expected "/var/task/handler.ps1" } | Should -Throw -ExpectedMessage "*Expected path '/var/task/handler.ps1' but got '/var/task/wrong.ps1'*"
}

It "Should show normalization details only when actual path was normalized" {
{ Assert-PathEquals -Actual "C:\var\task\handler.ps1" -Expected "C:/var/task/different.ps1" } | Should -Throw -ExpectedMessage "*normalized: 'C:/var/task/different.ps1' vs 'C:/var/task/handler.ps1'*"
}
}

Context "When testing verbose output" {
It "Should write verbose message on successful assertion" {
Mock Write-Verbose { }

Assert-PathEquals -Actual "/var/task/handler.ps1" -Expected "/var/task/handler.ps1" -Verbose

Should -Invoke Write-Verbose -Exactly 1 -ParameterFilter { $Message -like "*Path assertion passed*" }
}

It "Should write verbose message with both paths" {
Mock Write-Verbose { }

# This should pass since the paths are equivalent after normalization
Assert-PathEquals -Actual "C:\var\task\handler.ps1" -Expected "C:/var/task/handler.ps1" -Verbose

Should -Invoke Write-Verbose -Exactly 1 -ParameterFilter {
$Message -like "*'C:/var/task/handler.ps1' matches 'C:\var\task\handler.ps1'*"
}
}
}
}
58 changes: 57 additions & 1 deletion powershell-runtime/tests/helpers/AssertionHelpers.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -460,5 +460,61 @@ function Assert-FileExists {
Write-Verbose "File existence assertion passed: '$Path'"
}

<#
.SYNOPSIS
Verify path equality with cross-platform path separator handling.

.DESCRIPTION
Asserts that two paths are equal by normalizing path separators to forward slashes,
allowing tests to pass on both Windows and Linux regardless of the path separator
used by System.IO.Path.Combine() in the runtime.

.PARAMETER Actual
The actual path value from the test result.

.PARAMETER Expected
The expected path value (should use forward slashes for consistency).

.PARAMETER Because
Optional reason for the assertion failure.

.EXAMPLE
Assert-PathEquals -Actual $result.scriptFilePath -Expected "/var/task/handler.ps1"
Verifies that the script file path matches, regardless of platform path separators.

.EXAMPLE
Assert-PathEquals -Actual $result.scriptFilePath -Expected "/var/task/lib/utilities.ps1" -Because "subdirectory paths should be handled correctly"
Verifies path equality with a custom failure message.
#>
function Assert-PathEquals {
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[string]$Actual,

[Parameter(Mandatory)]
[string]$Expected,

[string]$Because
)

# Normalize both paths to use forward slashes for comparison
$normalizedActual = $Actual -replace '\\', '/'
$normalizedExpected = $Expected -replace '\\', '/'

if ($normalizedActual -ne $normalizedExpected) {
$message = "Expected path '$Expected' but got '$Actual'"
if ($normalizedActual -ne $Actual) {
$message += " (normalized: '$normalizedExpected' vs '$normalizedActual')"
}
if ($Because) {
$message += " because $Because"
}
throw $message
}

Write-Verbose "Path assertion passed: '$Expected' matches '$Actual'"
}

# Functions are available when dot-sourced
# To use: . ./AssertionHelpers.ps1
# To use: . ./AssertionHelpers.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -310,33 +310,6 @@ Describe "build-PwshRuntimeLayer.ps1" {
$_.Exception.Message | Should -Not -BeNullOrEmpty
}
}

It "Should handle read-only layer path appropriately" {
# Create a directory and make it read-only (if supported on platform)
$readOnlyPath = Join-Path $TestDrive "readonly-layer"
New-Item -Path $readOnlyPath -ItemType Directory -Force

try {
# Try to make directory read-only (Windows only test)
if ($IsWindows) {
Set-ItemProperty -Path $readOnlyPath -Name IsReadOnly -Value $true
# Build script should handle this appropriately on Windows
{ & $script:BuildScript -LayerPath $readOnlyPath -SkipRuntimeSetup 6>$null } | Should -Throw
}
else {
# On non-Windows platforms, test that build succeeds even with permission issues
# The build script should handle permission issues gracefully
{ & $script:BuildScript -LayerPath $readOnlyPath -SkipRuntimeSetup 6>$null } | Should -Not -Throw
}
}
finally {
# Clean up read-only attribute
if ($IsWindows -and (Test-Path $readOnlyPath)) {
Set-ItemProperty -Path $readOnlyPath -Name IsReadOnly -Value $false
Remove-Item $readOnlyPath -Recurse -Force -ErrorAction SilentlyContinue
}
}
}
}

Context "When testing download logic without runtime setup" {
Expand Down Expand Up @@ -754,4 +727,4 @@ function Production-Function {
}


}
}
20 changes: 10 additions & 10 deletions powershell-runtime/tests/unit/Private/Get-Handler.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ Describe "Get-Handler" {
$result | Should -Not -BeNullOrEmpty
$result.handlerType | Should -Be 'Script'
$result.scriptFileName | Should -Be "handler.ps1"
$result.scriptFilePath | Should -Be "/var/task/handler.ps1"
Assert-PathEquals -Actual $result.scriptFilePath -Expected "/var/task/handler.ps1"
$result.PSObject.Properties['functionName'] | Should -BeNullOrEmpty
$result.PSObject.Properties['moduleName'] | Should -BeNullOrEmpty
}
Expand All @@ -68,7 +68,7 @@ Describe "Get-Handler" {
# Assert
$result.handlerType | Should -Be 'Script'
$result.scriptFileName | Should -Be "my-complex-handler.ps1"
$result.scriptFilePath | Should -Be "/var/task/my-complex-handler.ps1"
Assert-PathEquals -Actual $result.scriptFilePath -Expected "/var/task/my-complex-handler.ps1"
}

It "Should use LAMBDA_TASK_ROOT environment variable for script path" {
Expand All @@ -80,7 +80,7 @@ Describe "Get-Handler" {
$result = pwsh-runtime\Get-Handler

# Assert
$result.scriptFilePath | Should -Be "/custom/task/root/test.ps1"
Assert-PathEquals -Actual $result.scriptFilePath -Expected "/custom/task/root/test.ps1"
}

It "Should handle script handler with subdirectory path" {
Expand All @@ -93,7 +93,7 @@ Describe "Get-Handler" {
# Assert
$result.handlerType | Should -Be 'Script'
$result.scriptFileName | Should -Be "subfolder/handler.ps1"
$result.scriptFilePath | Should -Be "/var/task/subfolder/handler.ps1"
Assert-PathEquals -Actual $result.scriptFilePath -Expected "/var/task/subfolder/handler.ps1"
}
}

Expand All @@ -109,7 +109,7 @@ Describe "Get-Handler" {
$result | Should -Not -BeNullOrEmpty
$result.handlerType | Should -Be 'Function'
$result.scriptFileName | Should -Be "handler.ps1"
$result.scriptFilePath | Should -Be "/var/task/handler.ps1"
Assert-PathEquals -Actual $result.scriptFilePath -Expected "/var/task/handler.ps1"
$result.functionName | Should -Be "MyFunction"
$result.PSObject.Properties['moduleName'] | Should -BeNullOrEmpty
}
Expand All @@ -125,7 +125,7 @@ Describe "Get-Handler" {
$result.handlerType | Should -Be 'Function'
$result.scriptFileName | Should -Be "my-script-file.ps1"
$result.functionName | Should -Be "My-Complex-Function-Name"
$result.scriptFilePath | Should -Be "/var/task/my-script-file.ps1"
Assert-PathEquals -Actual $result.scriptFilePath -Expected "/var/task/my-script-file.ps1"
}

It "Should use LAMBDA_TASK_ROOT for function handler script path" {
Expand All @@ -137,7 +137,7 @@ Describe "Get-Handler" {
$result = pwsh-runtime\Get-Handler

# Assert
$result.scriptFilePath | Should -Be "/custom/path/handler.ps1"
Assert-PathEquals -Actual $result.scriptFilePath -Expected "/custom/path/handler.ps1"
}

It "Should handle function handler with script in subdirectory" {
Expand All @@ -151,7 +151,7 @@ Describe "Get-Handler" {
$result.handlerType | Should -Be 'Function'
$result.scriptFileName | Should -Be "lib/utilities.ps1"
$result.functionName | Should -Be "Get-Data"
$result.scriptFilePath | Should -Be "/var/task/lib/utilities.ps1"
Assert-PathEquals -Actual $result.scriptFilePath -Expected "/var/task/lib/utilities.ps1"
}
}

Expand Down Expand Up @@ -229,7 +229,7 @@ Describe "Get-Handler" {

# Assert
$result.scriptFileName | Should -Be "custom-handler.ps1"
$result.scriptFilePath | Should -Be "/var/task/custom-handler.ps1"
Assert-PathEquals -Actual $result.scriptFilePath -Expected "/var/task/custom-handler.ps1"
}

It "Should handle custom function handler parameter" {
Expand Down Expand Up @@ -347,4 +347,4 @@ Describe "Get-Handler" {
{ pwsh-runtime\Get-Handler } | Should -Throw
}
}
}
}
Loading