From becb925456f5af79cea9d10b51249591226f5a5a Mon Sep 17 00:00:00 2001 From: agent-company Date: Sat, 28 Mar 2026 13:08:23 +0000 Subject: [PATCH] test: add unit tests for SubmitReview and ApplyLabel client methods Add four test functions using httptest.NewServer: - TestApplyLabel: verifies POST request path, auth header, label IDs in body, and cache invalidation after success - TestApplyLabel_Error: verifies 404 error propagation - TestSubmitReview: verifies POST path, event/body fields, and cache invalidation after success - TestSubmitReview_Error: verifies 422 error propagation Closes leeworks-agents/gitea-mobile#127 Co-Authored-By: Claude Opus 4.6 (1M context) --- internal/gitea/client_test.go | 135 ++++++++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) diff --git a/internal/gitea/client_test.go b/internal/gitea/client_test.go index a54f320..7e357ce 100644 --- a/internal/gitea/client_test.go +++ b/internal/gitea/client_test.go @@ -6,6 +6,7 @@ import ( "fmt" "net/http" "net/http/httptest" + "strings" "testing" "time" ) @@ -893,6 +894,140 @@ func TestListAllPullRequests_StateFilter(t *testing.T) { } } +// --- Issue #127: Tests for ApplyLabel and SubmitReview --- + +func TestApplyLabel(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/issues/42/labels" { + 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]interface{} + if err := json.NewDecoder(r.Body).Decode(&body); err != nil { + t.Fatalf("failed to decode body: %v", err) + } + labels, ok := body["labels"].([]interface{}) + if !ok { + t.Fatalf("expected labels array, got %T", body["labels"]) + } + if len(labels) != 2 { + t.Errorf("expected 2 label IDs, got %d", len(labels)) + } + // Verify the label IDs are correct (JSON numbers are float64). + if labels[0].(float64) != 10 || labels[1].(float64) != 20 { + t.Errorf("expected label IDs [10, 20], got %v", labels) + } + + w.WriteHeader(http.StatusOK) + json.NewEncoder(w).Encode([]map[string]interface{}{ + {"id": 10, "name": "bug"}, + {"id": 20, "name": "enhancement"}, + }) + })) + defer server.Close() + + c := NewClient(server.URL) + c.setCache("issues-org1", "should-be-invalidated") + + err := c.ApplyLabel(context.Background(), "test-token", "owner1", "repo1", 42, []int64{10, 20}) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + // Verify cache was invalidated. + _, ok := c.getFromCache("issues-org1") + if ok { + t.Error("expected cache to be invalidated after ApplyLabel") + } +} + +func TestApplyLabel_Error(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotFound) + fmt.Fprintln(w, `{"message":"issue not found"}`) + })) + defer server.Close() + + c := NewClient(server.URL) + err := c.ApplyLabel(context.Background(), "test-token", "owner1", "repo1", 999, []int64{10}) + if err == nil { + t.Fatal("expected error for 404 response, got nil") + } + if !strings.Contains(err.Error(), "404") { + t.Errorf("error should contain status code 404, got: %v", err) + } +} + +func TestSubmitReview(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/7/reviews" { + 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["event"] != "APPROVED" { + t.Errorf("expected event=APPROVED, got %q", body["event"]) + } + if body["body"] != "Looks good!" { + t.Errorf("expected body='Looks good!', got %q", body["body"]) + } + + w.WriteHeader(http.StatusOK) + json.NewEncoder(w).Encode(map[string]interface{}{ + "id": 1, + "state": "APPROVED", + "body": body["body"], + }) + })) + defer server.Close() + + c := NewClient(server.URL) + c.setCache("pulls-org1", "should-be-invalidated") + + err := c.SubmitReview(context.Background(), "test-token", "owner1", "repo1", 7, "APPROVED", "Looks good!") + 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 SubmitReview") + } +} + +func TestSubmitReview_Error(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusUnprocessableEntity) + fmt.Fprintln(w, `{"message":"validation failed"}`) + })) + defer server.Close() + + c := NewClient(server.URL) + err := c.SubmitReview(context.Background(), "test-token", "owner1", "repo1", 7, "INVALID", "") + if err == nil { + t.Fatal("expected error for 422 response, got nil") + } + if !strings.Contains(err.Error(), "422") { + t.Errorf("error should contain status code 422, got: %v", err) + } +} + func TestListAllPullRequests_Pagination(t *testing.T) { now := time.Date(2026, 3, 28, 12, 0, 0, 0, time.UTC)