@@ -565,6 +565,162 @@ func Test_MergePullRequest(t *testing.T) {
565
565
}
566
566
}
567
567
568
+ func Test_SearchPullRequests (t * testing.T ) {
569
+ mockClient := github .NewClient (nil )
570
+ tool ,_ := SearchPullRequests (stubGetClientFn (mockClient ),translations .NullTranslationHelper )
571
+ require .NoError (t ,toolsnaps .Test (tool .Name ,tool ))
572
+
573
+ assert .Equal (t ,"search_pull_requests" ,tool .Name )
574
+ assert .NotEmpty (t ,tool .Description )
575
+ assert .Contains (t ,tool .InputSchema .Properties ,"q" )
576
+ assert .Contains (t ,tool .InputSchema .Properties ,"sort" )
577
+ assert .Contains (t ,tool .InputSchema .Properties ,"order" )
578
+ assert .Contains (t ,tool .InputSchema .Properties ,"perPage" )
579
+ assert .Contains (t ,tool .InputSchema .Properties ,"page" )
580
+ assert .ElementsMatch (t ,tool .InputSchema .Required , []string {"q" })
581
+
582
+ mockSearchResult := & github.IssuesSearchResult {
583
+ Total :github .Ptr (2 ),
584
+ IncompleteResults :github .Ptr (false ),
585
+ Issues : []* github.Issue {
586
+ {
587
+ Number :github .Ptr (42 ),
588
+ Title :github .Ptr ("Test PR 1" ),
589
+ Body :github .Ptr ("Updated tests." ),
590
+ State :github .Ptr ("open" ),
591
+ HTMLURL :github .Ptr ("https://github.com/owner/repo/pull/1" ),
592
+ Comments :github .Ptr (5 ),
593
+ User :& github.User {
594
+ Login :github .Ptr ("user1" ),
595
+ },
596
+ },
597
+ {
598
+ Number :github .Ptr (43 ),
599
+ Title :github .Ptr ("Test PR 2" ),
600
+ Body :github .Ptr ("Updated build scripts." ),
601
+ State :github .Ptr ("open" ),
602
+ HTMLURL :github .Ptr ("https://github.com/owner/repo/pull/2" ),
603
+ Comments :github .Ptr (3 ),
604
+ User :& github.User {
605
+ Login :github .Ptr ("user2" ),
606
+ },
607
+ },
608
+ },
609
+ }
610
+
611
+ tests := []struct {
612
+ name string
613
+ mockedClient * http.Client
614
+ requestArgs map [string ]interface {}
615
+ expectError bool
616
+ expectedResult * github.IssuesSearchResult
617
+ expectedErrMsg string
618
+ }{
619
+ {
620
+ name :"successful pull request search with all parameters" ,
621
+ mockedClient :mock .NewMockedHTTPClient (
622
+ mock .WithRequestMatchHandler (
623
+ mock .GetSearchIssues ,
624
+ expectQueryParams (
625
+ t ,
626
+ map [string ]string {
627
+ "q" :"repo:owner/repo is:pr is:open" ,
628
+ "sort" :"created" ,
629
+ "order" :"desc" ,
630
+ "page" :"1" ,
631
+ "per_page" :"30" ,
632
+ },
633
+ ).andThen (
634
+ mockResponse (t ,http .StatusOK ,mockSearchResult ),
635
+ ),
636
+ ),
637
+ ),
638
+ requestArgs :map [string ]interface {}{
639
+ "q" :"repo:owner/repo is:pr is:open" ,
640
+ "sort" :"created" ,
641
+ "order" :"desc" ,
642
+ "page" :float64 (1 ),
643
+ "perPage" :float64 (30 ),
644
+ },
645
+ expectError :false ,
646
+ expectedResult :mockSearchResult ,
647
+ },
648
+ {
649
+ name :"pull request search with minimal parameters" ,
650
+ mockedClient :mock .NewMockedHTTPClient (
651
+ mock .WithRequestMatch (
652
+ mock .GetSearchIssues ,
653
+ mockSearchResult ,
654
+ ),
655
+ ),
656
+ requestArgs :map [string ]interface {}{
657
+ "q" :"repo:owner/repo is:pr is:open" ,
658
+ },
659
+ expectError :false ,
660
+ expectedResult :mockSearchResult ,
661
+ },
662
+ {
663
+ name :"search pull requests fails" ,
664
+ mockedClient :mock .NewMockedHTTPClient (
665
+ mock .WithRequestMatchHandler (
666
+ mock .GetSearchIssues ,
667
+ http .HandlerFunc (func (w http.ResponseWriter ,_ * http.Request ) {
668
+ w .WriteHeader (http .StatusBadRequest )
669
+ _ ,_ = w .Write ([]byte (`{"message": "Validation Failed"}` ))
670
+ }),
671
+ ),
672
+ ),
673
+ requestArgs :map [string ]interface {}{
674
+ "q" :"invalid:query" ,
675
+ },
676
+ expectError :true ,
677
+ expectedErrMsg :"failed to search issues" ,
678
+ },
679
+ }
680
+
681
+ for _ ,tc := range tests {
682
+ t .Run (tc .name ,func (t * testing.T ) {
683
+ // Setup client with mock
684
+ client := github .NewClient (tc .mockedClient )
685
+ _ ,handler := SearchIssues (stubGetClientFn (client ),translations .NullTranslationHelper )
686
+
687
+ // Create call request
688
+ request := createMCPRequest (tc .requestArgs )
689
+
690
+ // Call handler
691
+ result ,err := handler (context .Background (),request )
692
+
693
+ // Verify results
694
+ if tc .expectError {
695
+ require .Error (t ,err )
696
+ assert .Contains (t ,err .Error (),tc .expectedErrMsg )
697
+ return
698
+ }
699
+
700
+ require .NoError (t ,err )
701
+
702
+ // Parse the result and get the text content if no error
703
+ textContent := getTextResult (t ,result )
704
+
705
+ // Unmarshal and verify the result
706
+ var returnedResult github.IssuesSearchResult
707
+ err = json .Unmarshal ([]byte (textContent .Text ),& returnedResult )
708
+ require .NoError (t ,err )
709
+ assert .Equal (t ,* tc .expectedResult .Total ,* returnedResult .Total )
710
+ assert .Equal (t ,* tc .expectedResult .IncompleteResults ,* returnedResult .IncompleteResults )
711
+ assert .Len (t ,returnedResult .Issues ,len (tc .expectedResult .Issues ))
712
+ for i ,issue := range returnedResult .Issues {
713
+ assert .Equal (t ,* tc .expectedResult .Issues [i ].Number ,* issue .Number )
714
+ assert .Equal (t ,* tc .expectedResult .Issues [i ].Title ,* issue .Title )
715
+ assert .Equal (t ,* tc .expectedResult .Issues [i ].State ,* issue .State )
716
+ assert .Equal (t ,* tc .expectedResult .Issues [i ].HTMLURL ,* issue .HTMLURL )
717
+ assert .Equal (t ,* tc .expectedResult .Issues [i ].User .Login ,* issue .User .Login )
718
+ }
719
+ })
720
+ }
721
+
722
+ }
723
+
568
724
func Test_GetPullRequestFiles (t * testing.T ) {
569
725
// Verify tool definition once
570
726
mockClient := github .NewClient (nil )