package main import ( "context" "encoding/json" "net/http" "net/http/httptest" "strings" "testing" ) func TestListPullRequestsSearchesAssignedPRsInRepository(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if got := r.Header.Get("Authorization"); got != "Bearer secret" { t.Fatalf("authorization = %q", got) } var request graphQLRequest if err := json.NewDecoder(r.Body).Decode(&request); err != nil { t.Fatal(err) } if got := request.Variables["query"]; got != "is:pr is:open sort:updated-desc repo:o/r assignee:@me" { t.Fatalf("search query = %q", got) } _ = json.NewEncoder(w).Encode(map[string]any{"data": map[string]any{ "viewer": map[string]any{"login": "zam"}, "search": map[string]any{"nodes": []any{ map[string]any{ "id": "1", "number": 1, "title": "assigned", "url": "u", "isDraft": false, "updatedAt": "2026-01-01T00:00:00Z", "author": map[string]any{"login": "other"}, "repository": map[string]any{"name": "r", "nameWithOwner": "o/r", "owner": map[string]any{"login": "o"}}, "reviewThreads": map[string]any{"totalCount": 2}, }, }}, }}) })) defer server.Close() client := NewGitHubClient(server.URL, "secret") prs, err := client.ListPullRequests(context.Background(), "o", "r", 50, false) if err != nil { t.Fatal(err) } if len(prs) != 1 || prs[0].Number != 1 || prs[0].ReviewCount != 2 || prs[0].Owner != "o" || prs[0].Repository != "r" || prs[0].RepoWithOwner != "o/r" { t.Fatalf("unexpected PRs: %#v", prs) } } func TestListPullRequestsSearchesAllRepositoriesByDefault(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { var request graphQLRequest if err := json.NewDecoder(r.Body).Decode(&request); err != nil { t.Fatal(err) } query, _ := request.Variables["query"].(string) if !strings.Contains(query, "assignee:@me") || strings.Contains(query, "repo:") { t.Fatalf("global search query = %q", query) } _, _ = w.Write([]byte(`{"data":{"viewer":{"login":"zam"},"search":{"nodes":[]}}}`)) })) defer server.Close() client := NewGitHubClient(server.URL, "secret") if _, err := client.ListPullRequests(context.Background(), "", "", 50, false); err != nil { t.Fatal(err) } } func TestListPullRequestsRejectsGlobalShowAll(t *testing.T) { client := NewGitHubClient("unused", "secret") if _, err := client.ListPullRequests(context.Background(), "", "", 50, true); err == nil { t.Fatal("global --all search was accepted") } } func TestGraphQLErrorsAreReturned(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { _, _ = w.Write([]byte(`{"errors":[{"message":"no access"}]}`)) })) defer server.Close() client := NewGitHubClient(server.URL, "secret") _, err := client.ListPullRequests(context.Background(), "o", "r", 50, false) if err == nil || !strings.Contains(err.Error(), "no access") { t.Fatalf("error = %v", err) } } func TestGetPullRequestUsesOriginalLineAndMetadata(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { _, _ = w.Write([]byte(`{"data":{"repository":{"pullRequest":{ "id":"pr","number":9,"title":"Fix","url":"u","body":"body","isDraft":false, "createdAt":"2025-12-01T00:00:00Z","updatedAt":"2026-01-01T00:00:00Z", "mergeable":"MERGEABLE","mergeStateStatus":"CLEAN","reviewDecision":"APPROVED", "additions":12,"deletions":4,"changedFiles":3, "baseRefName":"main","headRefName":"fix","author":{"login":"zam"}, "assignees":{"nodes":[{"login":"sam"}]}, "labels":{"nodes":[{"name":"bug"},{"name":"backend"}]}, "milestone":{"title":"v2"}, "comments":{"totalCount":5}, "reviewRequests":{"nodes":[{"requestedReviewer":{"login":"lee"}}]}, "latestReviews":{"nodes":[{"state":"CHANGES_REQUESTED","author":{"login":"pat"}}]}, "commits":{"totalCount":7,"nodes":[{"commit":{"statusCheckRollup":{"state":"FAILURE"}}}]}, "reviewThreads":{"pageInfo":{"hasNextPage":false},"nodes":[{ "id":"t","isResolved":false,"isOutdated":true,"path":"main.go", "line":null,"originalLine":42,"diffSide":"RIGHT", "startLine":null,"originalStartLine":40,"startDiffSide":"RIGHT", "comments":{"pageInfo":{"hasNextPage":true},"nodes":[{ "id":"c","body":"change this","diffHunk":"@@ -1 +1 @@","createdAt":"2026-01-01T00:00:00Z", "url":"cu","author":{"login":"reviewer"},"outdated":true, "line":100,"startLine":99,"originalLine":42,"originalStartLine":40, "originalCommit":{"oid":"0123456789abcdef"} }]} }]} }}}}`)) })) defer server.Close() client := NewGitHubClient(server.URL, "secret") got, err := client.GetPullRequest(context.Background(), "o", "r", 9) if err != nil { t.Fatal(err) } if got.CheckState != "FAILURE" || got.BaseRef != "main" || got.HeadRef != "fix" || got.ReviewDecision != "APPROVED" { t.Fatalf("unexpected metadata: %#v", got) } if got.MergeState != "CLEAN" || got.Additions != 12 || got.Deletions != 4 || got.ChangedFiles != 3 || got.CommitCount != 7 || got.CommentCount != 5 || got.Milestone != "v2" || strings.Join(got.Labels, ",") != "bug,backend" || got.CreatedAt.IsZero() { t.Fatalf("unexpected dashboard metadata: %#v", got) } if len(got.Threads) != 1 || got.Threads[0].Line != 42 || got.Threads[0].StartLine != 40 || got.Threads[0].DiffSide != "RIGHT" || !got.Threads[0].IsTruncated { t.Fatalf("unexpected thread: %#v", got.Threads) } comment := got.Threads[0].Comments[0] if comment.OriginalLine != 42 || comment.OriginalStartLine != 40 || comment.OriginalCommitOID != "0123456789abcdef" || !comment.Outdated { t.Fatalf("unexpected comment snapshot: %#v", comment) } }