@@ -989,3 +989,201 @@ func Test_GetPullRequestReviews(t *testing.T) {
989989 })
990990 }
991991}
992+
993+ func Test_CreatePullRequestReview (t * testing.T ) {
994+ // Verify tool definition once
995+ mockClient := github .NewClient (nil )
996+ tool , _ := createPullRequestReview (mockClient , translations .NullTranslationHelper )
997+
998+ assert .Equal (t , "create_pull_request_review" , tool .Name )
999+ assert .NotEmpty (t , tool .Description )
1000+ assert .Contains (t , tool .InputSchema .Properties , "owner" )
1001+ assert .Contains (t , tool .InputSchema .Properties , "repo" )
1002+ assert .Contains (t , tool .InputSchema .Properties , "pull_number" )
1003+ assert .Contains (t , tool .InputSchema .Properties , "body" )
1004+ assert .Contains (t , tool .InputSchema .Properties , "event" )
1005+ assert .Contains (t , tool .InputSchema .Properties , "commit_id" )
1006+ assert .Contains (t , tool .InputSchema .Properties , "comments" )
1007+ assert .ElementsMatch (t , tool .InputSchema .Required , []string {"owner" , "repo" , "pull_number" , "event" })
1008+
1009+ // Setup mock review for success case
1010+ mockReview := & github.PullRequestReview {
1011+ ID : github .Ptr (int64 (301 )),
1012+ State : github .Ptr ("APPROVED" ),
1013+ Body : github .Ptr ("Looks good!" ),
1014+ HTMLURL : github .Ptr ("https://github.com/owner/repo/pull/42#pullrequestreview-301" ),
1015+ User : & github.User {
1016+ Login : github .Ptr ("reviewer" ),
1017+ },
1018+ CommitID : github .Ptr ("abcdef123456" ),
1019+ SubmittedAt : & github.Timestamp {Time : time .Now ()},
1020+ }
1021+
1022+ tests := []struct {
1023+ name string
1024+ mockedClient * http.Client
1025+ requestArgs map [string ]interface {}
1026+ expectError bool
1027+ expectedReview * github.PullRequestReview
1028+ expectedErrMsg string
1029+ }{
1030+ {
1031+ name : "successful review creation with body only" ,
1032+ mockedClient : mock .NewMockedHTTPClient (
1033+ mock .WithRequestMatch (
1034+ mock .PostReposPullsReviewsByOwnerByRepoByPullNumber ,
1035+ mockReview ,
1036+ ),
1037+ ),
1038+ requestArgs : map [string ]interface {}{
1039+ "owner" : "owner" ,
1040+ "repo" : "repo" ,
1041+ "pull_number" : float64 (42 ),
1042+ "body" : "Looks good!" ,
1043+ "event" : "APPROVE" ,
1044+ },
1045+ expectError : false ,
1046+ expectedReview : mockReview ,
1047+ },
1048+ {
1049+ name : "successful review creation with commit_id" ,
1050+ mockedClient : mock .NewMockedHTTPClient (
1051+ mock .WithRequestMatch (
1052+ mock .PostReposPullsReviewsByOwnerByRepoByPullNumber ,
1053+ mockReview ,
1054+ ),
1055+ ),
1056+ requestArgs : map [string ]interface {}{
1057+ "owner" : "owner" ,
1058+ "repo" : "repo" ,
1059+ "pull_number" : float64 (42 ),
1060+ "body" : "Looks good!" ,
1061+ "event" : "APPROVE" ,
1062+ "commit_id" : "abcdef123456" ,
1063+ },
1064+ expectError : false ,
1065+ expectedReview : mockReview ,
1066+ },
1067+ {
1068+ name : "successful review creation with comments" ,
1069+ mockedClient : mock .NewMockedHTTPClient (
1070+ mock .WithRequestMatch (
1071+ mock .PostReposPullsReviewsByOwnerByRepoByPullNumber ,
1072+ mockReview ,
1073+ ),
1074+ ),
1075+ requestArgs : map [string ]interface {}{
1076+ "owner" : "owner" ,
1077+ "repo" : "repo" ,
1078+ "pull_number" : float64 (42 ),
1079+ "body" : "Some issues to fix" ,
1080+ "event" : "REQUEST_CHANGES" ,
1081+ "comments" : []interface {}{
1082+ map [string ]interface {}{
1083+ "path" : "file1.go" ,
1084+ "position" : float64 (10 ),
1085+ "body" : "This needs to be fixed" ,
1086+ },
1087+ map [string ]interface {}{
1088+ "path" : "file2.go" ,
1089+ "position" : float64 (20 ),
1090+ "body" : "Consider a different approach here" ,
1091+ },
1092+ },
1093+ },
1094+ expectError : false ,
1095+ expectedReview : mockReview ,
1096+ },
1097+ {
1098+ name : "invalid comment format" ,
1099+ mockedClient : mock .NewMockedHTTPClient (
1100+ mock .WithRequestMatchHandler (
1101+ mock .PostReposPullsReviewsByOwnerByRepoByPullNumber ,
1102+ http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
1103+ w .WriteHeader (http .StatusUnprocessableEntity )
1104+ _ , _ = w .Write ([]byte (`{"message": "Invalid comment format"}` ))
1105+ }),
1106+ ),
1107+ ),
1108+ requestArgs : map [string ]interface {}{
1109+ "owner" : "owner" ,
1110+ "repo" : "repo" ,
1111+ "pull_number" : float64 (42 ),
1112+ "event" : "REQUEST_CHANGES" ,
1113+ "comments" : []interface {}{
1114+ map [string ]interface {}{
1115+ "path" : "file1.go" ,
1116+ // missing position
1117+ "body" : "This needs to be fixed" ,
1118+ },
1119+ },
1120+ },
1121+ expectError : false ,
1122+ expectedErrMsg : "each comment must have a position" ,
1123+ },
1124+ {
1125+ name : "review creation fails" ,
1126+ mockedClient : mock .NewMockedHTTPClient (
1127+ mock .WithRequestMatchHandler (
1128+ mock .PostReposPullsReviewsByOwnerByRepoByPullNumber ,
1129+ http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
1130+ w .WriteHeader (http .StatusUnprocessableEntity )
1131+ _ , _ = w .Write ([]byte (`{"message": "Invalid comment format"}` ))
1132+ }),
1133+ ),
1134+ ),
1135+ requestArgs : map [string ]interface {}{
1136+ "owner" : "owner" ,
1137+ "repo" : "repo" ,
1138+ "pull_number" : float64 (42 ),
1139+ "body" : "Looks good!" ,
1140+ "event" : "APPROVE" ,
1141+ },
1142+ expectError : true ,
1143+ expectedErrMsg : "failed to create pull request review" ,
1144+ },
1145+ }
1146+
1147+ for _ , tc := range tests {
1148+ t .Run (tc .name , func (t * testing.T ) {
1149+ // Setup client with mock
1150+ client := github .NewClient (tc .mockedClient )
1151+ _ , handler := createPullRequestReview (client , translations .NullTranslationHelper )
1152+
1153+ // Create call request
1154+ request := createMCPRequest (tc .requestArgs )
1155+
1156+ // Call handler
1157+ result , err := handler (context .Background (), request )
1158+
1159+ // Verify results
1160+ if tc .expectError {
1161+ require .Error (t , err )
1162+ assert .Contains (t , err .Error (), tc .expectedErrMsg )
1163+ return
1164+ }
1165+
1166+ require .NoError (t , err )
1167+
1168+ // For error messages in the result
1169+ if tc .expectedErrMsg != "" {
1170+ textContent := getTextResult (t , result )
1171+ assert .Contains (t , textContent .Text , tc .expectedErrMsg )
1172+ return
1173+ }
1174+
1175+ // Parse the result and get the text content if no error
1176+ textContent := getTextResult (t , result )
1177+
1178+ // Unmarshal and verify the result
1179+ var returnedReview github.PullRequestReview
1180+ err = json .Unmarshal ([]byte (textContent .Text ), & returnedReview )
1181+ require .NoError (t , err )
1182+ assert .Equal (t , * tc .expectedReview .ID , * returnedReview .ID )
1183+ assert .Equal (t , * tc .expectedReview .State , * returnedReview .State )
1184+ assert .Equal (t , * tc .expectedReview .Body , * returnedReview .Body )
1185+ assert .Equal (t , * tc .expectedReview .User .Login , * returnedReview .User .Login )
1186+ assert .Equal (t , * tc .expectedReview .HTMLURL , * returnedReview .HTMLURL )
1187+ })
1188+ }
1189+ }
0 commit comments