From 2771c859e404a81af39d0e5c0729e9d4f7e8301a Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 8 Feb 2025 13:31:52 +0100 Subject: [PATCH 01/21] Add Get-Uri function for URI conversion and normalization --- src/functions/public/Get-Uri.ps1 | 105 +++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 src/functions/public/Get-Uri.ps1 diff --git a/src/functions/public/Get-Uri.ps1 b/src/functions/public/Get-Uri.ps1 new file mode 100644 index 0000000..395d99c --- /dev/null +++ b/src/functions/public/Get-Uri.ps1 @@ -0,0 +1,105 @@ +function Get-Uri { + <# + .SYNOPSIS + Converts a string into a System.Uri, System.UriBuilder, or a normalized URI string. + + .DESCRIPTION + The Get-Uri function processes a string and attempts to convert it into a valid URI. + It supports three output formats: a System.Uri object, a System.UriBuilder object, + or a normalized absolute URI string. If no scheme is present, "http://" is prefixed + to ensure a valid URI. The function enforces mutual exclusivity between the output + format parameters. + + .EXAMPLE + Get-Uri -Uri 'example.com' + + Output: + ```powershell + http://example.com/ + ``` + + Converts 'example.com' into a normalized absolute URI string. + + .EXAMPLE + Get-Uri -Uri 'https://example.com' -AsUriBuilder + + Output: + ```powershell + Host : example.com + Scheme : https + Path : / + ``` + + Returns a [System.UriBuilder] object for the specified URI. + + .EXAMPLE + Get-Uri -Uri 'https://example.com/path' -AsUri + + Output: + ```powershell + AbsoluteUri : https://example.com/path + ``` + + Returns a [System.Uri] object with the full absolute URI. + + .LINK + https://psmodule.io/Uri/Functions/Get-Uri + #> + + [OutputType(ParameterSetName = 'UriBuilder', [System.UriBuilder])] + [OutputType(ParameterSetName = 'String', [string])] + [OutputType(ParameterSetName = 'AsUri', [System.Uri])] + [CmdletBinding(DefaultParameterSetName = 'AsUri')] + Param( + # The string representation of the URI to be processed. + [Parameter(Mandatory, Position = 0, ValueFromPipeline)] + [string] $Uri, + + # Outputs a System.UriBuilder object. + [Parameter(Mandatory, ParameterSetName = 'UriBuilder')] + [switch] $AsUriBuilder, + + # Outputs the URI as a normalized string. + [Parameter(Mandatory, ParameterSetName = 'String')] + [switch] $AsString + ) + + process { + # Ensure mutually exclusive output switches (cannot use both simultaneously) + if ($PSBoundParameters.ContainsKey('AsUriBuilder') -and $PSBoundParameters.ContainsKey('AsString')) { + throw 'Please specify only one of -AsUriBuilder or -AsString.' + } + + # Trim input to avoid issues with leading/trailing whitespace + $inputString = $Uri.Trim() + if ([string]::IsNullOrWhiteSpace($inputString)) { + throw 'The Uri parameter cannot be null or empty.' + } + + # Attempt to create a System.Uri (absolute) from the string + $uriObject = $null + $success = [System.Uri]::TryCreate($inputString, [System.UriKind]::Absolute, [ref]$uriObject) + if (-not $success) { + # If no scheme present, try adding "http://" + if ($inputString -notmatch '^[A-Za-z][A-Za-z0-9+.-]*:') { + $success = [System.Uri]::TryCreate("http://$inputString", [System.UriKind]::Absolute, [ref]$uriObject) + } + if (-not $success) { + throw "The provided value '$Uri' cannot be converted to a valid URI." + } + } + + switch ($PSCmdlet.ParameterSetName) { + 'UriBuilder' { + return [System.UriBuilder]::new($uriObject) # Return UriBuilder object + } + 'String' { + # Return normalized absolute URI string (safe unescaped format) + return $uriObject.GetComponents([System.UriComponents]::AbsoluteUri, [System.UriFormat]::SafeUnescaped) + } + 'AsUri' { + return $uriObject # Return the System.Uri object by default + } + } + } +} From 71e0d3d02f26728cacdaa4cae047d8c552addd46 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 8 Feb 2025 13:35:28 +0100 Subject: [PATCH 02/21] Refactor parameter declaration to use lowercase 'param' in Get-Uri function --- src/functions/public/Get-Uri.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/functions/public/Get-Uri.ps1 b/src/functions/public/Get-Uri.ps1 index 395d99c..3dba9f2 100644 --- a/src/functions/public/Get-Uri.ps1 +++ b/src/functions/public/Get-Uri.ps1 @@ -50,7 +50,7 @@ [OutputType(ParameterSetName = 'String', [string])] [OutputType(ParameterSetName = 'AsUri', [System.Uri])] [CmdletBinding(DefaultParameterSetName = 'AsUri')] - Param( + param( # The string representation of the URI to be processed. [Parameter(Mandatory, Position = 0, ValueFromPipeline)] [string] $Uri, From 627cc4e97be65609c19bc4f7f47c5cdf6f96d8cc Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 8 Feb 2025 13:38:47 +0100 Subject: [PATCH 03/21] Add tests for Get-Uri function covering default behavior, error handling, and pipeline input --- tests/Uri.Tests.ps1 | 150 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 149 insertions(+), 1 deletion(-) diff --git a/tests/Uri.Tests.ps1 b/tests/Uri.Tests.ps1 index 1753328..6aa8c35 100644 --- a/tests/Uri.Tests.ps1 +++ b/tests/Uri.Tests.ps1 @@ -27,8 +27,9 @@ It 'ConvertFrom-UriQueryString - removes leading question mark if present' { $result1 = ConvertFrom-UriQueryString -Query '?foo=bar' - $result2 = ConvertFrom-UriQueryString -Query 'foo=bar' $result1.foo | Should -Be 'bar' + + $result2 = ConvertFrom-UriQueryString -Query 'foo=bar' $result2.foo | Should -Be 'bar' } @@ -118,3 +119,150 @@ } } } + +Describe 'Get-Uri Function Tests' { + Context 'Default Behavior (returns a [System.Uri] object)' { + + It 'Should return a valid System.Uri when given a URI with scheme' { + $result = Get-Uri -Uri 'https://example.com/path' + $result | Should -BeOfType 'System.Uri' + $result.Scheme | Should -Be 'https' + $result.Host | Should -Be 'example.com' + } + + It 'Should add default scheme (http) when missing' { + $result = Get-Uri -Uri 'example.com/path' + $result | Should -BeOfType 'System.Uri' + $result.Scheme | Should -Be 'http' + $result.Host | Should -Be 'example.com' + } + } + + Context 'Switch: -AsUriBuilder' { + + It 'Should return a System.UriBuilder object' { + $result = Get-Uri -Uri 'https://example.com/path' -AsUriBuilder + $result | Should -BeOfType 'System.UriBuilder' + $result.Uri.Scheme | Should -Be 'https' + $result.Uri.Host | Should -Be 'example.com' + } + } + + Context 'Switch: -AsString' { + + It 'Should return a normalized URI string' { + # Example with uppercase scheme and percent-encoded characters + $inputUri = 'HTTP://Example.com/%7Euser/path/page.html' + $result = Get-Uri -Uri $inputUri -AsString + $expected = 'http://example.com/~user/path/page.html' + $result | Should -Be $expected + } + } + + Context 'Error Handling' { + + It 'Should throw an error for an invalid URI' { + { Get-Uri -Uri 'http://??' } | Should -Throw + } + + It 'Should throw an error when both -AsUriBuilder and -AsString are provided' { + { Get-Uri -Uri 'https://example.com' -AsUriBuilder -AsString } | Should -Throw + } + + It 'Should throw an error when an empty URI string is provided' { + { Get-Uri -Uri '' } | Should -Throw + } + } + + Context 'Pipeline Input' { + + It 'Should accept pipeline input and return a valid [System.Uri]' { + 'example.com/path' | Get-Uri | ForEach-Object { + $_ | Should -BeOfType 'System.Uri' + $_.Scheme | Should -Be 'http' + } + } + + It 'Should return a valid System.Uri when given a URI with scheme' { + $result = Get-Uri -Uri 'https://example.com/path' + $result | Should -BeOfType 'System.Uri' + $result.Scheme | Should -Be 'https' + $result.Host | Should -Be 'example.com' + } + + It 'Should add default scheme (http) when missing' { + $result = Get-Uri -Uri 'example.com/path' + $result | Should -BeOfType 'System.Uri' + $result.Scheme | Should -Be 'http' + $result.Host | Should -Be 'example.com' + } + } + + Context 'Edge Cases' { + It 'Should handle relative URIs correctly' { + $result = Get-Uri -Uri '/path/to/resource' + $result | Should -BeOfType 'System.Uri' + $result.IsAbsoluteUri | Should -Be $false + } + + It 'Should handle URIs with ports' { + $result = Get-Uri -Uri 'http://example.com:8080' + $result | Should -BeOfType 'System.Uri' + $result.Port | Should -Be 8080 + } + + It 'Should handle URIs with query strings' { + $result = Get-Uri -Uri 'https://example.com?query=test' + $result | Should -BeOfType 'System.Uri' + $result.Query | Should -Be '?query=test' + } + + It 'Should handle IPv6 addresses' { + $result = Get-Uri -Uri 'http://[::1]' + $result | Should -BeOfType 'System.Uri' + $result.Host | Should -Be '[::1]' + } + } + + Context 'Switch: -AsUriBuilder' { + It 'Should return a System.UriBuilder object' { + $result = Get-Uri -Uri 'https://example.com/path' -AsUriBuilder + $result | Should -BeOfType 'System.UriBuilder' + $result.Uri.Scheme | Should -Be 'https' + $result.Uri.Host | Should -Be 'example.com' + } + } + + Context 'Switch: -AsString' { + It 'Should return a normalized URI string' { + # Example with uppercase scheme and percent-encoded characters + $inputUri = 'HTTP://Example.com/%7Euser/path/page.html' + $result = Get-Uri -Uri $inputUri -AsString + $expected = 'http://example.com/~user/path/page.html' + $result | Should -Be $expected + } + } + + Context 'Error Handling' { + It 'Should throw an error for an invalid URI' { + { Get-Uri -Uri 'http://??' } | Should -Throw + } + + It 'Should throw an error when both -AsUriBuilder and -AsString are provided' { + { Get-Uri -Uri 'https://example.com' -AsUriBuilder -AsString } | Should -Throw + } + + It 'Should throw an error when an empty URI string is provided' { + { Get-Uri -Uri '' } | Should -Throw + } + } + + Context 'Pipeline Input' { + It 'Should accept pipeline input and return a valid [System.Uri]' { + 'example.com/path' | Get-Uri | ForEach-Object { + $_ | Should -BeOfType 'System.Uri' + $_.Scheme | Should -Be 'http' + } + } + } +} From 0e3ebb66c47ec0f7b2c14a1ec336e6b2e30d02d3 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 8 Feb 2025 14:01:17 +0100 Subject: [PATCH 04/21] Enhance Get-Uri function tests to cover edge cases for relative URIs, query strings, and fragments --- tests/Uri.Tests.ps1 | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/tests/Uri.Tests.ps1 b/tests/Uri.Tests.ps1 index 6aa8c35..9fc54ce 100644 --- a/tests/Uri.Tests.ps1 +++ b/tests/Uri.Tests.ps1 @@ -120,7 +120,7 @@ } } -Describe 'Get-Uri Function Tests' { +Describe 'Get-Uri' { Context 'Default Behavior (returns a [System.Uri] object)' { It 'Should return a valid System.Uri when given a URI with scheme' { @@ -199,10 +199,8 @@ Describe 'Get-Uri Function Tests' { } Context 'Edge Cases' { - It 'Should handle relative URIs correctly' { - $result = Get-Uri -Uri '/path/to/resource' - $result | Should -BeOfType 'System.Uri' - $result.IsAbsoluteUri | Should -Be $false + It 'Should throw with relative URIs' { + { Get-Uri -Uri '/path/to/resource' } | Should -Throw } It 'Should handle URIs with ports' { @@ -217,6 +215,33 @@ Describe 'Get-Uri Function Tests' { $result.Query | Should -Be '?query=test' } + It 'Should handle URIs with multiple query strings' { + $result = Get-Uri -Uri 'https://example.com?query=test&sort=asc&page=1' + $result | Should -BeOfType 'System.Uri' + $result.Query | Should -Be '?query=test' + } + + It 'Should handle URIs with query strings and fragments' { + $result = Get-Uri -Uri 'https://example.com?query=test#section1' + $result | Should -BeOfType 'System.Uri' + $result.Query | Should -Be '?query=test' + $result.Fragment | Should -Be 'section1' + } + + # Uri + same key query string and fragment + It 'Should handle URIs with query strings and fragments' { + $result = Get-Uri -Uri 'https://example.com?include=test&include=dev&include=prod#section1' + $result | Should -BeOfType 'System.Uri' + $result.Query | Should -Be '?include=test&include=dev&include=prod' + $result.Fragment | Should -Be 'section1' + } + + It 'Should handle URIs with fragments' { + $result = Get-Uri -Uri 'https://example.com#section1' + $result | Should -BeOfType 'System.Uri' + $result.Fragment | Should -Be 'section1' + } + It 'Should handle IPv6 addresses' { $result = Get-Uri -Uri 'http://[::1]' $result | Should -BeOfType 'System.Uri' From 5f0af3c3912b3c6577931021c70d6c2c377ea10b Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 8 Feb 2025 14:10:04 +0100 Subject: [PATCH 05/21] Fix Get-Uri tests to correctly validate query strings and fragments --- tests/Uri.Tests.ps1 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/Uri.Tests.ps1 b/tests/Uri.Tests.ps1 index 9fc54ce..5ffa97c 100644 --- a/tests/Uri.Tests.ps1 +++ b/tests/Uri.Tests.ps1 @@ -218,14 +218,14 @@ Describe 'Get-Uri' { It 'Should handle URIs with multiple query strings' { $result = Get-Uri -Uri 'https://example.com?query=test&sort=asc&page=1' $result | Should -BeOfType 'System.Uri' - $result.Query | Should -Be '?query=test' + $result.Query | Should -Be '?query=test&sort=asc&page=1' } It 'Should handle URIs with query strings and fragments' { $result = Get-Uri -Uri 'https://example.com?query=test#section1' $result | Should -BeOfType 'System.Uri' $result.Query | Should -Be '?query=test' - $result.Fragment | Should -Be 'section1' + $result.Fragment | Should -Be '#section1' } # Uri + same key query string and fragment @@ -233,13 +233,13 @@ Describe 'Get-Uri' { $result = Get-Uri -Uri 'https://example.com?include=test&include=dev&include=prod#section1' $result | Should -BeOfType 'System.Uri' $result.Query | Should -Be '?include=test&include=dev&include=prod' - $result.Fragment | Should -Be 'section1' + $result.Fragment | Should -Be '#section1' } It 'Should handle URIs with fragments' { $result = Get-Uri -Uri 'https://example.com#section1' $result | Should -BeOfType 'System.Uri' - $result.Fragment | Should -Be 'section1' + $result.Fragment | Should -Be '#section1' } It 'Should handle IPv6 addresses' { From ebf8c67ce932d7d64d59288d9f0e1afaef8030f7 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 8 Feb 2025 14:12:19 +0100 Subject: [PATCH 06/21] Improve error handling in Get-Uri tests for relative URIs by adding verbose output --- tests/Uri.Tests.ps1 | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/Uri.Tests.ps1 b/tests/Uri.Tests.ps1 index 5ffa97c..d346c0c 100644 --- a/tests/Uri.Tests.ps1 +++ b/tests/Uri.Tests.ps1 @@ -200,7 +200,10 @@ Describe 'Get-Uri' { Context 'Edge Cases' { It 'Should throw with relative URIs' { - { Get-Uri -Uri '/path/to/resource' } | Should -Throw + { + $test = Get-Uri -Uri '/path/to/resource' + $test | Out-String -Stream | ForEach-Object { Write-Verbose $_ -Verbose } + } | Should -Throw } It 'Should handle URIs with ports' { From eb434bc42a153b155150cd800fa2754029f7c93d Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 8 Feb 2025 14:29:19 +0100 Subject: [PATCH 07/21] Add jscpd configuration for duplicate code detection --- .github/linters/.jscpd.json | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .github/linters/.jscpd.json diff --git a/.github/linters/.jscpd.json b/.github/linters/.jscpd.json new file mode 100644 index 0000000..aa95628 --- /dev/null +++ b/.github/linters/.jscpd.json @@ -0,0 +1,10 @@ +{ + "threshold": 0, + "reporters": [ + "consoleFull" + ], + "ignore": [ + "**/tests/*" + ], + "absolute": true +} From 5f164f64df9aa79d7ffc1c357a3375ac41a3d18d Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 8 Feb 2025 14:32:43 +0100 Subject: [PATCH 08/21] Disable JSON validation in Linter workflow --- .github/workflows/Linter.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/Linter.yml b/.github/workflows/Linter.yml index d7650ae..a0ac5ce 100644 --- a/.github/workflows/Linter.yml +++ b/.github/workflows/Linter.yml @@ -29,3 +29,4 @@ jobs: GITHUB_TOKEN: ${{ github.token }} VALIDATE_MARKDOWN_PRETTIER: false VALIDATE_YAML_PRETTIER: false + VALIDATE_JSON_PRETTIER: false From b67dbc03d35b5d46e8ff5bc6a5829824e4a52b2e Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 8 Feb 2025 15:06:49 +0100 Subject: [PATCH 09/21] Add verbose output to Get-Uri tests for improved debugging --- tests/Uri.Tests.ps1 | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/Uri.Tests.ps1 b/tests/Uri.Tests.ps1 index d346c0c..fe86c5c 100644 --- a/tests/Uri.Tests.ps1 +++ b/tests/Uri.Tests.ps1 @@ -125,6 +125,7 @@ Describe 'Get-Uri' { It 'Should return a valid System.Uri when given a URI with scheme' { $result = Get-Uri -Uri 'https://example.com/path' + $result | Out-String -Stream | ForEach-Object { Write-Verbose $_ -Verbose } $result | Should -BeOfType 'System.Uri' $result.Scheme | Should -Be 'https' $result.Host | Should -Be 'example.com' @@ -132,6 +133,7 @@ Describe 'Get-Uri' { It 'Should add default scheme (http) when missing' { $result = Get-Uri -Uri 'example.com/path' + $result | Out-String -Stream | ForEach-Object { Write-Verbose $_ -Verbose } $result | Should -BeOfType 'System.Uri' $result.Scheme | Should -Be 'http' $result.Host | Should -Be 'example.com' @@ -142,6 +144,7 @@ Describe 'Get-Uri' { It 'Should return a System.UriBuilder object' { $result = Get-Uri -Uri 'https://example.com/path' -AsUriBuilder + $result | Out-String -Stream | ForEach-Object { Write-Verbose $_ -Verbose } $result | Should -BeOfType 'System.UriBuilder' $result.Uri.Scheme | Should -Be 'https' $result.Uri.Host | Should -Be 'example.com' @@ -154,6 +157,7 @@ Describe 'Get-Uri' { # Example with uppercase scheme and percent-encoded characters $inputUri = 'HTTP://Example.com/%7Euser/path/page.html' $result = Get-Uri -Uri $inputUri -AsString + $result | Out-String -Stream | ForEach-Object { Write-Verbose $_ -Verbose } $expected = 'http://example.com/~user/path/page.html' $result | Should -Be $expected } @@ -178,6 +182,7 @@ Describe 'Get-Uri' { It 'Should accept pipeline input and return a valid [System.Uri]' { 'example.com/path' | Get-Uri | ForEach-Object { + $_ | Out-String -Stream | ForEach-Object { Write-Verbose $_ -Verbose } $_ | Should -BeOfType 'System.Uri' $_.Scheme | Should -Be 'http' } @@ -185,6 +190,7 @@ Describe 'Get-Uri' { It 'Should return a valid System.Uri when given a URI with scheme' { $result = Get-Uri -Uri 'https://example.com/path' + $result | Out-String -Stream | ForEach-Object { Write-Verbose $_ -Verbose } $result | Should -BeOfType 'System.Uri' $result.Scheme | Should -Be 'https' $result.Host | Should -Be 'example.com' @@ -192,6 +198,7 @@ Describe 'Get-Uri' { It 'Should add default scheme (http) when missing' { $result = Get-Uri -Uri 'example.com/path' + $result | Out-String -Stream | ForEach-Object { Write-Verbose $_ -Verbose } $result | Should -BeOfType 'System.Uri' $result.Scheme | Should -Be 'http' $result.Host | Should -Be 'example.com' @@ -208,24 +215,28 @@ Describe 'Get-Uri' { It 'Should handle URIs with ports' { $result = Get-Uri -Uri 'http://example.com:8080' + $result | Out-String -Stream | ForEach-Object { Write-Verbose $_ -Verbose } $result | Should -BeOfType 'System.Uri' $result.Port | Should -Be 8080 } It 'Should handle URIs with query strings' { $result = Get-Uri -Uri 'https://example.com?query=test' + $result | Out-String -Stream | ForEach-Object { Write-Verbose $_ -Verbose } $result | Should -BeOfType 'System.Uri' $result.Query | Should -Be '?query=test' } It 'Should handle URIs with multiple query strings' { $result = Get-Uri -Uri 'https://example.com?query=test&sort=asc&page=1' + $result | Out-String -Stream | ForEach-Object { Write-Verbose $_ -Verbose } $result | Should -BeOfType 'System.Uri' $result.Query | Should -Be '?query=test&sort=asc&page=1' } It 'Should handle URIs with query strings and fragments' { $result = Get-Uri -Uri 'https://example.com?query=test#section1' + $result | Out-String -Stream | ForEach-Object { Write-Verbose $_ -Verbose } $result | Should -BeOfType 'System.Uri' $result.Query | Should -Be '?query=test' $result.Fragment | Should -Be '#section1' @@ -234,6 +245,7 @@ Describe 'Get-Uri' { # Uri + same key query string and fragment It 'Should handle URIs with query strings and fragments' { $result = Get-Uri -Uri 'https://example.com?include=test&include=dev&include=prod#section1' + $result | Out-String -Stream | ForEach-Object { Write-Verbose $_ -Verbose } $result | Should -BeOfType 'System.Uri' $result.Query | Should -Be '?include=test&include=dev&include=prod' $result.Fragment | Should -Be '#section1' @@ -241,12 +253,14 @@ Describe 'Get-Uri' { It 'Should handle URIs with fragments' { $result = Get-Uri -Uri 'https://example.com#section1' + $result | Out-String -Stream | ForEach-Object { Write-Verbose $_ -Verbose } $result | Should -BeOfType 'System.Uri' $result.Fragment | Should -Be '#section1' } It 'Should handle IPv6 addresses' { $result = Get-Uri -Uri 'http://[::1]' + $result | Out-String -Stream | ForEach-Object { Write-Verbose $_ -Verbose } $result | Should -BeOfType 'System.Uri' $result.Host | Should -Be '[::1]' } @@ -255,6 +269,7 @@ Describe 'Get-Uri' { Context 'Switch: -AsUriBuilder' { It 'Should return a System.UriBuilder object' { $result = Get-Uri -Uri 'https://example.com/path' -AsUriBuilder + $result | Out-String -Stream | ForEach-Object { Write-Verbose $_ -Verbose } $result | Should -BeOfType 'System.UriBuilder' $result.Uri.Scheme | Should -Be 'https' $result.Uri.Host | Should -Be 'example.com' @@ -266,6 +281,7 @@ Describe 'Get-Uri' { # Example with uppercase scheme and percent-encoded characters $inputUri = 'HTTP://Example.com/%7Euser/path/page.html' $result = Get-Uri -Uri $inputUri -AsString + $result | Out-String -Stream | ForEach-Object { Write-Verbose $_ -Verbose } $expected = 'http://example.com/~user/path/page.html' $result | Should -Be $expected } @@ -288,6 +304,7 @@ Describe 'Get-Uri' { Context 'Pipeline Input' { It 'Should accept pipeline input and return a valid [System.Uri]' { 'example.com/path' | Get-Uri | ForEach-Object { + $_ | Out-String -Stream | ForEach-Object { Write-Verbose $_ -Verbose } $_ | Should -BeOfType 'System.Uri' $_.Scheme | Should -Be 'http' } From 7ed8f1432556dbb0aae336edc6a239b254c88f3c Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 8 Feb 2025 15:29:02 +0100 Subject: [PATCH 10/21] Remove redundant tests and streamline Get-Uri test cases for clarity --- tests/Uri.Tests.ps1 | 52 --------------------------------------------- 1 file changed, 52 deletions(-) diff --git a/tests/Uri.Tests.ps1 b/tests/Uri.Tests.ps1 index fe86c5c..3bf9a00 100644 --- a/tests/Uri.Tests.ps1 +++ b/tests/Uri.Tests.ps1 @@ -206,13 +206,6 @@ Describe 'Get-Uri' { } Context 'Edge Cases' { - It 'Should throw with relative URIs' { - { - $test = Get-Uri -Uri '/path/to/resource' - $test | Out-String -Stream | ForEach-Object { Write-Verbose $_ -Verbose } - } | Should -Throw - } - It 'Should handle URIs with ports' { $result = Get-Uri -Uri 'http://example.com:8080' $result | Out-String -Stream | ForEach-Object { Write-Verbose $_ -Verbose } @@ -265,49 +258,4 @@ Describe 'Get-Uri' { $result.Host | Should -Be '[::1]' } } - - Context 'Switch: -AsUriBuilder' { - It 'Should return a System.UriBuilder object' { - $result = Get-Uri -Uri 'https://example.com/path' -AsUriBuilder - $result | Out-String -Stream | ForEach-Object { Write-Verbose $_ -Verbose } - $result | Should -BeOfType 'System.UriBuilder' - $result.Uri.Scheme | Should -Be 'https' - $result.Uri.Host | Should -Be 'example.com' - } - } - - Context 'Switch: -AsString' { - It 'Should return a normalized URI string' { - # Example with uppercase scheme and percent-encoded characters - $inputUri = 'HTTP://Example.com/%7Euser/path/page.html' - $result = Get-Uri -Uri $inputUri -AsString - $result | Out-String -Stream | ForEach-Object { Write-Verbose $_ -Verbose } - $expected = 'http://example.com/~user/path/page.html' - $result | Should -Be $expected - } - } - - Context 'Error Handling' { - It 'Should throw an error for an invalid URI' { - { Get-Uri -Uri 'http://??' } | Should -Throw - } - - It 'Should throw an error when both -AsUriBuilder and -AsString are provided' { - { Get-Uri -Uri 'https://example.com' -AsUriBuilder -AsString } | Should -Throw - } - - It 'Should throw an error when an empty URI string is provided' { - { Get-Uri -Uri '' } | Should -Throw - } - } - - Context 'Pipeline Input' { - It 'Should accept pipeline input and return a valid [System.Uri]' { - 'example.com/path' | Get-Uri | ForEach-Object { - $_ | Out-String -Stream | ForEach-Object { Write-Verbose $_ -Verbose } - $_ | Should -BeOfType 'System.Uri' - $_.Scheme | Should -Be 'http' - } - } - } } From c1445ebcd3050ea78f9fa1302184792903571e49 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 9 Feb 2025 20:40:35 +0100 Subject: [PATCH 11/21] Update Get-Uri example to reflect correct output for path parameter --- src/functions/public/Get-Uri.ps1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/functions/public/Get-Uri.ps1 b/src/functions/public/Get-Uri.ps1 index 3dba9f2..290e761 100644 --- a/src/functions/public/Get-Uri.ps1 +++ b/src/functions/public/Get-Uri.ps1 @@ -1,4 +1,4 @@ -function Get-Uri { +function Get-Uri { <# .SYNOPSIS Converts a string into a System.Uri, System.UriBuilder, or a normalized URI string. @@ -21,13 +21,13 @@ Converts 'example.com' into a normalized absolute URI string. .EXAMPLE - Get-Uri -Uri 'https://example.com' -AsUriBuilder + Get-Uri -Uri 'https://example.com/path' -AsUriBuilder Output: ```powershell Host : example.com Scheme : https - Path : / + Path : /path ``` Returns a [System.UriBuilder] object for the specified URI. From 94e042cf2d11c6002aac35bb7d445c9be2e8e537 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 9 Feb 2025 21:19:29 +0100 Subject: [PATCH 12/21] Refactor Get-Uri function to improve parameter handling and support relative URIs --- src/functions/public/Get-Uri.ps1 | 61 +++++++++++++++++++++----------- 1 file changed, 41 insertions(+), 20 deletions(-) diff --git a/src/functions/public/Get-Uri.ps1 b/src/functions/public/Get-Uri.ps1 index 290e761..b38f00f 100644 --- a/src/functions/public/Get-Uri.ps1 +++ b/src/functions/public/Get-Uri.ps1 @@ -1,4 +1,4 @@ -function Get-Uri { +function Get-Uri { <# .SYNOPSIS Converts a string into a System.Uri, System.UriBuilder, or a normalized URI string. @@ -42,10 +42,24 @@ function Get-Uri { Returns a [System.Uri] object with the full absolute URI. + .EXAMPLE + Get-Uri -Uri '/path/to/resource' + + Returns a relative URI (with no hostname). + .LINK https://psmodule.io/Uri/Functions/Get-Uri #> - + [Diagnostics.CodeAnalysis.SuppressMessageAttribute( + 'PSReviewUnusedParameter', 'AsString', + Scope = 'Function', + Justification = 'Present for parameter sets' + )] + [Diagnostics.CodeAnalysis.SuppressMessageAttribute( + 'PSReviewUnusedParameter', 'AsUriBuilder', + Scope = 'Function', + Justification = 'Present for parameter sets' + )] [OutputType(ParameterSetName = 'UriBuilder', [System.UriBuilder])] [OutputType(ParameterSetName = 'String', [string])] [OutputType(ParameterSetName = 'AsUri', [System.Uri])] @@ -56,21 +70,15 @@ function Get-Uri { [string] $Uri, # Outputs a System.UriBuilder object. - [Parameter(Mandatory, ParameterSetName = 'UriBuilder')] + [Parameter(Mandatory, ParameterSetName = 'AsUriBuilder')] [switch] $AsUriBuilder, # Outputs the URI as a normalized string. - [Parameter(Mandatory, ParameterSetName = 'String')] + [Parameter(Mandatory, ParameterSetName = 'AsString')] [switch] $AsString ) process { - # Ensure mutually exclusive output switches (cannot use both simultaneously) - if ($PSBoundParameters.ContainsKey('AsUriBuilder') -and $PSBoundParameters.ContainsKey('AsString')) { - throw 'Please specify only one of -AsUriBuilder or -AsString.' - } - - # Trim input to avoid issues with leading/trailing whitespace $inputString = $Uri.Trim() if ([string]::IsNullOrWhiteSpace($inputString)) { throw 'The Uri parameter cannot be null or empty.' @@ -78,7 +86,7 @@ function Get-Uri { # Attempt to create a System.Uri (absolute) from the string $uriObject = $null - $success = [System.Uri]::TryCreate($inputString, [System.UriKind]::Absolute, [ref]$uriObject) + $success = [System.Uri]::TryCreate($inputString, [System.UriKind]::RelativeOrAbsolute, [ref]$uriObject) if (-not $success) { # If no scheme present, try adding "http://" if ($inputString -notmatch '^[A-Za-z][A-Za-z0-9+.-]*:') { @@ -89,16 +97,29 @@ function Get-Uri { } } - switch ($PSCmdlet.ParameterSetName) { - 'UriBuilder' { - return [System.UriBuilder]::new($uriObject) # Return UriBuilder object - } - 'String' { - # Return normalized absolute URI string (safe unescaped format) - return $uriObject.GetComponents([System.UriComponents]::AbsoluteUri, [System.UriFormat]::SafeUnescaped) + if ($uriObject.IsAbsoluteUri) { + switch ($PSCmdlet.ParameterSetName) { + 'AsUriBuilder' { + return ([System.UriBuilder]::new($uriObject)) + } + 'AsString' { + return ($uriObject.GetComponents([System.UriComponents]::AbsoluteUri, [System.UriFormat]::SafeUnescaped)) + } + 'AsUri' { + return $uriObject + } } - 'AsUri' { - return $uriObject # Return the System.Uri object by default + } else { + switch ($PSCmdlet.ParameterSetName) { + 'AsUriBuilder' { + throw 'Cannot convert a relative URI to a UriBuilder. Please supply an absolute URI.' + } + 'AsString' { + return $uriObject.OriginalString + } + 'AsUri' { + return $uriObject + } } } } From 1d80624aacab97453f933ad991a6170dd868acaa Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 9 Feb 2025 21:32:38 +0100 Subject: [PATCH 13/21] Enhance Get-Uri documentation with detailed output examples and clarify return types --- src/functions/public/Get-Uri.ps1 | 48 +++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/src/functions/public/Get-Uri.ps1 b/src/functions/public/Get-Uri.ps1 index b38f00f..96f09c2 100644 --- a/src/functions/public/Get-Uri.ps1 +++ b/src/functions/public/Get-Uri.ps1 @@ -15,7 +15,28 @@ Output: ```powershell - http://example.com/ + AbsolutePath : / + AbsoluteUri : http://example.com/ + LocalPath : / + Authority : example.com + HostNameType : Dns + IsDefaultPort : True + IsFile : False + IsLoopback : False + PathAndQuery : / + Segments : {/} + IsUnc : False + Host : example.com + Port : 80 + Query : + Fragment : + Scheme : http + OriginalString : http://example.com + DnsSafeHost : example.com + IdnHost : example.com + IsAbsoluteUri : True + UserEscaped : False + UserInfo : ``` Converts 'example.com' into a normalized absolute URI string. @@ -25,27 +46,28 @@ Output: ```powershell - Host : example.com - Scheme : https - Path : /path + Scheme : https + UserName : + Password : + Host : example.com + Port : 443 + Path : /path + Query : + Fragment : + Uri : https://example.com/path ``` Returns a [System.UriBuilder] object for the specified URI. .EXAMPLE - Get-Uri -Uri 'https://example.com/path' -AsUri + 'example.com/path' | Get-Uri -AsString Output: ```powershell - AbsoluteUri : https://example.com/path + http://example.com/path ``` - Returns a [System.Uri] object with the full absolute URI. - - .EXAMPLE - Get-Uri -Uri '/path/to/resource' - - Returns a relative URI (with no hostname). + Returns a [string] with the full absolute URI. .LINK https://psmodule.io/Uri/Functions/Get-Uri @@ -86,7 +108,7 @@ # Attempt to create a System.Uri (absolute) from the string $uriObject = $null - $success = [System.Uri]::TryCreate($inputString, [System.UriKind]::RelativeOrAbsolute, [ref]$uriObject) + $success = [System.Uri]::TryCreate($inputString, [System.UriKind]::Absolute, [ref]$uriObject) if (-not $success) { # If no scheme present, try adding "http://" if ($inputString -notmatch '^[A-Za-z][A-Za-z0-9+.-]*:') { From 21e45738a33255dd5e2dd53abecdab87eaf8e577 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 9 Feb 2025 21:47:09 +0100 Subject: [PATCH 14/21] Add Test-Uri function to validate URI strings with optional relative support --- src/functions/public/Test-Uri.ps1 | 73 ++++++++ tests/Uri.Tests.ps1 | 274 ++++++++++++++++++------------ 2 files changed, 235 insertions(+), 112 deletions(-) create mode 100644 src/functions/public/Test-Uri.ps1 diff --git a/src/functions/public/Test-Uri.ps1 b/src/functions/public/Test-Uri.ps1 new file mode 100644 index 0000000..6f4aef2 --- /dev/null +++ b/src/functions/public/Test-Uri.ps1 @@ -0,0 +1,73 @@ +function Test-Uri { + <# + .SYNOPSIS + Validates whether a given string is a valid URI. + + .DESCRIPTION + The Test-Uri function checks whether a given string is a valid URI. By default, it enforces absolute URIs. + If the `-AllowRelative` switch is specified, it allows both absolute and relative URIs. + + .EXAMPLE + Test-Uri -Uri "https://example.com" + + Output: + ```powershell + True + ``` + + Checks if "https://example.com" is a valid URI, returning $true. + + .EXAMPLE + Test-Uri -Uri "invalid-uri" + + Output: + ```powershell + False + ``` + + Returns $false for an invalid URI string. + + .EXAMPLE + "https://example.com", "invalid-uri" | Test-Uri + + Output: + ```powershell + True + False + ``` + + Accepts input from the pipeline and validates multiple URIs. + + .OUTPUTS + [System.Boolean] + Returns `$true` if the input string is a valid URI, otherwise returns `$false`. + + .LINK + https://psmodule.io/Uri/Functions/Test-Uri + #> + [OutputType([bool])] + [CmdletBinding()] + param( + # Accept one or more URI strings from parameter or pipeline. + [Parameter(Mandatory, ValueFromPipeline)] + [string] $Uri, + + # If specified, allow valid relative URIs. + [Parameter()] + [switch] $AllowRelative + ) + + process { + # If -AllowRelative is set, try to create a URI using RelativeOrAbsolute. + # Otherwise, enforce an Absolute URI. + $uriKind = if ($AllowRelative) { + [System.UriKind]::RelativeOrAbsolute + } else { + [System.UriKind]::Absolute + } + + # Try to create the URI. The out parameter is not used. + $dummy = $null + [System.Uri]::TryCreate($Uri, $uriKind, [ref]$dummy) + } +} diff --git a/tests/Uri.Tests.ps1 b/tests/Uri.Tests.ps1 index 3bf9a00..a613f33 100644 --- a/tests/Uri.Tests.ps1 +++ b/tests/Uri.Tests.ps1 @@ -1,5 +1,4 @@ Describe 'Uri' { - Context 'Function: ConvertFrom-UriQueryString' { It 'ConvertFrom-UriQueryString - returns empty hashtable for empty input' { @@ -66,6 +65,58 @@ } } + Context 'Function: Test-Uri' { + $testUris = @( + # Valid URIs + @{ URI = 'http://example.com'; Valid = $true }, + @{ URI = 'https://sub.domain.com/path/to/resource'; Valid = $true }, + @{ URI = 'ftp://ftp.example.org/file.txt'; Valid = $true }, + @{ URI = 'http://example.com:8080/index.html'; Valid = $true }, + @{ URI = 'https://example.com/path/to/resource?query=123&another=test'; Valid = $true }, + @{ URI = 'https://example.com/path?encoded=%20%3C%3E%23%25'; Valid = $true }, + @{ URI = 'http://example.com/path#section1'; Valid = $true }, + @{ URI = 'mailto:user@example.com'; Valid = $true }, + @{ URI = 'tel:+1234567890'; Valid = $true }, + @{ URI = 'urn:isbn:0451450523'; Valid = $true }, + @{ URI = 'https://valid-url.com/resource?param=value&other=123'; Valid = $true }, + @{ URI = 'http://localhost:3000/api/test'; Valid = $true }, + @{ URI = 'http://192.168.1.1:8080/dashboard'; Valid = $true }, + @{ URI = 'https://secure-site.org/login?user=admin'; Valid = $true }, + @{ URI = 'https://example.com/valid/path/with/multiple/segments'; Valid = $true }, + @{ URI = 'http://user:pass@example.com:8080/path?query=test#fragment'; Valid = $true }, + @{ URI = 'ws://websocket.example.com/socket'; Valid = $true }, + @{ URI = 'wss://secure-websocket.com/path'; Valid = $true }, + + # Invalid URIs + @{ URI = 'http:///missing-host'; Valid = $false }, + @{ URI = 'htp://example.com'; Valid = $false }, + @{ URI = 'https:// example .com'; Valid = $false }, + @{ URI = 'https://example.com:99999'; Valid = $false }, + @{ URI = 'http://exa mple.com'; Valid = $false }, + @{ URI = 'http://example.com/ space in path'; Valid = $false }, + @{ URI = "http://example.com/<>#{}|\^~[]``"; Valid = $false }, + @{ URI = 'http://:8080/missing-host'; Valid = $false }, + @{ URI = 'http://example.com/%%invalid-encoding'; Valid = $false }, + @{ URI = 'http://example.com/path?query=%%invalid'; Valid = $false }, + @{ URI = 'http://-invalid-host.com'; Valid = $false }, + @{ URI = 'https://:invalid@hostname'; Valid = $false }, + @{ URI = 'ftp://missing/slash'; Valid = $false }, + @{ URI = 'https://example.com:abcd'; Valid = $false }, + @{ URI = 'https://ex ample.com/path'; Valid = $false }, + @{ URI = 'http://incomplete-path?query='; Valid = $false }, + @{ URI = 'http://::1/invalid-ipv6'; Valid = $false }, + @{ URI = 'https://double..dots.com'; Valid = $false }, + @{ URI = 'http://username:password@'; Valid = $false }, + @{ URI = 'ws://invalid:websocket'; Valid = $false }, + @{ URI = 'https://example.com/has|pipe'; Valid = $false } + ) + + It ' valid ' -ForEach $testUris { + $result = $URI | Test-Uri + $result | Should -BeExactly $Valid + } + } + Context 'Function: New-Uri' { It 'New-Uri - constructs a URI with base and appended path' { @@ -118,144 +169,143 @@ $query | Should -Match 'q=hello%20world' } } -} -Describe 'Get-Uri' { - Context 'Default Behavior (returns a [System.Uri] object)' { - - It 'Should return a valid System.Uri when given a URI with scheme' { - $result = Get-Uri -Uri 'https://example.com/path' - $result | Out-String -Stream | ForEach-Object { Write-Verbose $_ -Verbose } - $result | Should -BeOfType 'System.Uri' - $result.Scheme | Should -Be 'https' - $result.Host | Should -Be 'example.com' - } + Context 'Function: Get-Uri' { + Context 'Default Behavior (returns a [System.Uri] object)' { + It 'Should return a valid System.Uri when given a URI with scheme' { + $result = Get-Uri -Uri 'https://example.com/path' + $result | Out-String -Stream | ForEach-Object { Write-Verbose $_ -Verbose } + $result | Should -BeOfType 'System.Uri' + $result.Scheme | Should -Be 'https' + $result.Host | Should -Be 'example.com' + } - It 'Should add default scheme (http) when missing' { - $result = Get-Uri -Uri 'example.com/path' - $result | Out-String -Stream | ForEach-Object { Write-Verbose $_ -Verbose } - $result | Should -BeOfType 'System.Uri' - $result.Scheme | Should -Be 'http' - $result.Host | Should -Be 'example.com' + It 'Should add default scheme (http) when missing' { + $result = Get-Uri -Uri 'example.com/path' + $result | Out-String -Stream | ForEach-Object { Write-Verbose $_ -Verbose } + $result | Should -BeOfType 'System.Uri' + $result.Scheme | Should -Be 'http' + $result.Host | Should -Be 'example.com' + } } - } - Context 'Switch: -AsUriBuilder' { + Context 'Switch: -AsUriBuilder' { - It 'Should return a System.UriBuilder object' { - $result = Get-Uri -Uri 'https://example.com/path' -AsUriBuilder - $result | Out-String -Stream | ForEach-Object { Write-Verbose $_ -Verbose } - $result | Should -BeOfType 'System.UriBuilder' - $result.Uri.Scheme | Should -Be 'https' - $result.Uri.Host | Should -Be 'example.com' + It 'Should return a System.UriBuilder object' { + $result = Get-Uri -Uri 'https://example.com/path' -AsUriBuilder + $result | Out-String -Stream | ForEach-Object { Write-Verbose $_ -Verbose } + $result | Should -BeOfType 'System.UriBuilder' + $result.Uri.Scheme | Should -Be 'https' + $result.Uri.Host | Should -Be 'example.com' + } } - } - Context 'Switch: -AsString' { + Context 'Switch: -AsString' { - It 'Should return a normalized URI string' { - # Example with uppercase scheme and percent-encoded characters - $inputUri = 'HTTP://Example.com/%7Euser/path/page.html' - $result = Get-Uri -Uri $inputUri -AsString - $result | Out-String -Stream | ForEach-Object { Write-Verbose $_ -Verbose } - $expected = 'http://example.com/~user/path/page.html' - $result | Should -Be $expected + It 'Should return a normalized URI string' { + # Example with uppercase scheme and percent-encoded characters + $inputUri = 'HTTP://Example.com/%7Euser/path/page.html' + $result = Get-Uri -Uri $inputUri -AsString + $result | Out-String -Stream | ForEach-Object { Write-Verbose $_ -Verbose } + $expected = 'http://example.com/~user/path/page.html' + $result | Should -Be $expected + } } - } - Context 'Error Handling' { + Context 'Error Handling' { - It 'Should throw an error for an invalid URI' { - { Get-Uri -Uri 'http://??' } | Should -Throw - } + It 'Should throw an error for an invalid URI' { + { Get-Uri -Uri 'http://??' } | Should -Throw + } - It 'Should throw an error when both -AsUriBuilder and -AsString are provided' { - { Get-Uri -Uri 'https://example.com' -AsUriBuilder -AsString } | Should -Throw - } + It 'Should throw an error when both -AsUriBuilder and -AsString are provided' { + { Get-Uri -Uri 'https://example.com' -AsUriBuilder -AsString } | Should -Throw + } - It 'Should throw an error when an empty URI string is provided' { - { Get-Uri -Uri '' } | Should -Throw + It 'Should throw an error when an empty URI string is provided' { + { Get-Uri -Uri '' } | Should -Throw + } } - } - Context 'Pipeline Input' { + Context 'Pipeline Input' { - It 'Should accept pipeline input and return a valid [System.Uri]' { - 'example.com/path' | Get-Uri | ForEach-Object { - $_ | Out-String -Stream | ForEach-Object { Write-Verbose $_ -Verbose } - $_ | Should -BeOfType 'System.Uri' - $_.Scheme | Should -Be 'http' + It 'Should accept pipeline input and return a valid [System.Uri]' { + 'example.com/path' | Get-Uri | ForEach-Object { + $_ | Out-String -Stream | ForEach-Object { Write-Verbose $_ -Verbose } + $_ | Should -BeOfType 'System.Uri' + $_.Scheme | Should -Be 'http' + } } - } - It 'Should return a valid System.Uri when given a URI with scheme' { - $result = Get-Uri -Uri 'https://example.com/path' - $result | Out-String -Stream | ForEach-Object { Write-Verbose $_ -Verbose } - $result | Should -BeOfType 'System.Uri' - $result.Scheme | Should -Be 'https' - $result.Host | Should -Be 'example.com' - } + It 'Should return a valid System.Uri when given a URI with scheme' { + $result = Get-Uri -Uri 'https://example.com/path' + $result | Out-String -Stream | ForEach-Object { Write-Verbose $_ -Verbose } + $result | Should -BeOfType 'System.Uri' + $result.Scheme | Should -Be 'https' + $result.Host | Should -Be 'example.com' + } - It 'Should add default scheme (http) when missing' { - $result = Get-Uri -Uri 'example.com/path' - $result | Out-String -Stream | ForEach-Object { Write-Verbose $_ -Verbose } - $result | Should -BeOfType 'System.Uri' - $result.Scheme | Should -Be 'http' - $result.Host | Should -Be 'example.com' + It 'Should add default scheme (http) when missing' { + $result = Get-Uri -Uri 'example.com/path' + $result | Out-String -Stream | ForEach-Object { Write-Verbose $_ -Verbose } + $result | Should -BeOfType 'System.Uri' + $result.Scheme | Should -Be 'http' + $result.Host | Should -Be 'example.com' + } } - } - Context 'Edge Cases' { - It 'Should handle URIs with ports' { - $result = Get-Uri -Uri 'http://example.com:8080' - $result | Out-String -Stream | ForEach-Object { Write-Verbose $_ -Verbose } - $result | Should -BeOfType 'System.Uri' - $result.Port | Should -Be 8080 - } + Context 'Edge Cases' { + It 'Should handle URIs with ports' { + $result = Get-Uri -Uri 'http://example.com:8080' + $result | Out-String -Stream | ForEach-Object { Write-Verbose $_ -Verbose } + $result | Should -BeOfType 'System.Uri' + $result.Port | Should -Be 8080 + } - It 'Should handle URIs with query strings' { - $result = Get-Uri -Uri 'https://example.com?query=test' - $result | Out-String -Stream | ForEach-Object { Write-Verbose $_ -Verbose } - $result | Should -BeOfType 'System.Uri' - $result.Query | Should -Be '?query=test' - } + It 'Should handle URIs with query strings' { + $result = Get-Uri -Uri 'https://example.com?query=test' + $result | Out-String -Stream | ForEach-Object { Write-Verbose $_ -Verbose } + $result | Should -BeOfType 'System.Uri' + $result.Query | Should -Be '?query=test' + } - It 'Should handle URIs with multiple query strings' { - $result = Get-Uri -Uri 'https://example.com?query=test&sort=asc&page=1' - $result | Out-String -Stream | ForEach-Object { Write-Verbose $_ -Verbose } - $result | Should -BeOfType 'System.Uri' - $result.Query | Should -Be '?query=test&sort=asc&page=1' - } + It 'Should handle URIs with multiple query strings' { + $result = Get-Uri -Uri 'https://example.com?query=test&sort=asc&page=1' + $result | Out-String -Stream | ForEach-Object { Write-Verbose $_ -Verbose } + $result | Should -BeOfType 'System.Uri' + $result.Query | Should -Be '?query=test&sort=asc&page=1' + } - It 'Should handle URIs with query strings and fragments' { - $result = Get-Uri -Uri 'https://example.com?query=test#section1' - $result | Out-String -Stream | ForEach-Object { Write-Verbose $_ -Verbose } - $result | Should -BeOfType 'System.Uri' - $result.Query | Should -Be '?query=test' - $result.Fragment | Should -Be '#section1' - } + It 'Should handle URIs with query strings and fragments' { + $result = Get-Uri -Uri 'https://example.com?query=test#section1' + $result | Out-String -Stream | ForEach-Object { Write-Verbose $_ -Verbose } + $result | Should -BeOfType 'System.Uri' + $result.Query | Should -Be '?query=test' + $result.Fragment | Should -Be '#section1' + } - # Uri + same key query string and fragment - It 'Should handle URIs with query strings and fragments' { - $result = Get-Uri -Uri 'https://example.com?include=test&include=dev&include=prod#section1' - $result | Out-String -Stream | ForEach-Object { Write-Verbose $_ -Verbose } - $result | Should -BeOfType 'System.Uri' - $result.Query | Should -Be '?include=test&include=dev&include=prod' - $result.Fragment | Should -Be '#section1' - } + # Uri + same key query string and fragment + It 'Should handle URIs with query strings and fragments' { + $result = Get-Uri -Uri 'https://example.com?include=test&include=dev&include=prod#section1' + $result | Out-String -Stream | ForEach-Object { Write-Verbose $_ -Verbose } + $result | Should -BeOfType 'System.Uri' + $result.Query | Should -Be '?include=test&include=dev&include=prod' + $result.Fragment | Should -Be '#section1' + } - It 'Should handle URIs with fragments' { - $result = Get-Uri -Uri 'https://example.com#section1' - $result | Out-String -Stream | ForEach-Object { Write-Verbose $_ -Verbose } - $result | Should -BeOfType 'System.Uri' - $result.Fragment | Should -Be '#section1' - } + It 'Should handle URIs with fragments' { + $result = Get-Uri -Uri 'https://example.com#section1' + $result | Out-String -Stream | ForEach-Object { Write-Verbose $_ -Verbose } + $result | Should -BeOfType 'System.Uri' + $result.Fragment | Should -Be '#section1' + } - It 'Should handle IPv6 addresses' { - $result = Get-Uri -Uri 'http://[::1]' - $result | Out-String -Stream | ForEach-Object { Write-Verbose $_ -Verbose } - $result | Should -BeOfType 'System.Uri' - $result.Host | Should -Be '[::1]' + It 'Should handle IPv6 addresses' { + $result = Get-Uri -Uri 'http://[::1]' + $result | Out-String -Stream | ForEach-Object { Write-Verbose $_ -Verbose } + $result | Should -BeOfType 'System.Uri' + $result.Host | Should -Be '[::1]' + } } } } From 86eb538f7dd4638475afe76119b7161550565d0f Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 9 Feb 2025 21:55:25 +0100 Subject: [PATCH 15/21] Update Test-Uri documentation for consistency in output examples --- src/functions/public/Test-Uri.ps1 | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/functions/public/Test-Uri.ps1 b/src/functions/public/Test-Uri.ps1 index 6f4aef2..47d64d7 100644 --- a/src/functions/public/Test-Uri.ps1 +++ b/src/functions/public/Test-Uri.ps1 @@ -15,7 +15,7 @@ True ``` - Checks if "https://example.com" is a valid URI, returning $true. + Checks if `https://example.com` is a valid URI, returning `$true`. .EXAMPLE Test-Uri -Uri "invalid-uri" @@ -25,7 +25,7 @@ False ``` - Returns $false for an invalid URI string. + Returns `$false` for an invalid URI string. .EXAMPLE "https://example.com", "invalid-uri" | Test-Uri @@ -40,6 +40,8 @@ .OUTPUTS [System.Boolean] + + .NOTES Returns `$true` if the input string is a valid URI, otherwise returns `$false`. .LINK From 2c382a4d8dae74928bd59fafc7ecea4f87b80db0 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 9 Feb 2025 23:12:09 +0100 Subject: [PATCH 16/21] Refactor ConvertFrom-UriQueryString function and update test cases for clarity and consistency --- .../public/ConvertFrom-UriQueryString.ps1 | 9 +- tests/Uri.Tests.ps1 | 84 ++++++++++--------- 2 files changed, 47 insertions(+), 46 deletions(-) diff --git a/src/functions/public/ConvertFrom-UriQueryString.ps1 b/src/functions/public/ConvertFrom-UriQueryString.ps1 index 2ad05ca..1bd9362 100644 --- a/src/functions/public/ConvertFrom-UriQueryString.ps1 +++ b/src/functions/public/ConvertFrom-UriQueryString.ps1 @@ -11,7 +11,7 @@ back to their normal representation. .EXAMPLE - ConvertFrom-UriQueryString -QueryString 'name=John%20Doe&age=30&age=40' + ConvertFrom-UriQueryString -Query 'name=John%20Doe&age=30&age=40' Output: ```powershell @@ -25,7 +25,7 @@ values are decoded parameter values. .EXAMPLE - ConvertFrom-UriQueryString '?q=PowerShell%20URI' + '?q=PowerShell%20URI' | ConvertFrom-UriQueryString Output: ```powershell @@ -49,7 +49,6 @@ [string] $Query ) - # Early exit if $Query is null or empty. if ([string]::IsNullOrEmpty($Query)) { Write-Verbose 'Query string is null or empty.' return @{} @@ -60,9 +59,7 @@ if ($Query.StartsWith('?')) { $Query = $Query.Substring(1) } - if ([string]::IsNullOrEmpty($Query)) { - return @{} # return empty hashtable if no query present - } + $result = @{} # Split by '&' to get each key=value pair diff --git a/tests/Uri.Tests.ps1 b/tests/Uri.Tests.ps1 index a613f33..d1ddad6 100644 --- a/tests/Uri.Tests.ps1 +++ b/tests/Uri.Tests.ps1 @@ -68,51 +68,55 @@ Context 'Function: Test-Uri' { $testUris = @( # Valid URIs - @{ URI = 'http://example.com'; Valid = $true }, - @{ URI = 'https://sub.domain.com/path/to/resource'; Valid = $true }, - @{ URI = 'ftp://ftp.example.org/file.txt'; Valid = $true }, - @{ URI = 'http://example.com:8080/index.html'; Valid = $true }, - @{ URI = 'https://example.com/path/to/resource?query=123&another=test'; Valid = $true }, - @{ URI = 'https://example.com/path?encoded=%20%3C%3E%23%25'; Valid = $true }, - @{ URI = 'http://example.com/path#section1'; Valid = $true }, - @{ URI = 'mailto:user@example.com'; Valid = $true }, - @{ URI = 'tel:+1234567890'; Valid = $true }, - @{ URI = 'urn:isbn:0451450523'; Valid = $true }, - @{ URI = 'https://valid-url.com/resource?param=value&other=123'; Valid = $true }, - @{ URI = 'http://localhost:3000/api/test'; Valid = $true }, - @{ URI = 'http://192.168.1.1:8080/dashboard'; Valid = $true }, - @{ URI = 'https://secure-site.org/login?user=admin'; Valid = $true }, - @{ URI = 'https://example.com/valid/path/with/multiple/segments'; Valid = $true }, - @{ URI = 'http://user:pass@example.com:8080/path?query=test#fragment'; Valid = $true }, - @{ URI = 'ws://websocket.example.com/socket'; Valid = $true }, - @{ URI = 'wss://secure-websocket.com/path'; Valid = $true }, + @{ URI = 'http://example.com'; Expected = 'Valid' }, + @{ URI = 'https://sub.domain.com/path/to/resource'; Expected = 'Valid' }, + @{ URI = 'ftp://ftp.example.org/file.txt'; Expected = 'Valid' }, + @{ URI = 'http://example.com:8080/index.html'; Expected = 'Valid' }, + @{ URI = 'https://example.com/path/to/resource?query=123&another=test'; Expected = 'Valid' }, + @{ URI = 'https://example.com/path?encoded=%20%3C%3E%23%25'; Expected = 'Valid' }, + @{ URI = 'http://example.com/path#section1'; Expected = 'Valid' }, + @{ URI = 'mailto:user@example.com'; Expected = 'Valid' }, + @{ URI = 'tel:+1234567890'; Expected = 'Valid' }, + @{ URI = 'urn:isbn:0451450523'; Expected = 'Valid' }, + @{ URI = 'https://valid-url.com/resource?param=value&other=123'; Expected = 'Valid' }, + @{ URI = 'http://localhost:3000/api/test'; Expected = 'Valid' }, + @{ URI = 'http://192.168.1.1:8080/dashboard'; Expected = 'Valid' }, + @{ URI = 'https://secure-site.org/login?user=admin'; Expected = 'Valid' }, + @{ URI = 'https://example.com/valid/path/with/multiple/segments'; Expected = 'Valid' }, + @{ URI = 'http://user:pass@example.com:8080/path?query=test#fragment'; Expected = 'Valid' }, + @{ URI = 'ws://websocket.example.com/socket'; Expected = 'Valid' }, + @{ URI = 'wss://secure-websocket.com/path'; Expected = 'Valid' }, # Invalid URIs - @{ URI = 'http:///missing-host'; Valid = $false }, - @{ URI = 'htp://example.com'; Valid = $false }, - @{ URI = 'https:// example .com'; Valid = $false }, - @{ URI = 'https://example.com:99999'; Valid = $false }, - @{ URI = 'http://exa mple.com'; Valid = $false }, - @{ URI = 'http://example.com/ space in path'; Valid = $false }, - @{ URI = "http://example.com/<>#{}|\^~[]``"; Valid = $false }, - @{ URI = 'http://:8080/missing-host'; Valid = $false }, - @{ URI = 'http://example.com/%%invalid-encoding'; Valid = $false }, - @{ URI = 'http://example.com/path?query=%%invalid'; Valid = $false }, - @{ URI = 'http://-invalid-host.com'; Valid = $false }, - @{ URI = 'https://:invalid@hostname'; Valid = $false }, - @{ URI = 'ftp://missing/slash'; Valid = $false }, - @{ URI = 'https://example.com:abcd'; Valid = $false }, - @{ URI = 'https://ex ample.com/path'; Valid = $false }, - @{ URI = 'http://incomplete-path?query='; Valid = $false }, - @{ URI = 'http://::1/invalid-ipv6'; Valid = $false }, - @{ URI = 'https://double..dots.com'; Valid = $false }, - @{ URI = 'http://username:password@'; Valid = $false }, - @{ URI = 'ws://invalid:websocket'; Valid = $false }, - @{ URI = 'https://example.com/has|pipe'; Valid = $false } + @{ URI = 'http:///missing-host'; Expected = 'Invalid' }, + @{ URI = 'htp://example.com'; Expected = 'Invalid' }, + @{ URI = 'https:// example .com'; Expected = 'Invalid' }, + @{ URI = 'https://example.com:99999'; Expected = 'Invalid' }, + @{ URI = 'http://exa mple.com'; Expected = 'Invalid' }, + @{ URI = 'http://example.com/ space in path'; Expected = 'Invalid' }, + @{ URI = "http://example.com/<>#{}|\^~[]``"; Expected = 'Invalid' }, + @{ URI = 'http://:8080/missing-host'; Expected = 'Invalid' }, + @{ URI = 'http://example.com/%%invalid-encoding'; Expected = 'Invalid' }, + @{ URI = 'http://example.com/path?query=%%invalid'; Expected = 'Invalid' }, + @{ URI = 'http://-invalid-host.com'; Expected = 'Invalid' }, + @{ URI = 'https://:invalid@hostname'; Expected = 'Invalid' }, + @{ URI = 'ftp://missing/slash'; Expected = 'Invalid' }, + @{ URI = 'https://example.com:abcd'; Expected = 'Invalid' }, + @{ URI = 'https://ex ample.com/path'; Expected = 'Invalid' }, + @{ URI = 'http://incomplete-path?query='; Expected = 'Invalid' }, + @{ URI = 'http://::1/invalid-ipv6'; Expected = 'Invalid' }, + @{ URI = 'https://double..dots.com'; Expected = 'Invalid' }, + @{ URI = 'http://username:password@'; Expected = 'Invalid' }, + @{ URI = 'ws://invalid:websocket'; Expected = 'Invalid' }, + @{ URI = 'https://example.com/has|pipe'; Expected = 'Invalid' } ) - It ' valid ' -ForEach $testUris { + It ' is ' -ForEach $testUris { $result = $URI | Test-Uri + switch ($Expected) { + 'Valid' { $Valid = $true } + 'Invalid' { $Valid = $false } + } $result | Should -BeExactly $Valid } } From 9dd644e6cdc5c72b3eebe5034f12dbc0a893a038 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 9 Feb 2025 23:27:19 +0100 Subject: [PATCH 17/21] Refactor Get-Uri function to streamline parameter handling and improve return logic; update test case descriptions for clarity --- src/functions/public/Get-Uri.ps1 | 30 ++++++++---------------------- tests/Uri.Tests.ps1 | 2 +- 2 files changed, 9 insertions(+), 23 deletions(-) diff --git a/src/functions/public/Get-Uri.ps1 b/src/functions/public/Get-Uri.ps1 index 96f09c2..537824d 100644 --- a/src/functions/public/Get-Uri.ps1 +++ b/src/functions/public/Get-Uri.ps1 @@ -119,29 +119,15 @@ } } - if ($uriObject.IsAbsoluteUri) { - switch ($PSCmdlet.ParameterSetName) { - 'AsUriBuilder' { - return ([System.UriBuilder]::new($uriObject)) - } - 'AsString' { - return ($uriObject.GetComponents([System.UriComponents]::AbsoluteUri, [System.UriFormat]::SafeUnescaped)) - } - 'AsUri' { - return $uriObject - } + switch ($PSCmdlet.ParameterSetName) { + 'AsUriBuilder' { + return ([System.UriBuilder]::new($uriObject)) } - } else { - switch ($PSCmdlet.ParameterSetName) { - 'AsUriBuilder' { - throw 'Cannot convert a relative URI to a UriBuilder. Please supply an absolute URI.' - } - 'AsString' { - return $uriObject.OriginalString - } - 'AsUri' { - return $uriObject - } + 'AsString' { + return ($uriObject.GetComponents([System.UriComponents]::AbsoluteUri, [System.UriFormat]::SafeUnescaped)) + } + 'AsUri' { + return $uriObject } } } diff --git a/tests/Uri.Tests.ps1 b/tests/Uri.Tests.ps1 index d1ddad6..c726702 100644 --- a/tests/Uri.Tests.ps1 +++ b/tests/Uri.Tests.ps1 @@ -111,7 +111,7 @@ @{ URI = 'https://example.com/has|pipe'; Expected = 'Invalid' } ) - It ' is ' -ForEach $testUris { + It ' is ' -ForEach $testUris { $result = $URI | Test-Uri switch ($Expected) { 'Valid' { $Valid = $true } From be319605ef4067224c81134b24ac3f6b5978353b Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 9 Feb 2025 23:31:32 +0100 Subject: [PATCH 18/21] Add verbose output for test results in Uri.Tests to enhance debugging information --- tests/Uri.Tests.ps1 | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/Uri.Tests.ps1 b/tests/Uri.Tests.ps1 index c726702..517296d 100644 --- a/tests/Uri.Tests.ps1 +++ b/tests/Uri.Tests.ps1 @@ -117,6 +117,9 @@ 'Valid' { $Valid = $true } 'Invalid' { $Valid = $false } } + if ($result) { + $result | Out-String -Stream | ForEach-Object { Write-Verbose $_ -Verbose } + } $result | Should -BeExactly $Valid } } From fd1faec5c016902806858c0d562bdaa4c71af6b1 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 9 Feb 2025 23:34:45 +0100 Subject: [PATCH 19/21] Update test output in Uri.Tests to use Get-Uti for improved clarity in verbose logging --- tests/Uri.Tests.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Uri.Tests.ps1 b/tests/Uri.Tests.ps1 index 517296d..40cf0f8 100644 --- a/tests/Uri.Tests.ps1 +++ b/tests/Uri.Tests.ps1 @@ -118,7 +118,7 @@ 'Invalid' { $Valid = $false } } if ($result) { - $result | Out-String -Stream | ForEach-Object { Write-Verbose $_ -Verbose } + $URI | Get-Uti | Out-String -Stream | ForEach-Object { Write-Verbose $_ -Verbose } } $result | Should -BeExactly $Valid } From e2877dbc5ded0aff1898f6003f9c18a3e2722cec Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 9 Feb 2025 23:42:07 +0100 Subject: [PATCH 20/21] Refactor ConvertFrom-UriQueryString parameter handling and update Test-Uri cases for improved clarity and consistency --- src/functions/public/ConvertFrom-UriQueryString.ps1 | 3 +-- tests/Uri.Tests.ps1 | 11 ++++++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/functions/public/ConvertFrom-UriQueryString.ps1 b/src/functions/public/ConvertFrom-UriQueryString.ps1 index 1bd9362..e359245 100644 --- a/src/functions/public/ConvertFrom-UriQueryString.ps1 +++ b/src/functions/public/ConvertFrom-UriQueryString.ps1 @@ -44,7 +44,7 @@ param( # The query string to parse. This can include the leading '?' or just the key-value pairs. # For example, both "?foo=bar&count=10" and "foo=bar&count=10" are acceptable. - [Parameter(Position = 0, ValueFromPipeline)] + [Parameter(ValueFromPipeline)] [AllowNull()] [string] $Query ) @@ -60,7 +60,6 @@ $Query = $Query.Substring(1) } - $result = @{} # Split by '&' to get each key=value pair $pairs = $Query.Split('&') diff --git a/tests/Uri.Tests.ps1 b/tests/Uri.Tests.ps1 index 40cf0f8..711829a 100644 --- a/tests/Uri.Tests.ps1 +++ b/tests/Uri.Tests.ps1 @@ -114,11 +114,16 @@ It ' is ' -ForEach $testUris { $result = $URI | Test-Uri switch ($Expected) { - 'Valid' { $Valid = $true } - 'Invalid' { $Valid = $false } + 'Valid' { + $Valid = $true + $URI | Get-Uri | Should -Not -BeNullOrEmpty + } + 'Invalid' { + $Valid = $false + } } if ($result) { - $URI | Get-Uti | Out-String -Stream | ForEach-Object { Write-Verbose $_ -Verbose } + $URI | Get-Uri | Out-String -Stream | ForEach-Object { Write-Verbose $_ -Verbose } } $result | Should -BeExactly $Valid } From 4c788b1f9af637fba95ed0622ecf39be9af627cb Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 9 Feb 2025 23:52:20 +0100 Subject: [PATCH 21/21] Refactor URI test cases for improved clarity and consistency; update expected results for various valid and invalid URIs --- tests/Uri.Tests.ps1 | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/tests/Uri.Tests.ps1 b/tests/Uri.Tests.ps1 index 711829a..6cd03ec 100644 --- a/tests/Uri.Tests.ps1 +++ b/tests/Uri.Tests.ps1 @@ -86,41 +86,39 @@ @{ URI = 'http://user:pass@example.com:8080/path?query=test#fragment'; Expected = 'Valid' }, @{ URI = 'ws://websocket.example.com/socket'; Expected = 'Valid' }, @{ URI = 'wss://secure-websocket.com/path'; Expected = 'Valid' }, + @{ URI = 'htp://example.com'; Expected = 'Valid' }, + @{ URI = 'http://example.com/ space in path'; Expected = 'Valid' }, + @{ URI = "http://example.com/<>#{}|\^~[]``"; Expected = 'Valid' }, + @{ URI = 'http://example.com/%%invalid-encoding'; Expected = 'Valid' }, + @{ URI = 'http://example.com/path?query=%%invalid'; Expected = 'Valid' }, + @{ URI = 'http://-invalid-host.com'; Expected = 'Valid' }, + @{ URI = 'https://:invalid@hostname'; Expected = 'Valid' }, + @{ URI = 'ftp://missing/slash'; Expected = 'Valid' }, + @{ URI = 'http://incomplete-path?query='; Expected = 'Valid' }, + @{ URI = 'https://example.com/has|pipe'; Expected = 'Valid' } # Invalid URIs @{ URI = 'http:///missing-host'; Expected = 'Invalid' }, - @{ URI = 'htp://example.com'; Expected = 'Invalid' }, @{ URI = 'https:// example .com'; Expected = 'Invalid' }, @{ URI = 'https://example.com:99999'; Expected = 'Invalid' }, @{ URI = 'http://exa mple.com'; Expected = 'Invalid' }, - @{ URI = 'http://example.com/ space in path'; Expected = 'Invalid' }, - @{ URI = "http://example.com/<>#{}|\^~[]``"; Expected = 'Invalid' }, @{ URI = 'http://:8080/missing-host'; Expected = 'Invalid' }, - @{ URI = 'http://example.com/%%invalid-encoding'; Expected = 'Invalid' }, - @{ URI = 'http://example.com/path?query=%%invalid'; Expected = 'Invalid' }, - @{ URI = 'http://-invalid-host.com'; Expected = 'Invalid' }, - @{ URI = 'https://:invalid@hostname'; Expected = 'Invalid' }, - @{ URI = 'ftp://missing/slash'; Expected = 'Invalid' }, @{ URI = 'https://example.com:abcd'; Expected = 'Invalid' }, @{ URI = 'https://ex ample.com/path'; Expected = 'Invalid' }, - @{ URI = 'http://incomplete-path?query='; Expected = 'Invalid' }, @{ URI = 'http://::1/invalid-ipv6'; Expected = 'Invalid' }, @{ URI = 'https://double..dots.com'; Expected = 'Invalid' }, @{ URI = 'http://username:password@'; Expected = 'Invalid' }, - @{ URI = 'ws://invalid:websocket'; Expected = 'Invalid' }, - @{ URI = 'https://example.com/has|pipe'; Expected = 'Invalid' } + @{ URI = 'ws://invalid:websocket'; Expected = 'Invalid' } ) - It ' is ' -ForEach $testUris { + It 'Test-Uri - Deems [] a [] URI' -ForEach $testUris { $result = $URI | Test-Uri switch ($Expected) { 'Valid' { $Valid = $true $URI | Get-Uri | Should -Not -BeNullOrEmpty } - 'Invalid' { - $Valid = $false - } + 'Invalid' { $Valid = $false } } if ($result) { $URI | Get-Uri | Out-String -Stream | ForEach-Object { Write-Verbose $_ -Verbose }