Public/Tests/Import-ModuleFromFlatTextAndManifest.Tests.ps1
|
BeforeAll { $env:TCS_CONFIG_ROOT = Join-Path -Path $TestDrive -ChildPath 'config' $env:TCS_SKIP_UPDATE_CHECK = '1' $env:TCS_TELEMETRY_OPTOUT = '1' $ModuleRoot = Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent Import-Module -Name (Join-Path -Path $ModuleRoot -ChildPath 'tcs.utils.psd1') -Force } AfterAll { Remove-Module -Name tcs.utils -Force -ErrorAction SilentlyContinue } Describe 'Import-ModuleFromFlatTextAndManifest' { BeforeAll { $ModuleSource = Join-Path -Path $TestDrive -ChildPath 'src/my.module' $Files = [ordered]@{ 'my.module.psd1' = "@{ ModuleVersion = '1.0.0' }`n" 'Public/Get-Thing.ps1' = "function Get-Thing {`n 'thing'`n}`n" 'no-newline.txt' = 'last line' 'blank-lines.txt' = "`n`nmiddle`n`n" 'crlf.txt' = "one`r`ntwo`r`n" 'empty.txt' = '' } foreach ($name in $Files.Keys) { $path = Join-Path -Path $ModuleSource -ChildPath $name $null = New-Item -Path (Split-Path -Path $path -Parent) -ItemType Directory -Force Set-Content -Path $path -Value $Files[$name] -NoNewline } $null = New-Item -Path (Join-Path -Path $ModuleSource -ChildPath 'EmptyFolder') -ItemType Directory -Force $ExportPath = Join-Path -Path $TestDrive -ChildPath 'export' Export-ModuleToFlatTextAndManifest -ModulePath $ModuleSource -OutputPath $ExportPath $ManifestPath = Join-Path -Path $ExportPath -ChildPath 'my.module_manifest.json' $FlatTextPath = Join-Path -Path $ExportPath -ChildPath 'my.module_flat.txt' } It 'Recreates every file with exactly the exported content' { $importPath = Join-Path -Path $TestDrive -ChildPath 'import' Import-ModuleFromFlatTextAndManifest -ManifestPath $ManifestPath -FlatTextPath $FlatTextPath -OutputPath $importPath foreach ($name in $Files.Keys) { $path = Join-Path -Path (Join-Path -Path $importPath -ChildPath 'my.module') -ChildPath $name Test-Path -Path $path | Should -BeTrue -Because "$name should be recreated" $content = Get-Content -Path $path -Raw if ($null -eq $content) { $content = '' } $content | Should -BeExactly $Files[$name] -Because "$name should round-trip exactly" } } It 'Recreates empty folders' { $importPath = Join-Path -Path $TestDrive -ChildPath 'import-folders' Import-ModuleFromFlatTextAndManifest -ManifestPath $ManifestPath -FlatTextPath $FlatTextPath -OutputPath $importPath Test-Path -Path (Join-Path -Path $importPath -ChildPath 'my.module/EmptyFolder') -PathType Container | Should -BeTrue } It 'Accepts manifests with Windows-style relative paths' { $windowsManifest = Join-Path -Path $TestDrive -ChildPath 'windows_manifest.json' $windowsFlat = Join-Path -Path $TestDrive -ChildPath 'windows_flat.txt' @{ ModuleRoot = 'win.module' Items = @( @{ RelativePath = 'Public'; ParentPath = ''; ItemType = 'Directory' } @{ RelativePath = 'Public\Get-Win.ps1'; ParentPath = 'Public'; ItemType = 'File' } ) } | ConvertTo-Json -Depth 5 | Set-Content -Path $windowsManifest Set-Content -Path $windowsFlat -Value "###FILE-START:Public\Get-Win.ps1###`r`nfunction Get-Win { }`r`n`r`n###FILE-END:Public\Get-Win.ps1###`r`n" -NoNewline $importPath = Join-Path -Path $TestDrive -ChildPath 'import-windows' Import-ModuleFromFlatTextAndManifest -ManifestPath $windowsManifest -FlatTextPath $windowsFlat -OutputPath $importPath $file = Join-Path -Path $importPath -ChildPath 'win.module/Public/Get-Win.ps1' Test-Path -Path $file | Should -BeTrue Get-Content -Path $file -Raw | Should -BeExactly "function Get-Win { }`r`n" } It 'Keeps the UTF-8 byte order mark of each exported file' { $bomSource = Join-Path -Path $TestDrive -ChildPath 'bomsrc/bom.module' $null = New-Item -Path $bomSource -ItemType Directory -Force # '# e-acute' with a BOM, and a plain ASCII file without one [System.IO.File]::WriteAllBytes((Join-Path -Path $bomSource -ChildPath 'with-bom.ps1'), [byte[]](0xEF, 0xBB, 0xBF, 0x23, 0x20, 0xC3, 0xA9)) [System.IO.File]::WriteAllBytes((Join-Path -Path $bomSource -ChildPath 'no-bom.ps1'), [byte[]](0x23, 0x20, 0x61)) $bomExport = Join-Path -Path $TestDrive -ChildPath 'bomexport' Export-ModuleToFlatTextAndManifest -ModulePath $bomSource -OutputPath $bomExport $bomImport = Join-Path -Path $TestDrive -ChildPath 'bomimport' Import-ModuleFromFlatTextAndManifest -ManifestPath (Join-Path -Path $bomExport -ChildPath 'bom.module_manifest.json') -FlatTextPath (Join-Path -Path $bomExport -ChildPath 'bom.module_flat.txt') -OutputPath $bomImport [System.IO.File]::ReadAllBytes((Join-Path -Path $bomImport -ChildPath 'bom.module/with-bom.ps1')) | Should -Be ([byte[]](0xEF, 0xBB, 0xBF, 0x23, 0x20, 0xC3, 0xA9)) [System.IO.File]::ReadAllBytes((Join-Path -Path $bomImport -ChildPath 'bom.module/no-bom.ps1')) | Should -Be ([byte[]](0x23, 0x20, 0x61)) } It 'Writes non-ASCII files with a BOM when an older manifest does not record the BOM' { $legacyManifest = Join-Path -Path $TestDrive -ChildPath 'legacy_manifest.json' $legacyFlat = Join-Path -Path $TestDrive -ChildPath 'legacy_flat.txt' @{ ModuleRoot = 'legacy.module' Items = @( @{ RelativePath = 'accent.ps1'; ParentPath = ''; ItemType = 'File' }, @{ RelativePath = 'ascii.ps1'; ParentPath = ''; ItemType = 'File' } ) } | ConvertTo-Json -Depth 5 | Set-Content -Path $legacyManifest $flat = "###FILE-START:accent.ps1###`n# caf$([char]0x00E9)`n###FILE-END:accent.ps1###`n###FILE-START:ascii.ps1###`n# cafe`n###FILE-END:ascii.ps1###`n" [System.IO.File]::WriteAllText($legacyFlat, $flat, (New-Object -TypeName System.Text.UTF8Encoding -ArgumentList $true)) $importPath = Join-Path -Path $TestDrive -ChildPath 'import-legacy' Import-ModuleFromFlatTextAndManifest -ManifestPath $legacyManifest -FlatTextPath $legacyFlat -OutputPath $importPath $accent = [System.IO.File]::ReadAllBytes((Join-Path -Path $importPath -ChildPath 'legacy.module/accent.ps1')) $accent[0..2] | Should -Be ([byte[]](0xEF, 0xBB, 0xBF)) $ascii = [System.IO.File]::ReadAllBytes((Join-Path -Path $importPath -ChildPath 'legacy.module/ascii.ps1')) $ascii[0] | Should -Be 0x23 } It 'Does not write outside the output folder' { $unsafeManifest = Join-Path -Path $TestDrive -ChildPath 'unsafe_manifest.json' $unsafeFlat = Join-Path -Path $TestDrive -ChildPath 'unsafe_flat.txt' @{ ModuleRoot = 'unsafe.module' Items = @(@{ RelativePath = '../escaped.txt'; ParentPath = '..'; ItemType = 'File' }) } | ConvertTo-Json -Depth 5 | Set-Content -Path $unsafeManifest Set-Content -Path $unsafeFlat -Value "###FILE-START:../escaped.txt###`nbad`n###FILE-END:../escaped.txt###`n" -NoNewline $importPath = Join-Path -Path $TestDrive -ChildPath 'import-unsafe' Import-ModuleFromFlatTextAndManifest -ManifestPath $unsafeManifest -FlatTextPath $unsafeFlat -OutputPath $importPath -WarningVariable importWarnings -WarningAction SilentlyContinue Test-Path -Path (Join-Path -Path $importPath -ChildPath 'escaped.txt') | Should -BeFalse $importWarnings | Should -Not -BeNullOrEmpty } It 'Throws when the manifest does not exist' { { Import-ModuleFromFlatTextAndManifest -ManifestPath (Join-Path -Path $TestDrive -ChildPath 'missing.json') -FlatTextPath $FlatTextPath -OutputPath $TestDrive } | Should -Throw } } |