Skip to content

Commit 18818fb

Browse files
authored
Feature/133 100 code coverage (#134)
* feat(#133): enhance test coverage for NovaModuleTools - Add tests for CodeScene Cobertura remapping helpers - Implement coverage gap tests for quality helpers - Validate Nova package output directory initialization - Improve timeout handling in PowerShell script execution * feat(#133): enhance test coverage for NovaModuleTools - Add tests for CodeScene Cobertura remapping helpers - Implement coverage gap tests for quality helpers - Validate Nova package output directory initialization - Improve timeout handling in PowerShell script execution
1 parent ac6c27d commit 18818fb

7 files changed

Lines changed: 958 additions & 8 deletions

src/private/package/InitializeNovaPackageOutputDirectory.ps1

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,19 @@ function Initialize-NovaPackageOutputDirectory {
33
[CmdletBinding()]
44
param(
55
[Parameter(Mandatory)][pscustomobject]$ProjectInfo,
6-
[Alias('PackageMetadata')][Parameter(Mandatory)][object[]]$PackageMetadataList
6+
[AllowEmptyCollection()][Alias('PackageMetadata')][Parameter(Mandatory)][object[]]$PackageMetadataList
77
)
88

9-
$packageMetadata = @($PackageMetadataList)[0]
9+
$packageMetadata = @($PackageMetadataList) | Select-Object -First 1
1010
if ($null -eq $packageMetadata) {
1111
Stop-NovaOperation -Message 'Package metadata list cannot be empty.' -ErrorId 'Nova.Validation.PackageMetadataListEmpty' -Category InvalidArgument -TargetObject 'PackageMetadataList'
1212
}
1313

14-
if ($PackageMetadata.CleanOutputDirectory) {
15-
Clear-NovaPackageOutputDirectory -ProjectInfo $ProjectInfo -OutputDirectory $PackageMetadata.OutputDirectory
14+
if ($packageMetadata.CleanOutputDirectory) {
15+
Clear-NovaPackageOutputDirectory -ProjectInfo $ProjectInfo -OutputDirectory $packageMetadata.OutputDirectory
1616
}
1717

18-
if (-not (Test-Path -LiteralPath $PackageMetadata.OutputDirectory)) {
19-
$null = New-Item -ItemType Directory -Path $PackageMetadata.OutputDirectory -Force
18+
if (-not (Test-Path -LiteralPath $packageMetadata.OutputDirectory)) {
19+
$null = New-Item -ItemType Directory -Path $packageMetadata.OutputDirectory -Force
2020
}
2121
}

src/private/update/InvokeNovaPowerShellScriptWithTimeout.ps1

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@ function Invoke-NovaPowerShellScriptWithTimeout {
33
param(
44
[Parameter(Mandatory)][string]$Script,
55
[object[]]$ArgumentList = @(),
6-
[int]$TimeoutMilliseconds = 3000
6+
[int]$TimeoutMilliseconds = 3000,
7+
[scriptblock]$PowerShellFactory = {[powershell]::Create()}
78
)
89

9-
$powershell = [powershell]::Create()
10+
$powershell = & $PowerShellFactory
1011
try {
1112
$null = $powershell.AddScript($Script)
1213
foreach ($argument in $ArgumentList) {

tests/CiCoverage.Tests.ps1

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,47 @@ BeforeAll {
22
. (Join-Path $PSScriptRoot '..' 'scripts' 'build' 'ci' 'CodeSceneCoverageMap.ps1')
33
. (Join-Path $PSScriptRoot '..' 'scripts' 'build' 'ci' 'CodeSceneCoverageXml.ps1')
44
. (Join-Path $PSScriptRoot '..' 'scripts' 'build' 'ci' 'CoverageLowReport.ps1')
5+
6+
foreach ($functionName in @(
7+
'ConvertTo-CoberturaRelativePath'
8+
'Get-CodeSceneCoverageErrorRecord'
9+
'Get-SourceSectionListFromBuiltModule'
10+
'Find-SourceSectionForLine'
11+
'Get-EmptyCoberturaLineBucket'
12+
'Add-CoberturaLineHit'
13+
'Get-CoberturaLineStat'
14+
'Get-CoberturaPackageName'
15+
'Get-CoberturaSourceLineRange'
16+
'Test-CoberturaLineOutsideSourceRange'
17+
'Add-CoberturaMappedLineHit'
18+
'Add-CoberturaLineNodeHit'
19+
'Get-CoberturaLineBucketMap'
20+
'Add-CoberturaAttribute'
21+
'Get-CoberturaClassElement'
22+
'Get-CoberturaPackageElement'
23+
'Get-CoberturaCoverageAttributeMap'
24+
'Get-CoberturaCoverageDocument'
25+
'Convert-CoberturaCoverageToSourcePath'
26+
'ConvertTo-CoverageLineRate'
27+
'Get-CoverageLowReportEntryList'
28+
'Format-CoverageLowReportLine'
29+
'Write-CoverageLowReport'
30+
)) {
31+
$scriptBlock = (Get-Command -Name $functionName -CommandType Function -ErrorAction Stop).ScriptBlock
32+
Set-Item -Path "function:global:$functionName" -Value $scriptBlock
33+
}
34+
35+
$here = Split-Path -Parent $PSCommandPath
36+
$script:repoRoot = Split-Path -Parent $here
37+
$script:moduleName = (Get-Content -LiteralPath (Join-Path $script:repoRoot 'project.json') -Raw | ConvertFrom-Json).ProjectName
38+
$script:distModuleDir = Join-Path $script:repoRoot "dist/$script:moduleName"
39+
40+
if (-not (Test-Path -LiteralPath $script:distModuleDir)) {
41+
throw "Expected built $script:moduleName module at: $script:distModuleDir. Run Invoke-NovaBuild in the repo root first."
42+
}
43+
44+
Remove-Module $script:moduleName -ErrorAction SilentlyContinue
45+
Import-Module $script:distModuleDir -Force
546
}
647

748
Describe 'CodeScene Cobertura remapping helpers' {
@@ -204,3 +245,183 @@ Describe 'CodeScene Cobertura remapping helpers' {
204245
)
205246
}
206247
}
248+
249+
Describe 'Coverage gaps for quality helpers' {
250+
It 'Write-NovaPesterTestResultReport writes a success NUnit-style report with default suite name' {
251+
$outputPath = Join-Path $TestDrive 'success-report.xml'
252+
253+
InModuleScope $script:moduleName -Parameters @{OutputPath = $outputPath} {
254+
param($OutputPath)
255+
256+
$testResult = [pscustomobject]@{
257+
Tests = @(
258+
[pscustomobject]@{Result = 'Passed'}
259+
[pscustomobject]@{Result = 'Passed'}
260+
[pscustomobject]@{Result = 'Skipped'}
261+
[pscustomobject]@{Result = 'Inconclusive'}
262+
)
263+
}
264+
265+
Write-NovaPesterTestResultReport -TestResult $testResult -OutputPath $OutputPath
266+
267+
[xml]$report = Get-Content -LiteralPath $OutputPath -Raw
268+
$testResultsNode = $report.SelectSingleNode('/test-results')
269+
$testSuiteNode = $report.SelectSingleNode('/test-results/test-suite')
270+
271+
$testResultsNode.name | Should -Be 'NovaModuleTools'
272+
$testResultsNode.total | Should -Be '4'
273+
$testResultsNode.failures | Should -Be '0'
274+
$testResultsNode.inconclusive | Should -Be '1'
275+
$testResultsNode.skipped | Should -Be '1'
276+
$testSuiteNode.result | Should -Be 'Success'
277+
$testSuiteNode.success | Should -Be 'True'
278+
$testSuiteNode.passed | Should -Be '2'
279+
}
280+
}
281+
282+
It 'Write-NovaPesterTestResultReport writes a failure NUnit-style report when failed tests are present' {
283+
$outputPath = Join-Path $TestDrive 'failure-report.xml'
284+
285+
InModuleScope $script:moduleName -Parameters @{OutputPath = $outputPath} {
286+
param($OutputPath)
287+
288+
$testResult = [pscustomobject]@{
289+
Tests = @(
290+
[pscustomobject]@{Result = 'Passed'}
291+
[pscustomobject]@{Result = 'Failed'}
292+
)
293+
}
294+
295+
Write-NovaPesterTestResultReport -TestResult $testResult -OutputPath $OutputPath -TestSuiteName 'FocusedSuite'
296+
297+
[xml]$report = Get-Content -LiteralPath $OutputPath -Raw
298+
$testResultsNode = $report.SelectSingleNode('/test-results')
299+
$testSuiteNode = $report.SelectSingleNode('/test-results/test-suite')
300+
301+
$testResultsNode.name | Should -Be 'FocusedSuite'
302+
$testResultsNode.failures | Should -Be '1'
303+
$testSuiteNode.result | Should -Be 'Failure'
304+
$testSuiteNode.success | Should -Be 'False'
305+
$testSuiteNode.failed | Should -Be '1'
306+
}
307+
}
308+
309+
It 'Write-NovaPesterTestResultArtifact returns without writing when the result has no Tests property' {
310+
InModuleScope $script:moduleName {
311+
Mock Get-Command {throw 'Get-Command should not be called when Tests is missing.'}
312+
313+
{Write-NovaPesterTestResultArtifact -TestResult ([pscustomobject]@{Summary = 'no tests'}) -OutputPath '/tmp/unused.xml'} | Should -Not -Throw
314+
315+
Assert-MockCalled Get-Command -Times 0
316+
}
317+
}
318+
319+
It 'Write-NovaPesterTestResultArtifact uses the provided report writer when one is supplied' {
320+
InModuleScope $script:moduleName {
321+
$calls = [System.Collections.Generic.List[object]]::new()
322+
$reportWriter = {
323+
param($TestResult, $OutputPath)
324+
325+
$calls.Add([pscustomobject]@{
326+
TestResult = $TestResult
327+
OutputPath = $OutputPath
328+
}) | Out-Null
329+
}.GetNewClosure()
330+
$testResult = [pscustomobject]@{Tests = @([pscustomobject]@{Result = 'Passed'})}
331+
332+
Write-NovaPesterTestResultArtifact -TestResult $testResult -OutputPath '/tmp/provided.xml' -ReportWriter $reportWriter
333+
334+
$calls.Count | Should -Be 1
335+
$calls[0].OutputPath | Should -Be '/tmp/provided.xml'
336+
$calls[0].TestResult.Tests[0].Result | Should -Be 'Passed'
337+
}
338+
}
339+
340+
It 'Write-NovaPesterTestResultArtifact resolves the default report writer when none is supplied' {
341+
InModuleScope $script:moduleName {
342+
Mock Get-Command {
343+
[pscustomobject]@{
344+
ScriptBlock = {
345+
param($TestResult, $OutputPath)
346+
347+
return [pscustomobject]@{
348+
TestCount = @($TestResult.Tests).Count
349+
OutputPath = $OutputPath
350+
}
351+
}
352+
}
353+
} -ParameterFilter {
354+
$Name -eq 'Write-NovaPesterTestResultReport' -and $CommandType -eq 'Function'
355+
}
356+
357+
$result = Write-NovaPesterTestResultArtifact -TestResult ([pscustomobject]@{Tests = @([pscustomobject]@{Result = 'Passed'})}) -OutputPath '/tmp/default.xml'
358+
359+
$result.TestCount | Should -Be 1
360+
$result.OutputPath | Should -Be '/tmp/default.xml'
361+
Assert-MockCalled Get-Command -Times 1 -ParameterFilter {
362+
$Name -eq 'Write-NovaPesterTestResultReport' -and $CommandType -eq 'Function'
363+
}
364+
}
365+
}
366+
367+
It 'Initialize-NovaPesterExecutionConfiguration applies returned overrides when <Name>' -ForEach @(
368+
@{
369+
Name = 'both verbosity and render mode are provided'
370+
Override = [pscustomobject]@{Verbosity = 'Detailed'; RenderMode = 'Plaintext'}
371+
BoundParameters = @{OutputVerbosity = 'Detailed'; OutputRenderMode = 'Plaintext'}
372+
ExpectedVerbosity = 'Detailed'
373+
ExpectedRenderMode = 'Plaintext'
374+
}
375+
@{
376+
Name = 'only render mode is provided'
377+
Override = [pscustomobject]@{Verbosity = $null; RenderMode = 'Ansi'}
378+
BoundParameters = @{OutputRenderMode = 'Ansi'}
379+
ExpectedVerbosity = 'Normal'
380+
ExpectedRenderMode = 'Ansi'
381+
}
382+
) {
383+
$pesterConfig = [pscustomobject]@{
384+
Output = [pscustomobject]@{
385+
Verbosity = 'Normal'
386+
RenderMode = 'Auto'
387+
}
388+
TestResult = [pscustomobject]@{Enabled = $true}
389+
}
390+
391+
InModuleScope $script:moduleName -Parameters @{TestCase = $_; PesterConfig = $pesterConfig} {
392+
param($TestCase, $PesterConfig)
393+
394+
Mock Get-NovaPesterOutputOptionOverride {$TestCase.Override}
395+
396+
Initialize-NovaPesterExecutionConfiguration -PesterConfig $PesterConfig -BoundParameters $TestCase.BoundParameters
397+
398+
$PesterConfig.Output.Verbosity | Should -Be $TestCase.ExpectedVerbosity
399+
$PesterConfig.Output.RenderMode | Should -Be $TestCase.ExpectedRenderMode
400+
$PesterConfig.TestResult.Enabled | Should -BeFalse
401+
Assert-MockCalled Get-NovaPesterOutputOptionOverride -Times 1
402+
}
403+
}
404+
405+
It 'Initialize-NovaPesterExecutionConfiguration preserves unsupported settings when no overrides are returned' {
406+
$pesterConfig = [pscustomobject]@{
407+
Output = [pscustomobject]@{
408+
Verbosity = 'Normal'
409+
RenderMode = 'Auto'
410+
}
411+
TestResult = [pscustomobject]@{Summary = 'No Enabled property'}
412+
}
413+
414+
InModuleScope $script:moduleName -Parameters @{PesterConfig = $pesterConfig} {
415+
param($PesterConfig)
416+
417+
Mock Get-NovaPesterOutputOptionOverride {$null}
418+
419+
Initialize-NovaPesterExecutionConfiguration -PesterConfig $PesterConfig -BoundParameters @{}
420+
421+
$PesterConfig.Output.Verbosity | Should -Be 'Normal'
422+
$PesterConfig.Output.RenderMode | Should -Be 'Auto'
423+
$PesterConfig.TestResult.PSObject.Properties.Name | Should -Not -Contain 'Enabled'
424+
Assert-MockCalled Get-NovaPesterOutputOptionOverride -Times 1
425+
}
426+
}
427+
}

0 commit comments

Comments
 (0)