From b6273efd4fa81a78b84f4c28834d5a96ebbb2056 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 26 Jul 2026 09:52:37 +0200 Subject: [PATCH 1/3] Add Test-Toml function Implement Test-Toml that validates TOML content without throwing. Mirrors PowerShell's Test-Json pattern: - Returns [bool] -- $true for valid, $false for invalid - Writes non-terminating errors via Write-Error on failure - Supports InputObject (pipeline), Path, and LiteralPath parameter sets - Uses [AllowEmptyString()] so empty string returns $false gracefully Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- src/functions/public/Test-Toml.ps1 | 119 +++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 src/functions/public/Test-Toml.ps1 diff --git a/src/functions/public/Test-Toml.ps1 b/src/functions/public/Test-Toml.ps1 new file mode 100644 index 0000000..a06bc93 --- /dev/null +++ b/src/functions/public/Test-Toml.ps1 @@ -0,0 +1,119 @@ +function Test-Toml { + <# + .SYNOPSIS + Tests whether a string or file contains valid TOML. + + .DESCRIPTION + Validates TOML content without throwing. Returns $true when the input + parses successfully and $false when it does not. On failure, a + non-terminating error is written via Write-Error so the caller can + inspect $Error or use -ErrorVariable while the pipeline continues. + + Three parameter sets are supported: + - Default : -InputObject — validates a TOML string directly. + - Path : -Path — reads a file (relative paths resolved). + - LiteralPath: -LiteralPath — reads a file (no wildcard expansion). + + .EXAMPLE + Test-Toml -InputObject 'title = "Hello"' + + Returns $true because the string is valid TOML. + + .EXAMPLE + Test-Toml -InputObject 'bad = = "syntax"' + + Returns $false and writes a non-terminating error describing the parse + failure. + + .EXAMPLE + Test-Toml -Path '.\config.toml' + + Reads the file and returns $true if its content is valid TOML. + + .EXAMPLE + Test-Toml -LiteralPath 'C:\Configs\app.toml' + + Reads the file using a literal path (no glob expansion) and returns + $true if the content parses successfully. + + .INPUTS + [string] — pipeline input supported for the InputObject parameter. + + .OUTPUTS + [bool] + + .NOTES + Mirrors the pattern of the built-in Test-Json cmdlet. + All exceptions from the parser are caught and surfaced as + non-terminating errors so the function never throws. + + .LINK + https://psmodule.io/Toml/Functions/Test-Toml + #> + [OutputType([bool])] + [CmdletBinding(DefaultParameterSetName = 'Default')] + param( + # The TOML string to validate. Accepts pipeline input. + [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'Default')] + [AllowEmptyString()] + [string] $InputObject, + + # Path to a TOML file to validate. Relative paths are resolved against + # the current working directory. + [Parameter(Mandatory, ParameterSetName = 'Path')] + [string] $Path, + + # Literal path to a TOML file to validate. No wildcard expansion is + # performed. + [Parameter(Mandatory, ParameterSetName = 'LiteralPath')] + [string] $LiteralPath + ) + + process { + $content = $null + + if ($PSCmdlet.ParameterSetName -eq 'Path') { + Write-Verbose "Resolving path: $Path" + try { + $resolved = Resolve-Path -Path $Path -ErrorAction Stop + } catch { + Write-Error -Message "Cannot find path '$Path': $($_.Exception.Message)" -ErrorAction Continue + return $false + } + try { + $content = [System.IO.File]::ReadAllText($resolved.ProviderPath) + } catch { + Write-Error -Message "Cannot read file '$($resolved.ProviderPath)': $($_.Exception.Message)" -ErrorAction Continue + return $false + } + } elseif ($PSCmdlet.ParameterSetName -eq 'LiteralPath') { + Write-Verbose "Using literal path: $LiteralPath" + try { + $resolved = Resolve-Path -LiteralPath $LiteralPath -ErrorAction Stop + } catch { + Write-Error -Message "Cannot find path '$LiteralPath': $($_.Exception.Message)" -ErrorAction Continue + return $false + } + try { + $content = [System.IO.File]::ReadAllText($resolved.ProviderPath) + } catch { + Write-Error -Message "Cannot read file '$($resolved.ProviderPath)': $($_.Exception.Message)" -ErrorAction Continue + return $false + } + } else { + $content = $InputObject + } + + Write-Verbose "Validating TOML content ($($content.Length) character(s))." + try { + if ([string]::IsNullOrEmpty($content)) { + throw [System.ArgumentException]::new('Input is empty.') + } + $null = ConvertFrom-Toml -InputObject $content + return $true + } catch { + Write-Error -Message "TOML validation failed: $($_.Exception.Message)" -ErrorAction Continue + return $false + } + } +} From f47540daa93afbb64bd409828ea9212c5d9c9ade Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 26 Jul 2026 09:52:37 +0200 Subject: [PATCH 2/3] Add Test-Toml Pester tests Add Describe 'Test-Toml' block covering: - Module registration and [OutputType([bool])] declaration - Valid TOML string, [bool] return type, pipeline input - Invalid TOML: returns $false and non-terminating error - Empty string: returns $false - Duplicate-key document: returns $false - -Path: valid file returns $true, invalid returns $false, non-existent file returns $false and error - -LiteralPath: valid file returns $true and [bool] type Also update module-level 'exposes the expected public commands' test to include 'Test-Toml'. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- tests/Toml.Tests.ps1 | 104 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) diff --git a/tests/Toml.Tests.ps1 b/tests/Toml.Tests.ps1 index 1cbbcc3..37954f3 100644 --- a/tests/Toml.Tests.ps1 +++ b/tests/Toml.Tests.ps1 @@ -15,6 +15,7 @@ Describe 'Toml' { $commands | Should -Contain 'ConvertTo-Toml' $commands | Should -Contain 'Import-Toml' $commands | Should -Contain 'Export-Toml' + $commands | Should -Contain 'Test-Toml' } } @@ -801,4 +802,107 @@ Describe 'Toml' { } } } + + Describe 'Test-Toml' { + + Context 'Module registration' { + It 'is exported from the module' { + Get-Command -Module Toml | Select-Object -ExpandProperty Name | Should -Contain 'Test-Toml' + } + + It 'declares [OutputType([bool])]' { + $cmd = Get-Command -Name Test-Toml + $cmd.OutputType.Type | Should -Contain ([bool]) + } + } + + Context 'InputObject — valid TOML' { + It 'returns $true for a minimal key=value string' { + $result = Test-Toml -InputObject 'key = "value"' + $result | Should -BeTrue + } + + It 'returns a [bool]' { + $result = Test-Toml -InputObject 'key = 1' + $result | Should -BeOfType [bool] + } + + It 'accepts pipeline input and returns $true' { + $result = 'enabled = true' | Test-Toml + $result | Should -BeTrue + } + + It 'returns $true for a multi-section TOML string' { + $toml = @' +[server] +host = "localhost" +port = 8080 +'@ + Test-Toml -InputObject $toml | Should -BeTrue + } + } + + Context 'InputObject — invalid TOML' { + It 'returns $false for invalid syntax' { + $result = Test-Toml -InputObject 'bad = = "syntax"' 2>$null + $result | Should -BeFalse + } + + It 'writes a non-terminating error for invalid TOML' { + $errors = @() + $result = Test-Toml -InputObject 'bad = = "syntax"' -ErrorVariable errors 2>$null + $result | Should -BeFalse + $errors | Should -Not -BeNullOrEmpty + } + + It 'returns $false for an empty string' { + $result = Test-Toml -InputObject '' 2>$null + $result | Should -BeFalse + } + + It 'returns $false for a duplicate-key document' { + $result = Test-Toml -InputObject "key = 1`nkey = 2" 2>$null + $result | Should -BeFalse + } + } + + Context 'Path — valid file' { + It 'returns $true for a valid TOML file via -Path' { + $path = Join-Path $TestDrive 'valid.toml' + Set-Content -Path $path -Value 'name = "test"' -Encoding UTF8 + Test-Toml -Path $path | Should -BeTrue + } + } + + Context 'Path — invalid file' { + It 'returns $false for an invalid TOML file via -Path' { + $path = Join-Path $TestDrive 'invalid.toml' + Set-Content -Path $path -Value 'bad = = "syntax"' -Encoding UTF8 + $result = Test-Toml -Path $path 2>$null + $result | Should -BeFalse + } + + It 'returns $false and writes an error for a non-existent file via -Path' { + $errors = @() + $result = Test-Toml -Path (Join-Path $TestDrive 'does-not-exist.toml') -ErrorVariable errors 2>$null + $result | Should -BeFalse + $errors | Should -Not -BeNullOrEmpty + } + } + + Context 'LiteralPath — valid file' { + It 'returns $true for a valid TOML file via -LiteralPath' { + $path = Join-Path $TestDrive 'literal-valid.toml' + Set-Content -Path $path -Value 'answer = 42' -Encoding UTF8 + Test-Toml -LiteralPath $path | Should -BeTrue + } + + It 'returns a [bool] via -LiteralPath' { + $path = Join-Path $TestDrive 'literal-bool.toml' + Set-Content -Path $path -Value 'flag = true' -Encoding UTF8 + $result = Test-Toml -LiteralPath $path + $result | Should -BeOfType [bool] + } + } + } } From 18f9077434d6f38cc27f90c13c97395b39b6bd00 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 26 Jul 2026 10:57:11 +0200 Subject: [PATCH 3/3] Ignore Pester testResults.xml output file Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 4238184..fe7a40e 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,6 @@ output/ bin/ obj/ libs/ + +# Pester test output +testResults.xml