@@ -1009,6 +1009,175 @@ def test_add_project_values_storypoints(self, mock_requests_post):
10091009 )
10101010 mock_requests_post .reset_mock ()
10111011
1012+ @mock .patch (PATH + "requests.post" )
1013+ def test_add_project_values_pr_early_exit (self , mock_requests_post ):
1014+ """Test add_project_values early exit when using pr_updates."""
1015+ upstream_config = {
1016+ "pr_updates" : ["comments" , "title" ],
1017+ "github_project_number" : 1 ,
1018+ }
1019+ self .mock_config ["sync2jira" ]["map" ]["github" ]["org/repo" ] = upstream_config
1020+
1021+ mock_issue = {"number" : 1234 , "storypoints" : None , "priority" : None }
1022+
1023+ scenarios = (
1024+ ("github_project_fields is None" , None , ["github_project_fields" ]),
1025+ ("github_project_fields is empty" , {}, ["github_project_fields" ]),
1026+ (
1027+ "github_project_fields not in pr_updates" ,
1028+ {"storypoints" : {"gh_field" : "Estimate" }},
1029+ [],
1030+ ),
1031+ )
1032+ for description , gpf , extra_updates in scenarios :
1033+ with self .subTest (description = description ):
1034+ upstream_config ["github_project_fields" ] = gpf
1035+ upstream_config ["pr_updates" ] = ["comments" , "title" ] + extra_updates
1036+ result = u .add_project_values (
1037+ issue = mock_issue ,
1038+ upstream = "org/repo" ,
1039+ headers = {},
1040+ config = self .mock_config ,
1041+ updates_key = "pr_updates" ,
1042+ )
1043+ mock_requests_post .assert_not_called ()
1044+ self .assertIsNone (result )
1045+ mock_requests_post .reset_mock ()
1046+
1047+ @mock .patch (PATH + "requests.post" )
1048+ def test_add_project_values_pr (self , mock_requests_post ):
1049+ """Test add_project_values with pr_updates uses pullRequest query and response key.
1050+
1051+ The storypoints/priority processing logic is shared with issues and
1052+ is thoroughly tested by test_add_project_values_storypoints. This
1053+ test focuses on the PR-specific behavior: reading from pr_updates,
1054+ sending the pullRequest GraphQL query, and parsing the pullRequest
1055+ response key.
1056+ """
1057+ upstream_config = {
1058+ "pr_updates" : ["github_project_fields" ],
1059+ "github_project_number" : 1 ,
1060+ }
1061+ self .mock_config ["sync2jira" ]["map" ]["github" ]["org/repo" ] = upstream_config
1062+
1063+ mock_issue = {"number" : 1234 , "storypoints" : None , "priority" : None }
1064+
1065+ mock_requests_post .return_value .status_code = 200
1066+
1067+ scenarios = (
1068+ (
1069+ "Storypoints via Number field" ,
1070+ {
1071+ "priority" : {"gh_field" : "Priority" },
1072+ "storypoints" : {"gh_field" : "Estimate" },
1073+ },
1074+ [
1075+ {"fieldName" : {"name" : "Priority" }, "name" : "High" },
1076+ {"fieldName" : {"name" : "Estimate" }, "number" : 5 },
1077+ ],
1078+ 5 ,
1079+ "High" ,
1080+ ),
1081+ (
1082+ "Storypoints via Single Select" ,
1083+ {
1084+ "priority" : {"gh_field" : "Priority" },
1085+ "storypoints" : {
1086+ "gh_field" : "Size" ,
1087+ "options" : {"Small" : 1 , "Medium" : 3 , "Large" : 8 },
1088+ },
1089+ },
1090+ [
1091+ {"fieldName" : {"name" : "Size" }, "name" : "Medium" },
1092+ {"fieldName" : {"name" : "Priority" }, "name" : "Critical" },
1093+ ],
1094+ 3 ,
1095+ "Critical" ,
1096+ ),
1097+ (
1098+ "Priority only, no storypoints config" ,
1099+ {
1100+ "priority" : {"gh_field" : "Priority" },
1101+ },
1102+ [
1103+ {"fieldName" : {"name" : "Priority" }, "name" : "Low" },
1104+ ],
1105+ None ,
1106+ "Low" ,
1107+ ),
1108+ (
1109+ "Storypoints only, no priority config" ,
1110+ {
1111+ "storypoints" : {"gh_field" : "Estimate" },
1112+ },
1113+ [
1114+ {"fieldName" : {"name" : "Estimate" }, "number" : 8 },
1115+ ],
1116+ 8 ,
1117+ None ,
1118+ ),
1119+ )
1120+
1121+ for description , gpf , field_nodes , expected_sp , expected_prio in scenarios :
1122+ with self .subTest (description = description ):
1123+ upstream_config ["github_project_fields" ] = gpf
1124+ mock_issue ["storypoints" ] = None
1125+ mock_issue ["priority" ] = None
1126+
1127+ mock_requests_post .return_value .json .return_value = {
1128+ "data" : {
1129+ "repository" : {
1130+ "pullRequest" : {
1131+ "projectItems" : {
1132+ "nodes" : [
1133+ {
1134+ "project" : {
1135+ "title" : "Project 1" ,
1136+ "number" : 1 ,
1137+ },
1138+ "fieldValues" : {"nodes" : field_nodes },
1139+ }
1140+ ]
1141+ }
1142+ }
1143+ }
1144+ }
1145+ }
1146+
1147+ u .add_project_values (
1148+ issue = mock_issue ,
1149+ upstream = "org/repo" ,
1150+ headers = {},
1151+ config = self .mock_config ,
1152+ updates_key = "pr_updates" ,
1153+ )
1154+
1155+ query_sent = mock_requests_post .call_args [1 ]["json" ]["query" ]
1156+ self .assertIn (
1157+ "pullRequest(number:" ,
1158+ query_sent ,
1159+ "GraphQL query should use pullRequest, not issue" ,
1160+ )
1161+ self .assertNotIn (
1162+ "issue(number:" ,
1163+ query_sent ,
1164+ "GraphQL query should not contain issue(number:) for PRs" ,
1165+ )
1166+
1167+ self .assertEqual (
1168+ mock_issue ["priority" ],
1169+ expected_prio ,
1170+ f"{ description } : expected priority={ expected_prio !r} , "
1171+ f"got { mock_issue ['priority' ]!r} " ,
1172+ )
1173+ self .assertEqual (
1174+ mock_issue .get ("storypoints" ),
1175+ expected_sp ,
1176+ f"{ description } : expected storypoints={ expected_sp } , "
1177+ f"got { mock_issue .get ('storypoints' )} " ,
1178+ )
1179+ mock_requests_post .reset_mock ()
1180+
10121181 def test_passes_github_filters (self ):
10131182 """
10141183 Test passes_github_filters for labels, milestone, and other fields.
0 commit comments