@@ -3,13 +3,16 @@ package github
3
3
import (
4
4
"context"
5
5
"encoding/json"
6
+ "fmt"
6
7
"testing"
7
8
"time"
8
9
10
+ "github.com/github/github-mcp-server/internal/githubv4mock"
9
11
"github.com/github/github-mcp-server/internal/toolsnaps"
10
12
"github.com/github/github-mcp-server/pkg/translations"
11
13
"github.com/google/go-github/v73/github"
12
14
"github.com/migueleliasweb/go-github-mock/src/mock"
15
+ "github.com/shurcooL/githubv4"
13
16
"github.com/stretchr/testify/assert"
14
17
"github.com/stretchr/testify/require"
15
18
)
@@ -139,3 +142,217 @@ func Test_GetMe(t *testing.T) {
139
142
})
140
143
}
141
144
}
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 ("test@example.com" ),
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