Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d0bcbd1ed0 | |||
| d34916c276 | |||
| d350500486 |
@@ -972,6 +972,40 @@ func (c *Client) SetIssueState(ctx context.Context, token, owner, repo string, i
|
||||
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.
|
||||
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)
|
||||
|
||||
@@ -1516,3 +1516,81 @@ func TestGetChangedFiles_Error(t *testing.T) {
|
||||
t.Errorf("error should contain status code 404, got: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,6 +56,7 @@ func (h *Handler) RegisterRoutes(mux *http.ServeMux) {
|
||||
mux.HandleFunc("GET /pulls/{owner}/{repo}/{index}", h.PullDetail)
|
||||
mux.HandleFunc("POST /pulls/{owner}/{repo}/{index}/review", h.SubmitReview)
|
||||
mux.HandleFunc("POST /pulls/{owner}/{repo}/{index}/state", h.SetPullState)
|
||||
mux.HandleFunc("POST /pulls/{owner}/{repo}/{index}/assignees", h.AssignIssue)
|
||||
|
||||
// Settings (handled separately for auth bypass).
|
||||
settingsHandler := &SettingsHandler{
|
||||
|
||||
@@ -726,6 +726,66 @@ func TestIntegration_AssignIssue_MissingAssignee(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// --- Issue #228: Integration tests for POST /pulls/{owner}/{repo}/{index}/assignees ---
|
||||
|
||||
func TestIntegration_AssignPull_Valid(t *testing.T) {
|
||||
h, srv := newTestHandlerWithMock(t)
|
||||
defer srv.Close()
|
||||
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("POST /pulls/{owner}/{repo}/{index}/assignees", h.AssignIssue)
|
||||
|
||||
form := url.Values{"assignee": {"user1"}}
|
||||
req := reqWithToken(http.MethodPost, "/pulls/test-org/repo1/1/assignees", form.Encode())
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
mux.ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusSeeOther {
|
||||
t.Errorf("status = %d, want %d", w.Code, http.StatusSeeOther)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIntegration_AssignPull_HTMX(t *testing.T) {
|
||||
h, srv := newTestHandlerWithMock(t)
|
||||
defer srv.Close()
|
||||
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("POST /pulls/{owner}/{repo}/{index}/assignees", h.AssignIssue)
|
||||
|
||||
form := url.Values{"assignee": {"user1"}}
|
||||
req := reqWithToken(http.MethodPost, "/pulls/test-org/repo1/1/assignees", form.Encode())
|
||||
req.Header.Set("HX-Request", "true")
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
mux.ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("status = %d, want %d", w.Code, http.StatusOK)
|
||||
}
|
||||
body := w.Body.String()
|
||||
if !contains(body, "Assigned to user1") {
|
||||
t.Errorf("expected 'Assigned to user1' in HTMX response, got: %s", body)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIntegration_AssignPull_MissingAssignee(t *testing.T) {
|
||||
h, srv := newTestHandlerWithMock(t)
|
||||
defer srv.Close()
|
||||
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("POST /pulls/{owner}/{repo}/{index}/assignees", h.AssignIssue)
|
||||
|
||||
req := reqWithToken(http.MethodPost, "/pulls/test-org/repo1/1/assignees", "")
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
mux.ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusBadRequest {
|
||||
t.Errorf("status = %d, want %d", w.Code, http.StatusBadRequest)
|
||||
}
|
||||
}
|
||||
|
||||
// --- Issue #118: Integration tests for CloseIssue and AddComment ---
|
||||
|
||||
func TestIntegration_CloseIssue(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user