@@ -369,3 +369,158 @@ func Test_SearchIssues(t *testing.T) {
369369 })
370370 }
371371}
372+
373+ func Test_CreateIssue (t * testing.T ) {
374+ // Verify tool definition once
375+ mockClient := github .NewClient (nil )
376+ tool , _ := createIssue (mockClient )
377+
378+ assert .Equal (t , "create_issue" , tool .Name )
379+ assert .NotEmpty (t , tool .Description )
380+ assert .Contains (t , tool .InputSchema .Properties , "owner" )
381+ assert .Contains (t , tool .InputSchema .Properties , "repo" )
382+ assert .Contains (t , tool .InputSchema .Properties , "title" )
383+ assert .Contains (t , tool .InputSchema .Properties , "body" )
384+ assert .Contains (t , tool .InputSchema .Properties , "assignees" )
385+ assert .Contains (t , tool .InputSchema .Properties , "labels" )
386+ assert .ElementsMatch (t , tool .InputSchema .Required , []string {"owner" , "repo" , "title" })
387+
388+ // Setup mock issue for success case
389+ mockIssue := & github.Issue {
390+ Number : github .Ptr (123 ),
391+ Title : github .Ptr ("Test Issue" ),
392+ Body : github .Ptr ("This is a test issue" ),
393+ State : github .Ptr ("open" ),
394+ HTMLURL : github .Ptr ("https://github.com/owner/repo/issues/123" ),
395+ Assignees : []* github.User {{Login : github .Ptr ("user1" )}, {Login : github .Ptr ("user2" )}},
396+ Labels : []* github.Label {{Name : github .Ptr ("bug" )}, {Name : github .Ptr ("help wanted" )}},
397+ }
398+
399+ tests := []struct {
400+ name string
401+ mockedClient * http.Client
402+ requestArgs map [string ]interface {}
403+ expectError bool
404+ expectedIssue * github.Issue
405+ expectedErrMsg string
406+ }{
407+ {
408+ name : "successful issue creation with all fields" ,
409+ mockedClient : mock .NewMockedHTTPClient (
410+ mock .WithRequestMatchHandler (
411+ mock .PostReposIssuesByOwnerByRepo ,
412+ mockResponse (t , http .StatusCreated , mockIssue ),
413+ ),
414+ ),
415+ requestArgs : map [string ]interface {}{
416+ "owner" : "owner" ,
417+ "repo" : "repo" ,
418+ "title" : "Test Issue" ,
419+ "body" : "This is a test issue" ,
420+ "assignees" : []interface {}{"user1" , "user2" },
421+ "labels" : []interface {}{"bug" , "help wanted" },
422+ },
423+ expectError : false ,
424+ expectedIssue : mockIssue ,
425+ },
426+ {
427+ name : "successful issue creation with minimal fields" ,
428+ mockedClient : mock .NewMockedHTTPClient (
429+ mock .WithRequestMatchHandler (
430+ mock .PostReposIssuesByOwnerByRepo ,
431+ mockResponse (t , http .StatusCreated , & github.Issue {
432+ Number : github .Ptr (124 ),
433+ Title : github .Ptr ("Minimal Issue" ),
434+ HTMLURL : github .Ptr ("https://github.com/owner/repo/issues/124" ),
435+ State : github .Ptr ("open" ),
436+ }),
437+ ),
438+ ),
439+ requestArgs : map [string ]interface {}{
440+ "owner" : "owner" ,
441+ "repo" : "repo" ,
442+ "title" : "Minimal Issue" ,
443+ },
444+ expectError : false ,
445+ expectedIssue : & github.Issue {
446+ Number : github .Ptr (124 ),
447+ Title : github .Ptr ("Minimal Issue" ),
448+ HTMLURL : github .Ptr ("https://github.com/owner/repo/issues/124" ),
449+ State : github .Ptr ("open" ),
450+ },
451+ },
452+ {
453+ name : "issue creation fails" ,
454+ mockedClient : mock .NewMockedHTTPClient (
455+ mock .WithRequestMatchHandler (
456+ mock .PostReposIssuesByOwnerByRepo ,
457+ http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
458+ w .WriteHeader (http .StatusUnprocessableEntity )
459+ _ , _ = w .Write ([]byte (`{"message": "Validation failed"}` ))
460+ }),
461+ ),
462+ ),
463+ requestArgs : map [string ]interface {}{
464+ "owner" : "owner" ,
465+ "repo" : "repo" ,
466+ "title" : "" ,
467+ },
468+ expectError : true ,
469+ expectedErrMsg : "failed to create issue" ,
470+ },
471+ }
472+
473+ for _ , tc := range tests {
474+ t .Run (tc .name , func (t * testing.T ) {
475+ // Setup client with mock
476+ client := github .NewClient (tc .mockedClient )
477+ _ , handler := createIssue (client )
478+
479+ // Create call request
480+ request := createMCPRequest (tc .requestArgs )
481+
482+ // Call handler
483+ result , err := handler (context .Background (), request )
484+
485+ // Verify results
486+ if tc .expectError {
487+ require .Error (t , err )
488+ assert .Contains (t , err .Error (), tc .expectedErrMsg )
489+ return
490+ }
491+
492+ require .NoError (t , err )
493+ textContent := getTextResult (t , result )
494+
495+ // Unmarshal and verify the result
496+ var returnedIssue github.Issue
497+ err = json .Unmarshal ([]byte (textContent .Text ), & returnedIssue )
498+ require .NoError (t , err )
499+
500+ assert .Equal (t , * tc .expectedIssue .Number , * returnedIssue .Number )
501+ assert .Equal (t , * tc .expectedIssue .Title , * returnedIssue .Title )
502+ assert .Equal (t , * tc .expectedIssue .State , * returnedIssue .State )
503+ assert .Equal (t , * tc .expectedIssue .HTMLURL , * returnedIssue .HTMLURL )
504+
505+ if tc .expectedIssue .Body != nil {
506+ assert .Equal (t , * tc .expectedIssue .Body , * returnedIssue .Body )
507+ }
508+
509+ // Check assignees if expected
510+ if len (tc .expectedIssue .Assignees ) > 0 {
511+ assert .Equal (t , len (tc .expectedIssue .Assignees ), len (returnedIssue .Assignees ))
512+ for i , assignee := range returnedIssue .Assignees {
513+ assert .Equal (t , * tc .expectedIssue .Assignees [i ].Login , * assignee .Login )
514+ }
515+ }
516+
517+ // Check labels if expected
518+ if len (tc .expectedIssue .Labels ) > 0 {
519+ assert .Equal (t , len (tc .expectedIssue .Labels ), len (returnedIssue .Labels ))
520+ for i , label := range returnedIssue .Labels {
521+ assert .Equal (t , * tc .expectedIssue .Labels [i ].Name , * label .Name )
522+ }
523+ }
524+ })
525+ }
526+ }
0 commit comments