Skip to content

Commit 5bb6568

Browse files
committed
feat(#204): mirror release helper tests
Closes Migrate release domain tests (5) Fixes #204
1 parent 59aa50a commit 5bb6568

38 files changed

Lines changed: 1239 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
2525
- Added mirrored, dot-source-first Pester test files for scaffold and update private helpers (`src/private/scaffold/*.ps1` and `src/private/update/*.ps1`), so each unit-level helper now has a focused test file under `tests/private/scaffold/` or `tests/private/update/` instead of being covered only through broad legacy coverage files.
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.
28+
- 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.
2829

2930

3031
### Changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
BeforeAll {
2+
$projectRoot = Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $PSScriptRoot))
3+
. (Join-Path $projectRoot 'src/private/release/FindLocalModulePathMatch.ps1')
4+
5+
function Stop-NovaOperation {param($Message, $ErrorId, $Category, $TargetObject) throw $Message}
6+
}
7+
8+
Describe 'Find-LocalModulePathMatch' {
9+
It 'returns the first existing path matching the pattern' {
10+
$tmp = [System.IO.Path]::GetTempPath().TrimEnd([System.IO.Path]::DirectorySeparatorChar)
11+
$details = [pscustomobject]@{Message='m'; ErrorId='e'; Category=[System.Management.Automation.ErrorCategory]::ObjectNotFound; TargetObject='t'}
12+
Find-LocalModulePathMatch -ModulePaths @($tmp) -MatchPattern '.*' -ErrorDetails $details | Should -Be $tmp
13+
}
14+
15+
It 'throws when no entry matches the pattern' {
16+
$details = [pscustomobject]@{Message='not found'; ErrorId='e'; Category=[System.Management.Automation.ErrorCategory]::ObjectNotFound; TargetObject='t'}
17+
{Find-LocalModulePathMatch -ModulePaths @('/a') -MatchPattern '^nope$' -ErrorDetails $details} | Should -Throw '*not found*'
18+
}
19+
20+
It 'throws when the matched entry does not exist on disk' {
21+
$details = [pscustomobject]@{Message='missing'; ErrorId='e'; Category=[System.Management.Automation.ErrorCategory]::ObjectNotFound; TargetObject='t'}
22+
{Find-LocalModulePathMatch -ModulePaths @('/does/not/exist/abc') -MatchPattern '.*' -ErrorDetails $details} | Should -Throw '*missing*'
23+
}
24+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
BeforeAll {
2+
$projectRoot = Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $PSScriptRoot))
3+
. (Join-Path $projectRoot 'src/private/release/GetGitCommitMessagesForVersionBump.ps1')
4+
5+
function Test-GitRepositoryIsAvailable {param($ProjectRoot) return $true}
6+
function Invoke-NovaGitCommand {param($ProjectRoot, $Arguments) return [pscustomobject]@{ExitCode=0; Output=@()}}
7+
function Get-NovaGitCommandOutputText {param($Result) return ($Result.Output -join "`n")}
8+
}
9+
10+
Describe 'Get-GitCommitMessageForVersionBump' {
11+
It 'returns an empty array when no git repository is available' {
12+
Mock Test-GitRepositoryIsAvailable {return $false}
13+
$messages = Get-GitCommitMessageForVersionBump -ProjectRoot '/proj'
14+
@($messages).Count | Should -Be 0
15+
}
16+
17+
It 'returns parsed commit messages when git is available' {
18+
Mock Invoke-NovaGitCommand {return [pscustomobject]@{ExitCode=0; Output=@('feat: a','body','--END-COMMIT--','fix: b','--END-COMMIT--')}}
19+
$messages = Get-GitCommitMessageForVersionBump -ProjectRoot '/proj'
20+
@($messages).Count | Should -BeGreaterThan 0
21+
}
22+
}
23+
24+
Describe 'Get-NovaVersionBumpCommitLogResult' {
25+
It 'uses lastTag..HEAD range when a last tag was detected' {
26+
Mock Invoke-NovaGitCommand {return [pscustomobject]@{ExitCode=0; Output=@('out')}}
27+
$tagResult = [pscustomobject]@{ExitCode=0; Output=@('v1.0.0')}
28+
$result = Get-NovaVersionBumpCommitLogResult -ProjectRoot '/proj' -Format 'f' -LastTagResult $tagResult
29+
$result.ExitCode | Should -Be 0
30+
Should -Invoke Invoke-NovaGitCommand -ParameterFilter {$Arguments -contains 'v1.0.0..HEAD'}
31+
}
32+
33+
It 'falls back to plain log when no tag is found' {
34+
Mock Invoke-NovaGitCommand {return [pscustomobject]@{ExitCode=0; Output=@()}}
35+
$tagResult = [pscustomobject]@{ExitCode=128; Output=@()}
36+
Get-NovaVersionBumpCommitLogResult -ProjectRoot '/proj' -Format 'f' -LastTagResult $tagResult | Out-Null
37+
Should -Invoke Invoke-NovaGitCommand -ParameterFilter {-not ($Arguments -match '\.\.HEAD$')}
38+
}
39+
}
40+
41+
Describe 'ConvertFrom-NovaVersionBumpCommitLogResult' {
42+
It 'returns an empty array when the result has a non-zero exit code' {
43+
$result = [pscustomobject]@{ExitCode=1; Output=@('feat: x')}
44+
@((ConvertFrom-NovaVersionBumpCommitLogResult -Result $result)).Count | Should -Be 0
45+
}
46+
47+
It 'splits commits on the --END-COMMIT-- delimiter and trims them' {
48+
$result = [pscustomobject]@{ExitCode=0; Output=@('feat: a','body','--END-COMMIT--','fix: b','--END-COMMIT--')}
49+
$commits = ConvertFrom-NovaVersionBumpCommitLogResult -Result $result
50+
@($commits).Count | Should -BeGreaterThan 0
51+
}
52+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
BeforeAll {
2+
$projectRoot = Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $PSScriptRoot))
3+
. (Join-Path $projectRoot 'src/private/release/GetLocalModulePath.ps1')
4+
5+
function Get-LocalModulePathEntryList {return @('/a')}
6+
function Get-LocalModulePathPattern {return 'pattern'}
7+
function Get-LocalModulePathErrorMessage {param($MatchPattern) return "no $MatchPattern"}
8+
function Find-LocalModulePathMatch {param($ModulePaths, $MatchPattern, $ErrorDetails) return "match-of-$MatchPattern"}
9+
}
10+
11+
Describe 'Get-LocalModulePath' {
12+
It 'delegates to Find-LocalModulePathMatch with the assembled error details' {
13+
Mock Find-LocalModulePathMatch {return "ok-$($ErrorDetails.ErrorId)"}
14+
Get-LocalModulePath | Should -Be 'ok-Nova.Environment.LocalModulePathNotFound'
15+
}
16+
}
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/release/GetLocalModulePathEntryList.ps1')
4+
5+
function Get-NovaEnvironmentVariableValue {param($Name) return $null}
6+
}
7+
8+
Describe 'Get-LocalModulePathEntryList' {
9+
It 'returns an empty array when PSModulePath is empty' {
10+
Mock Get-NovaEnvironmentVariableValue {return ''}
11+
$result = Get-LocalModulePathEntryList
12+
@($result).Count | Should -Be 0
13+
}
14+
15+
It 'splits, trims, and deduplicates path entries' {
16+
$sep = [IO.Path]::PathSeparator
17+
Mock Get-NovaEnvironmentVariableValue {return "/a$sep /b $sep/a$sep"}
18+
$result = Get-LocalModulePathEntryList
19+
@($result).Count | Should -Be 2
20+
$result | Should -Contain '/a'
21+
$result | Should -Contain '/b'
22+
}
23+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
BeforeAll {
2+
$projectRoot = Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $PSScriptRoot))
3+
. (Join-Path $projectRoot 'src/private/release/GetLocalModulePathErrorMessage.ps1')
4+
}
5+
6+
Describe 'Get-LocalModulePathErrorMessage' {
7+
It 'mentions the match pattern in the message' {
8+
$message = Get-LocalModulePathErrorMessage -MatchPattern 'PATTERN'
9+
$message | Should -Match 'PATTERN'
10+
}
11+
12+
It 'uses the Windows phrasing on Windows' -Skip:(-not $IsWindows) {
13+
Get-LocalModulePathErrorMessage -MatchPattern 'p' | Should -Match '^No windows'
14+
}
15+
16+
It 'uses the macOS/Linux phrasing on POSIX' -Skip:$IsWindows {
17+
Get-LocalModulePathErrorMessage -MatchPattern 'p' | Should -Match '^No macOS/Linux'
18+
}
19+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
BeforeAll {
2+
$projectRoot = Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $PSScriptRoot))
3+
. (Join-Path $projectRoot 'src/private/release/GetLocalModulePathPattern.ps1')
4+
}
5+
6+
Describe 'Get-LocalModulePathPattern' {
7+
It 'returns a Windows pattern on Windows' -Skip:(-not $IsWindows) {
8+
Get-LocalModulePathPattern | Should -Be '\\Documents\\PowerShell\\Modules'
9+
}
10+
11+
It 'returns a POSIX pattern on macOS/Linux' -Skip:$IsWindows {
12+
Get-LocalModulePathPattern | Should -Be '/\.local/share/powershell/Modules$'
13+
}
14+
}
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/release/GetNovaInstalledProjectManifestPath.ps1')
4+
5+
function Resolve-NovaLocalPublishPath {param($ModuleDirectoryPath) return '/m'}
6+
function Get-NovaPublishedLocalManifestPath {param($PublishInvocation) return $PublishInvocation}
7+
function Get-NovaProjectInfo {return [pscustomobject]@{ProjectName='X'}}
8+
}
9+
10+
Describe 'Get-NovaInstalledProjectManifestPath' {
11+
It 'builds a local PublishInvocation and delegates to Get-NovaPublishedLocalManifestPath' {
12+
$project = [pscustomobject]@{ProjectName='Sample'}
13+
$invocation = Get-NovaInstalledProjectManifestPath -ProjectInfo $project -ModuleDirectoryPath '/m'
14+
$invocation.IsLocal | Should -BeTrue
15+
$invocation.Target | Should -Be '/m'
16+
$invocation.Parameters.ProjectInfo | Should -Be $project
17+
}
18+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
BeforeAll {
2+
$projectRoot = Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $PSScriptRoot))
3+
. (Join-Path $projectRoot 'src/private/release/GetNovaInstalledProjectVersion.ps1')
4+
5+
function Get-NovaInstalledProjectManifestPath {param($ProjectInfo, $ModuleDirectoryPath) return '/nonexistent.psd1'}
6+
function Format-NovaCliVersionString {param($Name, $Version) return "$Name $Version"}
7+
function Get-NovaProjectInfo {return [pscustomobject]@{ProjectName='X'}}
8+
function Stop-NovaOperation {param($Message, $ErrorId, $Category, $TargetObject) throw $Message}
9+
function Test-ModuleManifest {param($Path, $ErrorAction) return [pscustomobject]@{Version=[version]'1.2.3'}}
10+
}
11+
12+
Describe 'Get-NovaInstalledProjectVersion' {
13+
It 'throws when manifest does not exist' {
14+
Mock Get-NovaInstalledProjectManifestPath {return '/does/not/exist.psd1'}
15+
{Get-NovaInstalledProjectVersion -ProjectInfo ([pscustomobject]@{ProjectName='X'})} | Should -Throw '*Local module install not found*'
16+
}
17+
18+
It 'returns formatted name+version when manifest exists' {
19+
$tmp = [System.IO.Path]::GetTempFileName()
20+
try {
21+
Mock Get-NovaInstalledProjectManifestPath {return $tmp}
22+
Mock Test-ModuleManifest {return [pscustomobject]@{Version=[version]'2.0.0'}}
23+
Get-NovaInstalledProjectVersion -ProjectInfo ([pscustomobject]@{ProjectName='X'}) | Should -Be 'X 2.0.0'
24+
} finally {
25+
Remove-Item -LiteralPath $tmp -ErrorAction SilentlyContinue
26+
}
27+
}
28+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
BeforeAll {
2+
$projectRoot = Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $PSScriptRoot))
3+
. (Join-Path $projectRoot 'src/private/release/GetNovaLocalPublishActivation.ps1')
4+
5+
function Get-NovaPublishedLocalManifestPath {param($PublishInvocation) return '/m/x.psd1'}
6+
function Import-NovaPublishedLocalModule {param($ProjectName, $ManifestPath)}
7+
}
8+
9+
Describe 'Get-NovaLocalPublishActivation' {
10+
It 'returns $null when the invocation is not local' {
11+
Get-NovaLocalPublishActivation -PublishInvocation ([pscustomobject]@{IsLocal=$false}) | Should -BeNullOrEmpty
12+
}
13+
14+
It 'returns a manifest path and import action when local' {
15+
$result = Get-NovaLocalPublishActivation -PublishInvocation ([pscustomobject]@{IsLocal=$true})
16+
$result.ManifestPath | Should -Be '/m/x.psd1'
17+
$result.ImportAction | Should -Not -BeNullOrEmpty
18+
}
19+
}

0 commit comments

Comments
 (0)