Skip to content

Commit 3f7b93c

Browse files
committed
feat(#205): mirror package helper tests
Add focused, dot-source-first Pester tests under tests/private/package/ for unit-level package helpers (metadata, content, NuGet/Zip artifacts, upload option/target/headers/auth, file resolution, request, and per-type upload helpers). Multi-step package workflow orchestrators stay covered by the existing cross-cutting package tests. closes Migrate package domain tests (6) Fixes #205
1 parent 5bb6568 commit 3f7b93c

59 files changed

Lines changed: 1506 additions & 0 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
2626
- Added mirrored, dot-source-first Pester test files for build and shared private helpers (`src/private/build/*.ps1`, `src/private/build/manifest/*.ps1`, and `src/private/shared/*.ps1`), so each unit-level helper now has a focused test file under `tests/private/build/`, `tests/private/build/manifest/`, or `tests/private/shared/` instead of being covered only through broad legacy coverage files.
2727
- Added mirrored, dot-source-first Pester test files for CLI private helpers (`src/private/cli/*.ps1`), so most CLI argument parsers, prompt helpers, and per-command CLI wrappers now have a focused test file under `tests/private/cli/`. Large cross-cutting CLI orchestrators (`InvokeNovaCliCommandRoute.ps1`, `GetNovaCliArgumentRoutingState.ps1`, `FormatNovaCliCommandHelp.ps1`, `ConfirmNovaCliAction.ps1`, `GetNovaCliInvocationContext.ps1`) stay covered by the existing cross-cutting CLI tests.
2828
- Added mirrored, dot-source-first Pester test files for release private helpers (`src/private/release/*.ps1`), so most release, publish, and version-update helpers (local publish path resolution, publish parameter mapping, version label inference, version part calculation, prerelease label handling, release request shaping, and per-step write/import helpers) now have a focused test file under `tests/private/release/`. Multi-step release/publish/version-update workflow orchestrators (`InvokeNovaReleaseWorkflow.ps1`, `InvokeNovaPublishWorkflow.ps1`, `GetNovaVersionUpdateWorkflowContext.ps1`) stay covered by the existing cross-cutting release tests.
29+
- Added mirrored, dot-source-first Pester test files for package private helpers (`src/private/package/*.ps1`), so most package metadata, archive-content, NuGet/Zip artifact, upload option, upload target, upload header/auth, upload file-resolution, upload request, and per-type upload helpers now have a focused test file under `tests/private/package/`. Multi-step package workflow orchestrators (`InvokeNovaPackageWorkflow.ps1`, `InvokeNovaPackageUploadWorkflow.ps1`, `GetNovaPackageWorkflowContext.ps1`, `GetNovaPackageUploadWorkflowContext.ps1`, `InvokeNovaPackageArtifactCreation.ps1`, `NewNovaPackageArtifacts.ps1`) stay covered by the existing cross-cutting package tests.
2930

3031

3132
### Changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
BeforeAll {
2+
$projectRoot = Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $PSScriptRoot))
3+
. (Join-Path $projectRoot 'src/private/package/AddNovaZipFileEntry.ps1')
4+
}
5+
6+
Describe 'Add-NovaZipFileEntry' {
7+
It 'copies file content into the zip entry path' {
8+
$zipPath = Join-Path ([System.IO.Path]::GetTempPath()) ("nova-{0}.zip" -f [guid]::NewGuid())
9+
$sourcePath = Join-Path ([System.IO.Path]::GetTempPath()) ("nova-{0}.txt" -f [guid]::NewGuid())
10+
Set-Content -LiteralPath $sourcePath -Value 'hello' -NoNewline
11+
try {
12+
$fs = [System.IO.File]::Open($zipPath, [System.IO.FileMode]::CreateNew)
13+
$archive = [System.IO.Compression.ZipArchive]::new($fs, [System.IO.Compression.ZipArchiveMode]::Create, $false)
14+
try {
15+
Add-NovaZipFileEntry -Archive $archive -EntryPath 'a/b.txt' -SourcePath $sourcePath
16+
} finally {
17+
$archive.Dispose(); $fs.Dispose()
18+
}
19+
$archive2 = [System.IO.Compression.ZipFile]::OpenRead($zipPath)
20+
try {
21+
$entry = $archive2.GetEntry('a/b.txt')
22+
$entry | Should -Not -BeNullOrEmpty
23+
$reader = [System.IO.StreamReader]::new($entry.Open())
24+
try {$reader.ReadToEnd() | Should -Be 'hello'} finally {$reader.Dispose()}
25+
} finally {$archive2.Dispose()}
26+
} finally {
27+
Remove-Item -LiteralPath $zipPath -ErrorAction SilentlyContinue
28+
Remove-Item -LiteralPath $sourcePath -ErrorAction SilentlyContinue
29+
}
30+
}
31+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
BeforeAll {
2+
$projectRoot = Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $PSScriptRoot))
3+
. (Join-Path $projectRoot 'src/private/package/AddNovaZipTextEntry.ps1')
4+
}
5+
6+
Describe 'Add-NovaZipTextEntry' {
7+
It 'writes UTF-8 text without BOM into the zip entry' {
8+
$zipPath = Join-Path ([System.IO.Path]::GetTempPath()) ("nova-{0}.zip" -f [guid]::NewGuid())
9+
try {
10+
$fs = [System.IO.File]::Open($zipPath, [System.IO.FileMode]::CreateNew)
11+
$archive = [System.IO.Compression.ZipArchive]::new($fs, [System.IO.Compression.ZipArchiveMode]::Create, $false)
12+
try {Add-NovaZipTextEntry -Archive $archive -EntryPath 'note.txt' -Content 'hello'} finally {$archive.Dispose(); $fs.Dispose()}
13+
$archive2 = [System.IO.Compression.ZipFile]::OpenRead($zipPath)
14+
try {
15+
$reader = [System.IO.StreamReader]::new($archive2.GetEntry('note.txt').Open())
16+
try {$reader.ReadToEnd() | Should -Be 'hello'} finally {$reader.Dispose()}
17+
} finally {$archive2.Dispose()}
18+
} finally {Remove-Item -LiteralPath $zipPath -ErrorAction SilentlyContinue}
19+
}
20+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
BeforeAll {
2+
$projectRoot = Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $PSScriptRoot))
3+
. (Join-Path $projectRoot 'src/private/package/AssertNovaPackageMetadata.ps1')
4+
5+
function Stop-NovaOperation {param($Message, $ErrorId, $Category, $TargetObject) throw $Message}
6+
}
7+
8+
Describe 'Assert-NovaPackageMetadata' {
9+
It 'returns silently when all required fields are present' {
10+
$meta = [pscustomobject]@{Type='NuGet'; Id='X'; Version='1.0.0'; Description='d'; OutputDirectory='/o'; PackageFileName='X.nupkg'; PackagePath='/o/X.nupkg'; Authors=@('a')}
11+
{Assert-NovaPackageMetadata -PackageMetadata $meta} | Should -Not -Throw
12+
}
13+
14+
It 'throws when a required field is missing' {
15+
$meta = [pscustomobject]@{Type='NuGet'; Id=''; Version='1.0.0'; Description='d'; OutputDirectory='/o'; PackageFileName='X.nupkg'; PackagePath='/o/X.nupkg'; Authors=@('a')}
16+
{Assert-NovaPackageMetadata -PackageMetadata $meta} | Should -Throw '*Id*'
17+
}
18+
19+
It 'throws when Authors is empty' {
20+
$meta = [pscustomobject]@{Type='NuGet'; Id='X'; Version='1.0.0'; Description='d'; OutputDirectory='/o'; PackageFileName='X.nupkg'; PackagePath='/o/X.nupkg'; Authors=@()}
21+
{Assert-NovaPackageMetadata -PackageMetadata $meta} | Should -Throw '*Authors*'
22+
}
23+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
BeforeAll {
2+
$projectRoot = Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $PSScriptRoot))
3+
. (Join-Path $projectRoot 'src/private/package/AssertNovaPackageOutputDirectoryCanBeCleared.ps1')
4+
5+
function Stop-NovaOperation {param($Message, $ErrorId, $Category, $TargetObject) throw $Message}
6+
function Test-NovaPathContainsPath {param($ParentPath, $ChildPath) return $false}
7+
}
8+
9+
Describe 'Assert-NovaPackageOutputDirectoryCanBeCleared' {
10+
It 'returns silently for a normal directory that is not a parent of protected paths' {
11+
$project = [pscustomobject]@{ProjectRoot='/proj'; OutputModuleDir='/proj/dist'}
12+
{Assert-NovaPackageOutputDirectoryCanBeCleared -ProjectInfo $project -OutputDirectory ([System.IO.Path]::GetTempPath())} | Should -Not -Throw
13+
}
14+
15+
It 'throws when output directory contains a protected path' {
16+
Mock Test-NovaPathContainsPath {return $true}
17+
$project = [pscustomobject]@{ProjectRoot='/proj'; OutputModuleDir='/proj/dist'}
18+
{Assert-NovaPackageOutputDirectoryCanBeCleared -ProjectInfo $project -OutputDirectory ([System.IO.Path]::GetTempPath())} | Should -Throw '*required project content*'
19+
}
20+
21+
It 'throws when the resolved output directory is a filesystem root' {
22+
$root = [System.IO.Path]::GetPathRoot([System.IO.Path]::GetFullPath([System.IO.Path]::GetTempPath()))
23+
if (-not $root) {Set-ItResult -Skipped -Because 'No root path available on this platform'}
24+
$project = [pscustomobject]@{ProjectRoot='/proj'; OutputModuleDir='/proj/dist'}
25+
{Assert-NovaPackageOutputDirectoryCanBeCleared -ProjectInfo $project -OutputDirectory $root} | Should -Throw '*root*'
26+
}
27+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
BeforeAll {
2+
$projectRoot = Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $PSScriptRoot))
3+
. (Join-Path $projectRoot 'src/private/package/ClearNovaPackageOutputDirectory.ps1')
4+
5+
function Assert-NovaPackageOutputDirectoryCanBeCleared {param($ProjectInfo, $OutputDirectory)}
6+
}
7+
8+
Describe 'Clear-NovaPackageOutputDirectory' {
9+
It 'removes the existing output directory' {
10+
$dir = Join-Path ([System.IO.Path]::GetTempPath()) ([guid]::NewGuid())
11+
New-Item -ItemType Directory -Path $dir | Out-Null
12+
Set-Content -LiteralPath (Join-Path $dir 'a.txt') -Value 'x'
13+
try {
14+
Clear-NovaPackageOutputDirectory -ProjectInfo ([pscustomobject]@{}) -OutputDirectory $dir
15+
Test-Path -LiteralPath $dir | Should -BeFalse
16+
} finally {Remove-Item -LiteralPath $dir -Recurse -Force -ErrorAction SilentlyContinue}
17+
}
18+
19+
It 'returns silently when the directory does not exist' {
20+
$dir = Join-Path ([System.IO.Path]::GetTempPath()) ([guid]::NewGuid())
21+
{Clear-NovaPackageOutputDirectory -ProjectInfo ([pscustomobject]@{}) -OutputDirectory $dir} | Should -Not -Throw
22+
}
23+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
BeforeAll {
2+
$projectRoot = Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $PSScriptRoot))
3+
. (Join-Path $projectRoot 'src/private/package/GetNovaManifestValue.ps1')
4+
}
5+
6+
Describe 'Get-NovaManifestValue' {
7+
It 'returns dictionary entry by key' {
8+
Get-NovaManifestValue -Manifest @{Tags='a'} -Name 'Tags' | Should -Be 'a'
9+
}
10+
11+
It 'returns object property by name' {
12+
Get-NovaManifestValue -Manifest ([pscustomobject]@{Tags='b'}) -Name 'Tags' | Should -Be 'b'
13+
}
14+
15+
It 'returns $null when the property is missing on an object' {
16+
Get-NovaManifestValue -Manifest ([pscustomobject]@{Other='x'}) -Name 'Tags' | Should -BeNullOrEmpty
17+
}
18+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
BeforeAll {
2+
$projectRoot = Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $PSScriptRoot))
3+
. (Join-Path $projectRoot 'src/private/package/GetNovaPackageArtifactPatternInfo.ps1')
4+
5+
function Get-NovaPackageSettingValue {param($InputObject, $Name) return $InputObject.$Name}
6+
function ConvertTo-NovaPackageType {param($Type) if ($Type -match 'zip') {'Zip'} else {'NuGet'}}
7+
}
8+
9+
Describe 'Get-NovaPackageArtifactPatternInfo' {
10+
It 'derives pattern from PackageId when FileNamePattern is empty' {
11+
$project = [pscustomobject]@{Package=[pscustomobject]@{FileNamePattern=''; Id='X'}}
12+
$info = Get-NovaPackageArtifactPatternInfo -ProjectInfo $project
13+
$info.Pattern | Should -Be 'X*'
14+
$info.ExplicitPackageType | Should -BeNullOrEmpty
15+
}
16+
17+
It 'returns the explicit package type when the pattern ends with a known extension' {
18+
$project = [pscustomobject]@{Package=[pscustomobject]@{FileNamePattern='X*.zip'; Id='X'}}
19+
$info = Get-NovaPackageArtifactPatternInfo -ProjectInfo $project
20+
$info.Pattern | Should -Be 'X*.zip'
21+
$info.ExplicitPackageType | Should -Be 'Zip'
22+
}
23+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
BeforeAll {
2+
$projectRoot = Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $PSScriptRoot))
3+
. (Join-Path $projectRoot 'src/private/package/GetNovaPackageArtifactSearchPattern.ps1')
4+
5+
function Get-NovaPackageArtifactPatternInfo {param($ProjectInfo) return [pscustomobject]@{Pattern='X*'; ExplicitPackageType=$null}}
6+
function Get-NovaPackageTypeExtension {param($PackageType) return '.nupkg'}
7+
}
8+
9+
Describe 'Get-NovaPackageArtifactSearchPattern' {
10+
It 'returns the explicit pattern unchanged when ExplicitPackageType is set' {
11+
Mock Get-NovaPackageArtifactPatternInfo {return [pscustomobject]@{Pattern='X*.zip'; ExplicitPackageType='Zip'}}
12+
Get-NovaPackageArtifactSearchPattern -ProjectInfo ([pscustomobject]@{}) -PackageType 'Zip' | Should -Be 'X*.zip'
13+
}
14+
15+
It 'appends the type extension when no explicit type is configured' {
16+
Get-NovaPackageArtifactSearchPattern -ProjectInfo ([pscustomobject]@{}) -PackageType 'NuGet' | Should -Be 'X*.nupkg'
17+
}
18+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
BeforeAll {
2+
$projectRoot = Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $PSScriptRoot))
3+
. (Join-Path $projectRoot 'src/private/package/GetNovaPackageArtifactType.ps1')
4+
5+
function Stop-NovaOperation {param($Message, $ErrorId, $Category, $TargetObject) throw $Message}
6+
function ConvertTo-NovaPackageType {param($Type)
7+
switch -Regex ($Type) {'\.zip$' {'Zip'; break} '\.nupkg$' {'NuGet'; break} default {throw "bad: $Type"}}
8+
}
9+
}
10+
11+
Describe 'Get-NovaPackageArtifactType' {
12+
It 'returns NuGet for .nupkg' {
13+
Get-NovaPackageArtifactType -PackagePath '/o/x.nupkg' | Should -Be 'NuGet'
14+
}
15+
16+
It 'returns Zip for .zip' {
17+
Get-NovaPackageArtifactType -PackagePath '/o/x.zip' | Should -Be 'Zip'
18+
}
19+
20+
It 'throws when extension is unknown' {
21+
{Get-NovaPackageArtifactType -PackagePath '/o/x.tar'} | Should -Throw '*Unsupported package file extension*'
22+
}
23+
24+
It 'throws when no extension is present' {
25+
{Get-NovaPackageArtifactType -PackagePath '/o/x'} | Should -Throw '*Unsupported package file extension*'
26+
}
27+
}

0 commit comments

Comments
 (0)