@@ -3,13 +3,16 @@ package github
33import (
44 "context"
55 "encoding/json"
6+ "fmt"
67 "testing"
78 "time"
89
10+ "github.com/github/github-mcp-server/internal/githubv4mock"
911 "github.com/github/github-mcp-server/internal/toolsnaps"
1012 "github.com/github/github-mcp-server/pkg/translations"
1113 "github.com/google/go-github/v73/github"
1214 "github.com/migueleliasweb/go-github-mock/src/mock"
15+ "github.com/shurcooL/githubv4"
1316 "github.com/stretchr/testify/assert"
1417 "github.com/stretchr/testify/require"
1518)
@@ -139,3 +142,217 @@ func Test_GetMe(t *testing.T) {
139142 })
140143 }
141144}
145+
146+ func Test_GetMyTeams (t * testing.T ) {
147+ t .Parallel ()
148+
149+ tool , _ := GetMyTeams (nil , nil , translations .NullTranslationHelper )
150+ require .NoError (t , toolsnaps .Test (tool .Name , tool ))
151+
152+ assert .Equal (t , "get_my_teams" , tool .Name )
153+ assert .True (t , * tool .Annotations .ReadOnlyHint , "get_my_teams tool should be read-only" )
154+
155+ mockUser := & github.User {
156+ Login : github .Ptr ("testuser" ),
157+ Name : github .Ptr ("Test User" ),
158+ Email :
github .
Ptr (
"[email protected] " ),
159+ Bio : github .Ptr ("GitHub user for testing" ),
160+ Company : github .Ptr ("Test Company" ),
161+ Location : github .Ptr ("Test Location" ),
162+ HTMLURL : github .Ptr ("https://github.com/testuser" ),
163+ CreatedAt : & github.Timestamp {Time : time .Now ().Add (- 365 * 24 * time .Hour )},
164+ Type : github .Ptr ("User" ),
165+ Hireable : github .Ptr (true ),
166+ TwitterUsername : github .Ptr ("testuser_twitter" ),
167+ Plan : & github.Plan {
168+ Name : github .Ptr ("pro" ),
169+ },
170+ }
171+
172+ mockTeamsResponse := githubv4mock .DataResponse (map [string ]any {
173+ "user" : map [string ]any {
174+ "organizations" : map [string ]any {
175+ "nodes" : []map [string ]any {
176+ {
177+ "login" : "testorg1" ,
178+ "teams" : map [string ]any {
179+ "nodes" : []map [string ]any {
180+ {
181+ "name" : "Frontend Team" ,
182+ "slug" : "frontend-team" ,
183+ "description" : "Team responsible for frontend development" ,
184+ },
185+ {
186+ "name" : "Backend Team" ,
187+ "slug" : "backend-team" ,
188+ "description" : "Team responsible for backend development" ,
189+ },
190+ },
191+ },
192+ },
193+ {
194+ "login" : "testorg2" ,
195+ "teams" : map [string ]any {
196+ "nodes" : []map [string ]any {
197+ {
198+ "name" : "DevOps Team" ,
199+ "slug" : "devops-team" ,
200+ "description" : "Team responsible for DevOps and infrastructure" ,
201+ },
202+ },
203+ },
204+ },
205+ },
206+ },
207+ },
208+ })
209+
210+ mockNoTeamsResponse := githubv4mock .DataResponse (map [string ]any {
211+ "user" : map [string ]any {
212+ "organizations" : map [string ]any {
213+ "nodes" : []map [string ]any {},
214+ },
215+ },
216+ })
217+
218+ tests := []struct {
219+ name string
220+ stubbedGetClientFn GetClientFn
221+ stubbedGetGQLClientFn GetGQLClientFn
222+ requestArgs map [string ]any
223+ expectToolError bool
224+ expectedToolErrMsg string
225+ expectedTeamsCount int
226+ }{
227+ {
228+ name : "successful get teams" ,
229+ stubbedGetClientFn : stubGetClientFromHTTPFn (
230+ mock .NewMockedHTTPClient (
231+ mock .WithRequestMatch (
232+ mock .GetUser ,
233+ mockUser ,
234+ ),
235+ ),
236+ ),
237+ stubbedGetGQLClientFn : func (_ context.Context ) (* githubv4.Client , error ) {
238+ // The GraphQL query constructed by the Go struct
239+ queryStr := "query($login:String!){user(login: $login){organizations(first: 100){nodes{login,teams(first: 100, userLogins: [$login]){nodes{name,slug,description}}}}}}"
240+ vars := map [string ]interface {}{
241+ "login" : "testuser" ,
242+ }
243+ matcher := githubv4mock .NewQueryMatcher (queryStr , vars , mockTeamsResponse )
244+ httpClient := githubv4mock .NewMockedHTTPClient (matcher )
245+ return githubv4 .NewClient (httpClient ), nil
246+ },
247+ requestArgs : map [string ]any {},
248+ expectToolError : false ,
249+ expectedTeamsCount : 2 ,
250+ },
251+ {
252+ name : "no teams found" ,
253+ stubbedGetClientFn : stubGetClientFromHTTPFn (
254+ mock .NewMockedHTTPClient (
255+ mock .WithRequestMatch (
256+ mock .GetUser ,
257+ mockUser ,
258+ ),
259+ ),
260+ ),
261+ stubbedGetGQLClientFn : func (_ context.Context ) (* githubv4.Client , error ) {
262+ queryStr := "query($login:String!){user(login: $login){organizations(first: 100){nodes{login,teams(first: 100, userLogins: [$login]){nodes{name,slug,description}}}}}}"
263+ vars := map [string ]interface {}{
264+ "login" : "testuser" ,
265+ }
266+ matcher := githubv4mock .NewQueryMatcher (queryStr , vars , mockNoTeamsResponse )
267+ httpClient := githubv4mock .NewMockedHTTPClient (matcher )
268+ return githubv4 .NewClient (httpClient ), nil
269+ },
270+ requestArgs : map [string ]any {},
271+ expectToolError : true ,
272+ expectedToolErrMsg : "no teams found for user" ,
273+ },
274+ {
275+ name : "getting client fails" ,
276+ stubbedGetClientFn : stubGetClientFnErr ("expected test error" ),
277+ stubbedGetGQLClientFn : nil ,
278+ requestArgs : map [string ]any {},
279+ expectToolError : true ,
280+ expectedToolErrMsg : "failed to get GitHub client: expected test error" ,
281+ },
282+ {
283+ name : "get user fails" ,
284+ stubbedGetClientFn : stubGetClientFromHTTPFn (
285+ mock .NewMockedHTTPClient (
286+ mock .WithRequestMatchHandler (
287+ mock .GetUser ,
288+ badRequestHandler ("expected test failure" ),
289+ ),
290+ ),
291+ ),
292+ stubbedGetGQLClientFn : nil ,
293+ requestArgs : map [string ]any {},
294+ expectToolError : true ,
295+ expectedToolErrMsg : "expected test failure" ,
296+ },
297+ {
298+ name : "getting GraphQL client fails" ,
299+ stubbedGetClientFn : stubGetClientFromHTTPFn (
300+ mock .NewMockedHTTPClient (
301+ mock .WithRequestMatch (
302+ mock .GetUser ,
303+ mockUser ,
304+ ),
305+ ),
306+ ),
307+ stubbedGetGQLClientFn : func (_ context.Context ) (* githubv4.Client , error ) {
308+ return nil , fmt .Errorf ("GraphQL client error" )
309+ },
310+ requestArgs : map [string ]any {},
311+ expectToolError : true ,
312+ expectedToolErrMsg : "failed to get GitHub GQL client: GraphQL client error" ,
313+ },
314+ }
315+
316+ for _ , tc := range tests {
317+ t .Run (tc .name , func (t * testing.T ) {
318+ _ , handler := GetMyTeams (tc .stubbedGetClientFn , tc .stubbedGetGQLClientFn , translations .NullTranslationHelper )
319+
320+ request := createMCPRequest (tc .requestArgs )
321+ result , err := handler (context .Background (), request )
322+ require .NoError (t , err )
323+ textContent := getTextResult (t , result )
324+
325+ if tc .expectToolError {
326+ assert .True (t , result .IsError , "expected tool call result to be an error" )
327+ assert .Contains (t , textContent .Text , tc .expectedToolErrMsg )
328+ return
329+ }
330+
331+ var organizations []struct {
332+ Login string `json:"login"`
333+ Teams struct {
334+ Nodes []struct {
335+ Name string `json:"name"`
336+ Slug string `json:"slug"`
337+ Description string `json:"description"`
338+ } `json:"nodes"`
339+ } `json:"teams"`
340+ }
341+ err = json .Unmarshal ([]byte (textContent .Text ), & organizations )
342+ require .NoError (t , err )
343+
344+ assert .Len (t , organizations , tc .expectedTeamsCount )
345+
346+ if tc .expectedTeamsCount > 0 {
347+ assert .Equal (t , "testorg1" , organizations [0 ].Login )
348+ assert .Len (t , organizations [0 ].Teams .Nodes , 2 )
349+ assert .Equal (t , "Frontend Team" , organizations [0 ].Teams .Nodes [0 ].Name )
350+ assert .Equal (t , "frontend-team" , organizations [0 ].Teams .Nodes [0 ].Slug )
351+
352+ assert .Equal (t , "testorg2" , organizations [1 ].Login )
353+ assert .Len (t , organizations [1 ].Teams .Nodes , 1 )
354+ assert .Equal (t , "DevOps Team" , organizations [1 ].Teams .Nodes [0 ].Name )
355+ }
356+ })
357+ }
358+ }
0 commit comments