diff --git a/src/KubeClient/Utilities/UriHelper.cs b/src/KubeClient/Utilities/UriHelper.cs index face9e4..c67331b 100644 --- a/src/KubeClient/Utilities/UriHelper.cs +++ b/src/KubeClient/Utilities/UriHelper.cs @@ -7,6 +7,11 @@ namespace KubeClient.Utilities /// public static class UriHelper { + /// + /// A dummy URI to be used as the base URI when dealing with relative URIs. + /// + static readonly Uri DummyBaseUri = new Uri("https://dummy-host"); + /// /// Get the path (and, if present, the query) of a URI. /// @@ -27,10 +32,10 @@ public static string SafeGetPathAndQuery(this Uri uri) if (uri.IsAbsoluteUri) return uri.PathAndQuery; - // Slightly ugly, but System.Uri doesn't attempt to parse relative URIs so we have to resort to System.UriBuilder. - UriBuilder uriComponents = new UriBuilder(uri.OriginalString); + // Slightly ugly, but System.Uri doesn't attempt to parse relative URIs so we have to convert it to an absolute URI. + Uri absoluteUri = new Uri(DummyBaseUri, relativeUri: uri); - return $"{uriComponents.Path}{uriComponents.Query}"; + return absoluteUri.PathAndQuery; } } } diff --git a/test/KubeClient.Tests/UriHelperTests.cs b/test/KubeClient.Tests/UriHelperTests.cs new file mode 100644 index 0000000..9443050 --- /dev/null +++ b/test/KubeClient.Tests/UriHelperTests.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KubeClient.Tests +{ + using Utilities; + using Xunit; + + /// + /// Tests for . + /// + public class UriHelperTests + { + static readonly Uri BaseUri = new Uri("https://localhost"); + + [Theory] + [InlineData("/")] + [InlineData("/?param1=value1¶m2=value2")] + [InlineData("path1")] + [InlineData("path1?param1=value1¶m2=value2")] + [InlineData("path1/path2")] + [InlineData("path1/path2?param1=value1¶m2=value2")] + [InlineData("path1/path2/")] + [InlineData("path1/path2/?param1=value1¶m2=value2")] + [InlineData("/path1")] + [InlineData("/path1?param1=value1¶m2=value2")] + [InlineData("/path1/path2")] + [InlineData("/path1/path2?param1=value1¶m2=value2")] + [InlineData("/path1/path2/")] + [InlineData("/path1/path2/?param1=value1¶m2=value2")] + public void Can_SafeGetPathAndQuery_RelativeUri(string input) + { + Uri uri = new Uri(input, UriKind.RelativeOrAbsolute); + Assert.False(uri.IsAbsoluteUri); + + string pathAndQuery = uri.SafeGetPathAndQuery(); + Assert.Equal(pathAndQuery, + NormalizePath(input) + ); + } + + [Theory] + [InlineData("/")] + [InlineData("/?param1=value1¶m2=value2")] + [InlineData("path1")] + [InlineData("path1?param1=value1¶m2=value2")] + [InlineData("path1/path2")] + [InlineData("path1/path2?param1=value1¶m2=value2")] + [InlineData("path1/path2/")] + [InlineData("path1/path2/?param1=value1¶m2=value2")] + [InlineData("/path1")] + [InlineData("/path1?param1=value1¶m2=value2")] + [InlineData("/path1/path2")] + [InlineData("/path1/path2?param1=value1¶m2=value2")] + [InlineData("/path1/path2/")] + [InlineData("/path1/path2/?param1=value1¶m2=value2")] + public void Can_SafeGetPathAndQuery_AbsoluteUri(string input) + { + Uri relativeUri = new Uri(input, UriKind.RelativeOrAbsolute); + Assert.False(relativeUri.IsAbsoluteUri); + + Uri uri = new Uri(BaseUri, relativeUri); + + string pathAndQuery = uri.SafeGetPathAndQuery(); + Assert.Equal(pathAndQuery, + NormalizePath(input) + ); + } + + /// + /// Normalise the specified path and query for comparisons in tests. + /// + /// + /// The URI path and query components. + /// + /// + /// The normalised path and query. + /// + /// + /// System.Uri treats the path component of an absolute URI as an absolute path. + /// + static string NormalizePath(string pathAndQuery) + { + if (pathAndQuery == null) + throw new ArgumentNullException(nameof(pathAndQuery)); + + if (pathAndQuery.StartsWith("/")) + return pathAndQuery; + + return $"/{pathAndQuery}"; + } + } +}