Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3d1775a37b |
+34
-27
@@ -760,33 +760,6 @@ func (c *Client) GetPull(ctx context.Context, token, owner, repo string, index i
|
|||||||
return &pr, nil
|
return &pr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChangedFile represents a file changed in a pull request.
|
|
||||||
type ChangedFile struct {
|
|
||||||
Filename string `json:"filename"`
|
|
||||||
Status string `json:"status"` // "added", "modified", "removed", "renamed"
|
|
||||||
Additions int `json:"additions"`
|
|
||||||
Deletions int `json:"deletions"`
|
|
||||||
Changes int `json:"changes"`
|
|
||||||
PreviousFilename string `json:"previous_filename,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetChangedFiles fetches the list of files changed in a pull request.
|
|
||||||
func (c *Client) GetChangedFiles(ctx context.Context, token, owner, repo string, index int64) ([]ChangedFile, error) {
|
|
||||||
path := fmt.Sprintf("/repos/%s/%s/pulls/%d/files?limit=50", owner, repo, index)
|
|
||||||
resp, err := c.doRequest(ctx, token, http.MethodGet, path, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("fetching changed files: %w", err)
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
var files []ChangedFile
|
|
||||||
if err := json.NewDecoder(resp.Body).Decode(&files); err != nil {
|
|
||||||
return nil, fmt.Errorf("decoding changed files: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return files, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetIssueComments fetches comments for an issue or pull request.
|
// GetIssueComments fetches comments for an issue or pull request.
|
||||||
func (c *Client) GetIssueComments(ctx context.Context, token, owner, repo string, index int64) ([]Comment, error) {
|
func (c *Client) GetIssueComments(ctx context.Context, token, owner, repo string, index int64) ([]Comment, error) {
|
||||||
path := fmt.Sprintf("/repos/%s/%s/issues/%d/comments?limit=50", owner, repo, index)
|
path := fmt.Sprintf("/repos/%s/%s/issues/%d/comments?limit=50", owner, repo, index)
|
||||||
@@ -972,6 +945,40 @@ func (c *Client) SetIssueState(ctx context.Context, token, owner, repo string, i
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MergePull merges a pull request using the specified merge style.
|
||||||
|
// Valid styles: "merge", "rebase", "rebase-merge", "squash".
|
||||||
|
// If style is empty, defaults to "merge".
|
||||||
|
func (c *Client) MergePull(ctx context.Context, token, owner, repo string, index int64, style, title, message string) error {
|
||||||
|
if style == "" {
|
||||||
|
style = "merge"
|
||||||
|
}
|
||||||
|
|
||||||
|
payload := map[string]string{
|
||||||
|
"Do": style,
|
||||||
|
}
|
||||||
|
if title != "" {
|
||||||
|
payload["merge_message_field"] = title
|
||||||
|
}
|
||||||
|
if message != "" {
|
||||||
|
payload["merge_message_field"] = message
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonData, err := json.Marshal(payload)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("marshaling merge request: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
path := fmt.Sprintf("/repos/%s/%s/pulls/%d/merge", owner, repo, index)
|
||||||
|
resp, err := c.doRequest(ctx, token, http.MethodPost, path, strings.NewReader(string(jsonData)))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("merging pull request: %w", err)
|
||||||
|
}
|
||||||
|
resp.Body.Close()
|
||||||
|
|
||||||
|
c.InvalidateAll()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// AddComment creates a comment on an issue and returns the created Comment.
|
// AddComment creates a comment on an issue and returns the created Comment.
|
||||||
func (c *Client) AddComment(ctx context.Context, token, owner, repo string, index int64, body string) (*Comment, error) {
|
func (c *Client) AddComment(ctx context.Context, token, owner, repo string, index int64, body string) (*Comment, error) {
|
||||||
return c.PostComment(ctx, token, owner, repo, index, body)
|
return c.PostComment(ctx, token, owner, repo, index, body)
|
||||||
|
|||||||
@@ -1457,62 +1457,80 @@ func TestRetryDelay_ExponentialBackoff(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetChangedFiles(t *testing.T) {
|
func TestMergePull(t *testing.T) {
|
||||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
if r.Method != http.MethodGet {
|
if r.Method != http.MethodPost {
|
||||||
t.Errorf("expected GET, got %s", r.Method)
|
t.Errorf("expected POST, got %s", r.Method)
|
||||||
}
|
}
|
||||||
if r.URL.Path != "/api/v1/repos/owner1/repo1/pulls/5/files" {
|
if r.URL.Path != "/api/v1/repos/owner1/repo1/pulls/5/merge" {
|
||||||
t.Errorf("unexpected path: %s", r.URL.Path)
|
t.Errorf("unexpected path: %s", r.URL.Path)
|
||||||
}
|
}
|
||||||
if r.Header.Get("Authorization") != "token test-token" {
|
if r.Header.Get("Authorization") != "token test-token" {
|
||||||
t.Error("missing or wrong Authorization header")
|
t.Error("missing or wrong Authorization header")
|
||||||
}
|
}
|
||||||
|
|
||||||
files := []ChangedFile{
|
var body map[string]string
|
||||||
{Filename: "main.go", Status: "modified", Additions: 10, Deletions: 3, Changes: 13},
|
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
|
||||||
{Filename: "new_file.go", Status: "added", Additions: 25, Deletions: 0, Changes: 25},
|
t.Fatalf("failed to decode body: %v", err)
|
||||||
{Filename: "old_file.go", Status: "removed", Additions: 0, Deletions: 15, Changes: 15},
|
|
||||||
}
|
}
|
||||||
json.NewEncoder(w).Encode(files)
|
if body["Do"] != "squash" {
|
||||||
|
t.Errorf("expected Do=squash, got %q", body["Do"])
|
||||||
|
}
|
||||||
|
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
}))
|
}))
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
|
|
||||||
c := NewClient(server.URL)
|
c := NewClient(server.URL)
|
||||||
files, err := c.GetChangedFiles(context.Background(), "test-token", "owner1", "repo1", 5)
|
c.setCache("pulls-org1", "should-be-invalidated")
|
||||||
|
|
||||||
|
err := c.MergePull(context.Background(), "test-token", "owner1", "repo1", 5, "squash", "", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
if len(files) != 3 {
|
|
||||||
t.Fatalf("got %d files, want 3", len(files))
|
// Verify cache was invalidated.
|
||||||
}
|
_, ok := c.getFromCache("pulls-org1")
|
||||||
if files[0].Filename != "main.go" {
|
if ok {
|
||||||
t.Errorf("files[0].Filename = %q, want %q", files[0].Filename, "main.go")
|
t.Error("expected cache to be invalidated after MergePull")
|
||||||
}
|
|
||||||
if files[0].Status != "modified" {
|
|
||||||
t.Errorf("files[0].Status = %q, want %q", files[0].Status, "modified")
|
|
||||||
}
|
|
||||||
if files[1].Status != "added" {
|
|
||||||
t.Errorf("files[1].Status = %q, want %q", files[1].Status, "added")
|
|
||||||
}
|
|
||||||
if files[2].Status != "removed" {
|
|
||||||
t.Errorf("files[2].Status = %q, want %q", files[2].Status, "removed")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetChangedFiles_Error(t *testing.T) {
|
func TestMergePull_DefaultStyle(t *testing.T) {
|
||||||
|
var receivedStyle string
|
||||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
w.WriteHeader(http.StatusNotFound)
|
var body map[string]string
|
||||||
fmt.Fprintln(w, `{"message":"pull request not found"}`)
|
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
|
||||||
|
t.Fatalf("failed to decode body: %v", err)
|
||||||
|
}
|
||||||
|
receivedStyle = body["Do"]
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
}))
|
}))
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
|
|
||||||
c := NewClient(server.URL)
|
c := NewClient(server.URL)
|
||||||
_, err := c.GetChangedFiles(context.Background(), "test-token", "owner1", "repo1", 999)
|
err := c.MergePull(context.Background(), "test-token", "owner1", "repo1", 5, "", "", "")
|
||||||
if err == nil {
|
if err != nil {
|
||||||
t.Fatal("expected error for 404 response, got nil")
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
if !strings.Contains(err.Error(), "404") {
|
if receivedStyle != "merge" {
|
||||||
t.Errorf("error should contain status code 404, got: %v", err)
|
t.Errorf("expected default style 'merge', got %q", receivedStyle)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMergePull_Error(t *testing.T) {
|
||||||
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||||
|
fmt.Fprintln(w, `{"message":"not mergeable"}`)
|
||||||
|
}))
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
c := NewClient(server.URL)
|
||||||
|
err := c.MergePull(context.Background(), "test-token", "owner1", "repo1", 5, "merge", "", "")
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected error for 405 response, got nil")
|
||||||
|
}
|
||||||
|
if !strings.Contains(err.Error(), "405") {
|
||||||
|
t.Errorf("error should contain status code 405, got: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user