|
6 | 6 | "encoding/json" |
7 | 7 | "net/http" |
8 | 8 | "net/url" |
| 9 | + "strings" |
9 | 10 | "testing" |
10 | 11 | "time" |
11 | 12 |
|
@@ -2209,73 +2210,250 @@ func Test_filterPaths(t *testing.T) { |
2209 | 2210 | } |
2210 | 2211 |
|
2211 | 2212 | func Test_resolveGitReference(t *testing.T) { |
2212 | | - ctx := context.Background() |
2213 | | - owner := "owner" |
2214 | | - repo := "repo" |
2215 | | - mockedClient := mock.NewMockedHTTPClient( |
2216 | | - mock.WithRequestMatchHandler( |
2217 | | - mock.GetReposByOwnerByRepo, |
2218 | | - http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { |
2219 | | - w.WriteHeader(http.StatusOK) |
2220 | | - _, _ = w.Write([]byte(`{"name": "repo", "default_branch": "main"}`)) |
2221 | | - }), |
2222 | | - ), |
2223 | | - mock.WithRequestMatchHandler( |
2224 | | - mock.GetReposGitRefByOwnerByRepoByRef, |
2225 | | - http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { |
2226 | | - w.WriteHeader(http.StatusOK) |
2227 | | - _, _ = w.Write([]byte(`{"ref": "refs/heads/main", "object": {"sha": "123sha456"}}`)) |
2228 | | - }), |
2229 | | - ), |
2230 | | - ) |
2231 | | - |
2232 | | - tests := []struct { |
2233 | | - name string |
2234 | | - ref string |
2235 | | - sha string |
2236 | | - expectedOutput *raw.ContentOpts |
2237 | | - }{ |
2238 | | - { |
2239 | | - name: "sha takes precedence over ref", |
2240 | | - ref: "refs/heads/main", |
2241 | | - sha: "123sha456", |
2242 | | - expectedOutput: &raw.ContentOpts{ |
2243 | | - SHA: "123sha456", |
2244 | | - }, |
2245 | | - }, |
2246 | | - { |
2247 | | - name: "use default branch if ref and sha both empty", |
2248 | | - ref: "", |
2249 | | - sha: "", |
2250 | | - expectedOutput: &raw.ContentOpts{ |
2251 | | - Ref: "refs/heads/main", |
2252 | | - SHA: "123sha456", |
2253 | | - }, |
2254 | | - }, |
2255 | | - { |
2256 | | - name: "get SHA from ref", |
2257 | | - ref: "refs/heads/main", |
2258 | | - sha: "", |
2259 | | - expectedOutput: &raw.ContentOpts{ |
2260 | | - Ref: "refs/heads/main", |
2261 | | - SHA: "123sha456", |
2262 | | - }, |
2263 | | - }, |
2264 | | - } |
2265 | | - |
2266 | | - for _, tc := range tests { |
2267 | | - t.Run(tc.name, func(t *testing.T) { |
2268 | | - // Setup client with mock |
2269 | | - client := github.NewClient(mockedClient) |
2270 | | - opts, err := resolveGitReference(ctx, client, owner, repo, tc.ref, tc.sha) |
2271 | | - require.NoError(t, err) |
2272 | | - |
2273 | | - if tc.expectedOutput.SHA != "" { |
2274 | | - assert.Equal(t, tc.expectedOutput.SHA, opts.SHA) |
2275 | | - } |
2276 | | - if tc.expectedOutput.Ref != "" { |
2277 | | - assert.Equal(t, tc.expectedOutput.Ref, opts.Ref) |
2278 | | - } |
2279 | | - }) |
2280 | | - } |
| 2213 | + ctx := context.Background() |
| 2214 | + owner := "owner" |
| 2215 | + repo := "repo" |
| 2216 | + |
| 2217 | + tests := []struct { |
| 2218 | + name string |
| 2219 | + ref string |
| 2220 | + sha string |
| 2221 | + mockSetup func() *http.Client |
| 2222 | + expectedOutput *raw.ContentOpts |
| 2223 | + expectError bool |
| 2224 | + errorContains string |
| 2225 | + }{ |
| 2226 | + { |
| 2227 | + name: "sha takes precedence over ref", |
| 2228 | + ref: "refs/heads/main", |
| 2229 | + sha: "123sha456", |
| 2230 | + mockSetup: func() *http.Client { |
| 2231 | + // No API calls should be made when SHA is provided |
| 2232 | + return mock.NewMockedHTTPClient() |
| 2233 | + }, |
| 2234 | + expectedOutput: &raw.ContentOpts{ |
| 2235 | + SHA: "123sha456", |
| 2236 | + }, |
| 2237 | + expectError: false, |
| 2238 | + }, |
| 2239 | + { |
| 2240 | + name: "use default branch if ref and sha both empty", |
| 2241 | + ref: "", |
| 2242 | + sha: "", |
| 2243 | + mockSetup: func() *http.Client { |
| 2244 | + return mock.NewMockedHTTPClient( |
| 2245 | + mock.WithRequestMatchHandler( |
| 2246 | + mock.GetReposByOwnerByRepo, |
| 2247 | + http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { |
| 2248 | + w.WriteHeader(http.StatusOK) |
| 2249 | + _, _ = w.Write([]byte(`{"name": "repo", "default_branch": "main"}`)) |
| 2250 | + }), |
| 2251 | + ), |
| 2252 | + mock.WithRequestMatchHandler( |
| 2253 | + mock.GetReposGitRefByOwnerByRepoByRef, |
| 2254 | + http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
| 2255 | + assert.Contains(t, r.URL.Path, "/git/ref/heads/main") |
| 2256 | + w.WriteHeader(http.StatusOK) |
| 2257 | + _, _ = w.Write([]byte(`{"ref": "refs/heads/main", "object": {"sha": "main-sha"}}`)) |
| 2258 | + }), |
| 2259 | + ), |
| 2260 | + ) |
| 2261 | + }, |
| 2262 | + expectedOutput: &raw.ContentOpts{ |
| 2263 | + Ref: "refs/heads/main", |
| 2264 | + SHA: "main-sha", |
| 2265 | + }, |
| 2266 | + expectError: false, |
| 2267 | + }, |
| 2268 | + { |
| 2269 | + name: "fully qualified ref passed through unchanged", |
| 2270 | + ref: "refs/heads/feature-branch", |
| 2271 | + sha: "", |
| 2272 | + mockSetup: func() *http.Client { |
| 2273 | + return mock.NewMockedHTTPClient( |
| 2274 | + mock.WithRequestMatchHandler( |
| 2275 | + mock.GetReposGitRefByOwnerByRepoByRef, |
| 2276 | + http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
| 2277 | + assert.Contains(t, r.URL.Path, "/git/ref/heads/feature-branch") |
| 2278 | + w.WriteHeader(http.StatusOK) |
| 2279 | + _, _ = w.Write([]byte(`{"ref": "refs/heads/feature-branch", "object": {"sha": "feature-sha"}}`)) |
| 2280 | + }), |
| 2281 | + ), |
| 2282 | + ) |
| 2283 | + }, |
| 2284 | + expectedOutput: &raw.ContentOpts{ |
| 2285 | + Ref: "refs/heads/feature-branch", |
| 2286 | + SHA: "feature-sha", |
| 2287 | + }, |
| 2288 | + expectError: false, |
| 2289 | + }, |
| 2290 | + { |
| 2291 | + name: "short branch name resolves to refs/heads/", |
| 2292 | + ref: "main", |
| 2293 | + sha: "", |
| 2294 | + mockSetup: func() *http.Client { |
| 2295 | + callCount := 0 |
| 2296 | + return mock.NewMockedHTTPClient( |
| 2297 | + mock.WithRequestMatchHandler( |
| 2298 | + mock.GetReposGitRefByOwnerByRepoByRef, |
| 2299 | + http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
| 2300 | + callCount++ |
| 2301 | + if strings.Contains(r.URL.Path, "/git/ref/heads/main") { |
| 2302 | + w.WriteHeader(http.StatusOK) |
| 2303 | + _, _ = w.Write([]byte(`{"ref": "refs/heads/main", "object": {"sha": "main-sha"}}`)) |
| 2304 | + } else { |
| 2305 | + t.Errorf("Unexpected path: %s", r.URL.Path) |
| 2306 | + w.WriteHeader(http.StatusNotFound) |
| 2307 | + } |
| 2308 | + }), |
| 2309 | + ), |
| 2310 | + ) |
| 2311 | + }, |
| 2312 | + expectedOutput: &raw.ContentOpts{ |
| 2313 | + Ref: "refs/heads/main", |
| 2314 | + SHA: "main-sha", |
| 2315 | + }, |
| 2316 | + expectError: false, |
| 2317 | + }, |
| 2318 | + { |
| 2319 | + name: "short tag name falls back to refs/tags/ when branch not found", |
| 2320 | + ref: "v1.0.0", |
| 2321 | + sha: "", |
| 2322 | + mockSetup: func() *http.Client { |
| 2323 | + return mock.NewMockedHTTPClient( |
| 2324 | + mock.WithRequestMatchHandler( |
| 2325 | + mock.GetReposGitRefByOwnerByRepoByRef, |
| 2326 | + http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
| 2327 | + if strings.Contains(r.URL.Path, "/git/ref/heads/v1.0.0") { |
| 2328 | + w.WriteHeader(http.StatusNotFound) |
| 2329 | + _, _ = w.Write([]byte(`{"message": "Not Found"}`)) |
| 2330 | + } else if strings.Contains(r.URL.Path, "/git/ref/tags/v1.0.0") { |
| 2331 | + w.WriteHeader(http.StatusOK) |
| 2332 | + _, _ = w.Write([]byte(`{"ref": "refs/tags/v1.0.0", "object": {"sha": "tag-sha"}}`)) |
| 2333 | + } else { |
| 2334 | + t.Errorf("Unexpected path: %s", r.URL.Path) |
| 2335 | + w.WriteHeader(http.StatusNotFound) |
| 2336 | + } |
| 2337 | + }), |
| 2338 | + ), |
| 2339 | + ) |
| 2340 | + }, |
| 2341 | + expectedOutput: &raw.ContentOpts{ |
| 2342 | + Ref: "refs/tags/v1.0.0", |
| 2343 | + SHA: "tag-sha", |
| 2344 | + }, |
| 2345 | + expectError: false, |
| 2346 | + }, |
| 2347 | + { |
| 2348 | + name: "heads/ prefix gets refs/ prepended", |
| 2349 | + ref: "heads/feature-branch", |
| 2350 | + sha: "", |
| 2351 | + mockSetup: func() *http.Client { |
| 2352 | + return mock.NewMockedHTTPClient( |
| 2353 | + mock.WithRequestMatchHandler( |
| 2354 | + mock.GetReposGitRefByOwnerByRepoByRef, |
| 2355 | + http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
| 2356 | + assert.Contains(t, r.URL.Path, "/git/ref/heads/feature-branch") |
| 2357 | + w.WriteHeader(http.StatusOK) |
| 2358 | + _, _ = w.Write([]byte(`{"ref": "refs/heads/feature-branch", "object": {"sha": "feature-sha"}}`)) |
| 2359 | + }), |
| 2360 | + ), |
| 2361 | + ) |
| 2362 | + }, |
| 2363 | + expectedOutput: &raw.ContentOpts{ |
| 2364 | + Ref: "refs/heads/feature-branch", |
| 2365 | + SHA: "feature-sha", |
| 2366 | + }, |
| 2367 | + expectError: false, |
| 2368 | + }, |
| 2369 | + { |
| 2370 | + name: "tags/ prefix gets refs/ prepended", |
| 2371 | + ref: "tags/v1.0.0", |
| 2372 | + sha: "", |
| 2373 | + mockSetup: func() *http.Client { |
| 2374 | + return mock.NewMockedHTTPClient( |
| 2375 | + mock.WithRequestMatchHandler( |
| 2376 | + mock.GetReposGitRefByOwnerByRepoByRef, |
| 2377 | + http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
| 2378 | + assert.Contains(t, r.URL.Path, "/git/ref/tags/v1.0.0") |
| 2379 | + w.WriteHeader(http.StatusOK) |
| 2380 | + _, _ = w.Write([]byte(`{"ref": "refs/tags/v1.0.0", "object": {"sha": "tag-sha"}}`)) |
| 2381 | + }), |
| 2382 | + ), |
| 2383 | + ) |
| 2384 | + }, |
| 2385 | + expectedOutput: &raw.ContentOpts{ |
| 2386 | + Ref: "refs/tags/v1.0.0", |
| 2387 | + SHA: "tag-sha", |
| 2388 | + }, |
| 2389 | + expectError: false, |
| 2390 | + }, |
| 2391 | + { |
| 2392 | + name: "invalid short name that doesn't exist as branch or tag", |
| 2393 | + ref: "nonexistent", |
| 2394 | + sha: "", |
| 2395 | + mockSetup: func() *http.Client { |
| 2396 | + return mock.NewMockedHTTPClient( |
| 2397 | + mock.WithRequestMatchHandler( |
| 2398 | + mock.GetReposGitRefByOwnerByRepoByRef, |
| 2399 | + http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
| 2400 | + // Both branch and tag attempts should return 404 |
| 2401 | + w.WriteHeader(http.StatusNotFound) |
| 2402 | + _, _ = w.Write([]byte(`{"message": "Not Found"}`)) |
| 2403 | + }), |
| 2404 | + ), |
| 2405 | + ) |
| 2406 | + }, |
| 2407 | + expectError: true, |
| 2408 | + errorContains: "could not resolve ref \"nonexistent\" as a branch or a tag", |
| 2409 | + }, |
| 2410 | + { |
| 2411 | + name: "fully qualified pull request ref", |
| 2412 | + ref: "refs/pull/123/head", |
| 2413 | + sha: "", |
| 2414 | + mockSetup: func() *http.Client { |
| 2415 | + return mock.NewMockedHTTPClient( |
| 2416 | + mock.WithRequestMatchHandler( |
| 2417 | + mock.GetReposGitRefByOwnerByRepoByRef, |
| 2418 | + http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
| 2419 | + assert.Contains(t, r.URL.Path, "/git/ref/pull/123/head") |
| 2420 | + w.WriteHeader(http.StatusOK) |
| 2421 | + _, _ = w.Write([]byte(`{"ref": "refs/pull/123/head", "object": {"sha": "pr-sha"}}`)) |
| 2422 | + }), |
| 2423 | + ), |
| 2424 | + ) |
| 2425 | + }, |
| 2426 | + expectedOutput: &raw.ContentOpts{ |
| 2427 | + Ref: "refs/pull/123/head", |
| 2428 | + SHA: "pr-sha", |
| 2429 | + }, |
| 2430 | + expectError: false, |
| 2431 | + }, |
| 2432 | + } |
| 2433 | + |
| 2434 | + for _, tc := range tests { |
| 2435 | + t.Run(tc.name, func(t *testing.T) { |
| 2436 | + // Setup client with mock |
| 2437 | + client := github.NewClient(tc.mockSetup()) |
| 2438 | + opts, err := resolveGitReference(ctx, client, owner, repo, tc.ref, tc.sha) |
| 2439 | + |
| 2440 | + if tc.expectError { |
| 2441 | + require.Error(t, err) |
| 2442 | + if tc.errorContains != "" { |
| 2443 | + assert.Contains(t, err.Error(), tc.errorContains) |
| 2444 | + } |
| 2445 | + return |
| 2446 | + } |
| 2447 | + |
| 2448 | + require.NoError(t, err) |
| 2449 | + require.NotNil(t, opts) |
| 2450 | + |
| 2451 | + if tc.expectedOutput.SHA != "" { |
| 2452 | + assert.Equal(t, tc.expectedOutput.SHA, opts.SHA) |
| 2453 | + } |
| 2454 | + if tc.expectedOutput.Ref != "" { |
| 2455 | + assert.Equal(t, tc.expectedOutput.Ref, opts.Ref) |
| 2456 | + } |
| 2457 | + }) |
| 2458 | + } |
2281 | 2459 | } |
0 commit comments