@@ -241,3 +241,147 @@ func Test_GetGlobalSecurityAdvisory(t *testing.T) {
241241 })
242242 }
243243}
244+
245+ func Test_ListRepositorySecurityAdvisories (t * testing.T ) {
246+ // Verify tool definition once
247+ mockClient := github .NewClient (nil )
248+ tool , _ := ListRepositorySecurityAdvisories (stubGetClientFn (mockClient ), translations .NullTranslationHelper )
249+
250+ assert .Equal (t , "list_repository_security_advisories" , tool .Name )
251+ assert .NotEmpty (t , tool .Description )
252+ assert .Contains (t , tool .InputSchema .Properties , "owner" )
253+ assert .Contains (t , tool .InputSchema .Properties , "repo" )
254+ assert .Contains (t , tool .InputSchema .Properties , "direction" )
255+ assert .Contains (t , tool .InputSchema .Properties , "sort" )
256+ assert .Contains (t , tool .InputSchema .Properties , "state" )
257+ assert .ElementsMatch (t , tool .InputSchema .Required , []string {"owner" , "repo" })
258+
259+ // Local endpoint pattern for repository security advisories
260+ var GetReposSecurityAdvisoriesByOwnerByRepo = mock.EndpointPattern {
261+ Pattern : "/repos/{owner}/{repo}/security-advisories" ,
262+ Method : "GET" ,
263+ }
264+
265+ // Setup mock advisories for success cases
266+ adv1 := & github.SecurityAdvisory {
267+ GHSAID : github .Ptr ("GHSA-1111-1111-1111" ),
268+ Summary : github .Ptr ("Repo advisory one" ),
269+ Description : github .Ptr ("First repo advisory." ),
270+ Severity : github .Ptr ("high" ),
271+ }
272+ adv2 := & github.SecurityAdvisory {
273+ GHSAID : github .Ptr ("GHSA-2222-2222-2222" ),
274+ Summary : github .Ptr ("Repo advisory two" ),
275+ Description : github .Ptr ("Second repo advisory." ),
276+ Severity : github .Ptr ("medium" ),
277+ }
278+
279+ tests := []struct {
280+ name string
281+ mockedClient * http.Client
282+ requestArgs map [string ]interface {}
283+ expectError bool
284+ expectedAdvisories []* github.SecurityAdvisory
285+ expectedErrMsg string
286+ }{
287+ {
288+ name : "successful advisories listing (no filters)" ,
289+ mockedClient : mock .NewMockedHTTPClient (
290+ mock .WithRequestMatchHandler (
291+ GetReposSecurityAdvisoriesByOwnerByRepo ,
292+ expect (t , expectations {
293+ path : "/repos/owner/repo/security-advisories" ,
294+ queryParams : map [string ]string {},
295+ }).andThen (
296+ mockResponse (t , http .StatusOK , []* github.SecurityAdvisory {adv1 , adv2 }),
297+ ),
298+ ),
299+ ),
300+ requestArgs : map [string ]interface {}{
301+ "owner" : "owner" ,
302+ "repo" : "repo" ,
303+ },
304+ expectError : false ,
305+ expectedAdvisories : []* github.SecurityAdvisory {adv1 , adv2 },
306+ },
307+ {
308+ name : "successful advisories listing with filters" ,
309+ mockedClient : mock .NewMockedHTTPClient (
310+ mock .WithRequestMatchHandler (
311+ GetReposSecurityAdvisoriesByOwnerByRepo ,
312+ expect (t , expectations {
313+ path : "/repos/octo/hello-world/security-advisories" ,
314+ queryParams : map [string ]string {
315+ "direction" : "desc" ,
316+ "sort" : "updated" ,
317+ "state" : "published" ,
318+ },
319+ }).andThen (
320+ mockResponse (t , http .StatusOK , []* github.SecurityAdvisory {adv1 }),
321+ ),
322+ ),
323+ ),
324+ requestArgs : map [string ]interface {}{
325+ "owner" : "octo" ,
326+ "repo" : "hello-world" ,
327+ "direction" : "desc" ,
328+ "sort" : "updated" ,
329+ "state" : "published" ,
330+ },
331+ expectError : false ,
332+ expectedAdvisories : []* github.SecurityAdvisory {adv1 },
333+ },
334+ {
335+ name : "advisories listing fails" ,
336+ mockedClient : mock .NewMockedHTTPClient (
337+ mock .WithRequestMatchHandler (
338+ GetReposSecurityAdvisoriesByOwnerByRepo ,
339+ expect (t , expectations {
340+ path : "/repos/owner/repo/security-advisories" ,
341+ queryParams : map [string ]string {},
342+ }).andThen (
343+ mockResponse (t , http .StatusInternalServerError , map [string ]string {"message" : "Internal Server Error" }),
344+ ),
345+ ),
346+ ),
347+ requestArgs : map [string ]interface {}{
348+ "owner" : "owner" ,
349+ "repo" : "repo" ,
350+ },
351+ expectError : true ,
352+ expectedErrMsg : "failed to list repository security advisories" ,
353+ },
354+ }
355+
356+ for _ , tc := range tests {
357+ t .Run (tc .name , func (t * testing.T ) {
358+ client := github .NewClient (tc .mockedClient )
359+ _ , handler := ListRepositorySecurityAdvisories (stubGetClientFn (client ), translations .NullTranslationHelper )
360+
361+ request := createMCPRequest (tc .requestArgs )
362+
363+ result , err := handler (context .Background (), request )
364+
365+ if tc .expectError {
366+ require .Error (t , err )
367+ assert .Contains (t , err .Error (), tc .expectedErrMsg )
368+ return
369+ }
370+
371+ require .NoError (t , err )
372+
373+ textContent := getTextResult (t , result )
374+
375+ var returnedAdvisories []* github.SecurityAdvisory
376+ err = json .Unmarshal ([]byte (textContent .Text ), & returnedAdvisories )
377+ assert .NoError (t , err )
378+ assert .Len (t , returnedAdvisories , len (tc .expectedAdvisories ))
379+ for i , advisory := range returnedAdvisories {
380+ assert .Equal (t , * tc .expectedAdvisories [i ].GHSAID , * advisory .GHSAID )
381+ assert .Equal (t , * tc .expectedAdvisories [i ].Summary , * advisory .Summary )
382+ assert .Equal (t , * tc .expectedAdvisories [i ].Description , * advisory .Description )
383+ assert .Equal (t , * tc .expectedAdvisories [i ].Severity , * advisory .Severity )
384+ }
385+ })
386+ }
387+ }
0 commit comments