Spaces:
Build error
Build error
| package github | |
| import ( | |
| "context" | |
| "encoding/json" | |
| "net/http" | |
| "testing" | |
| "github.com/github/github-mcp-server/internal/toolsnaps" | |
| "github.com/github/github-mcp-server/pkg/translations" | |
| "github.com/google/go-github/v74/github" | |
| "github.com/migueleliasweb/go-github-mock/src/mock" | |
| "github.com/stretchr/testify/assert" | |
| "github.com/stretchr/testify/require" | |
| ) | |
| func Test_ListNotifications(t *testing.T) { | |
| // Verify tool definition and schema | |
| mockClient := github.NewClient(nil) | |
| tool, _ := ListNotifications(stubGetClientFn(mockClient), translations.NullTranslationHelper) | |
| require.NoError(t, toolsnaps.Test(tool.Name, tool)) | |
| assert.Equal(t, "list_notifications", tool.Name) | |
| assert.NotEmpty(t, tool.Description) | |
| assert.Contains(t, tool.InputSchema.Properties, "filter") | |
| assert.Contains(t, tool.InputSchema.Properties, "since") | |
| assert.Contains(t, tool.InputSchema.Properties, "before") | |
| assert.Contains(t, tool.InputSchema.Properties, "owner") | |
| assert.Contains(t, tool.InputSchema.Properties, "repo") | |
| assert.Contains(t, tool.InputSchema.Properties, "page") | |
| assert.Contains(t, tool.InputSchema.Properties, "perPage") | |
| // All fields are optional, so Required should be empty | |
| assert.Empty(t, tool.InputSchema.Required) | |
| mockNotification := &github.Notification{ | |
| ID: github.Ptr("123"), | |
| Reason: github.Ptr("mention"), | |
| } | |
| tests := []struct { | |
| name string | |
| mockedClient *http.Client | |
| requestArgs map[string]interface{} | |
| expectError bool | |
| expectedResult []*github.Notification | |
| expectedErrMsg string | |
| }{ | |
| { | |
| name: "success default filter (no params)", | |
| mockedClient: mock.NewMockedHTTPClient( | |
| mock.WithRequestMatch( | |
| mock.GetNotifications, | |
| []*github.Notification{mockNotification}, | |
| ), | |
| ), | |
| requestArgs: map[string]interface{}{}, | |
| expectError: false, | |
| expectedResult: []*github.Notification{mockNotification}, | |
| }, | |
| { | |
| name: "success with filter=include_read_notifications", | |
| mockedClient: mock.NewMockedHTTPClient( | |
| mock.WithRequestMatch( | |
| mock.GetNotifications, | |
| []*github.Notification{mockNotification}, | |
| ), | |
| ), | |
| requestArgs: map[string]interface{}{ | |
| "filter": "include_read_notifications", | |
| }, | |
| expectError: false, | |
| expectedResult: []*github.Notification{mockNotification}, | |
| }, | |
| { | |
| name: "success with filter=only_participating", | |
| mockedClient: mock.NewMockedHTTPClient( | |
| mock.WithRequestMatch( | |
| mock.GetNotifications, | |
| []*github.Notification{mockNotification}, | |
| ), | |
| ), | |
| requestArgs: map[string]interface{}{ | |
| "filter": "only_participating", | |
| }, | |
| expectError: false, | |
| expectedResult: []*github.Notification{mockNotification}, | |
| }, | |
| { | |
| name: "success for repo notifications", | |
| mockedClient: mock.NewMockedHTTPClient( | |
| mock.WithRequestMatch( | |
| mock.GetReposNotificationsByOwnerByRepo, | |
| []*github.Notification{mockNotification}, | |
| ), | |
| ), | |
| requestArgs: map[string]interface{}{ | |
| "filter": "default", | |
| "since": "2024-01-01T00:00:00Z", | |
| "before": "2024-01-02T00:00:00Z", | |
| "owner": "octocat", | |
| "repo": "hello-world", | |
| "page": float64(2), | |
| "perPage": float64(10), | |
| }, | |
| expectError: false, | |
| expectedResult: []*github.Notification{mockNotification}, | |
| }, | |
| { | |
| name: "error", | |
| mockedClient: mock.NewMockedHTTPClient( | |
| mock.WithRequestMatchHandler( | |
| mock.GetNotifications, | |
| mockResponse(t, http.StatusInternalServerError, `{"message": "error"}`), | |
| ), | |
| ), | |
| requestArgs: map[string]interface{}{}, | |
| expectError: true, | |
| expectedErrMsg: "error", | |
| }, | |
| } | |
| for _, tc := range tests { | |
| t.Run(tc.name, func(t *testing.T) { | |
| client := github.NewClient(tc.mockedClient) | |
| _, handler := ListNotifications(stubGetClientFn(client), translations.NullTranslationHelper) | |
| request := createMCPRequest(tc.requestArgs) | |
| result, err := handler(context.Background(), request) | |
| if tc.expectError { | |
| require.NoError(t, err) | |
| require.True(t, result.IsError) | |
| errorContent := getErrorResult(t, result) | |
| if tc.expectedErrMsg != "" { | |
| assert.Contains(t, errorContent.Text, tc.expectedErrMsg) | |
| } | |
| return | |
| } | |
| require.NoError(t, err) | |
| require.False(t, result.IsError) | |
| textContent := getTextResult(t, result) | |
| t.Logf("textContent: %s", textContent.Text) | |
| var returned []*github.Notification | |
| err = json.Unmarshal([]byte(textContent.Text), &returned) | |
| require.NoError(t, err) | |
| require.NotEmpty(t, returned) | |
| assert.Equal(t, *tc.expectedResult[0].ID, *returned[0].ID) | |
| }) | |
| } | |
| } | |
| func Test_ManageNotificationSubscription(t *testing.T) { | |
| // Verify tool definition and schema | |
| mockClient := github.NewClient(nil) | |
| tool, _ := ManageNotificationSubscription(stubGetClientFn(mockClient), translations.NullTranslationHelper) | |
| require.NoError(t, toolsnaps.Test(tool.Name, tool)) | |
| assert.Equal(t, "manage_notification_subscription", tool.Name) | |
| assert.NotEmpty(t, tool.Description) | |
| assert.Contains(t, tool.InputSchema.Properties, "notificationID") | |
| assert.Contains(t, tool.InputSchema.Properties, "action") | |
| assert.ElementsMatch(t, tool.InputSchema.Required, []string{"notificationID", "action"}) | |
| mockSub := &github.Subscription{Ignored: github.Ptr(true)} | |
| mockSubWatch := &github.Subscription{Ignored: github.Ptr(false), Subscribed: github.Ptr(true)} | |
| tests := []struct { | |
| name string | |
| mockedClient *http.Client | |
| requestArgs map[string]interface{} | |
| expectError bool | |
| expectIgnored *bool | |
| expectDeleted bool | |
| expectInvalid bool | |
| expectedErrMsg string | |
| }{ | |
| { | |
| name: "ignore subscription", | |
| mockedClient: mock.NewMockedHTTPClient( | |
| mock.WithRequestMatch( | |
| mock.PutNotificationsThreadsSubscriptionByThreadId, | |
| mockSub, | |
| ), | |
| ), | |
| requestArgs: map[string]interface{}{ | |
| "notificationID": "123", | |
| "action": "ignore", | |
| }, | |
| expectError: false, | |
| expectIgnored: github.Ptr(true), | |
| }, | |
| { | |
| name: "watch subscription", | |
| mockedClient: mock.NewMockedHTTPClient( | |
| mock.WithRequestMatch( | |
| mock.PutNotificationsThreadsSubscriptionByThreadId, | |
| mockSubWatch, | |
| ), | |
| ), | |
| requestArgs: map[string]interface{}{ | |
| "notificationID": "123", | |
| "action": "watch", | |
| }, | |
| expectError: false, | |
| expectIgnored: github.Ptr(false), | |
| }, | |
| { | |
| name: "delete subscription", | |
| mockedClient: mock.NewMockedHTTPClient( | |
| mock.WithRequestMatch( | |
| mock.DeleteNotificationsThreadsSubscriptionByThreadId, | |
| nil, | |
| ), | |
| ), | |
| requestArgs: map[string]interface{}{ | |
| "notificationID": "123", | |
| "action": "delete", | |
| }, | |
| expectError: false, | |
| expectDeleted: true, | |
| }, | |
| { | |
| name: "invalid action", | |
| mockedClient: mock.NewMockedHTTPClient(), | |
| requestArgs: map[string]interface{}{ | |
| "notificationID": "123", | |
| "action": "invalid", | |
| }, | |
| expectError: false, | |
| expectInvalid: true, | |
| }, | |
| { | |
| name: "missing required notificationID", | |
| mockedClient: mock.NewMockedHTTPClient(), | |
| requestArgs: map[string]interface{}{ | |
| "action": "ignore", | |
| }, | |
| expectError: true, | |
| }, | |
| { | |
| name: "missing required action", | |
| mockedClient: mock.NewMockedHTTPClient(), | |
| requestArgs: map[string]interface{}{ | |
| "notificationID": "123", | |
| }, | |
| expectError: true, | |
| }, | |
| } | |
| for _, tc := range tests { | |
| t.Run(tc.name, func(t *testing.T) { | |
| client := github.NewClient(tc.mockedClient) | |
| _, handler := ManageNotificationSubscription(stubGetClientFn(client), translations.NullTranslationHelper) | |
| request := createMCPRequest(tc.requestArgs) | |
| result, err := handler(context.Background(), request) | |
| if tc.expectError { | |
| require.NoError(t, err) | |
| require.NotNil(t, result) | |
| text := getTextResult(t, result).Text | |
| switch { | |
| case tc.requestArgs["notificationID"] == nil: | |
| assert.Contains(t, text, "missing required parameter: notificationID") | |
| case tc.requestArgs["action"] == nil: | |
| assert.Contains(t, text, "missing required parameter: action") | |
| default: | |
| assert.Contains(t, text, "error") | |
| } | |
| return | |
| } | |
| require.NoError(t, err) | |
| textContent := getTextResult(t, result) | |
| if tc.expectIgnored != nil { | |
| var returned github.Subscription | |
| err = json.Unmarshal([]byte(textContent.Text), &returned) | |
| require.NoError(t, err) | |
| assert.Equal(t, *tc.expectIgnored, *returned.Ignored) | |
| } | |
| if tc.expectDeleted { | |
| assert.Contains(t, textContent.Text, "deleted") | |
| } | |
| if tc.expectInvalid { | |
| assert.Contains(t, textContent.Text, "Invalid action") | |
| } | |
| }) | |
| } | |
| } | |
| func Test_ManageRepositoryNotificationSubscription(t *testing.T) { | |
| // Verify tool definition and schema | |
| mockClient := github.NewClient(nil) | |
| tool, _ := ManageRepositoryNotificationSubscription(stubGetClientFn(mockClient), translations.NullTranslationHelper) | |
| require.NoError(t, toolsnaps.Test(tool.Name, tool)) | |
| assert.Equal(t, "manage_repository_notification_subscription", tool.Name) | |
| assert.NotEmpty(t, tool.Description) | |
| assert.Contains(t, tool.InputSchema.Properties, "owner") | |
| assert.Contains(t, tool.InputSchema.Properties, "repo") | |
| assert.Contains(t, tool.InputSchema.Properties, "action") | |
| assert.ElementsMatch(t, tool.InputSchema.Required, []string{"owner", "repo", "action"}) | |
| mockSub := &github.Subscription{Ignored: github.Ptr(true)} | |
| mockWatchSub := &github.Subscription{Ignored: github.Ptr(false), Subscribed: github.Ptr(true)} | |
| tests := []struct { | |
| name string | |
| mockedClient *http.Client | |
| requestArgs map[string]interface{} | |
| expectError bool | |
| expectIgnored *bool | |
| expectSubscribed *bool | |
| expectDeleted bool | |
| expectInvalid bool | |
| expectedErrMsg string | |
| }{ | |
| { | |
| name: "ignore subscription", | |
| mockedClient: mock.NewMockedHTTPClient( | |
| mock.WithRequestMatch( | |
| mock.PutReposSubscriptionByOwnerByRepo, | |
| mockSub, | |
| ), | |
| ), | |
| requestArgs: map[string]interface{}{ | |
| "owner": "owner", | |
| "repo": "repo", | |
| "action": "ignore", | |
| }, | |
| expectError: false, | |
| expectIgnored: github.Ptr(true), | |
| }, | |
| { | |
| name: "watch subscription", | |
| mockedClient: mock.NewMockedHTTPClient( | |
| mock.WithRequestMatch( | |
| mock.PutReposSubscriptionByOwnerByRepo, | |
| mockWatchSub, | |
| ), | |
| ), | |
| requestArgs: map[string]interface{}{ | |
| "owner": "owner", | |
| "repo": "repo", | |
| "action": "watch", | |
| }, | |
| expectError: false, | |
| expectIgnored: github.Ptr(false), | |
| expectSubscribed: github.Ptr(true), | |
| }, | |
| { | |
| name: "delete subscription", | |
| mockedClient: mock.NewMockedHTTPClient( | |
| mock.WithRequestMatch( | |
| mock.DeleteReposSubscriptionByOwnerByRepo, | |
| nil, | |
| ), | |
| ), | |
| requestArgs: map[string]interface{}{ | |
| "owner": "owner", | |
| "repo": "repo", | |
| "action": "delete", | |
| }, | |
| expectError: false, | |
| expectDeleted: true, | |
| }, | |
| { | |
| name: "invalid action", | |
| mockedClient: mock.NewMockedHTTPClient(), | |
| requestArgs: map[string]interface{}{ | |
| "owner": "owner", | |
| "repo": "repo", | |
| "action": "invalid", | |
| }, | |
| expectError: false, | |
| expectInvalid: true, | |
| }, | |
| { | |
| name: "missing required owner", | |
| mockedClient: mock.NewMockedHTTPClient(), | |
| requestArgs: map[string]interface{}{ | |
| "repo": "repo", | |
| "action": "ignore", | |
| }, | |
| expectError: true, | |
| }, | |
| { | |
| name: "missing required repo", | |
| mockedClient: mock.NewMockedHTTPClient(), | |
| requestArgs: map[string]interface{}{ | |
| "owner": "owner", | |
| "action": "ignore", | |
| }, | |
| expectError: true, | |
| }, | |
| { | |
| name: "missing required action", | |
| mockedClient: mock.NewMockedHTTPClient(), | |
| requestArgs: map[string]interface{}{ | |
| "owner": "owner", | |
| "repo": "repo", | |
| }, | |
| expectError: true, | |
| }, | |
| } | |
| for _, tc := range tests { | |
| t.Run(tc.name, func(t *testing.T) { | |
| client := github.NewClient(tc.mockedClient) | |
| _, handler := ManageRepositoryNotificationSubscription(stubGetClientFn(client), translations.NullTranslationHelper) | |
| request := createMCPRequest(tc.requestArgs) | |
| result, err := handler(context.Background(), request) | |
| if tc.expectError { | |
| require.NoError(t, err) | |
| require.NotNil(t, result) | |
| text := getTextResult(t, result).Text | |
| switch { | |
| case tc.requestArgs["owner"] == nil: | |
| assert.Contains(t, text, "missing required parameter: owner") | |
| case tc.requestArgs["repo"] == nil: | |
| assert.Contains(t, text, "missing required parameter: repo") | |
| case tc.requestArgs["action"] == nil: | |
| assert.Contains(t, text, "missing required parameter: action") | |
| default: | |
| assert.Contains(t, text, "error") | |
| } | |
| return | |
| } | |
| require.NoError(t, err) | |
| textContent := getTextResult(t, result) | |
| if tc.expectIgnored != nil || tc.expectSubscribed != nil { | |
| var returned github.Subscription | |
| err = json.Unmarshal([]byte(textContent.Text), &returned) | |
| require.NoError(t, err) | |
| if tc.expectIgnored != nil { | |
| assert.Equal(t, *tc.expectIgnored, *returned.Ignored) | |
| } | |
| if tc.expectSubscribed != nil { | |
| assert.Equal(t, *tc.expectSubscribed, *returned.Subscribed) | |
| } | |
| } | |
| if tc.expectDeleted { | |
| assert.Contains(t, textContent.Text, "deleted") | |
| } | |
| if tc.expectInvalid { | |
| assert.Contains(t, textContent.Text, "Invalid action") | |
| } | |
| }) | |
| } | |
| } | |
| func Test_DismissNotification(t *testing.T) { | |
| // Verify tool definition and schema | |
| mockClient := github.NewClient(nil) | |
| tool, _ := DismissNotification(stubGetClientFn(mockClient), translations.NullTranslationHelper) | |
| require.NoError(t, toolsnaps.Test(tool.Name, tool)) | |
| assert.Equal(t, "dismiss_notification", tool.Name) | |
| assert.NotEmpty(t, tool.Description) | |
| assert.Contains(t, tool.InputSchema.Properties, "threadID") | |
| assert.Contains(t, tool.InputSchema.Properties, "state") | |
| assert.ElementsMatch(t, tool.InputSchema.Required, []string{"threadID"}) | |
| tests := []struct { | |
| name string | |
| mockedClient *http.Client | |
| requestArgs map[string]interface{} | |
| expectError bool | |
| expectRead bool | |
| expectDone bool | |
| expectInvalid bool | |
| expectedErrMsg string | |
| }{ | |
| { | |
| name: "mark as read", | |
| mockedClient: mock.NewMockedHTTPClient( | |
| mock.WithRequestMatch( | |
| mock.PatchNotificationsThreadsByThreadId, | |
| nil, | |
| ), | |
| ), | |
| requestArgs: map[string]interface{}{ | |
| "threadID": "123", | |
| "state": "read", | |
| }, | |
| expectError: false, | |
| expectRead: true, | |
| }, | |
| { | |
| name: "mark as done", | |
| mockedClient: mock.NewMockedHTTPClient( | |
| mock.WithRequestMatch( | |
| mock.DeleteNotificationsThreadsByThreadId, | |
| nil, | |
| ), | |
| ), | |
| requestArgs: map[string]interface{}{ | |
| "threadID": "123", | |
| "state": "done", | |
| }, | |
| expectError: false, | |
| expectDone: true, | |
| }, | |
| { | |
| name: "invalid threadID format", | |
| mockedClient: mock.NewMockedHTTPClient(), | |
| requestArgs: map[string]interface{}{ | |
| "threadID": "notanumber", | |
| "state": "done", | |
| }, | |
| expectError: false, | |
| expectInvalid: true, | |
| }, | |
| { | |
| name: "missing required threadID", | |
| mockedClient: mock.NewMockedHTTPClient(), | |
| requestArgs: map[string]interface{}{ | |
| "state": "read", | |
| }, | |
| expectError: true, | |
| }, | |
| { | |
| name: "missing required state", | |
| mockedClient: mock.NewMockedHTTPClient(), | |
| requestArgs: map[string]interface{}{ | |
| "threadID": "123", | |
| }, | |
| expectError: true, | |
| }, | |
| { | |
| name: "invalid state value", | |
| mockedClient: mock.NewMockedHTTPClient(), | |
| requestArgs: map[string]interface{}{ | |
| "threadID": "123", | |
| "state": "invalid", | |
| }, | |
| expectError: true, | |
| }, | |
| } | |
| for _, tc := range tests { | |
| t.Run(tc.name, func(t *testing.T) { | |
| client := github.NewClient(tc.mockedClient) | |
| _, handler := DismissNotification(stubGetClientFn(client), translations.NullTranslationHelper) | |
| request := createMCPRequest(tc.requestArgs) | |
| result, err := handler(context.Background(), request) | |
| if tc.expectError { | |
| // The tool returns a ToolResultError with a specific message | |
| require.NoError(t, err) | |
| require.NotNil(t, result) | |
| text := getTextResult(t, result).Text | |
| switch { | |
| case tc.requestArgs["threadID"] == nil: | |
| assert.Contains(t, text, "missing required parameter: threadID") | |
| case tc.requestArgs["state"] == nil: | |
| assert.Contains(t, text, "missing required parameter: state") | |
| case tc.name == "invalid threadID format": | |
| assert.Contains(t, text, "invalid threadID format") | |
| case tc.name == "invalid state value": | |
| assert.Contains(t, text, "Invalid state. Must be one of: read, done.") | |
| default: | |
| // fallback for other errors | |
| assert.Contains(t, text, "error") | |
| } | |
| return | |
| } | |
| require.NoError(t, err) | |
| textContent := getTextResult(t, result) | |
| if tc.expectRead { | |
| assert.Contains(t, textContent.Text, "Notification marked as read") | |
| } | |
| if tc.expectDone { | |
| assert.Contains(t, textContent.Text, "Notification marked as done") | |
| } | |
| if tc.expectInvalid { | |
| assert.Contains(t, textContent.Text, "invalid threadID format") | |
| } | |
| }) | |
| } | |
| } | |
| func Test_MarkAllNotificationsRead(t *testing.T) { | |
| // Verify tool definition and schema | |
| mockClient := github.NewClient(nil) | |
| tool, _ := MarkAllNotificationsRead(stubGetClientFn(mockClient), translations.NullTranslationHelper) | |
| require.NoError(t, toolsnaps.Test(tool.Name, tool)) | |
| assert.Equal(t, "mark_all_notifications_read", tool.Name) | |
| assert.NotEmpty(t, tool.Description) | |
| assert.Contains(t, tool.InputSchema.Properties, "lastReadAt") | |
| assert.Contains(t, tool.InputSchema.Properties, "owner") | |
| assert.Contains(t, tool.InputSchema.Properties, "repo") | |
| assert.Empty(t, tool.InputSchema.Required) | |
| tests := []struct { | |
| name string | |
| mockedClient *http.Client | |
| requestArgs map[string]interface{} | |
| expectError bool | |
| expectMarked bool | |
| expectedErrMsg string | |
| }{ | |
| { | |
| name: "success (no params)", | |
| mockedClient: mock.NewMockedHTTPClient( | |
| mock.WithRequestMatch( | |
| mock.PutNotifications, | |
| nil, | |
| ), | |
| ), | |
| requestArgs: map[string]interface{}{}, | |
| expectError: false, | |
| expectMarked: true, | |
| }, | |
| { | |
| name: "success with lastReadAt param", | |
| mockedClient: mock.NewMockedHTTPClient( | |
| mock.WithRequestMatch( | |
| mock.PutNotifications, | |
| nil, | |
| ), | |
| ), | |
| requestArgs: map[string]interface{}{ | |
| "lastReadAt": "2024-01-01T00:00:00Z", | |
| }, | |
| expectError: false, | |
| expectMarked: true, | |
| }, | |
| { | |
| name: "success with owner and repo", | |
| mockedClient: mock.NewMockedHTTPClient( | |
| mock.WithRequestMatch( | |
| mock.PutReposNotificationsByOwnerByRepo, | |
| nil, | |
| ), | |
| ), | |
| requestArgs: map[string]interface{}{ | |
| "owner": "octocat", | |
| "repo": "hello-world", | |
| }, | |
| expectError: false, | |
| expectMarked: true, | |
| }, | |
| { | |
| name: "API error", | |
| mockedClient: mock.NewMockedHTTPClient( | |
| mock.WithRequestMatchHandler( | |
| mock.PutNotifications, | |
| mockResponse(t, http.StatusInternalServerError, `{"message": "error"}`), | |
| ), | |
| ), | |
| requestArgs: map[string]interface{}{}, | |
| expectError: true, | |
| expectedErrMsg: "error", | |
| }, | |
| } | |
| for _, tc := range tests { | |
| t.Run(tc.name, func(t *testing.T) { | |
| client := github.NewClient(tc.mockedClient) | |
| _, handler := MarkAllNotificationsRead(stubGetClientFn(client), translations.NullTranslationHelper) | |
| request := createMCPRequest(tc.requestArgs) | |
| result, err := handler(context.Background(), request) | |
| if tc.expectError { | |
| require.NoError(t, err) | |
| require.True(t, result.IsError) | |
| errorContent := getErrorResult(t, result) | |
| if tc.expectedErrMsg != "" { | |
| assert.Contains(t, errorContent.Text, tc.expectedErrMsg) | |
| } | |
| return | |
| } | |
| require.NoError(t, err) | |
| require.False(t, result.IsError) | |
| textContent := getTextResult(t, result) | |
| if tc.expectMarked { | |
| assert.Contains(t, textContent.Text, "All notifications marked as read") | |
| } | |
| }) | |
| } | |
| } | |
| func Test_GetNotificationDetails(t *testing.T) { | |
| // Verify tool definition and schema | |
| mockClient := github.NewClient(nil) | |
| tool, _ := GetNotificationDetails(stubGetClientFn(mockClient), translations.NullTranslationHelper) | |
| require.NoError(t, toolsnaps.Test(tool.Name, tool)) | |
| assert.Equal(t, "get_notification_details", tool.Name) | |
| assert.NotEmpty(t, tool.Description) | |
| assert.Contains(t, tool.InputSchema.Properties, "notificationID") | |
| assert.ElementsMatch(t, tool.InputSchema.Required, []string{"notificationID"}) | |
| mockThread := &github.Notification{ID: github.Ptr("123"), Reason: github.Ptr("mention")} | |
| tests := []struct { | |
| name string | |
| mockedClient *http.Client | |
| requestArgs map[string]interface{} | |
| expectError bool | |
| expectResult *github.Notification | |
| expectedErrMsg string | |
| }{ | |
| { | |
| name: "success", | |
| mockedClient: mock.NewMockedHTTPClient( | |
| mock.WithRequestMatch( | |
| mock.GetNotificationsThreadsByThreadId, | |
| mockThread, | |
| ), | |
| ), | |
| requestArgs: map[string]interface{}{ | |
| "notificationID": "123", | |
| }, | |
| expectError: false, | |
| expectResult: mockThread, | |
| }, | |
| { | |
| name: "not found", | |
| mockedClient: mock.NewMockedHTTPClient( | |
| mock.WithRequestMatchHandler( | |
| mock.GetNotificationsThreadsByThreadId, | |
| mockResponse(t, http.StatusNotFound, `{"message": "not found"}`), | |
| ), | |
| ), | |
| requestArgs: map[string]interface{}{ | |
| "notificationID": "123", | |
| }, | |
| expectError: true, | |
| expectedErrMsg: "not found", | |
| }, | |
| } | |
| for _, tc := range tests { | |
| t.Run(tc.name, func(t *testing.T) { | |
| client := github.NewClient(tc.mockedClient) | |
| _, handler := GetNotificationDetails(stubGetClientFn(client), translations.NullTranslationHelper) | |
| request := createMCPRequest(tc.requestArgs) | |
| result, err := handler(context.Background(), request) | |
| if tc.expectError { | |
| require.NoError(t, err) | |
| require.True(t, result.IsError) | |
| errorContent := getErrorResult(t, result) | |
| if tc.expectedErrMsg != "" { | |
| assert.Contains(t, errorContent.Text, tc.expectedErrMsg) | |
| } | |
| return | |
| } | |
| require.NoError(t, err) | |
| require.False(t, result.IsError) | |
| textContent := getTextResult(t, result) | |
| var returned github.Notification | |
| err = json.Unmarshal([]byte(textContent.Text), &returned) | |
| require.NoError(t, err) | |
| assert.Equal(t, *tc.expectResult.ID, *returned.ID) | |
| }) | |
| } | |
| } | |