package connector import ( "context" "encoding/json" "net/http" "net/http/httptest" "strings" "testing" "gitea.com/gitea/gitea-mcp/pkg/flag" "github.com/modelcontextprotocol/go-sdk/mcp" ) func withTestServer(t *testing.T, handler http.HandlerFunc) { t.Helper() server := httptest.NewServer(handler) t.Cleanup(server.Close) origHost, origToken := flag.Host, flag.Token flag.Host, flag.Token = server.URL, "" t.Cleanup(func() { flag.Host, flag.Token = origHost, origToken }) } func resultText(t *testing.T, result *mcp.CallToolResult) string { t.Helper() text, ok := result.Content[0].(*mcp.TextContent) if !ok { t.Fatalf("result content = %T, want *mcp.TextContent", result.Content[0]) } return text.Text } func TestSearchFnReturnsConciseRepoEntries(t *testing.T) { withTestServer(t, func(w http.ResponseWriter, r *http.Request) { if strings.HasSuffix(r.URL.Path, "/version") { _, _ = w.Write([]byte(`{"version":"1.26.0"}`)) return } if !strings.HasSuffix(r.URL.Path, "/repos/search") { t.Fatalf("unexpected request path %q", r.URL.Path) } if q := r.URL.Query().Get("q"); q != "gitea-mcp" { t.Fatalf("query q = %q, want gitea-mcp", q) } _, _ = w.Write([]byte(`{"data":[{"id":42,"name":"gitea-mcp","full_name":"gitea/gitea-mcp","html_url":"https://gitea.com/gitea/gitea-mcp","description":"MCP server"}]}`)) }) result, err := SearchFn(context.Background(), map[string]any{"query": "gitea-mcp"}) if err != nil { t.Fatalf("SearchFn() error = %v", err) } var entries []map[string]any if err := json.Unmarshal([]byte(resultText(t, result)), &entries); err != nil { t.Fatalf("unmarshal result: %v", err) } if len(entries) != 1 { t.Fatalf("len(entries) = %d, want 1", len(entries)) } entry := entries[0] if entry["id"] != float64(42) { t.Errorf("id = %v, want 42", entry["id"]) } if entry["name"] != "gitea-mcp" { t.Errorf("name = %v, want gitea-mcp", entry["name"]) } if entry["url"] != "https://gitea.com/gitea/gitea-mcp" { t.Errorf("url = %v, want the repo html_url", entry["url"]) } if entry["html_url"] != "https://gitea.com/gitea/gitea-mcp" { t.Errorf("html_url = %v, want the repo html_url", entry["html_url"]) } } func TestSearchFnRequiresQuery(t *testing.T) { result, err := SearchFn(context.Background(), map[string]any{}) if err != nil { t.Fatalf("SearchFn() error = %v", err) } if !result.IsError { t.Fatal("SearchFn() result.IsError = false, want true for a missing query") } } func TestSearchFnResolvesOwnerFilterToOwnerID(t *testing.T) { withTestServer(t, func(w http.ResponseWriter, r *http.Request) { switch { case strings.HasSuffix(r.URL.Path, "/version"): _, _ = w.Write([]byte(`{"version":"1.26.0"}`)) case strings.HasSuffix(r.URL.Path, "/users/octo"): _, _ = w.Write([]byte(`{"id":7,"login":"octo"}`)) case strings.HasSuffix(r.URL.Path, "/repos/search"): if uid := r.URL.Query().Get("uid"); uid != "7" { t.Fatalf("uid = %q, want 7", uid) } _, _ = w.Write([]byte(`{"data":[]}`)) default: t.Fatalf("unexpected request path %q", r.URL.Path) } }) if _, err := SearchFn(context.Background(), map[string]any{"query": "demo", "owner": "octo"}); err != nil { t.Fatalf("SearchFn() error = %v", err) } } func TestFetchFnReturnsDecodedContentAndMetadata(t *testing.T) { withTestServer(t, func(w http.ResponseWriter, r *http.Request) { if !strings.Contains(r.URL.Path, "/contents/") { t.Fatalf("unexpected request path %q", r.URL.Path) } _, _ = w.Write([]byte(`{"name":"README.md","path":"README.md","sha":"abc123","type":"file","size":11,"encoding":"base64","content":"aGVsbG8gd29ybGQ="}`)) }) result, err := FetchFn(context.Background(), map[string]any{ "owner": "octo", "repo": "demo", "path": "README.md", }) if err != nil { t.Fatalf("FetchFn() error = %v", err) } var got map[string]any if err := json.Unmarshal([]byte(resultText(t, result)), &got); err != nil { t.Fatalf("unmarshal result: %v", err) } if got["content"] != "hello world" { t.Errorf("content = %v, want decoded %q", got["content"], "hello world") } if got["path"] != "README.md" { t.Errorf("path = %v, want README.md", got["path"]) } if got["sha"] != "abc123" { t.Errorf("sha = %v, want abc123", got["sha"]) } if got["size"] != float64(11) { t.Errorf("size = %v, want 11", got["size"]) } if got["encoding"] != "base64" { t.Errorf("encoding = %v, want base64", got["encoding"]) } } func TestFetchFnRequiresPath(t *testing.T) { result, err := FetchFn(context.Background(), map[string]any{"owner": "octo", "repo": "demo"}) if err != nil { t.Fatalf("FetchFn() error = %v", err) } if !result.IsError { t.Fatal("FetchFn() result.IsError = false, want true for a missing path") } }