feat: add MergePull() method to Gitea client

Add MergePull() that calls POST /repos/{owner}/{repo}/pulls/{index}/merge
with the specified merge style (merge, rebase, rebase-merge, squash).
Defaults to "merge" if no style specified. Includes unit tests for
success, default style, and error cases.

This is a prerequisite for #177 (merge PR button in UI) and #206
(POST /pulls merge handler).

Closes leeworks-agents/gitea-mobile#187

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
agent-company
2026-04-20 15:13:56 +00:00
parent baf829349c
commit 3d1775a37b
2 changed files with 112 additions and 0 deletions
+78
View File
@@ -1456,3 +1456,81 @@ func TestRetryDelay_ExponentialBackoff(t *testing.T) {
}
}
}
func TestMergePull(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
t.Errorf("expected POST, got %s", r.Method)
}
if r.URL.Path != "/api/v1/repos/owner1/repo1/pulls/5/merge" {
t.Errorf("unexpected path: %s", r.URL.Path)
}
if r.Header.Get("Authorization") != "token test-token" {
t.Error("missing or wrong Authorization header")
}
var body map[string]string
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
t.Fatalf("failed to decode body: %v", err)
}
if body["Do"] != "squash" {
t.Errorf("expected Do=squash, got %q", body["Do"])
}
w.WriteHeader(http.StatusOK)
}))
defer server.Close()
c := NewClient(server.URL)
c.setCache("pulls-org1", "should-be-invalidated")
err := c.MergePull(context.Background(), "test-token", "owner1", "repo1", 5, "squash", "", "")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
// Verify cache was invalidated.
_, ok := c.getFromCache("pulls-org1")
if ok {
t.Error("expected cache to be invalidated after MergePull")
}
}
func TestMergePull_DefaultStyle(t *testing.T) {
var receivedStyle string
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var body map[string]string
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()
c := NewClient(server.URL)
err := c.MergePull(context.Background(), "test-token", "owner1", "repo1", 5, "", "", "")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if receivedStyle != "merge" {
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)
}
}