@@ -155,26 +155,136 @@ 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):
162167 """Tests the changelog porting process for both RST and MD files, and the cancellation path."""
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
170177 mock_gh .reset_mock ()
171178
172- # Test Cancellation path
179+ # Test Cancellation path (when both branches exist and user cancels)
173180 mock_questionary .select .return_value .ask .return_value = None
174181 port_changelog_to_main (mock_gh , mock_config_rst , "1.1.1" , "- fix" , "1.1.x" )
175182 mock_gh .create_pr .assert_not_called ()
176183
177184
185+ @patch ("openwisp_utils.releaser.release.update_changelog_file" )
186+ @patch ("openwisp_utils.releaser.release.format_file_with_docstrfmt" )
187+ @patch ("openwisp_utils.releaser.release.subprocess.run" )
188+ @patch ("openwisp_utils.releaser.release.branch_exists" )
189+ def test_port_changelog_only_master_exists (
190+ mock_branch_exists , mock_subprocess , mock_format_file , mock_update_changelog
191+ ):
192+ """`master` is auto-selected when `main` does not exist locally."""
193+ mock_gh = MagicMock ()
194+ mock_config = {"changelog_path" : "CHANGES.rst" }
195+ # Simulate: main=False, master=True
196+ mock_branch_exists .side_effect = lambda name : name == "master"
197+ port_changelog_to_main (mock_gh , mock_config , "1.1.1" , "- fix" , "1.1.x" )
198+ mock_gh .create_pr .assert_called_once ()
199+ # Verify PR was opened against master
200+ assert mock_gh .create_pr .call_args [0 ][1 ] == "master"
201+
202+
203+ @patch ("openwisp_utils.releaser.release.update_changelog_file" )
204+ @patch ("openwisp_utils.releaser.release.format_file_with_docstrfmt" )
205+ @patch ("openwisp_utils.releaser.release.subprocess.run" )
206+ @patch ("openwisp_utils.releaser.release.branch_exists" )
207+ def test_port_changelog_only_main_exists (
208+ mock_branch_exists , mock_subprocess , mock_format_file , mock_update_changelog
209+ ):
210+ """`main` is auto-selected when it exists and `master` does not."""
211+ mock_gh = MagicMock ()
212+ mock_config = {"changelog_path" : "CHANGES.rst" }
213+ # Simulate: main=True, master=False
214+ mock_branch_exists .side_effect = lambda name : name == "main"
215+ port_changelog_to_main (mock_gh , mock_config , "1.1.1" , "- fix" , "1.1.x" )
216+ mock_gh .create_pr .assert_called_once ()
217+ # Verify PR was opened against main
218+ assert mock_gh .create_pr .call_args [0 ][1 ] == "main"
219+
220+
221+ @patch ("openwisp_utils.releaser.release.update_changelog_file" )
222+ @patch ("openwisp_utils.releaser.release.format_file_with_docstrfmt" )
223+ @patch ("openwisp_utils.releaser.release.subprocess.run" )
224+ @patch ("openwisp_utils.releaser.release.branch_exists" )
225+ @patch ("openwisp_utils.releaser.release.questionary" )
226+ def test_port_changelog_both_branches_prompts_user (
227+ mock_questionary ,
228+ mock_branch_exists ,
229+ mock_subprocess ,
230+ mock_format_file ,
231+ mock_update_changelog ,
232+ ):
233+ """User is prompted to choose when both `main` and `master` exist."""
234+ mock_gh = MagicMock ()
235+ mock_config = {"changelog_path" : "CHANGES.rst" }
236+ # Both branches exist
237+ mock_branch_exists .return_value = True
238+ mock_questionary .select .return_value .ask .return_value = "master"
239+ port_changelog_to_main (mock_gh , mock_config , "1.1.1" , "- fix" , "1.1.x" )
240+ mock_questionary .select .assert_called_once ()
241+ mock_gh .create_pr .assert_called_once ()
242+ assert mock_gh .create_pr .call_args [0 ][1 ] == "master"
243+
244+
245+ @patch ("openwisp_utils.releaser.release.update_changelog_file" )
246+ @patch ("openwisp_utils.releaser.release.format_file_with_docstrfmt" )
247+ @patch ("openwisp_utils.releaser.release.subprocess.run" )
248+ @patch ("openwisp_utils.releaser.release.branch_exists" )
249+ def test_port_changelog_neither_branch_exists (
250+ mock_branch_exists , mock_subprocess , mock_format_file , mock_update_changelog
251+ ):
252+ """Porting is skipped with a message if neither branch exists."""
253+ mock_gh = MagicMock ()
254+ mock_config = {"changelog_path" : "CHANGES.rst" }
255+ # Neither exists
256+ mock_branch_exists .return_value = False
257+ port_changelog_to_main (mock_gh , mock_config , "1.1.1" , "- fix" , "1.1.x" )
258+ # Verify no PR was created
259+ mock_gh .create_pr .assert_not_called ()
260+ # Verify no file update was attempted
261+ mock_update_changelog .assert_not_called ()
262+
263+
264+ @patch ("openwisp_utils.releaser.release.update_changelog_file" )
265+ @patch ("openwisp_utils.releaser.release.format_file_with_docstrfmt" )
266+ @patch ("openwisp_utils.releaser.release.subprocess.run" )
267+ @patch ("openwisp_utils.releaser.release.branch_exists" )
268+ @patch ("openwisp_utils.releaser.release.questionary" )
269+ def test_port_changelog_cancelled (
270+ mock_questionary ,
271+ mock_branch_exists ,
272+ mock_subprocess ,
273+ mock_format_file ,
274+ mock_update_changelog ,
275+ ):
276+ """Porting is cancelled if user doesn't select a branch."""
277+ mock_gh = MagicMock ()
278+ mock_config = {"changelog_path" : "CHANGES.rst" }
279+ # Both exist to trigger prompt
280+ mock_branch_exists .return_value = True
281+ # User cancels (Ctrl+C or Esc)
282+ mock_questionary .select .return_value .ask .return_value = None
283+ port_changelog_to_main (mock_gh , mock_config , "1.1.1" , "- fix" , "1.1.x" )
284+ # Verify no PR was created
285+ mock_gh .create_pr .assert_not_called ()
286+
287+
178288def test_main_bugfix_flow_with_porting (mock_all , mocker ):
179289 """Tests the main release flow for a bugfix, including accepting the changelog port."""
180290 mock_all ["_git_command_map" ][
@@ -239,11 +349,15 @@ def test_main_flow_skip_release_creation(mock_all):
239349 )
240350
241351
352+ @patch ("openwisp_utils.releaser.release.branch_exists" )
242353@patch ("openwisp_utils.releaser.release.subprocess.run" )
243- def test_port_changelog_to_main_flow_markdown (mock_subprocess , mock_all ):
354+ def test_port_changelog_to_main_flow_markdown (
355+ mock_subprocess , mock_branch_exists , mock_all
356+ ):
244357 """Tests the changelog porting process for a Markdown file."""
245358 mock_gh = MagicMock ()
246359 mock_config_md = {"changelog_path" : "CHANGES.md" }
360+ mock_branch_exists .return_value = True
247361 mock_all ["questionary_select" ].return_value .ask .return_value = "main"
248362
249363 with patch ("openwisp_utils.releaser.release.update_changelog_file" ) as mock_update :
@@ -253,12 +367,14 @@ def test_port_changelog_to_main_flow_markdown(mock_subprocess, mock_all):
253367 assert "## Version 1.1.1" in called_with_content
254368
255369
370+ @patch ("openwisp_utils.releaser.release.branch_exists" )
256371@patch ("openwisp_utils.releaser.release.subprocess.run" )
257- def test_port_changelog_skip_pr_creation (mock_subprocess , mock_all ):
372+ def test_port_changelog_skip_pr_creation (mock_subprocess , mock_branch_exists , mock_all ):
258373 """Tests skipping PR creation during changelog porting."""
259374 mock_gh = MagicMock ()
260375 mock_gh .create_pr .side_effect = SkipSignal
261376 mock_config = {"changelog_path" : "CHANGES.rst" }
377+ mock_branch_exists .return_value = True
262378 mock_all ["questionary_select" ].return_value .ask .return_value = "main"
263379
264380 with patch ("openwisp_utils.releaser.release.update_changelog_file" ):
0 commit comments