From 885dc240f74b823ba6629621cc11286d82201084 Mon Sep 17 00:00:00 2001 From: subhajitlucky Date: Tue, 12 May 2026 19:35:29 +0530 Subject: [PATCH] fix: search milestones on demand --- gitHappens.py | 17 ++++++--- tests/__init__.py | 1 + tests/test_milestones.py | 74 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 88 insertions(+), 4 deletions(-) create mode 100644 tests/__init__.py create mode 100644 tests/test_milestones.py diff --git a/gitHappens.py b/gitHappens.py index 27d47f3..faf6191 100755 --- a/gitHappens.py +++ b/gitHappens.py @@ -10,6 +10,7 @@ import requests import sys import webbrowser +from urllib.parse import quote, urlencode # Setup config parser and read settings config = configparser.ConfigParser() @@ -89,8 +90,11 @@ def enterProjectId(): return project_id exit('Invalid project ID.') -def list_milestones(current=False): - cmd = f'glab api /groups/{GROUP_ID}/milestones?state=active' +def list_milestones(current=False, search=''): + query_params = {'state': 'active'} + if search: + query_params['search'] = search + cmd = f'glab api /groups/{GROUP_ID}/milestones?{urlencode(query_params, quote_via=quote)}' result = subprocess.run(cmd.split(), stdout=subprocess.PIPE) milestones = json.loads(result.stdout) if current: @@ -187,7 +191,12 @@ def getSelectedMilestone(milestone, milestones): def get_milestone(manual): if manual: - milestones = list_milestones() + search_query = inquirer.prompt([ + inquirer.Text('milestone_search', message='Search milestone:'), + ])['milestone_search'] + milestones = list_milestones(search=search_query) + if not milestones: + milestones = list_milestones() return getSelectedMilestone(select_milestone(milestones), milestones) milestone = list_milestones(True) # select active for today return milestone @@ -842,4 +851,4 @@ def main(): startIssueCreation(project_id, title, milestone, epic, iteration, selectedSettings, onlyIssue) if __name__ == '__main__': - main() \ No newline at end of file + main() diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1 @@ + diff --git a/tests/test_milestones.py b/tests/test_milestones.py new file mode 100644 index 0000000..c973c0f --- /dev/null +++ b/tests/test_milestones.py @@ -0,0 +1,74 @@ +import importlib.util +import json +import sys +import types +import unittest +from pathlib import Path +from unittest import mock + + +def load_githappens_module(): + root = Path(__file__).resolve().parents[1] + config_dir = root / "configs" + config_dir.mkdir(exist_ok=True) + (config_dir / "config.ini").write_text( + "[DEFAULT]\n" + "base_url=https://gitlab.example\n" + "group_id=42\n" + "custom_template=Custom\n" + "GITLAB_TOKEN=test-token\n" + "squash_commits=true\n" + "delete_branch_after_merge=true\n", + encoding="utf-8", + ) + (config_dir / "templates.json").write_text( + '{"templates": [], "reviewers": []}', + encoding="utf-8", + ) + + inquirer_stub = types.SimpleNamespace( + prompt=mock.Mock(), + Text=lambda *args, **kwargs: ("Text", args, kwargs), + List=lambda *args, **kwargs: ("List", args, kwargs), + Checkbox=lambda *args, **kwargs: ("Checkbox", args, kwargs), + ) + sys.modules["inquirer"] = inquirer_stub + + spec = importlib.util.spec_from_file_location("gitHappens_under_test", root / "gitHappens.py") + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + + +class MilestoneSearchTest(unittest.TestCase): + def test_list_milestones_sends_search_query_to_gitlab(self): + git_happens = load_githappens_module() + result = mock.Mock() + result.stdout = json.dumps([{"id": 7, "title": "Release 1"}]).encode() + + with mock.patch.object(git_happens.subprocess, "run", return_value=result) as run: + milestones = git_happens.list_milestones(search="Release 1") + + self.assertEqual(milestones, [{"id": 7, "title": "Release 1"}]) + command = run.call_args.args[0] + self.assertEqual(command[0:2], ["glab", "api"]) + self.assertIn("search=Release%201", command[2]) + + def test_manual_milestone_search_falls_back_to_active_list_when_no_match(self): + git_happens = load_githappens_module() + + with mock.patch.object(git_happens, "list_milestones", side_effect=[[], [{"id": 3, "title": "Fallback"}]]) as list_milestones, \ + mock.patch.object(git_happens, "select_milestone", return_value="Fallback"): + git_happens.inquirer.prompt.return_value = {"milestone_search": "wrong"} + + milestone = git_happens.get_milestone(True) + + self.assertEqual(milestone, {"id": 3, "title": "Fallback"}) + list_milestones.assert_has_calls([ + mock.call(search="wrong"), + mock.call(), + ]) + + +if __name__ == "__main__": + unittest.main()