Expand Up @@ -5,7 +5,6 @@ import ( "encoding/json" "net/http" "testing" "time" "github.com/github/github-mcp-server/internal/githubv4mock" "github.com/github/github-mcp-server/pkg/translations" Expand All @@ -17,75 +16,89 @@ import ( var ( discussionsGeneral = []map[string]any{ {"number": 1, "title": "Discussion 1 title", "createdAt": "2023-01-01T00:00:00Z", "updatedAt": "2023-01-01T00:00:00Z", "author": map[string]any{"login": "user1"}, "url": "https://github.com/owner/repo/discussions/1", "category": map[string]any{"name": "General"}}, {"number": 3, "title": "Discussion 3 title", "createdAt": "2023-03-01T00:00:00Z", "updatedAt": "2023-02-01T00:00:00Z", "author": map[string]any{"login": "user1"}, "url": "https://github.com/owner/repo/discussions/3", "category": map[string]any{"name": "General"}}, {"number": 1, "title": "Discussion 1 title", "createdAt": "2023-01-01T00:00:00Z", "updatedAt": "2023-01-01T00:00:00Z", "closed": false, "isAnswered": false, " author": map[string]any{"login": "user1"}, "url": "https://github.com/owner/repo/discussions/1", "category": map[string]any{"name": "General"}}, {"number": 3, "title": "Discussion 3 title", "createdAt": "2023-03-01T00:00:00Z", "updatedAt": "2023-02-01T00:00:00Z", "closed": false, "isAnswered": false, " author": map[string]any{"login": "user1"}, "url": "https://github.com/owner/repo/discussions/3", "category": map[string]any{"name": "General"}}, } discussionsAll = []map[string]any{ { "number": 1, "title": "Discussion 1 title", "createdAt": "2023-01-01T00:00:00Z", "updatedAt": "2023-01-01T00:00:00Z", "author": map[string]any{"login": "user1"}, "url": "https://github.com/owner/repo/discussions/1", "category": map[string]any{"name": "General"}, "number": 1, "title": "Discussion 1 title", "createdAt": "2023-01-01T00:00:00Z", "updatedAt": "2023-01-01T00:00:00Z", "closed": false, "isAnswered": false, "author": map[string]any{"login": "user1"}, "url": "https://github.com/owner/repo/discussions/1", "category": map[string]any{"name": "General"}, }, { "number": 2, "title": "Discussion 2 title", "createdAt": "2023-02-01T00:00:00Z", "updatedAt": "2023-02-01T00:00:00Z", "author": map[string]any{"login": "user2"}, "url": "https://github.com/owner/repo/discussions/2", "category": map[string]any{"name": "Questions"}, "number": 2, "title": "Discussion 2 title", "createdAt": "2023-02-01T00:00:00Z", "updatedAt": "2023-02-01T00:00:00Z", "closed": false, "isAnswered": false, "author": map[string]any{"login": "user2"}, "url": "https://github.com/owner/repo/discussions/2", "category": map[string]any{"name": "Questions"}, }, { "number": 3, "title": "Discussion 3 title", "createdAt": "2023-03-01T00:00:00Z", "updatedAt": "2023-03-01T00:00:00Z", "author": map[string]any{"login": "user3"}, "url": "https://github.com/owner/repo/discussions/3", "category": map[string]any{"name": "General"}, "number": 3, "title": "Discussion 3 title", "createdAt": "2023-03-01T00:00:00Z", "updatedAt": "2023-03-01T00:00:00Z", "closed": false, "isAnswered": false, "author": map[string]any{"login": "user3"}, "url": "https://github.com/owner/repo/discussions/3", "category": map[string]any{"name": "General"}, }, } discussionsOrgLevel = []map[string]any{ { "number": 1, "title": "Org Discussion 1 - Community Guidelines", "createdAt": "2023-01-15T00:00:00Z", "updatedAt": "2023-01-15T00:00:00Z", "author": map[string]any{"login": "org-admin"}, "url": "https://github.com/owner/.github/discussions/1", "category": map[string]any{"name": "Announcements"}, "number": 1, "title": "Org Discussion 1 - Community Guidelines", "createdAt": "2023-01-15T00:00:00Z", "updatedAt": "2023-01-15T00:00:00Z", "closed": false, "isAnswered": false, "author": map[string]any{"login": "org-admin"}, "url": "https://github.com/owner/.github/discussions/1", "category": map[string]any{"name": "Announcements"}, }, { "number": 2, "title": "Org Discussion 2 - Roadmap 2023", "createdAt": "2023-02-20T00:00:00Z", "updatedAt": "2023-02-20T00:00:00Z", "author": map[string]any{"login": "org-admin"}, "url": "https://github.com/owner/.github/discussions/2", "category": map[string]any{"name": "General"}, "number": 2, "title": "Org Discussion 2 - Roadmap 2023", "createdAt": "2023-02-20T00:00:00Z", "updatedAt": "2023-02-20T00:00:00Z", "closed": false, "isAnswered": false, "author": map[string]any{"login": "org-admin"}, "url": "https://github.com/owner/.github/discussions/2", "category": map[string]any{"name": "General"}, }, { "number": 3, "title": "Org Discussion 3 - Roadmap 2024", "createdAt": "2023-02-20T00:00:00Z", "updatedAt": "2023-02-20T00:00:00Z", "author": map[string]any{"login": "org-admin"}, "url": "https://github.com/owner/.github/discussions/3", "category": map[string]any{"name": "General"}, "number": 3, "title": "Org Discussion 3 - Roadmap 2024", "createdAt": "2023-02-20T00:00:00Z", "updatedAt": "2023-02-20T00:00:00Z", "closed": false, "isAnswered": false, "author": map[string]any{"login": "org-admin"}, "url": "https://github.com/owner/.github/discussions/3", "category": map[string]any{"name": "General"}, }, { "number": 4, "title": "Org Discussion 4 - Roadmap 2025", "createdAt": "2023-02-20T00:00:00Z", "updatedAt": "2023-02-20T00:00:00Z", "author": map[string]any{"login": "org-admin"}, "url": "https://github.com/owner/.github/discussions/4", "category": map[string]any{"name": "General"}, "number": 4, "title": "Org Discussion 4 - Roadmap 2025", "createdAt": "2023-02-20T00:00:00Z", "updatedAt": "2023-02-20T00:00:00Z", "closed": false, "isAnswered": false, "author": map[string]any{"login": "org-admin"}, "url": "https://github.com/owner/.github/discussions/4", "category": map[string]any{"name": "General"}, }, } Expand Down Expand Up @@ -388,10 +401,10 @@ func Test_ListDiscussions(t *testing.T) { } // Define the actual query strings that match the implementation qBasicNoOrder := "query($after:String$first:Int!$owner:String!$repo:String!){repository(owner: $owner, name: $repo){discussions(first: $first, after: $after){nodes{number,title,createdAt,updatedAt,author{login},category{name},url},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor},totalCount}}}" qWithCategoryNoOrder := "query($after:String$categoryId:ID!$first:Int!$owner:String!$repo:String!){repository(owner: $owner, name: $repo){discussions(first: $first, after: $after, categoryId: $categoryId){nodes{number,title,createdAt,updatedAt,author{login},category{name},url},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor},totalCount}}}" qBasicWithOrder := "query($after:String$first:Int!$orderByDirection:OrderDirection!$orderByField:DiscussionOrderField!$owner:String!$repo:String!){repository(owner: $owner, name: $repo){discussions(first: $first, after: $after, orderBy: { field: $orderByField, direction: $orderByDirection }){nodes{number,title,createdAt,updatedAt,author{login},category{name},url},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor},totalCount}}}" qWithCategoryAndOrder := "query($after:String$categoryId:ID!$first:Int!$orderByDirection:OrderDirection!$orderByField:DiscussionOrderField!$owner:String!$repo:String!){repository(owner: $owner, name: $repo){discussions(first: $first, after: $after, categoryId: $categoryId, orderBy: { field: $orderByField, direction: $orderByDirection }){nodes{number,title,createdAt,updatedAt,author{login},category{name},url},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor},totalCount}}}" qBasicNoOrder := "query($after:String$first:Int!$owner:String!$repo:String!){repository(owner: $owner, name: $repo){discussions(first: $first, after: $after){nodes{number,title,createdAt,updatedAt,closed,isAnswered,answerChosenAt, author{login},category{name},url},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor},totalCount}}}" qWithCategoryNoOrder := "query($after:String$categoryId:ID!$first:Int!$owner:String!$repo:String!){repository(owner: $owner, name: $repo){discussions(first: $first, after: $after, categoryId: $categoryId){nodes{number,title,createdAt,updatedAt,closed,isAnswered,answerChosenAt, author{login},category{name},url},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor},totalCount}}}" qBasicWithOrder := "query($after:String$first:Int!$orderByDirection:OrderDirection!$orderByField:DiscussionOrderField!$owner:String!$repo:String!){repository(owner: $owner, name: $repo){discussions(first: $first, after: $after, orderBy: { field: $orderByField, direction: $orderByDirection }){nodes{number,title,createdAt,updatedAt,closed,isAnswered,answerChosenAt, author{login},category{name},url},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor},totalCount}}}" qWithCategoryAndOrder := "query($after:String$categoryId:ID!$first:Int!$orderByDirection:OrderDirection!$orderByField:DiscussionOrderField!$owner:String!$repo:String!){repository(owner: $owner, name: $repo){discussions(first: $first, after: $after, categoryId: $categoryId, orderBy: { field: $orderByField, direction: $orderByDirection }){nodes{number,title,createdAt,updatedAt,closed,isAnswered,answerChosenAt, author{login},category{name},url},pageInfo{hasNextPage,hasPreviousPage,startCursor,endCursor},totalCount}}}" for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { Expand Down Expand Up @@ -484,7 +497,7 @@ func Test_GetDiscussion(t *testing.T) { assert.ElementsMatch(t, toolDef.InputSchema.Required, []string{"owner", "repo", "discussionNumber"}) // Use exact string query that matches implementation output qGetDiscussion := "query($discussionNumber:Int!$owner:String!$repo:String!){repository(owner: $owner, name: $repo){discussion(number: $discussionNumber){number,title,body,createdAt,url,category{name}}}}" qGetDiscussion := "query($discussionNumber:Int!$owner:String!$repo:String!){repository(owner: $owner, name: $repo){discussion(number: $discussionNumber){number,title,body,createdAt,closed,isAnswered,answerChosenAt, url,category{name}}}}" vars := map[string]interface{}{ "owner": "owner", Expand All @@ -495,31 +508,31 @@ func Test_GetDiscussion(t *testing.T) { name string response githubv4mock.GQLResponse expectError bool expected*github.Discussion expectedmap[string]interface{} errContains string }{ { name: "successful retrieval", response: githubv4mock.DataResponse(map[string]any{ "repository": map[string]any{"discussion": map[string]any{ "number": 1, "title": "Test Discussion Title", "body": "This is a test discussion", "url": "https://github.com/owner/repo/discussions/1", "createdAt": "2025-04-25T12:00:00Z", "category": map[string]any{"name": "General"}, "number": 1, "title": "Test Discussion Title", "body": "This is a test discussion", "url": "https://github.com/owner/repo/discussions/1", "createdAt": "2025-04-25T12:00:00Z", "closed": false, "isAnswered": false, "category": map[string]any{"name": "General"}, }}, }), expectError: false, expected: &github.Discussion{ HTMLURL: github.Ptr("https://github.com/owner/repo/discussions/1"), Number: github.Ptr(1), Title: github.Ptr("Test Discussion Title"), Body: github.Ptr("This is a test discussion"), CreatedAt: &github.Timestamp{Time: time.Date(2025, 4, 25, 12, 0, 0, 0, time.UTC)}, DiscussionCategory: &github.DiscussionCategory{ Name: github.Ptr("General"), }, expected: map[string]interface{}{ "number": float64(1), "title": "Test Discussion Title", "body": "This is a test discussion", "url": "https://github.com/owner/repo/discussions/1", "closed": false, "isAnswered": false, }, }, { Expand Down Expand Up @@ -547,14 +560,18 @@ func Test_GetDiscussion(t *testing.T) { } require.NoError(t, err) var outgithub.Discussion var outmap[string]interface{} require.NoError(t, json.Unmarshal([]byte(text), &out)) assert.Equal(t, *tc.expected.HTMLURL, *out.HTMLURL) assert.Equal(t, *tc.expected.Number, *out.Number) assert.Equal(t, *tc.expected.Title, *out.Title) assert.Equal(t, *tc.expected.Body, *out.Body) // Check category label assert.Equal(t, *tc.expected.DiscussionCategory.Name, *out.DiscussionCategory.Name) assert.Equal(t, tc.expected["number"], out["number"]) assert.Equal(t, tc.expected["title"], out["title"]) assert.Equal(t, tc.expected["body"], out["body"]) assert.Equal(t, tc.expected["url"], out["url"]) assert.Equal(t, tc.expected["closed"], out["closed"]) assert.Equal(t, tc.expected["isAnswered"], out["isAnswered"]) // Check category is present category, ok := out["category"].(map[string]interface{}) require.True(t, ok) assert.Equal(t, "General", category["name"]) }) } } Expand Down