Skip to content

Commit 87db820

Browse files
committed
feat(#202): mirror build and shared helper tests
- Closes Migrate build and shared domain tests (3) Fixes #202
1 parent 1b2ae3c commit 87db820

46 files changed

Lines changed: 1604 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
@@ -23,6 +23,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
2323
- `Test-NovaBuild` and `% nova test` now enforce `Pester.CodeCoverage.CoveragePercentTarget` from `project.json` when `CodeCoverage.Enabled` is `true`, failing the test run when measured coverage drops below the configured target.
2424
- Added a non-blocking `scripts/build/Get-TestMirrorStatus.ps1` helper that reports `src/**/*.ps1` files without a matching mirrored test file under `tests/`, to support the migration to the source-mirrored, dot-source-first test layout.
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.
26+
- 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.
2627

2728

2829
### Changed
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/build/AddProjectPreambleToModuleBuilder.ps1')
4+
}
5+
6+
Describe 'Add-ProjectPreambleToModuleBuilder' {
7+
It 'appends each preamble line followed by a blank separator' {
8+
$builder = [System.Text.StringBuilder]::new()
9+
$projectInfo = [pscustomobject]@{Preamble = @('Set-StrictMode -Version Latest', '$ErrorActionPreference = ''Stop''')}
10+
11+
Add-ProjectPreambleToModuleBuilder -Builder $builder -ProjectInfo $projectInfo
12+
13+
$text = $builder.ToString()
14+
$text | Should -Match 'Set-StrictMode -Version Latest'
15+
$text | Should -Match 'ErrorActionPreference'
16+
($text -split "`r?`n").Count | Should -BeGreaterOrEqual 4
17+
}
18+
19+
It 'returns without writing when preamble is empty' {
20+
$builder = [System.Text.StringBuilder]::new()
21+
$projectInfo = [pscustomobject]@{Preamble = @()}
22+
23+
Add-ProjectPreambleToModuleBuilder -Builder $builder -ProjectInfo $projectInfo
24+
25+
$builder.Length | Should -Be 0
26+
}
27+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
BeforeAll {
2+
$projectRoot = Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $PSScriptRoot))
3+
. (Join-Path $projectRoot 'src/private/build/AddScriptFileContentToModuleBuilder.ps1')
4+
5+
function Get-NormalizedRelativePath {param([string]$Root, [string]$FullName)}
6+
}
7+
8+
Describe 'Add-ScriptFileContentToModuleBuilder' {
9+
BeforeEach {
10+
$script:tempFile = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [Guid]::NewGuid().ToString('N') + '.ps1')
11+
Set-Content -LiteralPath $script:tempFile -Value 'function Get-Foo {}'
12+
$script:fileInfo = Get-Item -LiteralPath $script:tempFile
13+
$script:builder = [System.Text.StringBuilder]::new()
14+
}
15+
16+
AfterEach {
17+
Remove-Item -LiteralPath $script:tempFile -ErrorAction SilentlyContinue
18+
}
19+
20+
It 'prepends a source comment when SetSourcePath is enabled' {
21+
Mock Get-NormalizedRelativePath {return 'src/public/Get-Foo.ps1'}
22+
$info = [pscustomobject]@{SetSourcePath = $true; ProjectRoot = '/proj'}
23+
24+
Add-ScriptFileContentToModuleBuilder -Builder $script:builder -ProjectInfo $info -File $script:fileInfo
25+
26+
$script:builder.ToString() | Should -Match '# Source: src/public/Get-Foo\.ps1'
27+
$script:builder.ToString() | Should -Match 'function Get-Foo'
28+
}
29+
30+
It 'skips the source comment when SetSourcePath is disabled' {
31+
$info = [pscustomobject]@{SetSourcePath = $false; ProjectRoot = '/proj'}
32+
33+
Add-ScriptFileContentToModuleBuilder -Builder $script:builder -ProjectInfo $info -File $script:fileInfo
34+
35+
$script:builder.ToString() | Should -Not -Match '# Source:'
36+
$script:builder.ToString() | Should -Match 'function Get-Foo'
37+
}
38+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
BeforeAll {
2+
$projectRoot = Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $PSScriptRoot))
3+
. (Join-Path $projectRoot 'src/private/build/CopyProjectResource.ps1')
4+
5+
function Get-NovaBuildProjectInfo {param([pscustomobject]$ProjectInfo)}
6+
function Get-ProjectResourceFolderPath {param([string]$ProjectRoot)}
7+
function Get-ProjectResourceItemList {param([string]$ResourceFolder)}
8+
function Copy-ProjectResourceContentToModuleRoot {param([System.IO.FileSystemInfo[]]$ItemList, [string]$OutputModuleDir)}
9+
function Copy-ProjectResourceFolderToOutputModuleDir {param([string]$ResourceFolder, [string]$OutputModuleDir)}
10+
}
11+
12+
Describe 'Copy-ProjectResource' {
13+
BeforeEach {
14+
$script:root = Join-Path ([System.IO.Path]::GetTempPath()) ([Guid]::NewGuid().ToString('N'))
15+
$script:resourceFolder = Join-Path $script:root 'resources'
16+
$null = New-Item -ItemType Directory -Path $script:resourceFolder -Force
17+
Set-Content -LiteralPath (Join-Path $script:resourceFolder 'item.txt') -Value 'x'
18+
$script:projectData = [pscustomobject]@{
19+
ProjectRoot = $script:root
20+
OutputModuleDir = (Join-Path $script:root 'dist')
21+
CopyResourcesToModuleRoot = $false
22+
}
23+
Mock Get-NovaBuildProjectInfo {return $script:projectData}
24+
Mock Get-ProjectResourceFolderPath {return $script:resourceFolder}
25+
Mock Get-ProjectResourceItemList {return @(Get-ChildItem -LiteralPath $script:resourceFolder)}
26+
Mock Copy-ProjectResourceContentToModuleRoot {}
27+
Mock Copy-ProjectResourceFolderToOutputModuleDir {}
28+
}
29+
30+
AfterEach {
31+
Remove-Item -LiteralPath $script:root -Recurse -Force -ErrorAction SilentlyContinue
32+
}
33+
34+
It 'returns silently when the resource folder does not exist' {
35+
Mock Get-ProjectResourceFolderPath {return (Join-Path $script:root 'missing')}
36+
37+
Copy-ProjectResource
38+
39+
Assert-MockCalled Copy-ProjectResourceContentToModuleRoot -Times 0
40+
Assert-MockCalled Copy-ProjectResourceFolderToOutputModuleDir -Times 0
41+
}
42+
43+
It 'returns silently when the resource folder is empty' {
44+
Mock Get-ProjectResourceItemList {return @()}
45+
46+
Copy-ProjectResource
47+
48+
Assert-MockCalled Copy-ProjectResourceContentToModuleRoot -Times 0
49+
Assert-MockCalled Copy-ProjectResourceFolderToOutputModuleDir -Times 0
50+
}
51+
52+
It 'flattens resources when CopyResourcesToModuleRoot is true' {
53+
$script:projectData.CopyResourcesToModuleRoot = $true
54+
55+
Copy-ProjectResource
56+
57+
Assert-MockCalled Copy-ProjectResourceContentToModuleRoot -Times 1
58+
Assert-MockCalled Copy-ProjectResourceFolderToOutputModuleDir -Times 0
59+
}
60+
61+
It 'copies the resource folder when CopyResourcesToModuleRoot is false' {
62+
Copy-ProjectResource
63+
64+
Assert-MockCalled Copy-ProjectResourceFolderToOutputModuleDir -Times 1
65+
Assert-MockCalled Copy-ProjectResourceContentToModuleRoot -Times 0
66+
}
67+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
BeforeAll {
2+
$projectRoot = Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $PSScriptRoot))
3+
. (Join-Path $projectRoot 'src/private/build/CopyProjectResourceContentToModuleRoot.ps1')
4+
}
5+
6+
Describe 'Copy-ProjectResourceContentToModuleRoot' {
7+
BeforeEach {
8+
$script:root = Join-Path ([System.IO.Path]::GetTempPath()) ([Guid]::NewGuid().ToString('N'))
9+
$script:source = Join-Path $script:root 'resources'
10+
$script:target = Join-Path $script:root 'dist'
11+
$null = New-Item -ItemType Directory -Path $script:source -Force
12+
$null = New-Item -ItemType Directory -Path $script:target -Force
13+
Set-Content -LiteralPath (Join-Path $script:source 'a.txt') -Value 'A'
14+
$null = New-Item -ItemType Directory -Path (Join-Path $script:source 'sub') -Force
15+
Set-Content -LiteralPath (Join-Path $script:source 'sub/b.txt') -Value 'B'
16+
}
17+
18+
AfterEach {
19+
Remove-Item -LiteralPath $script:root -Recurse -Force -ErrorAction SilentlyContinue
20+
}
21+
22+
It 'copies each item directly into the output module directory' {
23+
$items = Get-ChildItem -Path $script:source
24+
25+
Copy-ProjectResourceContentToModuleRoot -ItemList $items -OutputModuleDir $script:target
26+
27+
Test-Path -LiteralPath (Join-Path $script:target 'a.txt') | Should -BeTrue
28+
Test-Path -LiteralPath (Join-Path $script:target 'sub/b.txt') | Should -BeTrue
29+
}
30+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
BeforeAll {
2+
$projectRoot = Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $PSScriptRoot))
3+
. (Join-Path $projectRoot 'src/private/build/CopyProjectResourceFolderToOutputModuleDir.ps1')
4+
}
5+
6+
Describe 'Copy-ProjectResourceFolderToOutputModuleDir' {
7+
BeforeEach {
8+
$script:root = Join-Path ([System.IO.Path]::GetTempPath()) ([Guid]::NewGuid().ToString('N'))
9+
$script:source = Join-Path $script:root 'resources'
10+
$script:target = Join-Path $script:root 'dist'
11+
$null = New-Item -ItemType Directory -Path $script:source -Force
12+
$null = New-Item -ItemType Directory -Path $script:target -Force
13+
Set-Content -LiteralPath (Join-Path $script:source 'file.txt') -Value 'hello'
14+
}
15+
16+
AfterEach {
17+
Remove-Item -LiteralPath $script:root -Recurse -Force -ErrorAction SilentlyContinue
18+
}
19+
20+
It 'recursively copies the resource folder into the output module directory' {
21+
Copy-ProjectResourceFolderToOutputModuleDir -ResourceFolder $script:source -OutputModuleDir $script:target
22+
23+
$copied = Join-Path $script:target 'resources/file.txt'
24+
Test-Path -LiteralPath $copied | Should -BeTrue
25+
}
26+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
BeforeAll {
2+
$projectRoot = Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $PSScriptRoot))
3+
. (Join-Path $projectRoot 'src/private/build/Get-ProjectPreamble.ps1')
4+
5+
function Get-ProjectJsonValueTypeName {param($Value)}
6+
function Format-ProjectJsonValue {param($Value)}
7+
function Stop-NovaOperation {param([string]$Message, [string]$ErrorId, $Category, $TargetObject)
8+
throw $Message
9+
}
10+
}
11+
12+
Describe 'Get-ProjectPreamble' {
13+
It 'returns an empty array when Preamble is not configured' {
14+
$result = Get-ProjectPreamble -ProjectData @{}
15+
16+
@($result).Count | Should -Be 0
17+
}
18+
19+
It 'returns the configured preamble string array' {
20+
$result = Get-ProjectPreamble -ProjectData @{Preamble = @('a', 'b')}
21+
22+
$result.Count | Should -Be 2
23+
$result[0] | Should -Be 'a'
24+
$result[1] | Should -Be 'b'
25+
}
26+
27+
It 'throws when Preamble is not an array' {
28+
Mock Get-ProjectJsonValueTypeName {return 'String'}
29+
Mock Format-ProjectJsonValue {return '"x"'}
30+
31+
{Get-ProjectPreamble -ProjectData @{Preamble = 'oops'}} | Should -Throw
32+
}
33+
34+
It 'throws when a Preamble entry is not a string' {
35+
Mock Get-ProjectJsonValueTypeName {return 'Int32'}
36+
Mock Format-ProjectJsonValue {return '7'}
37+
38+
{Get-ProjectPreamble -ProjectData @{Preamble = @('a', 7)}} | Should -Throw
39+
}
40+
}
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/build/GetNovaBuildProjectInfo.ps1')
4+
5+
function Get-NovaProjectInfo {param([string]$Path)}
6+
}
7+
8+
Describe 'Get-NovaBuildProjectInfo' {
9+
It 'returns the provided ProjectInfo unchanged' {
10+
$info = [pscustomobject]@{ProjectRoot = '/tmp'}
11+
12+
Get-NovaBuildProjectInfo -ProjectInfo $info | Should -Be $info
13+
}
14+
15+
It 'falls back to Get-NovaProjectInfo when ProjectInfo is null' {
16+
Mock Get-NovaProjectInfo {return [pscustomobject]@{ProjectRoot = '/from-fallback'}}
17+
18+
$result = Get-NovaBuildProjectInfo -ProjectInfo $null
19+
20+
$result.ProjectRoot | Should -Be '/from-fallback'
21+
Assert-MockCalled Get-NovaProjectInfo -Times 1
22+
}
23+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
BeforeAll {
2+
$projectRoot = Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $PSScriptRoot))
3+
. (Join-Path $projectRoot 'src/private/build/GetNovaBuildWorkflowContext.ps1')
4+
5+
function Get-NovaBuildProjectInfo {param([pscustomobject]$ProjectInfo)}
6+
}
7+
8+
Describe 'Get-NovaBuildWorkflowContext' {
9+
BeforeEach {
10+
$script:projectInfo = [pscustomobject]@{
11+
ProjectRoot = '/proj'
12+
OutputModuleDir = '/proj/dist/Mod'
13+
}
14+
Mock Get-NovaBuildProjectInfo {return $script:projectInfo}
15+
}
16+
17+
It 'returns a workflow context populated from the resolved project info' {
18+
$ctx = Get-NovaBuildWorkflowContext
19+
20+
$ctx.ProjectInfo | Should -Be $script:projectInfo
21+
$ctx.Target | Should -Be '/proj/dist/Mod'
22+
$ctx.Operation | Should -Be 'Build Nova module output'
23+
$ctx.ContinuousIntegrationRequested | Should -BeFalse
24+
$ctx.OverrideWarningRequested | Should -BeFalse
25+
}
26+
27+
It 'reflects the CI and override switches as booleans' {
28+
$ctx = Get-NovaBuildWorkflowContext -ContinuousIntegrationRequested -OverrideWarningRequested
29+
30+
$ctx.ContinuousIntegrationRequested | Should -BeTrue
31+
$ctx.OverrideWarningRequested | Should -BeTrue
32+
}
33+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
BeforeAll {
2+
$projectRoot = Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $PSScriptRoot))
3+
. (Join-Path $projectRoot 'src/private/build/GetNovaHelpLocale.ps1')
4+
5+
function Stop-NovaOperation {param([string]$Message, [string]$ErrorId, $Category, $TargetObject)
6+
throw $Message
7+
}
8+
}
9+
10+
Describe 'Get-NovaHelpLocale' {
11+
BeforeEach {
12+
$script:root = Join-Path ([System.IO.Path]::GetTempPath()) ([Guid]::NewGuid().ToString('N'))
13+
$null = New-Item -ItemType Directory -Path $script:root -Force
14+
}
15+
16+
AfterEach {
17+
Remove-Item -LiteralPath $script:root -Recurse -Force -ErrorAction SilentlyContinue
18+
}
19+
20+
It 'returns the locale parsed from the help file' {
21+
$file = Join-Path $script:root 'a.md'
22+
Set-Content -LiteralPath $file -Value @('Locale: en-US', '')
23+
$info = Get-Item -LiteralPath $file
24+
25+
Get-NovaHelpLocale -HelpMarkdownFiles @($info) | Should -Be 'en-US'
26+
}
27+
28+
It 'returns en-US default when no locale line is found' {
29+
$file = Join-Path $script:root 'a.md'
30+
Set-Content -LiteralPath $file -Value 'no metadata here'
31+
$info = Get-Item -LiteralPath $file
32+
33+
Get-NovaHelpLocale -HelpMarkdownFiles @($info) | Should -Be 'en-US'
34+
}
35+
36+
It 'throws when files declare conflicting locales' {
37+
$a = Join-Path $script:root 'a.md'
38+
$b = Join-Path $script:root 'b.md'
39+
Set-Content -LiteralPath $a -Value 'Locale: en-US'
40+
Set-Content -LiteralPath $b -Value 'Locale: da-DK'
41+
42+
{Get-NovaHelpLocale -HelpMarkdownFiles @((Get-Item $a), (Get-Item $b))} | Should -Throw
43+
}
44+
}

0 commit comments

Comments
 (0)