mirror of
https://gitea.com/gitea/gitea-mcp.git
synced 2026-08-28 19:17:44 +00:00
feat(issue): add get_discussion_markdown to issue_read
Adds a new issue_read method that fetches an issue and its comments and renders them as a single Markdown document, instead of a JSON array. Formatting logic lives in a pure, unit-tested helper (formatDiscussionMarkdown) covering comment rendering, empty comment lists, and attachment-inlined bodies. Existing get/get_comments/ get_labels methods are unchanged. Co-Authored-By: Codet <codet@commitgo.dev> (GPT-5-Codex)
This commit is contained in:
@@ -266,6 +266,119 @@ func Test_getIssueByIndexFn_includesAttachments(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func Test_getIssueDiscussionMarkdownFn(t *testing.T) {
|
||||
const (
|
||||
owner = "octo"
|
||||
repo = "demo"
|
||||
)
|
||||
|
||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.URL.Path {
|
||||
case "/api/v1/version":
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_, _ = w.Write([]byte(`{"version":"1.12.0"}`))
|
||||
case fmt.Sprintf("/api/v1/repos/%s/%s/issues/42", owner, repo):
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_, _ = w.Write([]byte(`{
|
||||
"number": 42,
|
||||
"title": "bug with screenshot",
|
||||
"body": "see attached",
|
||||
"state": "open",
|
||||
"user": {"login": "octocat"},
|
||||
"labels": [{"name": "bug"}],
|
||||
"created_at": "2026-01-02T15:04:05Z",
|
||||
"assets": [
|
||||
{"id": 1, "name": "shot.png", "size": 1024, "browser_download_url": "https://example/shot.png"}
|
||||
]
|
||||
}`))
|
||||
case fmt.Sprintf("/api/v1/repos/%s/%s/issues/42/comments", owner, repo):
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_, _ = w.Write([]byte(`[
|
||||
{"id": 1, "body": "see this", "user": {"login": "reviewer"}, "created_at": "2026-01-03T09:00:00Z", "assets": [
|
||||
{"id": 9, "name": "log.txt", "size": 200, "browser_download_url": "https://example/log.txt"}
|
||||
]}
|
||||
]`))
|
||||
default:
|
||||
http.NotFound(w, r)
|
||||
}
|
||||
})
|
||||
server := httptest.NewServer(handler)
|
||||
defer server.Close()
|
||||
|
||||
origHost, origToken, origVersion := flag.Host, flag.Token, flag.Version
|
||||
flag.Host, flag.Token, flag.Version = server.URL, "", "test"
|
||||
defer func() { flag.Host, flag.Token, flag.Version = origHost, origToken, origVersion }()
|
||||
|
||||
args := map[string]any{
|
||||
"owner": owner, "repo": repo, "issue_number": float64(42),
|
||||
}
|
||||
res, err := getIssueDiscussionMarkdownFn(context.Background(), args)
|
||||
if err != nil {
|
||||
t.Fatalf("getIssueDiscussionMarkdownFn() error = %v", err)
|
||||
}
|
||||
if res.IsError {
|
||||
t.Fatalf("unexpected error result: %v", res.Content)
|
||||
}
|
||||
body := res.Content[0].(*mcp.TextContent).Text
|
||||
for _, want := range []string{
|
||||
"# bug with screenshot (#42)",
|
||||
"**Author:** octocat",
|
||||
"**State:** open",
|
||||
"**Labels:** bug",
|
||||
"[shot.png](https://example/shot.png)",
|
||||
"### reviewer on 2026-01-03T09:00:00Z",
|
||||
"[log.txt](https://example/log.txt)",
|
||||
} {
|
||||
if !strings.Contains(body, want) {
|
||||
t.Fatalf("expected markdown to contain %q, got:\n%s", want, body)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_getIssueDiscussionMarkdownFn_noComments(t *testing.T) {
|
||||
const (
|
||||
owner = "octo"
|
||||
repo = "demo"
|
||||
)
|
||||
|
||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.URL.Path {
|
||||
case "/api/v1/version":
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_, _ = w.Write([]byte(`{"version":"1.12.0"}`))
|
||||
case fmt.Sprintf("/api/v1/repos/%s/%s/issues/7", owner, repo):
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_, _ = w.Write([]byte(`{"number": 7, "title": "quiet issue", "state": "open", "user": {"login": "octocat"}}`))
|
||||
case fmt.Sprintf("/api/v1/repos/%s/%s/issues/7/comments", owner, repo):
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_, _ = w.Write([]byte(`[]`))
|
||||
default:
|
||||
http.NotFound(w, r)
|
||||
}
|
||||
})
|
||||
server := httptest.NewServer(handler)
|
||||
defer server.Close()
|
||||
|
||||
origHost, origToken, origVersion := flag.Host, flag.Token, flag.Version
|
||||
flag.Host, flag.Token, flag.Version = server.URL, "", "test"
|
||||
defer func() { flag.Host, flag.Token, flag.Version = origHost, origToken, origVersion }()
|
||||
|
||||
args := map[string]any{
|
||||
"owner": owner, "repo": repo, "issue_number": float64(7),
|
||||
}
|
||||
res, err := getIssueDiscussionMarkdownFn(context.Background(), args)
|
||||
if err != nil {
|
||||
t.Fatalf("getIssueDiscussionMarkdownFn() error = %v", err)
|
||||
}
|
||||
if res.IsError {
|
||||
t.Fatalf("unexpected error result: %v", res.Content)
|
||||
}
|
||||
body := res.Content[0].(*mcp.TextContent).Text
|
||||
if !strings.Contains(body, "_No comments yet._") {
|
||||
t.Fatalf("expected placeholder for no comments, got:\n%s", body)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_getIssueCommentsByIndexFn_includesAttachments(t *testing.T) {
|
||||
const (
|
||||
owner = "octo"
|
||||
|
||||
Reference in New Issue
Block a user