chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,488 @@
|
||||
package github
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/github/github-mcp-server/internal/githubv4mock"
|
||||
"github.com/github/github-mcp-server/internal/toolsnaps"
|
||||
"github.com/github/github-mcp-server/pkg/translations"
|
||||
"github.com/shurcooL/githubv4"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestGetLabel(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Verify tool definition
|
||||
serverTool := GetLabel(translations.NullTranslationHelper)
|
||||
tool := serverTool.Tool
|
||||
require.NoError(t, toolsnaps.Test(tool.Name, tool))
|
||||
|
||||
assert.Equal(t, "get_label", tool.Name)
|
||||
assert.NotEmpty(t, tool.Description)
|
||||
assert.True(t, tool.Annotations.ReadOnlyHint, "get_label tool should be read-only")
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
requestArgs map[string]any
|
||||
mockedClient *http.Client
|
||||
expectToolError bool
|
||||
expectedToolErrMsg string
|
||||
}{
|
||||
{
|
||||
name: "successful label retrieval",
|
||||
requestArgs: map[string]any{
|
||||
"owner": "owner",
|
||||
"repo": "repo",
|
||||
"name": "bug",
|
||||
},
|
||||
mockedClient: githubv4mock.NewMockedHTTPClient(
|
||||
githubv4mock.NewQueryMatcher(
|
||||
struct {
|
||||
Repository struct {
|
||||
Label struct {
|
||||
ID githubv4.ID
|
||||
Name githubv4.String
|
||||
Color githubv4.String
|
||||
Description githubv4.String
|
||||
} `graphql:"label(name: $name)"`
|
||||
} `graphql:"repository(owner: $owner, name: $repo)"`
|
||||
}{},
|
||||
map[string]any{
|
||||
"owner": githubv4.String("owner"),
|
||||
"repo": githubv4.String("repo"),
|
||||
"name": githubv4.String("bug"),
|
||||
},
|
||||
githubv4mock.DataResponse(map[string]any{
|
||||
"repository": map[string]any{
|
||||
"label": map[string]any{
|
||||
"id": githubv4.ID("test-label-id"),
|
||||
"name": githubv4.String("bug"),
|
||||
"color": githubv4.String("d73a4a"),
|
||||
"description": githubv4.String("Something isn't working"),
|
||||
},
|
||||
},
|
||||
}),
|
||||
),
|
||||
),
|
||||
expectToolError: false,
|
||||
},
|
||||
{
|
||||
name: "label not found",
|
||||
requestArgs: map[string]any{
|
||||
"owner": "owner",
|
||||
"repo": "repo",
|
||||
"name": "nonexistent",
|
||||
},
|
||||
mockedClient: githubv4mock.NewMockedHTTPClient(
|
||||
githubv4mock.NewQueryMatcher(
|
||||
struct {
|
||||
Repository struct {
|
||||
Label struct {
|
||||
ID githubv4.ID
|
||||
Name githubv4.String
|
||||
Color githubv4.String
|
||||
Description githubv4.String
|
||||
} `graphql:"label(name: $name)"`
|
||||
} `graphql:"repository(owner: $owner, name: $repo)"`
|
||||
}{},
|
||||
map[string]any{
|
||||
"owner": githubv4.String("owner"),
|
||||
"repo": githubv4.String("repo"),
|
||||
"name": githubv4.String("nonexistent"),
|
||||
},
|
||||
githubv4mock.DataResponse(map[string]any{
|
||||
"repository": map[string]any{
|
||||
"label": map[string]any{
|
||||
"id": githubv4.ID(""),
|
||||
"name": githubv4.String(""),
|
||||
"color": githubv4.String(""),
|
||||
"description": githubv4.String(""),
|
||||
},
|
||||
},
|
||||
}),
|
||||
),
|
||||
),
|
||||
expectToolError: true,
|
||||
expectedToolErrMsg: "label 'nonexistent' not found in owner/repo",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
client := githubv4.NewClient(tc.mockedClient)
|
||||
deps := BaseDeps{
|
||||
GQLClient: client,
|
||||
}
|
||||
handler := serverTool.Handler(deps)
|
||||
|
||||
request := createMCPRequest(tc.requestArgs)
|
||||
result, err := handler(ContextWithDeps(context.Background(), deps), &request)
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, result)
|
||||
|
||||
if tc.expectToolError {
|
||||
assert.True(t, result.IsError)
|
||||
if tc.expectedToolErrMsg != "" {
|
||||
textContent := getErrorResult(t, result)
|
||||
assert.Contains(t, textContent.Text, tc.expectedToolErrMsg)
|
||||
}
|
||||
} else {
|
||||
assert.False(t, result.IsError)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestListLabels(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Verify tool definition
|
||||
serverTool := ListLabels(translations.NullTranslationHelper)
|
||||
tool := serverTool.Tool
|
||||
require.NoError(t, toolsnaps.Test(tool.Name, tool))
|
||||
|
||||
assert.Equal(t, "list_label", tool.Name)
|
||||
assert.NotEmpty(t, tool.Description)
|
||||
assert.True(t, tool.Annotations.ReadOnlyHint, "list_label tool should be read-only")
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
requestArgs map[string]any
|
||||
mockedClient *http.Client
|
||||
expectToolError bool
|
||||
expectedToolErrMsg string
|
||||
}{
|
||||
{
|
||||
name: "successful repository labels listing",
|
||||
requestArgs: map[string]any{
|
||||
"owner": "owner",
|
||||
"repo": "repo",
|
||||
},
|
||||
mockedClient: githubv4mock.NewMockedHTTPClient(
|
||||
githubv4mock.NewQueryMatcher(
|
||||
struct {
|
||||
Repository struct {
|
||||
Labels struct {
|
||||
Nodes []struct {
|
||||
ID githubv4.ID
|
||||
Name githubv4.String
|
||||
Color githubv4.String
|
||||
Description githubv4.String
|
||||
}
|
||||
TotalCount githubv4.Int
|
||||
} `graphql:"labels(first: 100)"`
|
||||
} `graphql:"repository(owner: $owner, name: $repo)"`
|
||||
}{},
|
||||
map[string]any{
|
||||
"owner": githubv4.String("owner"),
|
||||
"repo": githubv4.String("repo"),
|
||||
},
|
||||
githubv4mock.DataResponse(map[string]any{
|
||||
"repository": map[string]any{
|
||||
"labels": map[string]any{
|
||||
"nodes": []any{
|
||||
map[string]any{
|
||||
"id": githubv4.ID("label-1"),
|
||||
"name": githubv4.String("bug"),
|
||||
"color": githubv4.String("d73a4a"),
|
||||
"description": githubv4.String("Something isn't working"),
|
||||
},
|
||||
map[string]any{
|
||||
"id": githubv4.ID("label-2"),
|
||||
"name": githubv4.String("enhancement"),
|
||||
"color": githubv4.String("a2eeef"),
|
||||
"description": githubv4.String("New feature or request"),
|
||||
},
|
||||
},
|
||||
"totalCount": githubv4.Int(2),
|
||||
},
|
||||
},
|
||||
}),
|
||||
),
|
||||
),
|
||||
expectToolError: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
client := githubv4.NewClient(tc.mockedClient)
|
||||
deps := BaseDeps{
|
||||
GQLClient: client,
|
||||
}
|
||||
handler := serverTool.Handler(deps)
|
||||
|
||||
request := createMCPRequest(tc.requestArgs)
|
||||
result, err := handler(ContextWithDeps(context.Background(), deps), &request)
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, result)
|
||||
|
||||
if tc.expectToolError {
|
||||
assert.True(t, result.IsError)
|
||||
if tc.expectedToolErrMsg != "" {
|
||||
textContent := getErrorResult(t, result)
|
||||
assert.Contains(t, textContent.Text, tc.expectedToolErrMsg)
|
||||
}
|
||||
} else {
|
||||
assert.False(t, result.IsError)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteLabel(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Verify tool definition
|
||||
serverTool := LabelWrite(translations.NullTranslationHelper)
|
||||
tool := serverTool.Tool
|
||||
require.NoError(t, toolsnaps.Test(tool.Name, tool))
|
||||
|
||||
assert.Equal(t, "label_write", tool.Name)
|
||||
assert.NotEmpty(t, tool.Description)
|
||||
assert.False(t, tool.Annotations.ReadOnlyHint, "label_write tool should not be read-only")
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
requestArgs map[string]any
|
||||
mockedClient *http.Client
|
||||
expectToolError bool
|
||||
expectedToolErrMsg string
|
||||
}{
|
||||
{
|
||||
name: "successful label creation",
|
||||
requestArgs: map[string]any{
|
||||
"method": "create",
|
||||
"owner": "owner",
|
||||
"repo": "repo",
|
||||
"name": "new-label",
|
||||
"color": "f29513",
|
||||
"description": "A new test label",
|
||||
},
|
||||
mockedClient: githubv4mock.NewMockedHTTPClient(
|
||||
githubv4mock.NewQueryMatcher(
|
||||
struct {
|
||||
Repository struct {
|
||||
ID githubv4.ID
|
||||
} `graphql:"repository(owner: $owner, name: $repo)"`
|
||||
}{},
|
||||
map[string]any{
|
||||
"owner": githubv4.String("owner"),
|
||||
"repo": githubv4.String("repo"),
|
||||
},
|
||||
githubv4mock.DataResponse(map[string]any{
|
||||
"repository": map[string]any{
|
||||
"id": githubv4.ID("test-repo-id"),
|
||||
},
|
||||
}),
|
||||
),
|
||||
githubv4mock.NewMutationMatcher(
|
||||
struct {
|
||||
CreateLabel struct {
|
||||
Label struct {
|
||||
Name githubv4.String
|
||||
ID githubv4.ID
|
||||
}
|
||||
} `graphql:"createLabel(input: $input)"`
|
||||
}{},
|
||||
githubv4.CreateLabelInput{
|
||||
RepositoryID: githubv4.ID("test-repo-id"),
|
||||
Name: githubv4.String("new-label"),
|
||||
Color: githubv4.String("f29513"),
|
||||
Description: func() *githubv4.String { s := githubv4.String("A new test label"); return &s }(),
|
||||
},
|
||||
nil,
|
||||
githubv4mock.DataResponse(map[string]any{
|
||||
"createLabel": map[string]any{
|
||||
"label": map[string]any{
|
||||
"id": githubv4.ID("new-label-id"),
|
||||
"name": githubv4.String("new-label"),
|
||||
},
|
||||
},
|
||||
}),
|
||||
),
|
||||
),
|
||||
expectToolError: false,
|
||||
},
|
||||
{
|
||||
name: "create label without color",
|
||||
requestArgs: map[string]any{
|
||||
"method": "create",
|
||||
"owner": "owner",
|
||||
"repo": "repo",
|
||||
"name": "new-label",
|
||||
},
|
||||
mockedClient: githubv4mock.NewMockedHTTPClient(),
|
||||
expectToolError: true,
|
||||
expectedToolErrMsg: "color is required for create",
|
||||
},
|
||||
{
|
||||
name: "successful label update",
|
||||
requestArgs: map[string]any{
|
||||
"method": "update",
|
||||
"owner": "owner",
|
||||
"repo": "repo",
|
||||
"name": "bug",
|
||||
"new_name": "defect",
|
||||
"color": "ff0000",
|
||||
},
|
||||
mockedClient: githubv4mock.NewMockedHTTPClient(
|
||||
githubv4mock.NewQueryMatcher(
|
||||
struct {
|
||||
Repository struct {
|
||||
Label struct {
|
||||
ID githubv4.ID
|
||||
Name githubv4.String
|
||||
} `graphql:"label(name: $name)"`
|
||||
} `graphql:"repository(owner: $owner, name: $repo)"`
|
||||
}{},
|
||||
map[string]any{
|
||||
"owner": githubv4.String("owner"),
|
||||
"repo": githubv4.String("repo"),
|
||||
"name": githubv4.String("bug"),
|
||||
},
|
||||
githubv4mock.DataResponse(map[string]any{
|
||||
"repository": map[string]any{
|
||||
"label": map[string]any{
|
||||
"id": githubv4.ID("bug-label-id"),
|
||||
"name": githubv4.String("bug"),
|
||||
},
|
||||
},
|
||||
}),
|
||||
),
|
||||
githubv4mock.NewMutationMatcher(
|
||||
struct {
|
||||
UpdateLabel struct {
|
||||
Label struct {
|
||||
Name githubv4.String
|
||||
ID githubv4.ID
|
||||
}
|
||||
} `graphql:"updateLabel(input: $input)"`
|
||||
}{},
|
||||
githubv4.UpdateLabelInput{
|
||||
ID: githubv4.ID("bug-label-id"),
|
||||
Name: func() *githubv4.String { s := githubv4.String("defect"); return &s }(),
|
||||
Color: func() *githubv4.String { s := githubv4.String("ff0000"); return &s }(),
|
||||
},
|
||||
nil,
|
||||
githubv4mock.DataResponse(map[string]any{
|
||||
"updateLabel": map[string]any{
|
||||
"label": map[string]any{
|
||||
"id": githubv4.ID("bug-label-id"),
|
||||
"name": githubv4.String("defect"),
|
||||
},
|
||||
},
|
||||
}),
|
||||
),
|
||||
),
|
||||
expectToolError: false,
|
||||
},
|
||||
{
|
||||
name: "update label without any changes",
|
||||
requestArgs: map[string]any{
|
||||
"method": "update",
|
||||
"owner": "owner",
|
||||
"repo": "repo",
|
||||
"name": "bug",
|
||||
},
|
||||
mockedClient: githubv4mock.NewMockedHTTPClient(),
|
||||
expectToolError: true,
|
||||
expectedToolErrMsg: "at least one of new_name, color, or description must be provided for update",
|
||||
},
|
||||
{
|
||||
name: "successful label deletion",
|
||||
requestArgs: map[string]any{
|
||||
"method": "delete",
|
||||
"owner": "owner",
|
||||
"repo": "repo",
|
||||
"name": "bug",
|
||||
},
|
||||
mockedClient: githubv4mock.NewMockedHTTPClient(
|
||||
githubv4mock.NewQueryMatcher(
|
||||
struct {
|
||||
Repository struct {
|
||||
Label struct {
|
||||
ID githubv4.ID
|
||||
Name githubv4.String
|
||||
} `graphql:"label(name: $name)"`
|
||||
} `graphql:"repository(owner: $owner, name: $repo)"`
|
||||
}{},
|
||||
map[string]any{
|
||||
"owner": githubv4.String("owner"),
|
||||
"repo": githubv4.String("repo"),
|
||||
"name": githubv4.String("bug"),
|
||||
},
|
||||
githubv4mock.DataResponse(map[string]any{
|
||||
"repository": map[string]any{
|
||||
"label": map[string]any{
|
||||
"id": githubv4.ID("bug-label-id"),
|
||||
"name": githubv4.String("bug"),
|
||||
},
|
||||
},
|
||||
}),
|
||||
),
|
||||
githubv4mock.NewMutationMatcher(
|
||||
struct {
|
||||
DeleteLabel struct {
|
||||
ClientMutationID githubv4.String
|
||||
} `graphql:"deleteLabel(input: $input)"`
|
||||
}{},
|
||||
githubv4.DeleteLabelInput{
|
||||
ID: githubv4.ID("bug-label-id"),
|
||||
},
|
||||
nil,
|
||||
githubv4mock.DataResponse(map[string]any{
|
||||
"deleteLabel": map[string]any{
|
||||
"clientMutationId": githubv4.String("test-mutation-id"),
|
||||
},
|
||||
}),
|
||||
),
|
||||
),
|
||||
expectToolError: false,
|
||||
},
|
||||
{
|
||||
name: "invalid method",
|
||||
requestArgs: map[string]any{
|
||||
"method": "invalid",
|
||||
"owner": "owner",
|
||||
"repo": "repo",
|
||||
"name": "bug",
|
||||
},
|
||||
mockedClient: githubv4mock.NewMockedHTTPClient(),
|
||||
expectToolError: true,
|
||||
expectedToolErrMsg: "unknown method: invalid",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
client := githubv4.NewClient(tc.mockedClient)
|
||||
deps := BaseDeps{
|
||||
GQLClient: client,
|
||||
}
|
||||
handler := serverTool.Handler(deps)
|
||||
|
||||
request := createMCPRequest(tc.requestArgs)
|
||||
result, err := handler(ContextWithDeps(context.Background(), deps), &request)
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, result)
|
||||
|
||||
if tc.expectToolError {
|
||||
assert.True(t, result.IsError)
|
||||
if tc.expectedToolErrMsg != "" {
|
||||
textContent := getErrorResult(t, result)
|
||||
assert.Contains(t, textContent.Text, tc.expectedToolErrMsg)
|
||||
}
|
||||
} else {
|
||||
assert.False(t, result.IsError)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user