Skip to content

Commit 81ba2fa

Browse files
committed
fix: Parse GitLab API URL during SetClient setup
* Added validation by parsing the determined GitLab API URL before initializing the client in `SetClient`. * Let the user know if the URL is invalid by returning an error instead of a cryptic error message later on. * Refactored `TestSetClientDetectAPIURL` into a table-driven test for better coverage and clarity of URL detection logic. Jira: https://issues.redhat.com/browse/SRVKP-7451 Signed-off-by: Chmouel Boudjnah <[email protected]>
1 parent b1aeae0 commit 81ba2fa

File tree

2 files changed

+134
-23
lines changed

2 files changed

+134
-23
lines changed

pkg/provider/gitlab/gitlab.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,10 @@ func (v *Provider) SetClient(_ context.Context, run *params.Run, runevent *info.
130130
// this really should not happen but let's just hope this is it
131131
apiURL = apiPublicURL
132132
}
133+
_, err = url.Parse(apiURL)
134+
if err != nil {
135+
return fmt.Errorf("failed to parse api url %s: %w", apiURL, err)
136+
}
133137
v.apiURL = apiURL
134138

135139
v.Client, err = gitlab.NewClient(runevent.Provider.Token, gitlab.WithBaseURL(apiURL))

pkg/provider/gitlab/gitlab_test.go

Lines changed: 130 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -276,33 +276,140 @@ func TestSetClient(t *testing.T) {
276276
}
277277

278278
func TestSetClientDetectAPIURL(t *testing.T) {
279-
fakehost := "https://fakehost.com"
280279
ctx, _ := rtesting.SetupFakeContext(t)
281-
client, _, tearDown := thelp.Setup(t)
280+
mockClient, _, tearDown := thelp.Setup(t)
282281
defer tearDown()
283-
v := &Provider{Client: client}
284-
event := info.NewEvent()
285-
err := v.SetClient(ctx, nil, event, nil, nil)
286-
assert.ErrorContains(t, err, "no git_provider.secret has been set")
287-
288-
event.Provider.Token = "hello"
289-
event.TargetProjectID = 10
290-
event.SourceProjectID = 10
291-
292-
v.repoURL, event.URL, event.Provider.URL = "", "", ""
293-
event.URL = fmt.Sprintf("%s/hello-this-is-me-ze/project", fakehost)
294-
err = v.SetClient(ctx, nil, event, nil, nil)
295-
assert.NilError(t, err)
296-
assert.Equal(t, fakehost, v.apiURL)
297282

298-
v.repoURL, event.URL, event.Provider.URL = "", "", ""
299-
event.Provider.URL = fmt.Sprintf("%s/hello-this-is-me-ze/anotherproject", fakehost)
300-
assert.Equal(t, fakehost, v.apiURL)
283+
// Define test cases
284+
tests := []struct {
285+
name string
286+
providerToken string
287+
providerURL string // input: event.Provider.URL
288+
repoURL string // input: v.repoURL
289+
pathWithNamespace string // input: v.pathWithNamespace (needed if repoURL is used)
290+
eventURL string // input: event.URL
291+
// Define expected outcomes
292+
expectedAPIURL string
293+
expectedError string // Substring expected in the error message, "" for no error
294+
}{
295+
{
296+
name: "Error: No token provided",
297+
providerToken: "",
298+
expectedError: "no git_provider.secret has been set",
299+
},
300+
{
301+
name: "Success: API URL from event.Provider.URL (highest precedence)",
302+
providerToken: "token",
303+
providerURL: "https://provider.example.com",
304+
repoURL: "https://repo.example.com/foo/bar", // Should be ignored
305+
pathWithNamespace: "foo/bar",
306+
eventURL: "https://event.example.com/foo/bar", // Should be ignored
307+
expectedAPIURL: "https://provider.example.com",
308+
expectedError: "",
309+
},
310+
{
311+
name: "Success: API URL from v.repoURL (non-public)",
312+
providerToken: "token",
313+
providerURL: "", // This must be empty to test the next case
314+
repoURL: "https://private-gitlab.com/my/repo",
315+
pathWithNamespace: "my/repo",
316+
eventURL: "https://event.example.com/my/repo", // Should be ignored
317+
expectedAPIURL: "https://private-gitlab.com/",
318+
expectedError: "",
319+
},
320+
{
321+
name: "Success: API URL from event.URL",
322+
providerToken: "token",
323+
providerURL: "", // This must be empty
324+
repoURL: "", // This must be empty
325+
eventURL: "https://event-url.com/org/project",
326+
expectedAPIURL: "https://event-url.com",
327+
expectedError: "",
328+
},
329+
{
330+
name: "Success: Fallback to default public API URL",
331+
providerToken: "token",
332+
providerURL: "",
333+
repoURL: "",
334+
eventURL: "",
335+
expectedAPIURL: apiPublicURL, // Default case
336+
expectedError: "",
337+
},
338+
{
339+
name: "Success: Default URL when repoURL is public Gitlab",
340+
providerToken: "token",
341+
providerURL: "",
342+
repoURL: apiPublicURL + "/public/repo", // Starts with public URL, so skipped
343+
pathWithNamespace: "public/repo",
344+
eventURL: "", // Falls through to default
345+
expectedAPIURL: apiPublicURL,
346+
expectedError: "",
347+
},
348+
{
349+
name: "Error: Invalid URL from event.URL",
350+
providerToken: "token",
351+
providerURL: "",
352+
repoURL: "",
353+
eventURL: "://bad-schema",
354+
expectedError: "parse \"://bad-schema\": missing protocol scheme", // Specific error from url.Parse
355+
},
356+
{
357+
name: "Error: Invalid URL from event.Provider.URL (final parse)",
358+
providerToken: "token",
359+
providerURL: "ht tp://invalid host", // Invalid URL format
360+
repoURL: "",
361+
eventURL: "",
362+
expectedError: "failed to parse api url", // Wrapper error message
363+
},
364+
{
365+
name: "Error: Invalid URL from v.repoURL (final parse)",
366+
providerToken: "token",
367+
providerURL: "",
368+
repoURL: "ht tp://invalid.repo.url/foo/bar", // Invalid format
369+
pathWithNamespace: "foo/bar",
370+
eventURL: "",
371+
// Note: The calculated apiURL would be "ht tp://invalid.repo.url" before parsing
372+
expectedError: "failed to parse api url",
373+
},
374+
}
301375

302-
v.repoURL = fmt.Sprintf("%s/hello-this-is-me-ze/anotherproject", fakehost)
303-
v.pathWithNamespace = "hello-this-is-me-ze/anotherproject"
304-
event.URL, event.Provider.URL = "", ""
305-
assert.Equal(t, fakehost, v.apiURL)
376+
// Run test cases
377+
for _, tc := range tests {
378+
t.Run(tc.name, func(t *testing.T) {
379+
// Setup specific to this test case
380+
v := &Provider{
381+
Client: mockClient, // Use the shared mock client
382+
repoURL: tc.repoURL,
383+
pathWithNamespace: tc.pathWithNamespace,
384+
}
385+
event := info.NewEvent()
386+
event.Provider.Token = tc.providerToken
387+
event.Provider.URL = tc.providerURL
388+
event.URL = tc.eventURL
389+
// Set some default IDs to avoid potential nil pointer issues or side effects
390+
// if the GetProject part of SetClient is reached unexpectedly.
391+
event.TargetProjectID = 1
392+
event.SourceProjectID = 1
393+
394+
// Execute the function under test
395+
// Using placeholder nil values for arguments not directly related to URL detection
396+
err := v.SetClient(ctx, nil, event, nil, nil)
397+
398+
// Assertions
399+
if tc.expectedError != "" {
400+
assert.ErrorContains(t, err, tc.expectedError)
401+
// If an error is expected, we usually don't check the apiURL state,
402+
// as it might be indeterminate or irrelevant.
403+
} else {
404+
assert.NilError(t, err)
405+
// Only check the resulting apiURL if no error was expected
406+
assert.Equal(t, tc.expectedAPIURL, v.apiURL)
407+
// Optionally, check if the client was actually set (if no error)
408+
assert.Assert(t, v.Client != nil)
409+
assert.Assert(t, v.Token != nil && *v.Token == tc.providerToken)
410+
}
411+
})
412+
}
306413
}
307414

308415
func TestGetTektonDir(t *testing.T) {

0 commit comments

Comments
 (0)