@@ -56,6 +56,108 @@ var adsLegacySSHRemoteMap = map[string][]string{
5656}
5757var adsLegacySSHRemoteList = []string {"origin myorg@vs-ssh.visualstudio.com:v3/myorg/myproject/myrepo (fetch)" , "origin myorg@vs-ssh.visualstudio.com:v3/myorg/myproject/myrepo (push)" }
5858
59+ func TestDetectProvider (t * testing.T ) {
60+ tests := []struct {
61+ url string
62+ want string
63+ }{
64+ {"git@github.com:user/repo.git" , "GITHUB" },
65+ {"https://github.com/user/repo" , "GITHUB" },
66+ {"git@gitlab.com:user/repo.git" , "GITLAB" },
67+ {"https://gitlab.com/user/repo" , "GITLAB" },
68+ {"git@bitbucket.org:user/repo.git" , "BITBUCKET" },
69+ {"https://bitbucket.org/user/repo" , "BITBUCKET" },
70+ {"https://dev.azure.com/org/proj/_git/repo" , "ADS" },
71+ {"https://org.visualstudio.com/proj/_git/repo" , "ADS" },
72+ {"git@ssh.dev.azure.com:v3/org/proj/repo" , "ADS" },
73+ {"https://example.com/user/repo" , "" },
74+ {"git@selfhosted.example.com:user/repo.git" , "" },
75+ }
76+ for _ , tt := range tests {
77+ t .Run (tt .url , func (t * testing.T ) {
78+ got := detectProvider (tt .url )
79+ if got != tt .want {
80+ t .Errorf ("detectProvider(%q) = %q, want %q" , tt .url , got , tt .want )
81+ }
82+ })
83+ }
84+ }
85+
86+ func TestExtractRepoName (t * testing.T ) {
87+ tests := []struct {
88+ url string
89+ want string
90+ }{
91+ {"git@github.com:user/myrepo.git" , "myrepo" },
92+ {"https://github.com/user/myrepo" , "myrepo" },
93+ {"https://github.com/user/myrepo.git" , "myrepo" },
94+ {"https://dev.azure.com/org/proj/_git/myrepo" , "myrepo" },
95+ {"git@ssh.dev.azure.com:v3/org/proj/myrepo" , "myrepo" },
96+ }
97+ for _ , tt := range tests {
98+ t .Run (tt .url , func (t * testing.T ) {
99+ got := extractRepoName (tt .url )
100+ if got != tt .want {
101+ t .Errorf ("extractRepoName(%q) = %q, want %q" , tt .url , got , tt .want )
102+ }
103+ })
104+ }
105+ }
106+
107+ func TestExtractOwner (t * testing.T ) {
108+ tests := []struct {
109+ url string
110+ provider string
111+ want string
112+ }{
113+ // SSH GitHub
114+ {"git@github.com:myowner/repo.git" , "GITHUB" , "myowner" },
115+ // HTTPS GitHub
116+ {"https://github.com/myowner/repo" , "GITHUB" , "myowner" },
117+ // HTTPS with PAT
118+ {"https://user:token@github.com/myowner/repo" , "GITHUB" , "myowner" },
119+ // SSH GitLab
120+ {"git@gitlab.com:myowner/repo.git" , "GITLAB" , "myowner" },
121+ // HTTPS GitLab
122+ {"https://gitlab.com/myowner/repo" , "GITLAB" , "myowner" },
123+ // ADS delegation
124+ {"https://dev.azure.com/myorg/proj/_git/repo" , "ADS" , "myorg" },
125+ // Unknown prefix
126+ {"ftp://example.com/owner/repo" , "GITHUB" , "" },
127+ }
128+ for _ , tt := range tests {
129+ t .Run (tt .url , func (t * testing.T ) {
130+ got := extractOwner (tt .url , tt .provider )
131+ if got != tt .want {
132+ t .Errorf ("extractOwner(%q, %q) = %q, want %q" , tt .url , tt .provider , got , tt .want )
133+ }
134+ })
135+ }
136+ }
137+
138+ func TestExtractADSOwner (t * testing.T ) {
139+ tests := []struct {
140+ name string
141+ url string
142+ want string
143+ }{
144+ {"HTTPS dev.azure.com" , "https://dev.azure.com/myorg/proj/_git/repo" , "myorg" },
145+ {"HTTPS visualstudio.com" , "https://myorg.visualstudio.com/proj/_git/repo" , "myorg" },
146+ {"SSH dev.azure.com" , "git@ssh.dev.azure.com:v3/myorg/proj/repo" , "myorg" },
147+ {"SSH visualstudio.com" , "myorg@vs-ssh.visualstudio.com:v3/myorg/proj/repo" , "myorg" },
148+ {"HTTPS dev.azure.com empty path" , "https://dev.azure.com/" , "" },
149+ {"HTTPS invalid URL" , "https://://bad" , "" },
150+ }
151+ for _ , tt := range tests {
152+ t .Run (tt .name , func (t * testing.T ) {
153+ got := extractADSOwner (tt .url )
154+ if got != tt .want {
155+ t .Errorf ("extractADSOwner(%q) = %q, want %q" , tt .url , got , tt .want )
156+ }
157+ })
158+ }
159+ }
160+
59161// TestGetRemoteMap_EmptyInput verifies that an empty remote list returns an empty map.
60162// Note: the ListRemotes() code path (auto-detecting remotes from git) requires an
61163// integration test environment with a real git process, so it is not covered here.
@@ -116,6 +218,34 @@ func TestGetRemoteMap(t *testing.T) {
116218 adsLegacySSHRemoteList ,
117219 adsLegacySSHRemoteMap ,
118220 },
221+ {
222+ "GitLab HTTPS URLs" ,
223+ []string {
224+ "origin https://gitlab.com/myuser/myrepo (fetch)" ,
225+ "origin https://gitlab.com/myuser/myrepo (push)" ,
226+ },
227+ map [string ][]string {
228+ "origin" : {"myuser" , "myrepo" , "GITLAB" , "myuser/myrepo" },
229+ },
230+ },
231+ {
232+ "Bitbucket SSH URLs" ,
233+ []string {
234+ "origin git@bitbucket.org:myuser/myrepo.git (fetch)" ,
235+ "origin git@bitbucket.org:myuser/myrepo.git (push)" ,
236+ },
237+ map [string ][]string {
238+ "origin" : {"myuser" , "myrepo" , "BITBUCKET" , "myuser/myrepo" },
239+ },
240+ },
241+ {
242+ "unsupported provider is skipped" ,
243+ []string {
244+ "origin https://selfhosted.example.com/user/repo (fetch)" ,
245+ "origin https://selfhosted.example.com/user/repo (push)" ,
246+ },
247+ map [string ][]string {},
248+ },
119249 }
120250
121251 for _ , tt := range tests {
@@ -126,6 +256,10 @@ func TestGetRemoteMap(t *testing.T) {
126256 return
127257 }
128258
259+ if len (remoteMap ) != len (tt .want ) {
260+ t .Errorf ("got %d remotes, want %d" , len (remoteMap ), len (tt .want ))
261+ }
262+
129263 for remote := range remoteMap {
130264 got := remoteMap [remote ]
131265 want := tt .want [remote ]
0 commit comments