|
4 | 4 | "bytes" |
5 | 5 | "context" |
6 | 6 | "encoding/json" |
| 7 | + "fmt" |
7 | 8 | "os" |
8 | 9 | "path/filepath" |
9 | 10 | "runtime" |
@@ -420,6 +421,101 @@ func TestIssuesMultipleFilters(t *testing.T) { |
420 | 421 | } |
421 | 422 | } |
422 | 423 |
|
| 424 | +// loadGoldenFile reads a golden file and fails the test if it cannot be read. |
| 425 | +func loadGoldenFile(t *testing.T, name string) []byte { |
| 426 | + t.Helper() |
| 427 | + data, err := os.ReadFile(goldenPath(name)) |
| 428 | + if err != nil { |
| 429 | + t.Fatalf("failed to read golden file %s: %v", name, err) |
| 430 | + } |
| 431 | + return data |
| 432 | +} |
| 433 | + |
| 434 | +// TestIssuesPRFallbackUsesRunIssues verifies that when auto-branch resolution |
| 435 | +// detects a PR AND the current run is in-progress (fallback), the code fetches |
| 436 | +// issues via GetRunIssuesFlat (commit-scoped) instead of GetPRIssues. |
| 437 | +// Regression test for ticket #6884175. |
| 438 | +func TestIssuesPRFallbackUsesRunIssues(t *testing.T) { |
| 439 | + cfgMgr := testutil.CreateTestConfigManager(t, "test-token", "deepsource.com", "test@example.com") |
| 440 | + |
| 441 | + prFoundData := loadGoldenFile(t, "get_pr_by_branch_found_response.json") |
| 442 | + runsFirstData := loadGoldenFile(t, "get_analysis_runs_pr_fallback_first_response.json") |
| 443 | + runsCompletedData := loadGoldenFile(t, "get_analysis_runs_pr_fallback_completed_response.json") |
| 444 | + commitScopeData := loadGoldenFile(t, "commit_scope_response.json") |
| 445 | + |
| 446 | + mock := graphqlclient.NewMockClient() |
| 447 | + analysisRunsCalls := 0 |
| 448 | + mock.QueryFunc = func(_ context.Context, query string, _ map[string]any, result any) error { |
| 449 | + switch { |
| 450 | + case strings.Contains(query, "pullRequests("): |
| 451 | + return json.Unmarshal(prFoundData, result) |
| 452 | + case strings.Contains(query, "query GetAnalysisRuns("): |
| 453 | + analysisRunsCalls++ |
| 454 | + if analysisRunsCalls == 1 { |
| 455 | + // First call (ResolveLatestRunForBranch, limit=1): RUNNING run |
| 456 | + return json.Unmarshal(runsFirstData, result) |
| 457 | + } |
| 458 | + // Second call (ResolveLatestCompletedRun, limit=10): RUNNING + SUCCESS |
| 459 | + return json.Unmarshal(runsCompletedData, result) |
| 460 | + case strings.Contains(query, "checks {"): |
| 461 | + // GetRunIssuesFlat for the fallback commit |
| 462 | + return json.Unmarshal(commitScopeData, result) |
| 463 | + default: |
| 464 | + t.Fatalf("unexpected query: %s", query) |
| 465 | + return nil |
| 466 | + } |
| 467 | + } |
| 468 | + client := deepsource.NewWithGraphQLClient(mock) |
| 469 | + |
| 470 | + var buf bytes.Buffer |
| 471 | + deps := &cmddeps.Deps{ |
| 472 | + Client: client, |
| 473 | + ConfigMgr: cfgMgr, |
| 474 | + Stdout: &buf, |
| 475 | + BranchNameFunc: func() (string, error) { |
| 476 | + return "feature/new-auth", nil |
| 477 | + }, |
| 478 | + HasUnpushedCommitsFunc: func() bool { return false }, |
| 479 | + HasUncommittedChangesFunc: func() bool { return false }, |
| 480 | + } |
| 481 | + |
| 482 | + cmd := issuesCmd.NewCmdIssuesWithDeps(deps) |
| 483 | + cmd.SetArgs([]string{"--repo", "gh/testowner/testrepo", "--output", "json"}) |
| 484 | + |
| 485 | + if err := cmd.Execute(); err != nil { |
| 486 | + t.Fatalf("unexpected error: %v", err) |
| 487 | + } |
| 488 | + |
| 489 | + got := buf.String() |
| 490 | + |
| 491 | + // Verify the fallback info message is present. |
| 492 | + if !strings.Contains(got, "Analysis is running on commit") { |
| 493 | + t.Errorf("expected fallback info message, got: %q", got) |
| 494 | + } |
| 495 | + |
| 496 | + // Extract the JSON array from the output (after the info message line). |
| 497 | + // The JSON starts at the first '[' character. |
| 498 | + jsonStart := strings.Index(got, "[") |
| 499 | + if jsonStart < 0 { |
| 500 | + t.Fatalf("no JSON array found in output: %s", got) |
| 501 | + } |
| 502 | + |
| 503 | + var issues []map[string]any |
| 504 | + if err := json.Unmarshal([]byte(got[jsonStart:]), &issues); err != nil { |
| 505 | + t.Fatalf("failed to parse JSON output: %v\nraw output: %s", err, got) |
| 506 | + } |
| 507 | + |
| 508 | + // Must NOT be empty — this was the bug. |
| 509 | + if len(issues) == 0 { |
| 510 | + t.Fatal("expected non-empty issues from fallback commit, got empty array") |
| 511 | + } |
| 512 | + |
| 513 | + // Verify we got the expected issues from commit_scope_response.json. |
| 514 | + if issues[0]["issue_code"] != "GO-W1007" { |
| 515 | + t.Errorf("expected first issue GO-W1007, got %v", issues[0]["issue_code"]) |
| 516 | + } |
| 517 | +} |
| 518 | + |
423 | 519 | func TestIssuesRunInProgress(t *testing.T) { |
424 | 520 | cfgMgr := testutil.CreateTestConfigManager(t, "test-token", "deepsource.com", "test@example.com") |
425 | 521 | mock := testutil.MockQueryFunc(t, map[string]string{ |
@@ -946,4 +1042,58 @@ func TestIssuesLimitCapAfterPagination(t *testing.T) { |
946 | 1042 | } |
947 | 1043 | } |
948 | 1044 |
|
| 1045 | +func TestIssuesMonorepoHintError(t *testing.T) { |
| 1046 | + cfgMgr := testutil.CreateTestConfigManager(t, "test-token", "deepsource.com", "test@example.com") |
| 1047 | + |
| 1048 | + mock := graphqlclient.NewMockClient() |
| 1049 | + mock.QueryFunc = func(_ context.Context, _ string, _ map[string]any, _ any) error { |
| 1050 | + return fmt.Errorf("This repository is a monorepo. Please specify a sub-project") |
| 1051 | + } |
| 1052 | + client := deepsource.NewWithGraphQLClient(mock) |
949 | 1053 |
|
| 1054 | + var buf bytes.Buffer |
| 1055 | + deps := &cmddeps.Deps{ |
| 1056 | + Client: client, |
| 1057 | + ConfigMgr: cfgMgr, |
| 1058 | + Stdout: &buf, |
| 1059 | + } |
| 1060 | + |
| 1061 | + cmd := issuesCmd.NewCmdIssuesWithDeps(deps) |
| 1062 | + cmd.SetArgs([]string{"--repo", "gh/testowner/testrepo", "--commit", "abc123f", "--output", "json"}) |
| 1063 | + |
| 1064 | + err := cmd.Execute() |
| 1065 | + if err == nil { |
| 1066 | + t.Fatal("expected error for monorepo hint") |
| 1067 | + } |
| 1068 | + if !strings.Contains(err.Error(), "Use --repo to specify a sub-project") { |
| 1069 | + t.Errorf("expected monorepo hint in error, got: %s", err.Error()) |
| 1070 | + } |
| 1071 | +} |
| 1072 | + |
| 1073 | +func TestIssuesRepoNotFoundError(t *testing.T) { |
| 1074 | + cfgMgr := testutil.CreateTestConfigManager(t, "test-token", "deepsource.com", "test@example.com") |
| 1075 | + |
| 1076 | + mock := graphqlclient.NewMockClient() |
| 1077 | + mock.QueryFunc = func(_ context.Context, _ string, _ map[string]any, _ any) error { |
| 1078 | + return fmt.Errorf("Repository does not exist on DeepSource") |
| 1079 | + } |
| 1080 | + client := deepsource.NewWithGraphQLClient(mock) |
| 1081 | + |
| 1082 | + var buf bytes.Buffer |
| 1083 | + deps := &cmddeps.Deps{ |
| 1084 | + Client: client, |
| 1085 | + ConfigMgr: cfgMgr, |
| 1086 | + Stdout: &buf, |
| 1087 | + } |
| 1088 | + |
| 1089 | + cmd := issuesCmd.NewCmdIssuesWithDeps(deps) |
| 1090 | + cmd.SetArgs([]string{"--repo", "gh/testowner/testrepo", "--commit", "abc123f", "--output", "json"}) |
| 1091 | + |
| 1092 | + err := cmd.Execute() |
| 1093 | + if err == nil { |
| 1094 | + t.Fatal("expected error for repository not found") |
| 1095 | + } |
| 1096 | + if !strings.Contains(err.Error(), "Repository does not exist") { |
| 1097 | + t.Errorf("expected repo-not-found error, got: %s", err.Error()) |
| 1098 | + } |
| 1099 | +} |
0 commit comments