woodpecker-mcp/internal/woodpecker/client_test.go
2026-08-08 01:37:47 +03:00

150 lines
4.3 KiB
Go

package woodpecker
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"strings"
"testing"
)
func TestNewClientValidation(t *testing.T) {
if _, err := NewClient("", "tok"); err == nil {
t.Error("expected error for empty base URL")
}
if _, err := NewClient("https://example.com/api", ""); err == nil {
t.Error("expected error for empty token")
}
}
func TestNewClientBaseURL(t *testing.T) {
c, err := NewClient("https://woodpecker.example.com/api/", "tok")
if err != nil {
t.Fatal(err)
}
if c.baseURL != "https://woodpecker.example.com/api" {
t.Errorf("baseURL = %q, want trailing slash trimmed", c.baseURL)
}
}
func TestGetSuccess(t *testing.T) {
var gotPath, gotAuth string
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
gotPath = r.URL.Path
gotAuth = r.Header.Get("Authorization")
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"id":42,"name":"demo","owner":"me","full_name":"me/demo","private":true,"trusted":false,"active":true,"forge_id":1}`))
}))
defer srv.Close()
c, err := NewClient(srv.URL+"/api", "secret", WithHTTPClient(srv.Client()))
if err != nil {
t.Fatal(err)
}
var repo Repo
if err := c.Get(context.Background(), "/repos/42", &repo); err != nil {
t.Fatal(err)
}
if gotPath != "/api/repos/42" {
t.Errorf("path = %q", gotPath)
}
if gotAuth != "Bearer secret" {
t.Errorf("auth header = %q", gotAuth)
}
if repo.ID != 42 || repo.FullName != "me/demo" || !repo.Private {
t.Errorf("unexpected repo: %+v", repo)
}
}
func TestGetEmptyBodySkipsDecode(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusOK)
}))
defer srv.Close()
c, _ := NewClient(srv.URL+"/api", "t", WithHTTPClient(srv.Client()))
var out Repo
if err := c.Get(context.Background(), "/repos/1", &out); err != nil {
t.Errorf("empty body should not fail: %v", err)
}
}
func TestGetErrorStatusAndMessage(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusNotFound)
_, _ = w.Write([]byte(`{"error":"repository not found"}`))
}))
defer srv.Close()
c, _ := NewClient(srv.URL+"/api", "t", WithHTTPClient(srv.Client()))
err := c.Get(context.Background(), "/repos/999", &struct{}{})
apiErr, ok := err.(*APIError)
if !ok {
t.Fatalf("expected *APIError, got %T: %v", err, err)
}
if apiErr.Status != 404 {
t.Errorf("status = %d, want 404", apiErr.Status)
}
if apiErr.Message != "repository not found" {
t.Errorf("message = %q", apiErr.Message)
}
}
func TestGetNetworkErrorBecomes500(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(http.ResponseWriter, *http.Request) {}))
url := srv.URL
srv.Close() // connections will fail now
c, _ := NewClient(url+"/api", "t", WithHTTPClient(srv.Client()))
err := c.Get(context.Background(), "/repos/1", &struct{}{})
apiErr, ok := err.(*APIError)
if !ok {
t.Fatalf("expected *APIError, got %T: %v", err, err)
}
if apiErr.Status != 500 {
t.Errorf("status = %d, want 500 for transport error", apiErr.Status)
}
if apiErr.Message == "" {
t.Error("message must carry the transport error detail")
}
}
func TestGetInvalidJSONBecomes500(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
_, _ = w.Write([]byte(`{not json`))
}))
defer srv.Close()
c, _ := NewClient(srv.URL+"/api", "t", WithHTTPClient(srv.Client()))
err := c.Get(context.Background(), "/repos/1", &Repo{})
apiErr, ok := err.(*APIError)
if !ok {
t.Fatalf("expected *APIError, got %T: %v", err, err)
}
if apiErr.Status != 500 {
t.Errorf("status = %d, want 500", apiErr.Status)
}
}
func TestErrorTypeUnmarshalsWithPointers(t *testing.T) {
var p Pipeline
payload := `{"id":1,"number":5,"status":"running","event":"push","commit":"abc","branch":"main","ref":"refs/heads/main","created_at":10,"updated_at":11,"started_at":null,"message":null}`
if err := json.Unmarshal([]byte(payload), &p); err != nil {
t.Fatal(err)
}
if p.Message != nil {
t.Error("message should stay nil for explicit null")
}
if p.StartedAt != nil {
t.Error("started_at should stay nil for explicit null")
}
if p.Branch != "main" {
t.Errorf("branch = %q", p.Branch)
}
if !strings.HasPrefix(payload, "{") {
t.Fatal("sanity")
}
}