From 2722411b5b826ed0e8b317127f2cb4d85ca5405e Mon Sep 17 00:00:00 2001 From: "Martin Hinshelwood nkdAgility.com" Date: Wed, 20 Dec 2023 12:05:19 +0000 Subject: [PATCH 1/2] Update both code and tests to enable use with all TFS URL's not just Repositories. This better matches the name and implied function of the tool. Note: Im not happy with line 31! "if (repoUrl.ToString().Contains("_git"))" --- .../RepositoryDescriptionTests.cs | 115 +++++++++++++++++- src/TfsUrlParser/RepositoryDescription.cs | 27 +++- 2 files changed, 133 insertions(+), 9 deletions(-) diff --git a/src/TfsUrlParser.Tests/RepositoryDescriptionTests.cs b/src/TfsUrlParser.Tests/RepositoryDescriptionTests.cs index bacb2e8..4948d84 100644 --- a/src/TfsUrlParser.Tests/RepositoryDescriptionTests.cs +++ b/src/TfsUrlParser.Tests/RepositoryDescriptionTests.cs @@ -7,9 +7,6 @@ public class RepositoryDescriptionTests { [Theory] - [InlineData( - @"http://myserver:8080/tfs/defaultcollection/myproject/", - "No valid Git repository URL.")] [InlineData( @"http://myserver:8080/tfs/defaultcollection/myproject/_git", "No valid Git repository URL.")] @@ -25,6 +22,7 @@ public void Should_Throw_If_No_Valid_Url(string repoUrl, string expectedMessage) var result = Record.Exception(() => new RepositoryDescription(new Uri(repoUrl))); // Then + result.IsUriFormatExceptionException(expectedMessage); } @@ -192,6 +190,117 @@ public void Should_Parse_Repo_Url( repositoryDescription.ProjectName.ShouldBe(projectName); repositoryDescription.RepositoryName.ShouldBe(repositoryName); repositoryDescription.RepositoryUrl.ShouldBe(new Uri(repositoryUrl)); + repositoryDescription.IsRepository.ShouldBe(true); } + + [Theory] + [InlineData( + @"http://myserver:8080/tfs/defaultcollection/myproject/", + @"http://myserver:8080/", + "defaultcollection", + @"http://myserver:8080/tfs/defaultcollection", + "myproject")] + [InlineData( + @"http://tfs.myserver/defaultcollection/myproject/", + @"http://tfs.myserver/", + "defaultcollection", + @"http://tfs.myserver/defaultcollection", + "myproject")] + [InlineData( + @"http://mytenant.visualstudio.com/defaultcollection/myproject/", + @"http://mytenant.visualstudio.com/", + "defaultcollection", + @"http://mytenant.visualstudio.com/defaultcollection", + "myproject")] + [InlineData( + @"http://tfs.foo.com/foo/foo", + @"http://tfs.foo.com/", + "foo", + @"http://tfs.foo.com/foo", + "foo")] + [InlineData( + @"https://myserver:8080/tfs/defaultcollection/myproject/", + @"https://myserver:8080/", + "defaultcollection", + @"https://myserver:8080/tfs/defaultcollection", + "myproject")] + [InlineData( + @"https://tfs.myserver/defaultcollection/myproject/", + @"https://tfs.myserver/", + "defaultcollection", + @"https://tfs.myserver/defaultcollection", + "myproject")] + [InlineData( + @"https://mytenant.visualstudio.com/defaultcollection/myproject/", + @"https://mytenant.visualstudio.com/", + "defaultcollection", + @"https://mytenant.visualstudio.com/defaultcollection", + "myproject")] + [InlineData( + @"https://tfs.foo.com/foo/foo/", + @"https://tfs.foo.com/", + "foo", + @"https://tfs.foo.com/foo", + "foo")] + [InlineData( + @"ssh://myserver:8080/tfs/defaultcollection/myproject/", + @"ssh://myserver:8080/", + "defaultcollection", + @"https://myserver:8080/tfs/defaultcollection", + "myproject")] + [InlineData( + @"ssh://tfs.myserver/defaultcollection/myproject/", + @"ssh://tfs.myserver/", + "defaultcollection", + @"https://tfs.myserver/defaultcollection", + "myproject")] + [InlineData( + @"ssh://mytenant.visualstudio.com/defaultcollection/myproject/", + @"ssh://mytenant.visualstudio.com/", + "defaultcollection", + @"https://mytenant.visualstudio.com/defaultcollection", + "myproject")] + [InlineData( + @"ssh://tfs.foo.com/foo/foo/", + @"ssh://tfs.foo.com/", + "foo", + @"https://tfs.foo.com/foo", + "foo")] + [InlineData( + @"ssh://foo:bar@myserver:8080/tfs/defaultcollection/myproject/", + @"ssh://myserver:8080/", + "defaultcollection", + @"https://myserver:8080/tfs/defaultcollection", + "myproject")] + [InlineData( + @"https://myorganization@dev.azure.com/myorganization/myproject/", + @"https://myorganization@dev.azure.com/", + "myorganization", + @"https://myorganization@dev.azure.com/myorganization", + "myproject")] + [InlineData( + @"https://myorganization.visualstudio.com/myproject/", + @"https://myorganization.visualstudio.com/", + "DefaultCollection", + @"https://myorganization.visualstudio.com", + "myproject")] + public void Should_Parse_NonRepo_Url( + string repoUrl, + string serverUrl, + string collectionName, + string collectionurl, + string projectName) + { + // Given / When + var repositoryDescription = new RepositoryDescription(new Uri(repoUrl)); + + // Then + repositoryDescription.ServerUrl.ShouldBe(new Uri(serverUrl)); + repositoryDescription.CollectionName.ShouldBe(collectionName); + repositoryDescription.CollectionUrl.ShouldBe(new Uri(collectionurl)); + repositoryDescription.ProjectName.ShouldBe(projectName); + repositoryDescription.IsRepository.ShouldBe(false); + } + } } diff --git a/src/TfsUrlParser/RepositoryDescription.cs b/src/TfsUrlParser/RepositoryDescription.cs index fba203f..07e365f 100644 --- a/src/TfsUrlParser/RepositoryDescription.cs +++ b/src/TfsUrlParser/RepositoryDescription.cs @@ -28,9 +28,17 @@ public RepositoryDescription(Uri repoUrl) var gitSeparator = new[] { "/_git/" }; var splitPath = repoUrl.AbsolutePath.Split(gitSeparator, StringSplitOptions.None); - if (splitPath.Length < 2) + if (repoUrl.ToString().Contains("_git")) { - throw new UriFormatException("No valid Git repository URL."); + this.IsRepository = true; + if (splitPath.Length < 2) + { + throw new UriFormatException("No valid Git repository URL."); + } + + } else + { + this.IsRepository = false; } this.ServerUrl = new Uri(repoUrl.GetComponents(UriComponents.SchemeAndServer, UriFormat.Unescaped)); @@ -56,11 +64,13 @@ public RepositoryDescription(Uri repoUrl) { throw new UriFormatException("No valid Git repository URL containing default collection and project name."); } - - var splitLastPart = splitPath[1].Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries); - this.ProjectName = splitFirstPart.Last(); - this.RepositoryName = splitLastPart.First(); + + if (this.IsRepository) + { + var splitLastPart = splitPath[1].Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries); + this.RepositoryName = splitLastPart.First(); + } } /// @@ -94,6 +104,11 @@ public RepositoryDescription(Uri repoUrl) /// public Uri RepositoryUrl { get; private set; } + /// + /// Get a value that indicates if this is a Git Repo or another TFS URL. + /// + public bool IsRepository { get; private set; } + /// /// Converts the repository URL to a supported scheme if possible. /// From f640b8368fb21b2db82f47c42cd3c1d46bbb2c58 Mon Sep 17 00:00:00 2001 From: "Martin Hinshelwood nkdAgility.com" Date: Tue, 27 Aug 2024 09:18:16 +0100 Subject: [PATCH 2/2] =?UTF-8?q?Update=20for=20@christianbumann=20comment?= =?UTF-8?q?=20on=20`/=5Fgit/`:=20=F0=9F=90=9B=20(RepositoryDescription.cs)?= =?UTF-8?q?:=20fix=20incorrect=20URL=20check=20for=20Git=20repository?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Correct the URL check to ensure it accurately identifies Git repository URLs. The previous condition was too broad and could lead to false positives. The updated condition checks for the specific "/_git/" segment, ensuring more precise validation. --- src/TfsUrlParser/RepositoryDescription.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/TfsUrlParser/RepositoryDescription.cs b/src/TfsUrlParser/RepositoryDescription.cs index 07e365f..5c6076e 100644 --- a/src/TfsUrlParser/RepositoryDescription.cs +++ b/src/TfsUrlParser/RepositoryDescription.cs @@ -28,14 +28,13 @@ public RepositoryDescription(Uri repoUrl) var gitSeparator = new[] { "/_git/" }; var splitPath = repoUrl.AbsolutePath.Split(gitSeparator, StringSplitOptions.None); - if (repoUrl.ToString().Contains("_git")) + if (repoUrl.ToString().Contains("/_git/")) { this.IsRepository = true; if (splitPath.Length < 2) { throw new UriFormatException("No valid Git repository URL."); } - } else { this.IsRepository = false;