diff --git a/gitea/client_test.go b/gitea/client_test.go index c35a474..30cee22 100644 --- a/gitea/client_test.go +++ b/gitea/client_test.go @@ -1420,3 +1420,80 @@ func TestNewSafeHTTPClient_PreservesDefaultTransportSettings(t *testing.T) { t.Error("DialContext is nil; expected safeDialContext") } } + +func TestGetTimelineReviewCommentIDForReview(t *testing.T) { + const reviewID = int64(42) + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch r.URL.Path { + case "/api/v1/repos/owner/repo/pulls/5/reviews/42": + w.Write([]byte(`{"body": "The review body ", "user": {"login": "sonnet-review"}}`)) + case "/api/v1/repos/owner/repo/issues/5/timeline": + w.Write([]byte(`[ + {"id": 100, "type": "comment", "body": "unrelated", "user": {"login": "sonnet-review"}}, + {"id": 200, "type": "review", "body": "The review body ", "user": {"login": "sonnet-review"}} + ]`)) + default: + t.Errorf("unexpected path: %s", r.URL.Path) + w.WriteHeader(http.StatusNotFound) + } + })) + defer server.Close() + + client := NewTestClient(server.URL, "test-token") + id, err := client.GetTimelineReviewCommentIDForReview(context.Background(), "owner", "repo", 5, reviewID) + if err != nil { + t.Fatalf("GetTimelineReviewCommentIDForReview() error = %v", err) + } + if id != 200 { + t.Errorf("got id=%d, want 200", id) + } +} + +func TestGetTimelineReviewCommentIDForReview_ReviewFetchError(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotFound) + w.Write([]byte(`{"message":"not found"}`)) + })) + defer server.Close() + + client := NewTestClient(server.URL, "test-token") + _, err := client.GetTimelineReviewCommentIDForReview(context.Background(), "owner", "repo", 5, 99) + if err == nil { + t.Fatal("expected error for missing review, got nil") + } +} + +func TestGetTimelineReviewCommentIDForReview_EmptyBody(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte(`{"body": "", "user": {"login": "bot"}}`)) + })) + defer server.Close() + + client := NewTestClient(server.URL, "test-token") + _, err := client.GetTimelineReviewCommentIDForReview(context.Background(), "owner", "repo", 5, 42) + if err == nil { + t.Fatal("expected error for empty body, got nil") + } + if !strings.Contains(err.Error(), "empty body") { + t.Errorf("error = %q, want to contain 'empty body'", err.Error()) + } +} + +func TestGetTimelineReviewCommentIDForReview_NotFoundInTimeline(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch r.URL.Path { + case "/api/v1/repos/owner/repo/pulls/5/reviews/42": + w.Write([]byte(`{"body": "review content ", "user": {"login": "bot"}}`)) + default: + // Timeline returns events that don't match (different user) + w.Write([]byte(`[{"id": 1, "type": "review", "body": "review content ", "user": {"login": "other-user"}}]`)) + } + })) + defer server.Close() + + client := NewTestClient(server.URL, "test-token") + _, err := client.GetTimelineReviewCommentIDForReview(context.Background(), "owner", "repo", 5, 42) + if err == nil { + t.Fatal("expected error when review not found in timeline, got nil") + } +} diff --git a/github/client_test.go b/github/client_test.go index 8aa22fb..a8738fe 100644 --- a/github/client_test.go +++ b/github/client_test.go @@ -1130,3 +1130,98 @@ func TestEscapePath_SpecialChars(t *testing.T) { } } } + +func TestGetAllFilesInPath_DirectoryWithFiles(t *testing.T) { + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch r.URL.Path { + case "/repos/owner/repo/contents/patterns": + // Directory listing + w.WriteHeader(http.StatusOK) + w.Write([]byte(`[{"name":"go.md","path":"patterns/go.md","type":"file"}]`)) + case "/repos/owner/repo/contents/patterns/go.md": + // GitHub file response with base64 content + w.WriteHeader(http.StatusOK) + w.Write([]byte(`{"name":"go.md","path":"patterns/go.md","type":"file","encoding":"base64","content":"IyBHbyBwYXR0ZXJucwo="}`)) + default: + t.Errorf("unexpected path: %s", r.URL.Path) + w.WriteHeader(http.StatusNotFound) + } + })) + defer srv.Close() + + c := NewClient("tok", srv.URL, AllowInsecureHTTPForTest()) + files, err := c.GetAllFilesInPath(context.Background(), "owner", "repo", "patterns") + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if len(files) != 1 { + t.Fatalf("len(files) = %d, want 1", len(files)) + } + if files["patterns/go.md"] != "# Go patterns\n" { + t.Errorf("unexpected content: %q", files["patterns/go.md"]) + } +} + +func TestGetAllFilesInPath_404FallsBackToFile(t *testing.T) { + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch r.URL.Path { + case "/repos/owner/repo/contents/README.md": + // ListContents returns 404 for file paths + w.WriteHeader(http.StatusNotFound) + w.Write([]byte(`{"message":"Not Found"}`)) + default: + t.Errorf("unexpected path: %s", r.URL.Path) + w.WriteHeader(http.StatusNotFound) + } + })) + defer srv.Close() + + c := NewClient("tok", srv.URL, AllowInsecureHTTPForTest()) + // GetFileContent also goes to /contents/ — this will 404 too. + // The function should return the path-not-found error. + _, err := c.GetAllFilesInPath(context.Background(), "owner", "repo", "README.md") + if err == nil { + t.Fatal("expected error when both dir and file 404, got nil") + } +} + +func TestGetAllFilesInPath_DirectoryWithSubdir(t *testing.T) { + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch r.URL.Path { + case "/repos/owner/repo/contents/src": + w.WriteHeader(http.StatusOK) + w.Write([]byte(`[ + {"name":"main.go","path":"src/main.go","type":"file"}, + {"name":"sub","path":"src/sub","type":"dir"} + ]`)) + case "/repos/owner/repo/contents/src/main.go": + w.WriteHeader(http.StatusOK) + w.Write([]byte(`{"name":"main.go","path":"src/main.go","type":"file","encoding":"base64","content":"cGFja2FnZSBtYWluCg=="}`)) + case "/repos/owner/repo/contents/src/sub": + w.WriteHeader(http.StatusOK) + w.Write([]byte(`[{"name":"util.go","path":"src/sub/util.go","type":"file"}]`)) + case "/repos/owner/repo/contents/src/sub/util.go": + w.WriteHeader(http.StatusOK) + w.Write([]byte(`{"name":"util.go","path":"src/sub/util.go","type":"file","encoding":"base64","content":"cGFja2FnZSBzdWIK"}`)) + default: + t.Errorf("unexpected path: %s", r.URL.Path) + w.WriteHeader(http.StatusNotFound) + } + })) + defer srv.Close() + + c := NewClient("tok", srv.URL, AllowInsecureHTTPForTest()) + files, err := c.GetAllFilesInPath(context.Background(), "owner", "repo", "src") + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if len(files) != 2 { + t.Fatalf("len(files) = %d, want 2: %v", len(files), files) + } + if files["src/main.go"] != "package main\n" { + t.Errorf("src/main.go content unexpected: %q", files["src/main.go"]) + } + if files["src/sub/util.go"] != "package sub\n" { + t.Errorf("src/sub/util.go content unexpected: %q", files["src/sub/util.go"]) + } +}