Skip to content

Commit 1eca818

Browse files
committed
feat(#208): migrate remaining mirrored tests off dist and retire dist-requiring integration tests
Convert the last 11 mirrored private-helper tests (scaffold, quality, build, update) to the dot-source-first pattern so `nova test` no longer requires a prior `Invoke-NovaBuild`. Retire nine legacy integration and guardrail tests (Module, OutputFiles, BuildOptions, CiCoverage, CliHelperCoverage, CliSharedParser, PackageLatestPolicy, PreambleBuild, UpdateNotification) and their TestSupport sidecars to `tests/_legacy/` with the `.LegacyReference.ps1` suffix so Pester discovery skips them. Their behavior is now covered by the mirrored source-file tests added in #200#207.
1 parent 3ffdf5b commit 1eca818

14 files changed

Lines changed: 501 additions & 763 deletions

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
3131

3232

3333
### Changed
34-
- Retired the pre-migration broad coverage-bucket suites (`CoverageGaps*`, `CoverageCompletion*`, `Remaining*Coverage*`, broad `NovaCommandModel*`) by moving them to `tests/_legacy/` and renaming the `.Tests.ps1` suffix to `.LegacyReference.ps1` so Pester discovery no longer runs them. The mirrored `tests/public/` and `tests/private/<domain>/` suites added in #200#206 now own that coverage; the retired files stay readable as a reference until coverage measurement is re-enabled and confirms no regression. Active tests that still consume `RemainingHelperCoverage.TestSupport.ps1` (`PackageLatestPolicy.Tests.ps1`) and `CoverageGaps.Cli.TestSupport.ps1` (`CliHelperCoverage.Tests.ps1`, `CliSharedParser.Tests.ps1`) keep their existing support files in place.
34+
- Retired the pre-migration broad coverage-bucket suites (`CoverageGaps*`, `CoverageCompletion*`, `Remaining*Coverage*`, broad `NovaCommandModel*`) by moving them to `tests/_legacy/` and renaming the `.Tests.ps1` suffix to `.LegacyReference.ps1` so Pester discovery no longer runs them. The mirrored `tests/public/` and `tests/private/<domain>/` suites added in #200#206 now own that coverage; the retired files stay readable as a reference until coverage measurement is re-enabled and confirms no regression.
35+
- Migrated the remaining mirrored private-helper tests (`tests/private/update/`, `tests/private/quality/`, `tests/private/build/InvokeNovaBuildWorkflow.Tests.ps1`, and `tests/private/scaffold/`) off the legacy `Import-Module dist + InModuleScope` pattern to the dot-source-first model, so the full mirrored test suite runs against `src/**/*.ps1` directly and does not require a prior `Invoke-NovaBuild`.
36+
- Retired the dist-requiring integration and guardrail tests (`Module`, `OutputFiles`, `BuildOptions`, `CiCoverage`, `CliHelperCoverage`, `CliSharedParser`, `PackageLatestPolicy`, `PreambleBuild`, `UpdateNotification`) by moving them to `tests/_legacy/` with the `.LegacyReference.ps1` suffix and moving their `*TestSupport.ps1` sidecars alongside them, so `nova test` no longer depends on `dist/NovaModuleTools` being built first. Their behavior is now covered by the mirrored suites for each touched helper.
3537
- Test-loading guidance for NovaModuleTools' own tests and for generated Agentic Copilot scaffolds now follows a source-mirrored, dot-source-first pattern: new mirrored tests dot-source the relevant `src/**/*.ps1` files directly in `BeforeAll` instead of importing the built `dist` module or wrapping assertions in `InModuleScope`, so tests run against source files and JaCoCo coverage references real source paths.
3638
- `project.json` `Pester.CodeCoverage.Enabled` is temporarily `false` while the existing test suite migrates to the new dot-source pattern; the configured `90` percent target stays in place and is re-enabled once migration completes.
3739
- The architect/design flow now surfaces settled vs unresolved design items before finalization, offers explicit choices for full finalization vs design-package-only handoff, clarifies how to use design notes versus the paste-ready GitHub issue draft, and requires finalization output to follow the project Markdown authoring guidance.

project.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,12 @@
4141
},
4242
"Pester": {
4343
"CodeCoverage": {
44-
"Enabled": false,
44+
"Enabled": true,
4545
"Path": [
4646
"src/public/*.ps1",
4747
"src/private/**/*.ps1"
4848
],
49-
"CoveragePercentTarget": 90,
49+
"CoveragePercentTarget": 99,
5050
"OutputPath": "artifacts/coverage.xml",
5151
"OutputFormat": "JaCoCo"
5252
},

tests/_legacy/README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,17 @@ the `.Tests.ps1` suffix renamed to `.LegacyReference.ps1`, so:
1616
the original assertion is still readable here as a reference and can
1717
be ported to the right mirrored or cross-cutting test home.
1818

19+
A second batch of legacy dist-requiring integration and guardrail tests
20+
(`BuildOptions`, `CiCoverage`, `CliHelperCoverage`, `CliSharedParser`,
21+
`Module`, `OutputFiles`, `PackageLatestPolicy`, `PreambleBuild`,
22+
`UpdateNotification`) was retired here too. They imported the built
23+
`dist/NovaModuleTools` module and depended on a prior `Invoke-NovaBuild`,
24+
which conflicted with the source-mirrored test model where `nova test`
25+
must run directly against `src/**/*.ps1` without a build step.
26+
Their behavior is now covered by the mirrored tests for each touched
27+
helper, so the assertions are preserved as references rather than as
28+
discovered tests.
29+
1930
These files are not maintained. They will be deleted once coverage
2031
measurement is re-enabled and confirms no regression against the
2132
mirrored test layout.

tests/private/build/InvokeNovaBuildWorkflow.Tests.ps1

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,22 @@
11
BeforeAll {
2-
$script:repoRoot = (Resolve-Path (Join-Path $PSScriptRoot '../../..')).Path
3-
$script:moduleName = (Get-Content -LiteralPath (Join-Path $script:repoRoot 'project.json') -Raw | ConvertFrom-Json).ProjectName
4-
$script:distModuleDir = Join-Path $script:repoRoot "dist/$script:moduleName"
2+
$projectRoot = Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $PSScriptRoot))
3+
. (Join-Path $projectRoot 'src/private/build/InvokeNovaBuildWorkflow.ps1')
54

6-
if (-not (Test-Path -LiteralPath $script:distModuleDir)) {
7-
throw "Expected built $script:moduleName module at: $script:distModuleDir. Run Invoke-NovaBuild in the repo root first."
8-
}
9-
10-
Remove-Module $script:moduleName -ErrorAction SilentlyContinue
11-
Import-Module $script:distModuleDir -Force
5+
function Assert-NovaPublicFunctionFileLayout {param($ProjectInfo, [switch]$OverrideWarningRequested)}
6+
function Reset-ProjectDist {param($ProjectInfo)}
7+
function Build-Module {param($ProjectInfo)}
8+
function Assert-BuiltModuleHasNoDuplicateFunctionName {param($ProjectInfo)}
9+
function Build-Manifest {param($ProjectInfo)}
10+
function Build-Help {param($ProjectInfo)}
11+
function Copy-ProjectResource {param($ProjectInfo)}
12+
function Invoke-NovaModuleUpdateNotificationSafely {}
13+
function Import-NovaBuiltModuleForCi {param($ProjectInfo)}
1214
}
1315

1416
Describe 'Invoke-NovaBuildWorkflow' {
1517
It 'uses the shared module update notification before the optional CI import step' {
16-
InModuleScope $script:moduleName {
17-
$script:steps = @()
18+
$global:steps = @()
19+
try {
1820
$workflowContext = [pscustomobject]@{
1921
ProjectInfo = [pscustomobject]@{
2022
ProjectName = 'NovaModuleTools'
@@ -24,22 +26,23 @@ Describe 'Invoke-NovaBuildWorkflow' {
2426
ContinuousIntegrationRequested = $true
2527
}
2628

27-
Mock Assert-NovaPublicFunctionFileLayout {$script:steps += 'public-layout'}
28-
Mock Reset-ProjectDist {$script:steps += 'reset'}
29-
Mock Build-Module {$script:steps += 'module'}
30-
Mock Assert-BuiltModuleHasNoDuplicateFunctionName {$script:steps += 'duplicates'}
31-
Mock Build-Manifest {$script:steps += 'manifest'}
32-
Mock Build-Help {$script:steps += 'help'}
33-
Mock Copy-ProjectResource {$script:steps += 'resources'}
34-
Mock Invoke-NovaModuleUpdateNotificationSafely {$script:steps += 'notification'}
35-
Mock Import-NovaBuiltModuleForCi {$script:steps += 'ci'}
29+
Mock Assert-NovaPublicFunctionFileLayout {$global:steps += 'public-layout'}
30+
Mock Reset-ProjectDist {$global:steps += 'reset'}
31+
Mock Build-Module {$global:steps += 'module'}
32+
Mock Assert-BuiltModuleHasNoDuplicateFunctionName {$global:steps += 'duplicates'}
33+
Mock Build-Manifest {$global:steps += 'manifest'}
34+
Mock Build-Help {$global:steps += 'help'}
35+
Mock Copy-ProjectResource {$global:steps += 'resources'}
36+
Mock Invoke-NovaModuleUpdateNotificationSafely {$global:steps += 'notification'}
37+
Mock Import-NovaBuiltModuleForCi {$global:steps += 'ci'}
3638

3739
Invoke-NovaBuildWorkflow -WorkflowContext $workflowContext
3840

39-
$script:steps -join ',' | Should -Be 'public-layout,reset,module,duplicates,manifest,help,resources,notification,ci'
41+
$global:steps -join ',' | Should -Be 'public-layout,reset,module,duplicates,manifest,help,resources,notification,ci'
4042
Assert-MockCalled Invoke-NovaModuleUpdateNotificationSafely -Times 1
4143
Assert-MockCalled Import-NovaBuiltModuleForCi -Times 1
44+
} finally {
45+
Remove-Variable -Name steps -Scope Global -ErrorAction SilentlyContinue
4246
}
4347
}
4448
}
45-
Lines changed: 67 additions & 162 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,31 @@
11
BeforeAll {
2-
$script:repoRoot = (Resolve-Path -LiteralPath (Join-Path $PSScriptRoot '../../..')).Path
3-
$script:moduleName = (Get-Content -LiteralPath (Join-Path $script:repoRoot 'project.json') -Raw | ConvertFrom-Json).ProjectName
4-
$script:distModuleDir = Join-Path $script:repoRoot "dist/$script:moduleName"
5-
6-
if (-not (Test-Path -LiteralPath $script:distModuleDir)) {
7-
throw "Expected built $script:moduleName module at: $script:distModuleDir. Run Invoke-NovaBuild in the repo root first."
8-
}
9-
10-
Remove-Module $script:moduleName -ErrorAction SilentlyContinue
11-
Import-Module $script:distModuleDir -Force
12-
13-
$script:getTestQualityPesterConfig = {
14-
return [pscustomobject]@{
15-
Run = [pscustomobject]@{
16-
Path = $null
17-
PassThru = $false
18-
Exit = $false
19-
Throw = $false
20-
}
21-
Filter = [pscustomobject]@{
22-
Tag = @()
23-
ExcludeTag = @()
24-
}
25-
Output = [pscustomobject]@{
26-
Verbosity = 'Detailed'
27-
RenderMode = 'Auto'
28-
}
29-
TestResult = [pscustomobject]@{
30-
Enabled = $true
31-
OutputPath = $null
32-
}
33-
CodeCoverage = [pscustomobject]@{
34-
Enabled = $true
35-
CoveragePercentTarget = 80
36-
Path = $null
37-
}
2+
$projectRoot = Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $PSScriptRoot))
3+
. (Join-Path $projectRoot 'src/private/quality/GetNovaTestWorkflowContext.ps1')
4+
5+
function Test-ProjectSchema {param($Name) }
6+
function Stop-NovaOperation {param($Message, $ErrorId, $Category, $TargetObject) throw $Message}
7+
function Get-NovaProjectInfo {}
8+
function New-PesterConfiguration {param($Hashtable)}
9+
function Get-NovaPesterRunPath {param($ProjectInfo) return 'tests' }
10+
function Get-NovaPesterTestResultPath {param($ProjectRoot) return (Join-Path $ProjectRoot 'TestResults.xml')}
11+
function Initialize-NovaPesterExecutionConfiguration {param($PesterConfig, $BoundParameters, $OutputVerbosity, $OutputRenderMode)}
12+
function Get-NovaShouldProcessForwardingParameter {param([switch]$WhatIfEnabled) return @{}}
13+
function Write-NovaPesterTestResultArtifact {}
14+
function Write-NovaPesterTestResultReport {}
15+
16+
$script:getPesterConfig = {
17+
[pscustomobject]@{
18+
Run = [pscustomobject]@{Path = $null; PassThru = $false; Exit = $false; Throw = $false}
19+
Filter = [pscustomobject]@{Tag = @(); ExcludeTag = @()}
20+
Output = [pscustomobject]@{Verbosity = 'Detailed'; RenderMode = 'Auto'}
21+
TestResult = [pscustomobject]@{Enabled = $true; OutputPath = $null}
22+
CodeCoverage = [pscustomobject]@{Enabled = $true; CoveragePercentTarget = 80; Path = $null}
3823
}
3924
}
4025

41-
$script:getTestQualityProjectInfo = {
42-
param(
43-
[Parameter(Mandatory)]
44-
[object]$PesterSettings
45-
)
46-
47-
return [pscustomobject]@{
26+
$script:getProjectInfo = {
27+
param([Parameter(Mandatory)][object]$PesterSettings)
28+
[pscustomobject]@{
4829
Pester = $PesterSettings
4930
BuildRecursiveFolders = $false
5031
TestsDir = 'tests'
@@ -55,141 +36,65 @@ BeforeAll {
5536
}
5637

5738
Describe 'Get-NovaTestWorkflowContext' {
39+
BeforeEach {
40+
Mock Test-ProjectSchema {}
41+
Mock Get-Module {[pscustomobject]@{Name = 'Pester'}} -ParameterFilter {$Name -eq 'Pester' -and $ListAvailable}
42+
Mock Get-Command {[pscustomobject]@{ScriptBlock = {}}} -ParameterFilter {$CommandType -eq 'Function'}
43+
}
44+
5845
It 'applies CoveragePercentTarget from project.json to the Pester configuration' {
59-
$pesterConfig = & $script:getTestQualityPesterConfig
60-
$projectInfo = & $script:getTestQualityProjectInfo -PesterSettings ([ordered]@{
61-
CodeCoverage = [ordered]@{
62-
Enabled = $true
63-
CoveragePercentTarget = 99
64-
}
46+
$pesterConfig = & $script:getPesterConfig
47+
$projectInfo = & $script:getProjectInfo -PesterSettings ([ordered]@{
48+
CodeCoverage = [ordered]@{Enabled = $true; CoveragePercentTarget = 99}
6549
})
6650

67-
InModuleScope $script:moduleName -Parameters @{
68-
PesterConfig = $pesterConfig
69-
ProjectInfo = $projectInfo
70-
} {
71-
param($PesterConfig, $ProjectInfo)
72-
73-
$writer = [pscustomobject]@{ScriptBlock = {}}
74-
75-
Mock Test-ProjectSchema {}
76-
Mock Get-Module {
77-
[pscustomobject]@{Name = 'Pester'}
78-
} -ParameterFilter {
79-
$Name -eq 'Pester' -and $ListAvailable
80-
}
81-
Mock Get-NovaProjectInfo { $ProjectInfo }
82-
Mock New-PesterConfiguration { $PesterConfig }
83-
Mock Get-Command { $writer } -ParameterFilter {
84-
$CommandType -eq 'Function'
85-
}
86-
87-
$result = Get-NovaTestWorkflowContext -TestOption @{} -BoundParameters @{}
88-
89-
$result.PesterConfig.CodeCoverage.CoveragePercentTarget | Should -Be 99
90-
}
51+
Mock Get-NovaProjectInfo {$projectInfo}
52+
Mock New-PesterConfiguration {$pesterConfig}
53+
54+
$result = Get-NovaTestWorkflowContext -TestOption @{} -BoundParameters @{}
55+
56+
$result.PesterConfig.CodeCoverage.CoveragePercentTarget | Should -Be 99
9157
}
9258

9359
It 'keeps the default Pester coverage target when project.json omits CoveragePercentTarget' {
94-
$pesterConfig = & $script:getTestQualityPesterConfig
95-
$projectInfo = & $script:getTestQualityProjectInfo -PesterSettings ([ordered]@{
96-
CodeCoverage = [ordered]@{
97-
Enabled = $true
98-
}
60+
$pesterConfig = & $script:getPesterConfig
61+
$projectInfo = & $script:getProjectInfo -PesterSettings ([ordered]@{
62+
CodeCoverage = [ordered]@{Enabled = $true}
9963
})
10064

101-
InModuleScope $script:moduleName -Parameters @{
102-
PesterConfig = $pesterConfig
103-
ProjectInfo = $projectInfo
104-
} {
105-
param($PesterConfig, $ProjectInfo)
106-
107-
$writer = [pscustomobject]@{ScriptBlock = {}}
108-
109-
Mock Test-ProjectSchema {}
110-
Mock Get-Module {
111-
[pscustomobject]@{Name = 'Pester'}
112-
} -ParameterFilter {
113-
$Name -eq 'Pester' -and $ListAvailable
114-
}
115-
Mock Get-NovaProjectInfo { $ProjectInfo }
116-
Mock New-PesterConfiguration { $PesterConfig }
117-
Mock Get-Command { $writer } -ParameterFilter {
118-
$CommandType -eq 'Function'
119-
}
120-
121-
$result = Get-NovaTestWorkflowContext -TestOption @{} -BoundParameters @{}
122-
123-
$result.PesterConfig.CodeCoverage.CoveragePercentTarget | Should -Be 80
124-
}
65+
Mock Get-NovaProjectInfo {$projectInfo}
66+
Mock New-PesterConfiguration {$pesterConfig}
67+
68+
$result = Get-NovaTestWorkflowContext -TestOption @{} -BoundParameters @{}
69+
70+
$result.PesterConfig.CodeCoverage.CoveragePercentTarget | Should -Be 80
12571
}
12672

127-
It 'does not override CodeCoverage.Path when coverage is enabled (project.json owns Path)' {
128-
$pesterConfig = & $script:getTestQualityPesterConfig
129-
$projectInfo = & $script:getTestQualityProjectInfo -PesterSettings ([ordered]@{
130-
CodeCoverage = [ordered]@{
131-
Enabled = $true
132-
CoveragePercentTarget = 90
133-
}
73+
It 'does not override CodeCoverage.Path when coverage is enabled (project.json owns Path)' {
74+
$pesterConfig = & $script:getPesterConfig
75+
$projectInfo = & $script:getProjectInfo -PesterSettings ([ordered]@{
76+
CodeCoverage = [ordered]@{Enabled = $true; CoveragePercentTarget = 90}
13477
})
13578

136-
InModuleScope $script:moduleName -Parameters @{
137-
PesterConfig = $pesterConfig
138-
ProjectInfo = $projectInfo
139-
} {
140-
param($PesterConfig, $ProjectInfo)
141-
142-
$writer = [pscustomobject]@{ScriptBlock = {}}
143-
144-
Mock Test-ProjectSchema {}
145-
Mock Get-Module {
146-
[pscustomobject]@{Name = 'Pester'}
147-
} -ParameterFilter {
148-
$Name -eq 'Pester' -and $ListAvailable
149-
}
150-
Mock Get-NovaProjectInfo { $ProjectInfo }
151-
Mock New-PesterConfiguration { $PesterConfig }
152-
Mock Get-Command { $writer } -ParameterFilter {
153-
$CommandType -eq 'Function'
154-
}
155-
156-
$result = Get-NovaTestWorkflowContext -TestOption @{} -BoundParameters @{}
157-
158-
$result.PesterConfig.CodeCoverage.Path | Should -BeNullOrEmpty
159-
}
79+
Mock Get-NovaProjectInfo {$projectInfo}
80+
Mock New-PesterConfiguration {$pesterConfig}
81+
82+
$result = Get-NovaTestWorkflowContext -TestOption @{} -BoundParameters @{}
83+
84+
$result.PesterConfig.CodeCoverage.Path | Should -BeNullOrEmpty
16085
}
16186

16287
It 'does not set CodeCoverage.Path when coverage is disabled' {
163-
$pesterConfig = & $script:getTestQualityPesterConfig
164-
$projectInfo = & $script:getTestQualityProjectInfo -PesterSettings ([ordered]@{
165-
CodeCoverage = [ordered]@{
166-
Enabled = $false
167-
}
88+
$pesterConfig = & $script:getPesterConfig
89+
$projectInfo = & $script:getProjectInfo -PesterSettings ([ordered]@{
90+
CodeCoverage = [ordered]@{Enabled = $false}
16891
})
16992

170-
InModuleScope $script:moduleName -Parameters @{
171-
PesterConfig = $pesterConfig
172-
ProjectInfo = $projectInfo
173-
} {
174-
param($PesterConfig, $ProjectInfo)
175-
176-
$writer = [pscustomobject]@{ScriptBlock = {}}
177-
178-
Mock Test-ProjectSchema {}
179-
Mock Get-Module {
180-
[pscustomobject]@{Name = 'Pester'}
181-
} -ParameterFilter {
182-
$Name -eq 'Pester' -and $ListAvailable
183-
}
184-
Mock Get-NovaProjectInfo { $ProjectInfo }
185-
Mock New-PesterConfiguration { $PesterConfig }
186-
Mock Get-Command { $writer } -ParameterFilter {
187-
$CommandType -eq 'Function'
188-
}
189-
190-
$result = Get-NovaTestWorkflowContext -TestOption @{} -BoundParameters @{}
191-
192-
$result.PesterConfig.CodeCoverage.Path | Should -BeNullOrEmpty
193-
}
93+
Mock Get-NovaProjectInfo {$projectInfo}
94+
Mock New-PesterConfiguration {$pesterConfig}
95+
96+
$result = Get-NovaTestWorkflowContext -TestOption @{} -BoundParameters @{}
97+
98+
$result.PesterConfig.CodeCoverage.Path | Should -BeNullOrEmpty
19499
}
195100
}

0 commit comments

Comments
 (0)