Files
github--github-mcp-server/pkg/github/repository_resource_test.go
Adam Holt 60aef5d2e3 Convert to modelcontextprotocol/go-sdk (#1428)
Move from `mark3labs/mcp-go` to `modelcontextprotocol/go-sdk`.

This is mostly focused on updating tool schema and tool handler signatures, along with any associated internal changes related to those changes.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: omgitsads <4619+omgitsads@users.noreply.github.com>
Co-authored-by: LuluBeatson <lulubeatson@github.com>
Co-authored-by: Lulu <59149422+LuluBeatson@users.noreply.github.com>
Co-authored-by: SamMorrowDrums <4811358+SamMorrowDrums@users.noreply.github.com>
Co-authored-by: Sam Morrow <info@sam-morrow.com>
2025-12-01 14:30:59 +01:00

315 lines
12 KiB
Go

package github
import (
"context"
"net/http"
"net/url"
"testing"
"github.com/github/github-mcp-server/pkg/raw"
"github.com/github/github-mcp-server/pkg/translations"
"github.com/google/go-github/v79/github"
"github.com/migueleliasweb/go-github-mock/src/mock"
"github.com/modelcontextprotocol/go-sdk/mcp"
"github.com/stretchr/testify/require"
)
type resourceResponseType int
const (
resourceResponseTypeUnknown resourceResponseType = iota
resourceResponseTypeBlob
resourceResponseTypeText
)
func Test_repositoryResourceContents(t *testing.T) {
base, _ := url.Parse("https://raw.example.com/")
tests := []struct {
name string
mockedClient *http.Client
uri string
handlerFn func(getClient GetClientFn, getRawClient raw.GetRawClientFn, t translations.TranslationHelperFunc) mcp.ResourceHandler
expectedResponseType resourceResponseType
expectError string
expectedResult *mcp.ReadResourceResult
}{
{
name: "missing owner",
mockedClient: mock.NewMockedHTTPClient(
mock.WithRequestMatchHandler(
raw.GetRawReposContentsByOwnerByRepoByPath,
http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "text/markdown")
_, err := w.Write([]byte("# Test Repository\n\nThis is a test repository."))
require.NoError(t, err)
}),
),
),
uri: "repo:///repo/contents/README.md",
handlerFn: func(getClient GetClientFn, getRawClient raw.GetRawClientFn, t translations.TranslationHelperFunc) mcp.ResourceHandler {
_, handler := GetRepositoryResourceContent(getClient, getRawClient, t)
return handler
},
expectedResponseType: resourceResponseTypeText, // Ignored as error is expected
expectError: "owner is required",
},
{
name: "missing repo",
mockedClient: mock.NewMockedHTTPClient(
mock.WithRequestMatchHandler(
raw.GetRawReposContentsByOwnerByRepoByBranchByPath,
http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "text/markdown")
_, err := w.Write([]byte("# Test Repository\n\nThis is a test repository."))
require.NoError(t, err)
}),
),
),
uri: "repo://owner//refs/heads/main/contents/README.md",
handlerFn: func(getClient GetClientFn, getRawClient raw.GetRawClientFn, t translations.TranslationHelperFunc) mcp.ResourceHandler {
_, handler := GetRepositoryResourceBranchContent(getClient, getRawClient, t)
return handler
},
expectedResponseType: resourceResponseTypeText, // Ignored as error is expected
expectError: "repo is required",
},
{
name: "successful blob content fetch",
mockedClient: mock.NewMockedHTTPClient(
mock.WithRequestMatchHandler(
raw.GetRawReposContentsByOwnerByRepoByPath,
http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "image/png")
_, err := w.Write([]byte("# Test Repository\n\nThis is a test repository."))
require.NoError(t, err)
}),
),
),
uri: "repo://owner/repo/contents/data.png",
handlerFn: func(getClient GetClientFn, getRawClient raw.GetRawClientFn, t translations.TranslationHelperFunc) mcp.ResourceHandler {
_, handler := GetRepositoryResourceContent(getClient, getRawClient, t)
return handler
},
expectedResponseType: resourceResponseTypeBlob,
expectedResult: &mcp.ReadResourceResult{
Contents: []*mcp.ResourceContents{{
Blob: []byte("IyBUZXN0IFJlcG9zaXRvcnkKClRoaXMgaXMgYSB0ZXN0IHJlcG9zaXRvcnku"),
MIMEType: "image/png",
URI: "",
}}},
},
{
name: "successful text content fetch (HEAD)",
mockedClient: mock.NewMockedHTTPClient(
mock.WithRequestMatchHandler(
raw.GetRawReposContentsByOwnerByRepoByPath,
http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "text/markdown")
_, err := w.Write([]byte("# Test Repository\n\nThis is a test repository."))
require.NoError(t, err)
}),
),
),
uri: "repo://owner/repo/contents/README.md",
handlerFn: func(getClient GetClientFn, getRawClient raw.GetRawClientFn, t translations.TranslationHelperFunc) mcp.ResourceHandler {
_, handler := GetRepositoryResourceContent(getClient, getRawClient, t)
return handler
},
expectedResponseType: resourceResponseTypeText,
expectedResult: &mcp.ReadResourceResult{
Contents: []*mcp.ResourceContents{{
Text: "# Test Repository\n\nThis is a test repository.",
MIMEType: "text/markdown",
URI: "",
}}},
},
{
name: "successful text content fetch (HEAD)",
mockedClient: mock.NewMockedHTTPClient(
mock.WithRequestMatchHandler(
raw.GetRawReposContentsByOwnerByRepoByPath,
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/plain")
require.Contains(t, r.URL.Path, "pkg/github/actions.go")
_, err := w.Write([]byte("package actions\n\nfunc main() {\n // Sample Go file content\n}\n"))
require.NoError(t, err)
}),
),
),
uri: "repo://owner/repo/contents/pkg/github/actions.go",
handlerFn: func(getClient GetClientFn, getRawClient raw.GetRawClientFn, t translations.TranslationHelperFunc) mcp.ResourceHandler {
_, handler := GetRepositoryResourceContent(getClient, getRawClient, t)
return handler
},
expectedResponseType: resourceResponseTypeText,
expectedResult: &mcp.ReadResourceResult{
Contents: []*mcp.ResourceContents{{
Text: "package actions\n\nfunc main() {\n // Sample Go file content\n}\n",
MIMEType: "text/plain",
URI: "",
}}},
},
{
name: "successful text content fetch (branch)",
mockedClient: mock.NewMockedHTTPClient(
mock.WithRequestMatchHandler(
raw.GetRawReposContentsByOwnerByRepoByBranchByPath,
http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "text/markdown")
_, err := w.Write([]byte("# Test Repository\n\nThis is a test repository."))
require.NoError(t, err)
}),
),
),
uri: "repo://owner/repo/refs/heads/main/contents/README.md",
handlerFn: func(getClient GetClientFn, getRawClient raw.GetRawClientFn, t translations.TranslationHelperFunc) mcp.ResourceHandler {
_, handler := GetRepositoryResourceBranchContent(getClient, getRawClient, t)
return handler
},
expectedResponseType: resourceResponseTypeText,
expectedResult: &mcp.ReadResourceResult{
Contents: []*mcp.ResourceContents{{
Text: "# Test Repository\n\nThis is a test repository.",
MIMEType: "text/markdown",
URI: "",
}}},
},
{
name: "successful text content fetch (tag)",
mockedClient: mock.NewMockedHTTPClient(
mock.WithRequestMatchHandler(
raw.GetRawReposContentsByOwnerByRepoByTagByPath,
http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "text/markdown")
_, err := w.Write([]byte("# Test Repository\n\nThis is a test repository."))
require.NoError(t, err)
}),
),
),
uri: "repo://owner/repo/refs/tags/v1.0.0/contents/README.md",
handlerFn: func(getClient GetClientFn, getRawClient raw.GetRawClientFn, t translations.TranslationHelperFunc) mcp.ResourceHandler {
_, handler := GetRepositoryResourceTagContent(getClient, getRawClient, t)
return handler
},
expectedResponseType: resourceResponseTypeText,
expectedResult: &mcp.ReadResourceResult{
Contents: []*mcp.ResourceContents{{
Text: "# Test Repository\n\nThis is a test repository.",
MIMEType: "text/markdown",
URI: "",
}}},
},
{
name: "successful text content fetch (sha)",
mockedClient: mock.NewMockedHTTPClient(
mock.WithRequestMatchHandler(
raw.GetRawReposContentsByOwnerByRepoBySHAByPath,
http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "text/markdown")
_, err := w.Write([]byte("# Test Repository\n\nThis is a test repository."))
require.NoError(t, err)
}),
),
),
uri: "repo://owner/repo/sha/abc123/contents/README.md",
handlerFn: func(getClient GetClientFn, getRawClient raw.GetRawClientFn, t translations.TranslationHelperFunc) mcp.ResourceHandler {
_, handler := GetRepositoryResourceCommitContent(getClient, getRawClient, t)
return handler
},
expectedResponseType: resourceResponseTypeText,
expectedResult: &mcp.ReadResourceResult{
Contents: []*mcp.ResourceContents{{
Text: "# Test Repository\n\nThis is a test repository.",
MIMEType: "text/markdown",
URI: "",
}}},
},
{
name: "successful text content fetch (pr)",
mockedClient: mock.NewMockedHTTPClient(
mock.WithRequestMatchHandler(
mock.GetReposPullsByOwnerByRepoByPullNumber,
http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, err := w.Write([]byte(`{"head": {"sha": "abc123"}}`))
require.NoError(t, err)
}),
),
mock.WithRequestMatchHandler(
raw.GetRawReposContentsByOwnerByRepoBySHAByPath,
http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "text/markdown")
_, err := w.Write([]byte("# Test Repository\n\nThis is a test repository."))
require.NoError(t, err)
}),
),
),
uri: "repo://owner/repo/refs/pull/42/head/contents/README.md",
handlerFn: func(getClient GetClientFn, getRawClient raw.GetRawClientFn, t translations.TranslationHelperFunc) mcp.ResourceHandler {
_, handler := GetRepositoryResourcePrContent(getClient, getRawClient, t)
return handler
},
expectedResponseType: resourceResponseTypeText,
expectedResult: &mcp.ReadResourceResult{
Contents: []*mcp.ResourceContents{{
Text: "# Test Repository\n\nThis is a test repository.",
MIMEType: "text/markdown",
URI: "",
}}},
},
{
name: "content fetch fails",
mockedClient: mock.NewMockedHTTPClient(
mock.WithRequestMatchHandler(
mock.GetReposContentsByOwnerByRepoByPath,
http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusNotFound)
_, _ = w.Write([]byte(`{"message": "Not Found"}`))
}),
),
),
uri: "repo://owner/repo/contents/nonexistent.md",
handlerFn: func(getClient GetClientFn, getRawClient raw.GetRawClientFn, t translations.TranslationHelperFunc) mcp.ResourceHandler {
_, handler := GetRepositoryResourceContent(getClient, getRawClient, t)
return handler
},
expectedResponseType: resourceResponseTypeText, // Ignored as error is expected
expectError: "404 Not Found",
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
client := github.NewClient(tc.mockedClient)
mockRawClient := raw.NewClient(client, base)
handler := tc.handlerFn(stubGetClientFn(client), stubGetRawClientFn(mockRawClient), translations.NullTranslationHelper)
request := &mcp.ReadResourceRequest{
Params: &mcp.ReadResourceParams{
URI: tc.uri,
},
}
resp, err := handler(context.TODO(), request)
if tc.expectError != "" {
require.ErrorContains(t, err, tc.expectError)
return
}
require.NoError(t, err)
content := resp.Contents[0]
switch tc.expectedResponseType {
case resourceResponseTypeBlob:
require.Equal(t, tc.expectedResult.Contents[0].Blob, content.Blob)
case resourceResponseTypeText:
require.Equal(t, tc.expectedResult.Contents[0].Text, content.Text)
default:
t.Fatalf("unknown expectedResponseType %v", tc.expectedResponseType)
}
})
}
}