feat(forge): add PR review iterator
Some checks failed
Security Scan / security (push) Failing after 11s
Test / test (push) Failing after 51s

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Virgil 2026-04-01 08:18:36 +00:00
parent 94c5870c46
commit b94caf0a9d
2 changed files with 77 additions and 0 deletions

View file

@ -4,6 +4,7 @@ package forge
import ( import (
"bytes" "bytes"
"iter"
fmt "dappco.re/go/core/scm/internal/ax/fmtx" fmt "dappco.re/go/core/scm/internal/ax/fmtx"
json "dappco.re/go/core/scm/internal/ax/jsonx" json "dappco.re/go/core/scm/internal/ax/jsonx"
"net/http" "net/http"
@ -109,6 +110,33 @@ func (c *Client) ListPRReviews(owner, repo string, index int64) ([]*forgejo.Pull
return all, nil return all, nil
} }
// ListPRReviewsIter returns an iterator over reviews for a pull request.
// Usage: ListPRReviewsIter(...)
func (c *Client) ListPRReviewsIter(owner, repo string, index int64) iter.Seq2[*forgejo.PullReview, error] {
return func(yield func(*forgejo.PullReview, error) bool) {
page := 1
for {
reviews, resp, err := c.api.ListPullReviews(owner, repo, index, forgejo.ListPullReviewsOptions{
ListOptions: forgejo.ListOptions{Page: page, PageSize: 50},
})
if err != nil {
yield(nil, log.E("forge.ListPRReviews", "failed to list reviews", err))
return
}
for _, review := range reviews {
if !yield(review, nil) {
return
}
}
if resp == nil || page >= resp.LastPage {
break
}
page++
}
}
}
// GetCombinedStatus returns the combined commit status for a ref (SHA or branch). // GetCombinedStatus returns the combined commit status for a ref (SHA or branch).
// Usage: GetCombinedStatus(...) // Usage: GetCombinedStatus(...)
func (c *Client) GetCombinedStatus(owner, repo string, ref string) (*forgejo.CombinedStatus, error) { func (c *Client) GetCombinedStatus(owner, repo string, ref string) (*forgejo.CombinedStatus, error) {

View file

@ -60,6 +60,42 @@ func TestClient_ListPRReviews_Good(t *testing.T) {
require.Len(t, reviews, 1) require.Len(t, reviews, 1)
} }
func TestClient_ListPRReviewsIter_Good_Paginates_Good(t *testing.T) {
mux := http.NewServeMux()
mux.HandleFunc("/api/v1/version", func(w http.ResponseWriter, r *http.Request) {
jsonResponse(w, map[string]string{"version": "1.21.0"})
})
mux.HandleFunc("/api/v1/repos/test-org/org-repo/pulls/1/reviews", func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Query().Get("page") {
case "2":
jsonResponse(w, []map[string]any{
{"id": 2, "state": "REQUEST_CHANGES", "user": map[string]any{"login": "reviewer2"}},
})
case "3":
jsonResponse(w, []map[string]any{})
default:
w.Header().Set("Link", "<http://"+r.Host+"/api/v1/repos/test-org/org-repo/pulls/1/reviews?page=2>; rel=\"next\", <http://"+r.Host+"/api/v1/repos/test-org/org-repo/pulls/1/reviews?page=2>; rel=\"last\"")
jsonResponse(w, []map[string]any{
{"id": 1, "state": "APPROVED", "user": map[string]any{"login": "reviewer1"}},
})
}
})
srv := httptest.NewServer(mux)
defer srv.Close()
client, err := New(srv.URL, "test-token")
require.NoError(t, err)
var states []string
for review, err := range client.ListPRReviewsIter("test-org", "org-repo", 1) {
require.NoError(t, err)
states = append(states, review.State)
}
require.Equal(t, []string{"APPROVED", "REQUEST_CHANGES"}, states)
}
func TestClient_ListPRReviews_Bad_ServerError_Good(t *testing.T) { func TestClient_ListPRReviews_Bad_ServerError_Good(t *testing.T) {
client, srv := newErrorServer(t) client, srv := newErrorServer(t)
defer srv.Close() defer srv.Close()
@ -69,6 +105,19 @@ func TestClient_ListPRReviews_Bad_ServerError_Good(t *testing.T) {
assert.Contains(t, err.Error(), "failed to list reviews") assert.Contains(t, err.Error(), "failed to list reviews")
} }
func TestClient_ListPRReviewsIter_Bad_ServerError_Good(t *testing.T) {
client, srv := newErrorServer(t)
defer srv.Close()
var got bool
for _, err := range client.ListPRReviewsIter("test-org", "org-repo", 1) {
assert.Error(t, err)
got = true
}
assert.True(t, got)
}
func TestClient_GetCombinedStatus_Good(t *testing.T) { func TestClient_GetCombinedStatus_Good(t *testing.T) {
client, srv := newTestClient(t) client, srv := newTestClient(t)
defer srv.Close() defer srv.Close()