@@ -989,3 +989,201 @@ func Test_GetPullRequestReviews(t *testing.T) {
989
989
})
990
990
}
991
991
}
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
+ }