Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions pkg/github/repository_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,13 +138,14 @@ func RepositoryResourceContentsHandler(getClient GetClientFn, getRawClient raw.G
}

resp, err := rawClient.GetRawContent(ctx, owner, repo, path, rawOpts)
if err != nil {
return nil, fmt.Errorf("failed to get raw content: %w", err)
}
defer func() {
_ = resp.Body.Close()
}()
// If the raw content is not found, we will fall back to the GitHub API (in case it is a directory)
switch {
case err != nil:
return nil, fmt.Errorf("failed to get raw content: %w", err)
case resp.StatusCode == http.StatusOK:
ext := filepath.Ext(path)
mimeType := resp.Header.Get("Content-Type")
Expand Down
41 changes: 41 additions & 0 deletions pkg/github/repository_resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package github

import (
"context"
"errors"
"net/http"
"net/url"
"testing"
Expand All @@ -14,6 +15,15 @@ import (
"github.com/stretchr/testify/require"
)

// errorTransport is a http.RoundTripper that always returns an error.
type errorTransport struct {
err error
}

func (t *errorTransport) RoundTrip(*http.Request) (*http.Response, error) {
return nil, t.err
}

func Test_repositoryResourceContentsHandler(t *testing.T) {
base, _ := url.Parse("https://raw.example.com/")
tests := []struct {
Expand Down Expand Up @@ -256,6 +266,37 @@ func Test_repositoryResourceContentsHandler(t *testing.T) {
}
}

// Test_repositoryResourceContentsHandler_NetworkError tests that a network error
// during raw content fetch does not cause a panic (nil response body dereference).
func Test_repositoryResourceContentsHandler_NetworkError(t *testing.T) {
base, _ := url.Parse("https://raw.example.com/")
networkErr := errors.New("network error: connection refused")

httpClient := &http.Client{Transport: &errorTransport{err: networkErr}}
client := github.NewClient(httpClient)
mockRawClient := raw.NewClient(client, base)
handler := RepositoryResourceContentsHandler(stubGetClientFn(client), stubGetRawClientFn(mockRawClient))

request := mcp.ReadResourceRequest{
Params: struct {
URI string `json:"uri"`
Arguments map[string]any `json:"arguments,omitempty"`
}{
Arguments: map[string]any{
"owner": []string{"owner"},
"repo": []string{"repo"},
"path": []string{"README.md"},
},
},
}

// This should not panic, even though the HTTP client returns an error
resp, err := handler(context.TODO(), request)
require.Error(t, err)
require.Nil(t, resp)
require.ErrorContains(t, err, "failed to get raw content")
}

func Test_GetRepositoryResourceContent(t *testing.T) {
mockRawClient := raw.NewClient(github.NewClient(nil), &url.URL{})
tmpl, _ := GetRepositoryResourceContent(nil, stubGetRawClientFn(mockRawClient), translations.NullTranslationHelper)
Expand Down