feat(gitea): add issue mutation helpers
Some checks failed
Security Scan / security (push) Failing after 14s
Test / test (push) Has been cancelled

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Virgil 2026-04-02 07:08:43 +00:00
parent 5a561690be
commit 9a0a1f4435
3 changed files with 254 additions and 8 deletions

View file

@ -133,6 +133,41 @@ func (c *Client) CreateIssue(owner, repo string, opts gitea.CreateIssueOption) (
return issue, nil
}
// EditIssue edits an existing issue.
// Usage: EditIssue(...)
func (c *Client) EditIssue(owner, repo string, number int64, opts gitea.EditIssueOption) (*gitea.Issue, error) {
issue, _, err := c.api.EditIssue(owner, repo, number, opts)
if err != nil {
return nil, log.E("gitea.EditIssue", "failed to edit issue", err)
}
return issue, nil
}
// AssignIssue assigns an issue to the specified users.
// Usage: AssignIssue(...)
func (c *Client) AssignIssue(owner, repo string, number int64, assignees []string) error {
_, _, err := c.api.EditIssue(owner, repo, number, gitea.EditIssueOption{
Assignees: assignees,
})
if err != nil {
return log.E("gitea.AssignIssue", "failed to assign issue", err)
}
return nil
}
// CreateIssueComment posts a comment on an issue or pull request.
// Usage: CreateIssueComment(...)
func (c *Client) CreateIssueComment(owner, repo string, issue int64, body string) error {
_, _, err := c.api.CreateIssueComment(owner, repo, issue, gitea.CreateIssueCommentOption{
Body: body,
})
if err != nil {
return log.E("gitea.CreateIssueComment", "failed to create comment", err)
}
return nil
}
// ListIssueComments returns all comments for an issue.
// Usage: ListIssueComments(...)
func (c *Client) ListIssueComments(owner, repo string, number int64) ([]*gitea.Comment, error) {
@ -253,6 +288,52 @@ func (c *Client) ListIssueCommentsIter(owner, repo string, number int64) iter.Se
}
}
// GetIssueLabels returns the labels currently attached to an issue.
// Usage: GetIssueLabels(...)
func (c *Client) GetIssueLabels(owner, repo string, number int64) ([]*gitea.Label, error) {
labels, _, err := c.api.GetIssueLabels(owner, repo, number, gitea.ListLabelsOptions{})
if err != nil {
return nil, log.E("gitea.GetIssueLabels", "failed to get issue labels", err)
}
return labels, nil
}
// AddIssueLabels adds labels to an issue.
// Usage: AddIssueLabels(...)
func (c *Client) AddIssueLabels(owner, repo string, number int64, labelIDs []int64) error {
_, _, err := c.api.AddIssueLabels(owner, repo, number, gitea.IssueLabelsOption{
Labels: labelIDs,
})
if err != nil {
return log.E("gitea.AddIssueLabels", "failed to add labels to issue", err)
}
return nil
}
// RemoveIssueLabel removes a label from an issue.
// Usage: RemoveIssueLabel(...)
func (c *Client) RemoveIssueLabel(owner, repo string, number, labelID int64) error {
_, err := c.api.DeleteIssueLabel(owner, repo, number, labelID)
if err != nil {
return log.E("gitea.RemoveIssueLabel", "failed to remove label from issue", err)
}
return nil
}
// CloseIssue closes an issue by setting its state to closed.
// Usage: CloseIssue(...)
func (c *Client) CloseIssue(owner, repo string, number int64) error {
closed := gitea.StateClosed
_, _, err := c.api.EditIssue(owner, repo, number, gitea.EditIssueOption{
State: &closed,
})
if err != nil {
return log.E("gitea.CloseIssue", "failed to close issue", err)
}
return nil
}
// GetPullRequest returns a single pull request by number.
// Usage: GetPullRequest(...)
func (c *Client) GetPullRequest(owner, repo string, number int64) (*gitea.PullRequest, error) {

View file

@ -234,6 +234,132 @@ func TestClient_CreateIssue_Bad_ServerError_Good(t *testing.T) {
assert.Contains(t, err.Error(), "failed to create issue")
}
func TestClient_EditIssue_Good(t *testing.T) {
client, srv := newTestClient(t)
defer srv.Close()
issue, err := client.EditIssue("test-org", "org-repo", 1, giteaSDK.EditIssueOption{
Title: "Updated Title",
})
require.NoError(t, err)
assert.NotNil(t, issue)
}
func TestClient_EditIssue_Bad_ServerError_Good(t *testing.T) {
client, srv := newErrorServer(t)
defer srv.Close()
_, err := client.EditIssue("test-org", "org-repo", 1, giteaSDK.EditIssueOption{
Title: "Updated Title",
})
assert.Error(t, err)
assert.Contains(t, err.Error(), "failed to edit issue")
}
func TestClient_AssignIssue_Good(t *testing.T) {
client, srv := newTestClient(t)
defer srv.Close()
err := client.AssignIssue("test-org", "org-repo", 1, []string{"dev1", "dev2"})
require.NoError(t, err)
}
func TestClient_AssignIssue_Bad_ServerError_Good(t *testing.T) {
client, srv := newErrorServer(t)
defer srv.Close()
err := client.AssignIssue("test-org", "org-repo", 1, []string{"dev1"})
assert.Error(t, err)
assert.Contains(t, err.Error(), "failed to assign issue")
}
func TestClient_CreateIssueComment_Good(t *testing.T) {
client, srv := newTestClient(t)
defer srv.Close()
err := client.CreateIssueComment("test-org", "org-repo", 1, "LGTM")
require.NoError(t, err)
}
func TestClient_CreateIssueComment_Bad_ServerError_Good(t *testing.T) {
client, srv := newErrorServer(t)
defer srv.Close()
err := client.CreateIssueComment("test-org", "org-repo", 1, "LGTM")
assert.Error(t, err)
assert.Contains(t, err.Error(), "failed to create comment")
}
func TestClient_GetIssueLabels_Good(t *testing.T) {
client, srv := newTestClient(t)
defer srv.Close()
labels, err := client.GetIssueLabels("test-org", "org-repo", 1)
require.NoError(t, err)
require.Len(t, labels, 1)
assert.Equal(t, "bug", labels[0].Name)
}
func TestClient_GetIssueLabels_Bad_ServerError_Good(t *testing.T) {
client, srv := newErrorServer(t)
defer srv.Close()
_, err := client.GetIssueLabels("test-org", "org-repo", 1)
assert.Error(t, err)
assert.Contains(t, err.Error(), "failed to get issue labels")
}
func TestClient_AddIssueLabels_Good(t *testing.T) {
client, srv := newTestClient(t)
defer srv.Close()
err := client.AddIssueLabels("test-org", "org-repo", 1, []int64{1})
require.NoError(t, err)
}
func TestClient_AddIssueLabels_Bad_ServerError_Good(t *testing.T) {
client, srv := newErrorServer(t)
defer srv.Close()
err := client.AddIssueLabels("test-org", "org-repo", 1, []int64{1})
assert.Error(t, err)
assert.Contains(t, err.Error(), "failed to add labels to issue")
}
func TestClient_RemoveIssueLabel_Good(t *testing.T) {
client, srv := newTestClient(t)
defer srv.Close()
err := client.RemoveIssueLabel("test-org", "org-repo", 1, 1)
require.NoError(t, err)
}
func TestClient_RemoveIssueLabel_Bad_ServerError_Good(t *testing.T) {
client, srv := newErrorServer(t)
defer srv.Close()
err := client.RemoveIssueLabel("test-org", "org-repo", 1, 1)
assert.Error(t, err)
assert.Contains(t, err.Error(), "failed to remove label from issue")
}
func TestClient_CloseIssue_Good(t *testing.T) {
client, srv := newTestClient(t)
defer srv.Close()
err := client.CloseIssue("test-org", "org-repo", 1)
require.NoError(t, err)
}
func TestClient_CloseIssue_Bad_ServerError_Good(t *testing.T) {
client, srv := newErrorServer(t)
defer srv.Close()
err := client.CloseIssue("test-org", "org-repo", 1)
assert.Error(t, err)
assert.Contains(t, err.Error(), "failed to close issue")
}
func TestClient_ListPullRequests_Good(t *testing.T) {
client, srv := newTestClient(t)
defer srv.Close()

View file

@ -85,18 +85,57 @@ func newGiteaMux() *http.ServeMux {
// Single issue.
mux.HandleFunc("/api/v1/repos/test-org/org-repo/issues/1", func(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodPatch:
jsonResponse(w, map[string]any{
"id": 1, "number": 1, "title": "Issue 1", "state": "closed",
"body": "First issue body",
})
default:
jsonResponse(w, map[string]any{
"id": 1, "number": 1, "title": "Issue 1", "state": "open",
"body": "First issue body",
})
}
})
// Issue comments.
mux.HandleFunc("/api/v1/repos/test-org/org-repo/issues/1/comments", func(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodPost:
w.WriteHeader(http.StatusCreated)
jsonResponse(w, map[string]any{
"id": 100, "body": "test comment",
"user": map[string]any{"login": "test-user"},
})
default:
jsonResponse(w, []map[string]any{
{"id": 100, "body": "comment 1", "user": map[string]any{"login": "user1"}, "created_at": "2026-01-01T00:00:00Z", "updated_at": "2026-01-01T00:00:00Z"},
{"id": 101, "body": "comment 2", "user": map[string]any{"login": "user2"}, "created_at": "2026-01-02T00:00:00Z", "updated_at": "2026-01-02T00:00:00Z"},
})
}
})
// Issue labels.
mux.HandleFunc("/api/v1/repos/test-org/org-repo/issues/1/labels", func(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodPost:
jsonResponse(w, []map[string]any{
{"id": 1, "name": "bug", "color": "#ff0000"},
})
default:
jsonResponse(w, []map[string]any{
{"id": 1, "name": "bug", "color": "#ff0000"},
})
}
})
// Remove issue label.
mux.HandleFunc("/api/v1/repos/test-org/org-repo/issues/1/labels/1", func(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodDelete {
w.WriteHeader(http.StatusNoContent)
return
}
})
// Pull requests.