package vcs import ( "context" "encoding/json" "net/http" "net/http/httptest" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/yan1h/agent-coding-workflow/internal/infra/errs" ) func newTestProvider(t *testing.T, h http.Handler) *GiteaProvider { t.Helper() srv := httptest.NewServer(h) t.Cleanup(srv.Close) return NewGiteaProvider(srv.URL, "test-token", 5*time.Second) } func TestGitea_CreatePR(t *testing.T) { repo := RepoRef{Owner: "owner", Name: "repo"} mux := http.NewServeMux() mux.HandleFunc("/api/v1/repos/owner/repo/pulls", func(w http.ResponseWriter, r *http.Request) { assert.Equal(t, http.MethodPost, r.Method) assert.Equal(t, "token test-token", r.Header.Get("Authorization")) var body map[string]any require.NoError(t, json.NewDecoder(r.Body).Decode(&body)) assert.Equal(t, "feature", body["head"]) assert.Equal(t, "main", body["base"]) w.WriteHeader(http.StatusCreated) _ = json.NewEncoder(w).Encode(giteaPR{ Number: 42, State: "open", Mergeable: true, HTMLURL: "https://git.jerryyan.net/owner/repo/pulls/42", Head: giteaBranch{Sha: "abc123", Ref: "feature"}, }) }) mux.HandleFunc("/api/v1/repos/owner/repo/commits/abc123/status", func(w http.ResponseWriter, _ *http.Request) { _ = json.NewEncoder(w).Encode(giteaCombinedStatus{State: "success"}) }) p := newTestProvider(t, mux) pr, err := p.CreatePR(context.Background(), CreatePRInput{ Repo: repo, Head: "feature", Base: "main", Title: "T", Body: "B", }) require.NoError(t, err) assert.Equal(t, int64(42), pr.Number) assert.Equal(t, "open", pr.State) assert.Equal(t, "https://git.jerryyan.net/owner/repo/pulls/42", pr.HTMLURL) assert.Equal(t, "success", pr.CIState) } func TestGitea_GetPR_MergedAndCI(t *testing.T) { mux := http.NewServeMux() mux.HandleFunc("/api/v1/repos/owner/repo/pulls/7", func(w http.ResponseWriter, _ *http.Request) { _ = json.NewEncoder(w).Encode(giteaPR{ Number: 7, State: "closed", Merged: true, MergedCommitID: "deadbeef", HTMLURL: "https://git.jerryyan.net/owner/repo/pulls/7", Head: giteaBranch{Sha: "sha7"}, }) }) mux.HandleFunc("/api/v1/repos/owner/repo/commits/sha7/status", func(w http.ResponseWriter, _ *http.Request) { _ = json.NewEncoder(w).Encode(giteaCombinedStatus{State: "failure"}) }) p := newTestProvider(t, mux) pr, err := p.GetPR(context.Background(), RepoRef{Owner: "owner", Name: "repo"}, 7) require.NoError(t, err) assert.True(t, pr.Merged) assert.Equal(t, "merged", pr.State) assert.Equal(t, "deadbeef", pr.MergeCommitSHA) assert.Equal(t, "failure", pr.CIState) } func TestGitea_Merge(t *testing.T) { var mergeCalled bool mux := http.NewServeMux() mux.HandleFunc("/api/v1/repos/owner/repo/pulls/3/merge", func(w http.ResponseWriter, r *http.Request) { mergeCalled = true assert.Equal(t, http.MethodPost, r.Method) var body map[string]any require.NoError(t, json.NewDecoder(r.Body).Decode(&body)) assert.Equal(t, "squash", body["Do"]) w.WriteHeader(http.StatusOK) }) mux.HandleFunc("/api/v1/repos/owner/repo/pulls/3", func(w http.ResponseWriter, _ *http.Request) { _ = json.NewEncoder(w).Encode(giteaPR{Number: 3, State: "closed", Merged: true, MergedCommitID: "mergesha", Head: giteaBranch{Sha: "h3"}}) }) mux.HandleFunc("/api/v1/repos/owner/repo/commits/h3/status", func(w http.ResponseWriter, _ *http.Request) { _ = json.NewEncoder(w).Encode(giteaCombinedStatus{State: "success"}) }) p := newTestProvider(t, mux) sha, err := p.Merge(context.Background(), RepoRef{Owner: "owner", Name: "repo"}, 3, MergeInput{Method: "squash"}) require.NoError(t, err) assert.True(t, mergeCalled) assert.Equal(t, "mergesha", sha) } func TestGitea_Comment(t *testing.T) { var gotBody string mux := http.NewServeMux() mux.HandleFunc("/api/v1/repos/owner/repo/issues/5/comments", func(w http.ResponseWriter, r *http.Request) { var body map[string]any require.NoError(t, json.NewDecoder(r.Body).Decode(&body)) gotBody, _ = body["body"].(string) w.WriteHeader(http.StatusCreated) }) p := newTestProvider(t, mux) require.NoError(t, p.Comment(context.Background(), RepoRef{Owner: "owner", Name: "repo"}, 5, "hello")) assert.Equal(t, "hello", gotBody) } func TestGitea_ListIssues(t *testing.T) { mux := http.NewServeMux() mux.HandleFunc("/api/v1/repos/owner/repo/issues", func(w http.ResponseWriter, r *http.Request) { assert.Equal(t, "issues", r.URL.Query().Get("type")) _ = json.NewEncoder(w).Encode([]giteaIssue{ {Number: 1, Title: "bug", State: "open", HTMLURL: "u1"}, }) }) p := newTestProvider(t, mux) issues, err := p.ListIssues(context.Background(), RepoRef{Owner: "owner", Name: "repo"}, IssueListOptions{State: "open"}) require.NoError(t, err) require.Len(t, issues, 1) assert.Equal(t, "bug", issues[0].Title) } func TestGitea_Non2xxError(t *testing.T) { mux := http.NewServeMux() mux.HandleFunc("/api/v1/repos/owner/repo/pulls/9", func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusNotFound) _, _ = w.Write([]byte(`{"message":"not found"}`)) }) p := newTestProvider(t, mux) _, err := p.GetPR(context.Background(), RepoRef{Owner: "owner", Name: "repo"}, 9) require.Error(t, err) ae, ok := errs.As(err) require.True(t, ok) assert.Equal(t, errs.CodeNotFound, ae.Code) } func TestGitea_Host(t *testing.T) { p := NewGiteaProvider("https://git.jerryyan.net:3000", "", 0) assert.Equal(t, "git.jerryyan.net", p.Host()) }