@@ -155,23 +155,126 @@ def test_main_flow_pr_merge_wait(mock_all):
155155@patch ("openwisp_utils.releaser.release.update_changelog_file" )
156156@patch ("openwisp_utils.releaser.release.format_file_with_docstrfmt" )
157157@patch ("openwisp_utils.releaser.release.subprocess.run" )
158+ @patch ("openwisp_utils.releaser.release.branch_exists" )
158159@patch ("openwisp_utils.releaser.release.questionary" )
159160def test_port_changelog_to_main_flow (
160- mock_questionary , mock_subprocess , mock_format_file , mock_update_changelog
161+ mock_questionary ,
162+ mock_branch_exists ,
163+ mock_subprocess ,
164+ mock_format_file ,
165+ mock_update_changelog ,
161166):
162- """Tests the changelog porting process for both RST and MD files, and the cancellation path ."""
167+ """Tests the changelog porting process for RST files."""
163168 mock_gh = MagicMock ()
164169 mock_config_rst = {"changelog_path" : "CHANGES.rst" }
170+ # Both branches exist: user is asked
171+ mock_branch_exists .return_value = True
165172 mock_questionary .select .return_value .ask .return_value = "main"
166173 port_changelog_to_main (mock_gh , mock_config_rst , "1.1.1" , "- fix" , "1.1.x" )
167174 mock_gh .create_pr .assert_called_once ()
168175 mock_format_file .assert_called_once_with ("CHANGES.rst" )
169176
170- mock_gh .reset_mock ()
171177
172- # Test Cancellation path
178+ @patch ("openwisp_utils.releaser.release.update_changelog_file" )
179+ @patch ("openwisp_utils.releaser.release.format_file_with_docstrfmt" )
180+ @patch ("openwisp_utils.releaser.release.subprocess.run" )
181+ @patch ("openwisp_utils.releaser.release.branch_exists" )
182+ def test_port_changelog_only_master_exists (
183+ mock_branch_exists , mock_subprocess , mock_format_file , mock_update_changelog
184+ ):
185+ """`master` is auto-selected when `main` does not exist locally."""
186+ mock_gh = MagicMock ()
187+ mock_config = {"changelog_path" : "CHANGES.rst" }
188+ # Simulate: main=False, master=True
189+ mock_branch_exists .side_effect = lambda name : name == "master"
190+ port_changelog_to_main (mock_gh , mock_config , "1.1.1" , "- fix" , "1.1.x" )
191+ mock_gh .create_pr .assert_called_once ()
192+ # Verify PR was opened against master
193+ assert mock_gh .create_pr .call_args [0 ][1 ] == "master"
194+
195+
196+ @patch ("openwisp_utils.releaser.release.update_changelog_file" )
197+ @patch ("openwisp_utils.releaser.release.format_file_with_docstrfmt" )
198+ @patch ("openwisp_utils.releaser.release.subprocess.run" )
199+ @patch ("openwisp_utils.releaser.release.branch_exists" )
200+ def test_port_changelog_only_main_exists (
201+ mock_branch_exists , mock_subprocess , mock_format_file , mock_update_changelog
202+ ):
203+ """`main` is auto-selected when it exists and `master` does not."""
204+ mock_gh = MagicMock ()
205+ mock_config = {"changelog_path" : "CHANGES.rst" }
206+ # Simulate: main=True, master=False
207+ mock_branch_exists .side_effect = lambda name : name == "main"
208+ port_changelog_to_main (mock_gh , mock_config , "1.1.1" , "- fix" , "1.1.x" )
209+ mock_gh .create_pr .assert_called_once ()
210+ # Verify PR was opened against main
211+ assert mock_gh .create_pr .call_args [0 ][1 ] == "main"
212+
213+
214+ @patch ("openwisp_utils.releaser.release.update_changelog_file" )
215+ @patch ("openwisp_utils.releaser.release.format_file_with_docstrfmt" )
216+ @patch ("openwisp_utils.releaser.release.subprocess.run" )
217+ @patch ("openwisp_utils.releaser.release.branch_exists" )
218+ @patch ("openwisp_utils.releaser.release.questionary" )
219+ def test_port_changelog_both_branches_prompts_user (
220+ mock_questionary ,
221+ mock_branch_exists ,
222+ mock_subprocess ,
223+ mock_format_file ,
224+ mock_update_changelog ,
225+ ):
226+ """User is prompted to choose when both `main` and `master` exist."""
227+ mock_gh = MagicMock ()
228+ mock_config = {"changelog_path" : "CHANGES.rst" }
229+ # Both branches exist
230+ mock_branch_exists .return_value = True
231+ mock_questionary .select .return_value .ask .return_value = "master"
232+ port_changelog_to_main (mock_gh , mock_config , "1.1.1" , "- fix" , "1.1.x" )
233+ mock_questionary .select .assert_called_once ()
234+ mock_gh .create_pr .assert_called_once ()
235+ assert mock_gh .create_pr .call_args [0 ][1 ] == "master"
236+
237+
238+ @patch ("openwisp_utils.releaser.release.update_changelog_file" )
239+ @patch ("openwisp_utils.releaser.release.format_file_with_docstrfmt" )
240+ @patch ("openwisp_utils.releaser.release.subprocess.run" )
241+ @patch ("openwisp_utils.releaser.release.branch_exists" )
242+ def test_port_changelog_neither_branch_exists (
243+ mock_branch_exists , mock_subprocess , mock_format_file , mock_update_changelog
244+ ):
245+ """Porting is skipped with a message if neither branch exists."""
246+ mock_gh = MagicMock ()
247+ mock_config = {"changelog_path" : "CHANGES.rst" }
248+ # Neither exists
249+ mock_branch_exists .return_value = False
250+ port_changelog_to_main (mock_gh , mock_config , "1.1.1" , "- fix" , "1.1.x" )
251+ # Verify no PR was created
252+ mock_gh .create_pr .assert_not_called ()
253+ # Verify no file update was attempted
254+ mock_update_changelog .assert_not_called ()
255+
256+
257+ @patch ("openwisp_utils.releaser.release.update_changelog_file" )
258+ @patch ("openwisp_utils.releaser.release.format_file_with_docstrfmt" )
259+ @patch ("openwisp_utils.releaser.release.subprocess.run" )
260+ @patch ("openwisp_utils.releaser.release.branch_exists" )
261+ @patch ("openwisp_utils.releaser.release.questionary" )
262+ def test_port_changelog_cancelled (
263+ mock_questionary ,
264+ mock_branch_exists ,
265+ mock_subprocess ,
266+ mock_format_file ,
267+ mock_update_changelog ,
268+ ):
269+ """Porting is cancelled if user doesn't select a branch."""
270+ mock_gh = MagicMock ()
271+ mock_config = {"changelog_path" : "CHANGES.rst" }
272+ # Both exist to trigger prompt
273+ mock_branch_exists .return_value = True
274+ # User cancels (Ctrl+C or Esc)
173275 mock_questionary .select .return_value .ask .return_value = None
174- port_changelog_to_main (mock_gh , mock_config_rst , "1.1.1" , "- fix" , "1.1.x" )
276+ port_changelog_to_main (mock_gh , mock_config , "1.1.1" , "- fix" , "1.1.x" )
277+ # Verify no PR was created
175278 mock_gh .create_pr .assert_not_called ()
176279
177280
@@ -239,11 +342,15 @@ def test_main_flow_skip_release_creation(mock_all):
239342 )
240343
241344
345+ @patch ("openwisp_utils.releaser.release.branch_exists" )
242346@patch ("openwisp_utils.releaser.release.subprocess.run" )
243- def test_port_changelog_to_main_flow_markdown (mock_subprocess , mock_all ):
347+ def test_port_changelog_to_main_flow_markdown (
348+ mock_subprocess , mock_branch_exists , mock_all
349+ ):
244350 """Tests the changelog porting process for a Markdown file."""
245351 mock_gh = MagicMock ()
246352 mock_config_md = {"changelog_path" : "CHANGES.md" }
353+ mock_branch_exists .return_value = True
247354 mock_all ["questionary_select" ].return_value .ask .return_value = "main"
248355
249356 with patch ("openwisp_utils.releaser.release.update_changelog_file" ) as mock_update :
@@ -253,12 +360,14 @@ def test_port_changelog_to_main_flow_markdown(mock_subprocess, mock_all):
253360 assert "## Version 1.1.1" in called_with_content
254361
255362
363+ @patch ("openwisp_utils.releaser.release.branch_exists" )
256364@patch ("openwisp_utils.releaser.release.subprocess.run" )
257- def test_port_changelog_skip_pr_creation (mock_subprocess , mock_all ):
365+ def test_port_changelog_skip_pr_creation (mock_subprocess , mock_branch_exists , mock_all ):
258366 """Tests skipping PR creation during changelog porting."""
259367 mock_gh = MagicMock ()
260368 mock_gh .create_pr .side_effect = SkipSignal
261369 mock_config = {"changelog_path" : "CHANGES.rst" }
370+ mock_branch_exists .return_value = True
262371 mock_all ["questionary_select" ].return_value .ask .return_value = "main"
263372
264373 with patch ("openwisp_utils.releaser.release.update_changelog_file" ):
0 commit comments