@@ -1058,44 +1058,236 @@ def test_task_search_yaml_output_is_parsable(self, mock_init, capsys):
10581058
10591059
10601060class TestTaskSearchTextQuery :
1061- """Test suite for free-text search functionality (#107) ."""
1061+ """Test suite for free-text search functionality."""
10621062
1063- def test_task_search_with_text_query_only (self ):
1063+ @patch ("phabfive.maniphest.Phabfive.__init__" )
1064+ def test_task_search_with_text_query_only (self , mock_init , capsys ):
10641065 """Test searching with only a free-text query."""
1065- # This test documents expected behavior for the new text search feature
1066- # It would need a mock Phabricator instance to run
1067- # Expected: API called with fullText constraint, no projects constraint
1068- pass
1066+ mock_init .return_value = None
1067+ maniphest = Maniphest ()
1068+ maniphest .phab = MagicMock ()
1069+ maniphest .url = "https://phabricator.example.com"
1070+
1071+ # Mock the API response
1072+ mock_response = MagicMock ()
1073+ mock_response .response = {"data" : []}
1074+ mock_response .get .return_value = {"after" : None }
1075+ maniphest .phab .maniphest .search .return_value = mock_response
1076+
1077+ # Mock project.query for board name resolution
1078+ maniphest .phab .project .query .return_value = {"data" : {}}
1079+
1080+ # Call with text query only
1081+ maniphest .task_search (text_query = "authentication bug" )
1082+
1083+ # Verify API was called with query constraint only
1084+ assert maniphest .phab .maniphest .search .called
1085+ call_kwargs = maniphest .phab .maniphest .search .call_args [1 ]
1086+ constraints = call_kwargs ["constraints" ]
1087+
1088+ # Should have query constraint
1089+ assert "query" in constraints
1090+ assert constraints ["query" ] == "authentication bug"
1091+
1092+ # Should NOT have projects constraint
1093+ assert "projects" not in constraints
10691094
1070- def test_task_search_with_tag_only (self ):
1095+ @patch ("phabfive.maniphest.Phabfive.__init__" )
1096+ def test_task_search_with_tag_only (self , mock_init , capsys ):
10711097 """Test searching with only --tag option (backward compat)."""
1072- # This test documents that --tag works for project filtering
1073- # Expected: API called with projects constraint, no fullText
1074- pass
1098+ mock_init .return_value = None
1099+ maniphest = Maniphest ()
1100+ maniphest .phab = MagicMock ()
1101+ maniphest .url = "https://phabricator.example.com"
1102+
1103+ # Mock project resolution
1104+ mock_project_result = MagicMock ()
1105+ mock_project_result .get .return_value = {
1106+ "PHID-PROJ-123" : {
1107+ "name" : "MyProject" ,
1108+ "slugs" : ["myproject" ],
1109+ }
1110+ }
1111+ maniphest .phab .project .query .return_value = mock_project_result
10751112
1076- def test_task_search_with_text_and_tag (self ):
1113+ # Mock the API response
1114+ mock_response = MagicMock ()
1115+ mock_response .response = {"data" : []}
1116+ mock_response .get .return_value = {"after" : None }
1117+ maniphest .phab .maniphest .search .return_value = mock_response
1118+
1119+ # Call with tag only
1120+ maniphest .task_search (tag = "MyProject" )
1121+
1122+ # Verify API was called with projects constraint only
1123+ assert maniphest .phab .maniphest .search .called
1124+ call_kwargs = maniphest .phab .maniphest .search .call_args [1 ]
1125+ constraints = call_kwargs ["constraints" ]
1126+
1127+ # Should have projects constraint
1128+ assert "projects" in constraints
1129+ assert constraints ["projects" ] == ["PHID-PROJ-123" ]
1130+
1131+ # Should NOT have query constraint
1132+ assert "query" not in constraints
1133+
1134+ @patch ("phabfive.maniphest.Phabfive.__init__" )
1135+ def test_task_search_with_text_and_tag (self , mock_init , capsys ):
10771136 """Test searching with both text query and tag filter."""
1078- # This test documents combined search behavior
1079- # Expected: API called with both fullText and projects constraints
1080- pass
1137+ mock_init .return_value = None
1138+ maniphest = Maniphest ()
1139+ maniphest .phab = MagicMock ()
1140+ maniphest .url = "https://phabricator.example.com"
10811141
1082- def test_task_search_requires_at_least_one_filter (self ):
1142+ # Mock project resolution
1143+ mock_project_result = MagicMock ()
1144+ mock_project_result .get .return_value = {
1145+ "PHID-PROJ-123" : {
1146+ "name" : "MyProject" ,
1147+ "slugs" : ["myproject" ],
1148+ }
1149+ }
1150+ maniphest .phab .project .query .return_value = mock_project_result
1151+
1152+ # Mock the API response
1153+ mock_response = MagicMock ()
1154+ mock_response .response = {"data" : []}
1155+ mock_response .get .return_value = {"after" : None }
1156+ maniphest .phab .maniphest .search .return_value = mock_response
1157+
1158+ # Call with both text query and tag
1159+ maniphest .task_search (text_query = "bug" , tag = "MyProject" )
1160+
1161+ # Verify API was called with both constraints
1162+ assert maniphest .phab .maniphest .search .called
1163+ call_kwargs = maniphest .phab .maniphest .search .call_args [1 ]
1164+ constraints = call_kwargs ["constraints" ]
1165+
1166+ # Should have both constraints
1167+ assert "query" in constraints
1168+ assert constraints ["query" ] == "bug"
1169+ assert "projects" in constraints
1170+ assert constraints ["projects" ] == ["PHID-PROJ-123" ]
1171+
1172+ @patch ("phabfive.maniphest.Phabfive.__init__" )
1173+ def test_task_search_requires_at_least_one_filter (self , mock_init , capsys ):
10831174 """Test that search without any filters shows error."""
1084- # This test documents validation behavior
1085- # Expected: Error logged, no API call made
1086- pass
1175+ mock_init .return_value = None
1176+ maniphest = Maniphest ()
1177+ maniphest .phab = MagicMock ()
1178+
1179+ # Call with no filters
1180+ maniphest .task_search ()
1181+
1182+ # Verify error was logged (captured in stderr by capsys)
1183+ _ = capsys .readouterr ()
10871184
1088- def test_task_search_with_text_and_date_filters (self ):
1185+ # Verify API was NOT called
1186+ assert not maniphest .phab .maniphest .search .called
1187+
1188+ @patch ("phabfive.maniphest.Phabfive.__init__" )
1189+ def test_task_search_with_text_and_date_filters (self , mock_init , capsys ):
10891190 """Test text search with date filters."""
1090- # Expected: API called with fullText, createdStart, and/or modifiedStart
1091- pass
1191+ mock_init .return_value = None
1192+ maniphest = Maniphest ()
1193+ maniphest .phab = MagicMock ()
1194+ maniphest .url = "https://phabricator.example.com"
1195+
1196+ # Mock the API response
1197+ mock_response = MagicMock ()
1198+ mock_response .response = {"data" : []}
1199+ mock_response .get .return_value = {"after" : None }
1200+ maniphest .phab .maniphest .search .return_value = mock_response
10921201
1093- def test_task_search_tag_supports_wildcards (self ):
1202+ # Mock project.query for board name resolution
1203+ maniphest .phab .project .query .return_value = {"data" : {}}
1204+
1205+ # Call with text query and date filters
1206+ maniphest .task_search (text_query = "bug" , created_after = 7 , updated_after = 3 )
1207+
1208+ # Verify API was called with all constraints
1209+ assert maniphest .phab .maniphest .search .called
1210+ call_kwargs = maniphest .phab .maniphest .search .call_args [1 ]
1211+ constraints = call_kwargs ["constraints" ]
1212+
1213+ # Should have query constraint
1214+ assert "query" in constraints
1215+ assert constraints ["query" ] == "bug"
1216+
1217+ # Should have date constraints (converted to Unix timestamps)
1218+ assert "createdStart" in constraints
1219+ assert "modifiedStart" in constraints
1220+ assert isinstance (constraints ["createdStart" ], int )
1221+ assert isinstance (constraints ["modifiedStart" ], int )
1222+
1223+ @patch ("phabfive.maniphest.Phabfive.__init__" )
1224+ def test_task_search_tag_supports_wildcards (self , mock_init , capsys ):
10941225 """Test that --tag option supports wildcard patterns."""
1095- # Expected: Wildcard resolution works same as before
1096- pass
1226+ mock_init .return_value = None
1227+ maniphest = Maniphest ()
1228+ maniphest .phab = MagicMock ()
1229+ maniphest .url = "https://phabricator.example.com"
10971230
1098- def test_task_search_tag_supports_and_or_logic (self ):
1231+ # Mock project resolution with wildcard match
1232+ mock_project_result = MagicMock ()
1233+ mock_project_result .get .return_value = {
1234+ "PHID-PROJ-1" : {
1235+ "name" : "Development" ,
1236+ "slugs" : ["development" , "dev" ],
1237+ },
1238+ "PHID-PROJ-2" : {
1239+ "name" : "DevOps" ,
1240+ "slugs" : ["devops" ],
1241+ },
1242+ }
1243+ maniphest .phab .project .query .return_value = mock_project_result
1244+
1245+ # Mock the API response
1246+ mock_response = MagicMock ()
1247+ mock_response .response = {"data" : []}
1248+ mock_response .get .return_value = {"after" : None }
1249+ maniphest .phab .maniphest .search .return_value = mock_response
1250+
1251+ # Call with wildcard pattern
1252+ maniphest .task_search (tag = "dev*" )
1253+
1254+ # Verify API was called multiple times (once per matched project)
1255+ assert maniphest .phab .maniphest .search .called
1256+ # With 2 matching projects, should make 2 calls
1257+ assert maniphest .phab .maniphest .search .call_count >= 2
1258+
1259+ @patch ("phabfive.maniphest.Phabfive.__init__" )
1260+ def test_task_search_tag_supports_and_or_logic (self , mock_init , capsys ):
10991261 """Test that --tag option supports AND/OR pattern logic."""
1100- # Expected: Complex patterns like "ProjectA+ProjectB,ProjectC" work
1101- pass
1262+ mock_init .return_value = None
1263+ maniphest = Maniphest ()
1264+ maniphest .phab = MagicMock ()
1265+ maniphest .url = "https://phabricator.example.com"
1266+
1267+ # Mock project resolution
1268+ mock_project_result = MagicMock ()
1269+ mock_project_result .get .return_value = {
1270+ "PHID-PROJ-A" : {
1271+ "name" : "ProjectA" ,
1272+ "slugs" : ["projecta" ],
1273+ },
1274+ "PHID-PROJ-B" : {
1275+ "name" : "ProjectB" ,
1276+ "slugs" : ["projectb" ],
1277+ },
1278+ }
1279+ maniphest .phab .project .query .return_value = mock_project_result
1280+
1281+ # Mock the API response
1282+ mock_response = MagicMock ()
1283+ mock_response .response = {"data" : []}
1284+ mock_response .get .return_value = {"after" : None }
1285+ maniphest .phab .maniphest .search .return_value = mock_response
1286+
1287+ # Test OR logic: ProjectA,ProjectB
1288+ maniphest .task_search (tag = "ProjectA,ProjectB" )
1289+
1290+ # Verify API was called (implementation fetches both projects)
1291+ assert maniphest .phab .maniphest .search .called
1292+ # Should make multiple calls for OR logic
1293+ assert maniphest .phab .maniphest .search .call_count >= 2
0 commit comments