647 lines
19 KiB
Go
647 lines
19 KiB
Go
package github
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"net/http"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/github/github-mcp-server/internal/toolsnaps"
|
|
"github.com/github/github-mcp-server/pkg/translations"
|
|
"github.com/google/go-github/v89/github"
|
|
"github.com/google/jsonschema-go/jsonschema"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func Test_ListGists(t *testing.T) {
|
|
// Verify tool definition
|
|
serverTool := ListGists(translations.NullTranslationHelper)
|
|
tool := serverTool.Tool
|
|
|
|
require.NoError(t, toolsnaps.Test(tool.Name, tool))
|
|
|
|
assert.Equal(t, "list_gists", tool.Name)
|
|
assert.NotEmpty(t, tool.Description)
|
|
assert.True(t, tool.Annotations.ReadOnlyHint, "list_gists tool should be read-only")
|
|
|
|
schema, ok := tool.InputSchema.(*jsonschema.Schema)
|
|
require.True(t, ok, "InputSchema should be *jsonschema.Schema")
|
|
assert.Contains(t, schema.Properties, "username")
|
|
assert.Contains(t, schema.Properties, "since")
|
|
assert.Contains(t, schema.Properties, "page")
|
|
assert.Contains(t, schema.Properties, "perPage")
|
|
assert.Empty(t, schema.Required)
|
|
|
|
// Setup mock gists for success case
|
|
mockGists := []*github.Gist{
|
|
{
|
|
ID: github.Ptr("gist1"),
|
|
Description: github.Ptr("First Gist"),
|
|
HTMLURL: github.Ptr("https://gist.github.com/user/gist1"),
|
|
Public: github.Ptr(true),
|
|
CreatedAt: &github.Timestamp{Time: time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC)},
|
|
Owner: &github.User{Login: github.Ptr("user")},
|
|
Files: map[github.GistFilename]github.GistFile{
|
|
"file1.txt": {
|
|
Filename: github.Ptr("file1.txt"),
|
|
Content: github.Ptr("content of file 1"),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
ID: github.Ptr("gist2"),
|
|
Description: github.Ptr("Second Gist"),
|
|
HTMLURL: github.Ptr("https://gist.github.com/testuser/gist2"),
|
|
Public: github.Ptr(false),
|
|
CreatedAt: &github.Timestamp{Time: time.Date(2023, 2, 1, 0, 0, 0, 0, time.UTC)},
|
|
Owner: &github.User{Login: github.Ptr("testuser")},
|
|
Files: map[github.GistFilename]github.GistFile{
|
|
"file2.js": {
|
|
Filename: github.Ptr("file2.js"),
|
|
Content: github.Ptr("console.log('hello');"),
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
mockedClient *http.Client
|
|
requestArgs map[string]any
|
|
expectError bool
|
|
expectedGists []*github.Gist
|
|
expectedErrMsg string
|
|
}{
|
|
{
|
|
name: "list authenticated user's gists",
|
|
mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{
|
|
GetGists: mockResponse(t, http.StatusOK, mockGists),
|
|
}),
|
|
requestArgs: map[string]any{},
|
|
expectError: false,
|
|
expectedGists: mockGists,
|
|
},
|
|
{
|
|
name: "list specific user's gists",
|
|
mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{
|
|
GetUsersGistsByUsername: mockResponse(t, http.StatusOK, mockGists),
|
|
}),
|
|
requestArgs: map[string]any{
|
|
"username": "testuser",
|
|
},
|
|
expectError: false,
|
|
expectedGists: mockGists,
|
|
},
|
|
{
|
|
name: "list gists with pagination and since parameter",
|
|
mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{
|
|
GetGists: expectQueryParams(t, map[string]string{
|
|
"since": "2023-01-01T00:00:00Z",
|
|
"page": "2",
|
|
"per_page": "5",
|
|
}).andThen(
|
|
mockResponse(t, http.StatusOK, mockGists),
|
|
),
|
|
}),
|
|
requestArgs: map[string]any{
|
|
"since": "2023-01-01T00:00:00Z",
|
|
"page": float64(2),
|
|
"perPage": float64(5),
|
|
},
|
|
expectError: false,
|
|
expectedGists: mockGists,
|
|
},
|
|
{
|
|
name: "invalid since parameter",
|
|
mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{
|
|
GetGists: mockResponse(t, http.StatusOK, mockGists),
|
|
}),
|
|
requestArgs: map[string]any{
|
|
"since": "invalid-date",
|
|
},
|
|
expectError: true,
|
|
expectedErrMsg: "invalid since timestamp",
|
|
},
|
|
{
|
|
name: "list gists fails with error",
|
|
mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{
|
|
GetGists: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
|
w.WriteHeader(http.StatusUnauthorized)
|
|
_, _ = w.Write([]byte(`{"message": "Requires authentication"}`))
|
|
}),
|
|
}),
|
|
requestArgs: map[string]any{},
|
|
expectError: true,
|
|
expectedErrMsg: "failed to list gists",
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
// Setup client with mock
|
|
client := mustNewGHClient(t, tc.mockedClient)
|
|
deps := BaseDeps{
|
|
Client: client,
|
|
}
|
|
handler := serverTool.Handler(deps)
|
|
|
|
// Create call request
|
|
request := createMCPRequest(tc.requestArgs)
|
|
|
|
// Call handler
|
|
result, err := handler(ContextWithDeps(context.Background(), deps), &request)
|
|
require.NoError(t, err)
|
|
|
|
// Verify results
|
|
if tc.expectError {
|
|
require.True(t, result.IsError)
|
|
errorContent := getErrorResult(t, result)
|
|
assert.Contains(t, errorContent.Text, tc.expectedErrMsg)
|
|
return
|
|
}
|
|
|
|
require.False(t, result.IsError)
|
|
|
|
// Parse the result and get the text content if no error
|
|
textContent := getTextResult(t, result)
|
|
|
|
// Unmarshal and verify the result
|
|
var returnedGists []*github.Gist
|
|
err = json.Unmarshal([]byte(textContent.Text), &returnedGists)
|
|
require.NoError(t, err)
|
|
|
|
assert.Len(t, returnedGists, len(tc.expectedGists))
|
|
for i, gist := range returnedGists {
|
|
assert.Equal(t, *tc.expectedGists[i].ID, *gist.ID)
|
|
assert.Equal(t, *tc.expectedGists[i].Description, *gist.Description)
|
|
assert.Equal(t, *tc.expectedGists[i].HTMLURL, *gist.HTMLURL)
|
|
assert.Equal(t, *tc.expectedGists[i].Public, *gist.Public)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_GetGist(t *testing.T) {
|
|
// Verify tool definition
|
|
serverTool := GetGist(translations.NullTranslationHelper)
|
|
tool := serverTool.Tool
|
|
|
|
require.NoError(t, toolsnaps.Test(tool.Name, tool))
|
|
|
|
assert.Equal(t, "get_gist", tool.Name)
|
|
assert.NotEmpty(t, tool.Description)
|
|
assert.True(t, tool.Annotations.ReadOnlyHint, "get_gist tool should be read-only")
|
|
|
|
schema, ok := tool.InputSchema.(*jsonschema.Schema)
|
|
require.True(t, ok, "InputSchema should be *jsonschema.Schema")
|
|
assert.Contains(t, schema.Properties, "gist_id")
|
|
|
|
assert.Contains(t, schema.Required, "gist_id")
|
|
|
|
// Setup mock gist for success case
|
|
mockGist := github.Gist{
|
|
ID: github.Ptr("gist1"),
|
|
Description: github.Ptr("First Gist"),
|
|
HTMLURL: github.Ptr("https://gist.github.com/user/gist1"),
|
|
Public: github.Ptr(true),
|
|
CreatedAt: &github.Timestamp{Time: time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC)},
|
|
Owner: &github.User{Login: github.Ptr("user")},
|
|
Files: map[github.GistFilename]github.GistFile{
|
|
github.GistFilename("file1.txt"): {
|
|
Filename: github.Ptr("file1.txt"),
|
|
Content: github.Ptr("content of file 1"),
|
|
},
|
|
},
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
mockedClient *http.Client
|
|
requestArgs map[string]any
|
|
expectError bool
|
|
expectedGists github.Gist
|
|
expectedErrMsg string
|
|
}{
|
|
{
|
|
name: "Successful fetching different gist",
|
|
mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{
|
|
GetGistsByGistID: mockResponse(t, http.StatusOK, mockGist),
|
|
}),
|
|
requestArgs: map[string]any{
|
|
"gist_id": "gist1",
|
|
},
|
|
expectError: false,
|
|
expectedGists: mockGist,
|
|
},
|
|
{
|
|
name: "gist_id parameter missing",
|
|
mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{
|
|
GetGistsByGistID: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
|
w.WriteHeader(http.StatusUnprocessableEntity)
|
|
_, _ = w.Write([]byte(`{"message": "Invalid Request"}`))
|
|
}),
|
|
}),
|
|
requestArgs: map[string]any{},
|
|
expectError: true,
|
|
expectedErrMsg: "missing required parameter: gist_id",
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
// Setup client with mock
|
|
client := mustNewGHClient(t, tc.mockedClient)
|
|
deps := BaseDeps{
|
|
Client: client,
|
|
}
|
|
handler := serverTool.Handler(deps)
|
|
|
|
// Create call request
|
|
request := createMCPRequest(tc.requestArgs)
|
|
|
|
// Call handler
|
|
result, err := handler(ContextWithDeps(context.Background(), deps), &request)
|
|
require.NoError(t, err)
|
|
|
|
// Verify results
|
|
if tc.expectError {
|
|
require.True(t, result.IsError)
|
|
errorContent := getErrorResult(t, result)
|
|
assert.Contains(t, errorContent.Text, tc.expectedErrMsg)
|
|
return
|
|
}
|
|
|
|
require.False(t, result.IsError)
|
|
|
|
// Parse the result and get the text content if no error
|
|
textContent := getTextResult(t, result)
|
|
|
|
// Unmarshal and verify the result
|
|
var returnedGists github.Gist
|
|
err = json.Unmarshal([]byte(textContent.Text), &returnedGists)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, *tc.expectedGists.ID, *returnedGists.ID)
|
|
assert.Equal(t, *tc.expectedGists.Description, *returnedGists.Description)
|
|
assert.Equal(t, *tc.expectedGists.HTMLURL, *returnedGists.HTMLURL)
|
|
assert.Equal(t, *tc.expectedGists.Public, *returnedGists.Public)
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_CreateGist(t *testing.T) {
|
|
// Verify tool definition
|
|
serverTool := CreateGist(translations.NullTranslationHelper)
|
|
tool := serverTool.Tool
|
|
|
|
require.NoError(t, toolsnaps.Test(tool.Name, tool))
|
|
|
|
assert.Equal(t, "create_gist", tool.Name)
|
|
assert.NotEmpty(t, tool.Description)
|
|
assert.False(t, tool.Annotations.ReadOnlyHint, "create_gist tool should not be read-only")
|
|
|
|
schema, ok := tool.InputSchema.(*jsonschema.Schema)
|
|
require.True(t, ok, "InputSchema should be *jsonschema.Schema")
|
|
assert.Contains(t, schema.Properties, "description")
|
|
assert.Contains(t, schema.Properties, "filename")
|
|
assert.Contains(t, schema.Properties, "content")
|
|
assert.Contains(t, schema.Properties, "public")
|
|
|
|
// Verify required parameters
|
|
assert.Contains(t, schema.Required, "filename")
|
|
assert.Contains(t, schema.Required, "content")
|
|
|
|
// Setup mock data for test cases
|
|
createdGist := &github.Gist{
|
|
ID: github.Ptr("new-gist-id"),
|
|
Description: github.Ptr("Test Gist"),
|
|
HTMLURL: github.Ptr("https://gist.github.com/user/new-gist-id"),
|
|
Public: github.Ptr(false),
|
|
CreatedAt: &github.Timestamp{Time: time.Now()},
|
|
Owner: &github.User{Login: github.Ptr("user")},
|
|
Files: map[github.GistFilename]github.GistFile{
|
|
"test.go": {
|
|
Filename: github.Ptr("test.go"),
|
|
Content: github.Ptr("package main\n\nfunc main() {\n\tfmt.Println(\"Hello, Gist!\")\n}"),
|
|
},
|
|
},
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
mockedClient *http.Client
|
|
requestArgs map[string]any
|
|
expectError bool
|
|
expectedErrMsg string
|
|
expectedGist *github.Gist
|
|
}{
|
|
{
|
|
name: "create gist successfully",
|
|
mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{
|
|
PostGists: mockResponse(t, http.StatusCreated, createdGist),
|
|
}),
|
|
requestArgs: map[string]any{
|
|
"filename": "test.go",
|
|
"content": "package main\n\nfunc main() {\n\tfmt.Println(\"Hello, Gist!\")\n}",
|
|
"description": "Test Gist",
|
|
"public": false,
|
|
},
|
|
expectError: false,
|
|
expectedGist: createdGist,
|
|
},
|
|
{
|
|
name: "missing required filename",
|
|
mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}),
|
|
requestArgs: map[string]any{
|
|
"content": "test content",
|
|
"description": "Test Gist",
|
|
},
|
|
expectError: true,
|
|
expectedErrMsg: "missing required parameter: filename",
|
|
},
|
|
{
|
|
name: "missing required content",
|
|
mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}),
|
|
requestArgs: map[string]any{
|
|
"filename": "test.go",
|
|
"description": "Test Gist",
|
|
},
|
|
expectError: true,
|
|
expectedErrMsg: "missing required parameter: content",
|
|
},
|
|
{
|
|
name: "api returns error",
|
|
mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{
|
|
PostGists: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
|
w.WriteHeader(http.StatusUnauthorized)
|
|
_, _ = w.Write([]byte(`{"message": "Requires authentication"}`))
|
|
}),
|
|
}),
|
|
requestArgs: map[string]any{
|
|
"filename": "test.go",
|
|
"content": "package main",
|
|
"description": "Test Gist",
|
|
},
|
|
expectError: true,
|
|
expectedErrMsg: "failed to create gist",
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
// Setup client with mock
|
|
client := mustNewGHClient(t, tc.mockedClient)
|
|
deps := BaseDeps{
|
|
Client: client,
|
|
}
|
|
handler := serverTool.Handler(deps)
|
|
|
|
// Create call request
|
|
request := createMCPRequest(tc.requestArgs)
|
|
|
|
// Call handler
|
|
result, err := handler(ContextWithDeps(context.Background(), deps), &request)
|
|
require.NoError(t, err)
|
|
|
|
// Verify results
|
|
if tc.expectError {
|
|
require.True(t, result.IsError)
|
|
errorContent := getErrorResult(t, result)
|
|
assert.Contains(t, errorContent.Text, tc.expectedErrMsg)
|
|
return
|
|
}
|
|
|
|
require.False(t, result.IsError)
|
|
assert.NotNil(t, result)
|
|
|
|
// Parse the result and get the text content
|
|
textContent := getTextResult(t, result)
|
|
|
|
// Unmarshal and verify the minimal result
|
|
var gist MinimalResponse
|
|
err = json.Unmarshal([]byte(textContent.Text), &gist)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, tc.expectedGist.GetHTMLURL(), gist.URL)
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_UpdateGist(t *testing.T) {
|
|
// Verify tool definition
|
|
serverTool := UpdateGist(translations.NullTranslationHelper)
|
|
tool := serverTool.Tool
|
|
|
|
require.NoError(t, toolsnaps.Test(tool.Name, tool))
|
|
|
|
assert.Equal(t, "update_gist", tool.Name)
|
|
assert.NotEmpty(t, tool.Description)
|
|
assert.False(t, tool.Annotations.ReadOnlyHint, "update_gist tool should not be read-only")
|
|
|
|
schema, ok := tool.InputSchema.(*jsonschema.Schema)
|
|
require.True(t, ok, "InputSchema should be *jsonschema.Schema")
|
|
assert.Contains(t, schema.Properties, "gist_id")
|
|
assert.Contains(t, schema.Properties, "description")
|
|
assert.Contains(t, schema.Properties, "filename")
|
|
assert.Contains(t, schema.Properties, "content")
|
|
|
|
// Verify required parameters
|
|
assert.Contains(t, schema.Required, "gist_id")
|
|
assert.Contains(t, schema.Required, "filename")
|
|
assert.Contains(t, schema.Required, "content")
|
|
|
|
// Setup mock data for test cases
|
|
updatedGist := &github.Gist{
|
|
ID: github.Ptr("existing-gist-id"),
|
|
Description: github.Ptr("Updated Test Gist"),
|
|
HTMLURL: github.Ptr("https://gist.github.com/user/existing-gist-id"),
|
|
Public: github.Ptr(true),
|
|
UpdatedAt: &github.Timestamp{Time: time.Now()},
|
|
Owner: &github.User{Login: github.Ptr("user")},
|
|
Files: map[github.GistFilename]github.GistFile{
|
|
"updated.go": {
|
|
Filename: github.Ptr("updated.go"),
|
|
Content: github.Ptr("package main\n\nfunc main() {\n\tfmt.Println(\"Updated Gist!\")\n}"),
|
|
},
|
|
},
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
mockedClient *http.Client
|
|
requestArgs map[string]any
|
|
expectError bool
|
|
expectedErrMsg string
|
|
expectedGist *github.Gist
|
|
}{
|
|
{
|
|
name: "update gist successfully",
|
|
mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{
|
|
PatchGistsByGistID: mockResponse(t, http.StatusOK, updatedGist),
|
|
}),
|
|
requestArgs: map[string]any{
|
|
"gist_id": "existing-gist-id",
|
|
"filename": "updated.go",
|
|
"content": "package main\n\nfunc main() {\n\tfmt.Println(\"Updated Gist!\")\n}",
|
|
"description": "Updated Test Gist",
|
|
},
|
|
expectError: false,
|
|
expectedGist: updatedGist,
|
|
},
|
|
{
|
|
name: "missing required gist_id",
|
|
mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}),
|
|
requestArgs: map[string]any{
|
|
"filename": "updated.go",
|
|
"content": "updated content",
|
|
"description": "Updated Test Gist",
|
|
},
|
|
expectError: true,
|
|
expectedErrMsg: "missing required parameter: gist_id",
|
|
},
|
|
{
|
|
name: "missing required filename",
|
|
mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}),
|
|
requestArgs: map[string]any{
|
|
"gist_id": "existing-gist-id",
|
|
"content": "updated content",
|
|
"description": "Updated Test Gist",
|
|
},
|
|
expectError: true,
|
|
expectedErrMsg: "missing required parameter: filename",
|
|
},
|
|
{
|
|
name: "missing required content",
|
|
mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}),
|
|
requestArgs: map[string]any{
|
|
"gist_id": "existing-gist-id",
|
|
"filename": "updated.go",
|
|
"description": "Updated Test Gist",
|
|
},
|
|
expectError: true,
|
|
expectedErrMsg: "missing required parameter: content",
|
|
},
|
|
{
|
|
name: "api returns error",
|
|
mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{
|
|
PatchGistsByGistID: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
|
w.WriteHeader(http.StatusNotFound)
|
|
_, _ = w.Write([]byte(`{"message": "Not Found"}`))
|
|
}),
|
|
}),
|
|
requestArgs: map[string]any{
|
|
"gist_id": "nonexistent-gist-id",
|
|
"filename": "updated.go",
|
|
"content": "package main",
|
|
"description": "Updated Test Gist",
|
|
},
|
|
expectError: true,
|
|
expectedErrMsg: "failed to update gist",
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
// Setup client with mock
|
|
client := mustNewGHClient(t, tc.mockedClient)
|
|
deps := BaseDeps{
|
|
Client: client,
|
|
}
|
|
handler := serverTool.Handler(deps)
|
|
|
|
// Create call request
|
|
request := createMCPRequest(tc.requestArgs)
|
|
|
|
// Call handler
|
|
result, err := handler(ContextWithDeps(context.Background(), deps), &request)
|
|
require.NoError(t, err)
|
|
|
|
// Verify results
|
|
if tc.expectError {
|
|
require.True(t, result.IsError)
|
|
errorContent := getErrorResult(t, result)
|
|
assert.Contains(t, errorContent.Text, tc.expectedErrMsg)
|
|
return
|
|
}
|
|
|
|
require.False(t, result.IsError)
|
|
assert.NotNil(t, result)
|
|
|
|
// Parse the result and get the text content
|
|
textContent := getTextResult(t, result)
|
|
|
|
// Unmarshal and verify the minimal result
|
|
var updateResp MinimalResponse
|
|
err = json.Unmarshal([]byte(textContent.Text), &updateResp)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, tc.expectedGist.GetHTMLURL(), updateResp.URL)
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_UpdateGist_DescriptionOnlySentWhenProvided(t *testing.T) {
|
|
serverTool := UpdateGist(translations.NullTranslationHelper)
|
|
|
|
updatedGist := &github.Gist{
|
|
ID: github.Ptr("existing-gist-id"),
|
|
HTMLURL: github.Ptr("https://gist.github.com/user/existing-gist-id"),
|
|
}
|
|
|
|
cases := []struct {
|
|
name string
|
|
args map[string]any
|
|
wantDescPresent bool
|
|
wantDescValue string
|
|
}{
|
|
{
|
|
name: "omitted description is not sent, preserving the existing one",
|
|
args: map[string]any{
|
|
"gist_id": "existing-gist-id",
|
|
"filename": "updated.go",
|
|
"content": "package main",
|
|
},
|
|
wantDescPresent: false,
|
|
},
|
|
{
|
|
name: "explicit empty description is sent to clear it",
|
|
args: map[string]any{
|
|
"gist_id": "existing-gist-id",
|
|
"filename": "updated.go",
|
|
"content": "package main",
|
|
"description": "",
|
|
},
|
|
wantDescPresent: true,
|
|
wantDescValue: "",
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
var gotBody map[string]any
|
|
client := mustNewGHClient(t, MockHTTPClientWithHandlers(map[string]http.HandlerFunc{
|
|
PatchGistsByGistID: func(w http.ResponseWriter, r *http.Request) {
|
|
require.NoError(t, json.NewDecoder(r.Body).Decode(&gotBody))
|
|
w.WriteHeader(http.StatusOK)
|
|
_, _ = w.Write(MustMarshal(updatedGist))
|
|
},
|
|
}))
|
|
deps := BaseDeps{Client: client}
|
|
handler := serverTool.Handler(deps)
|
|
request := createMCPRequest(tc.args)
|
|
|
|
result, err := handler(ContextWithDeps(context.Background(), deps), &request)
|
|
require.NoError(t, err)
|
|
require.False(t, result.IsError)
|
|
|
|
desc, present := gotBody["description"]
|
|
assert.Equal(t, tc.wantDescPresent, present, "description key presence in PATCH body")
|
|
if tc.wantDescPresent {
|
|
assert.Equal(t, tc.wantDescValue, desc)
|
|
}
|
|
})
|
|
}
|
|
}
|