Skip to content

Commit c5adf8d

Browse files
authored
If create_pr_issue is set, search before creating (#413)
* If create_pr_issue is set, search before creating * Take two
1 parent 3a22478 commit c5adf8d

4 files changed

Lines changed: 171 additions & 30 deletions

File tree

sync2jira/downstream_issue.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ def get_jira_client(issue, config):
294294
return client
295295

296296

297-
def _get_existing_jira_issue(client, issue, config):
297+
def get_existing_jira_issue(client, issue, config):
298298
"""
299299
Get a jira issue by the linked remote issue.
300300
@@ -1414,7 +1414,7 @@ def update_jira(client, config, issue):
14141414
# First, check to see if we have a matching issue using the new method.
14151415
# If we do, then bail out. No sync needed.
14161416
log.info("Looking for matching downstream issue via new method.")
1417-
existing = _get_existing_jira_issue(client, issue, config)
1417+
existing = get_existing_jira_issue(client, issue, config)
14181418
if existing:
14191419
# If we found an existing JIRA issue already
14201420
log.info("Found existing, matching downstream %r.", existing.key)

sync2jira/downstream_pr.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,8 +225,11 @@ def update_jira(client, config, pr):
225225
if not pr.jira_key:
226226
create_pr_issue = pr.downstream.get("create_pr_issue", False)
227227
if create_pr_issue:
228-
# Convert PR to Issue-like object for creation
229-
_create_jira_issue_from_pr(client, pr, config)
228+
if existing := d_issue.get_existing_jira_issue(client, pr, config):
229+
log.info(f"Found existing JIRA issue {existing.key} for PR {pr.url}")
230+
update_jira_issue(existing, pr, client)
231+
else:
232+
_create_jira_issue_from_pr(client, pr, config)
230233
else:
231234
log.info("No JIRA key found in PR, skipping.")
232235
return

tests/test_downstream_issue.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ class MockIssue(object):
191191
@mock.patch("jira.client.JIRA")
192192
def test_get_existing_newstyle(self, mock_client, mock_get_query, mock_filter):
193193
"""
194-
This tests '_get_existing_jira_issue' function.
194+
This tests 'get_existing_jira_issue' function.
195195
"""
196196
mock_issue_1: JIssue = MagicMock(spec=JIssue, name="mock_issue_1")
197197
mock_issue_1.key = "MOCK-1"
@@ -248,7 +248,7 @@ def test_get_existing_newstyle(self, mock_client, mock_get_query, mock_filter):
248248
mock_get_query.return_value = x["jira_results"]
249249
mock_client.search_issues.return_value = x["search_issues"]
250250
mock_filter.return_value = x["filter_results"]
251-
result = d._get_existing_jira_issue(
251+
result = d.get_existing_jira_issue(
252252
client=mock_client, issue=self.mock_issue, config=self.mock_config
253253
)
254254
self.assertEqual(result, x["expected"])
@@ -893,7 +893,7 @@ def update_state(
893893
self.assertEqual(actual, expected, f"In scenario {scenario}")
894894

895895
@mock.patch(PATH + "get_jira_client")
896-
@mock.patch(PATH + "_get_existing_jira_issue")
896+
@mock.patch(PATH + "get_existing_jira_issue")
897897
@mock.patch(PATH + "_update_jira_issue")
898898
@mock.patch(PATH + "_create_jira_issue")
899899
@mock.patch("jira.client.JIRA")
@@ -930,7 +930,7 @@ def test_sync_with_jira_matching(
930930
mock_existing_jira_issue_legacy.assert_not_called()
931931

932932
@mock.patch(PATH + "get_jira_client")
933-
@mock.patch(PATH + "_get_existing_jira_issue")
933+
@mock.patch(PATH + "get_existing_jira_issue")
934934
@mock.patch(PATH + "_update_jira_issue")
935935
@mock.patch(PATH + "_create_jira_issue")
936936
@mock.patch("jira.client.JIRA")
@@ -965,7 +965,7 @@ def test_sync_with_jira_down(
965965
mock_existing_jira_issue_legacy.assert_not_called()
966966

967967
@mock.patch(PATH + "get_jira_client")
968-
@mock.patch(PATH + "_get_existing_jira_issue")
968+
@mock.patch(PATH + "get_existing_jira_issue")
969969
@mock.patch(PATH + "_update_jira_issue")
970970
@mock.patch(PATH + "_create_jira_issue")
971971
@mock.patch("jira.client.JIRA")

tests/test_downstream_pr.py

Lines changed: 159 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -411,51 +411,189 @@ def test_sync_with_jira_create_pr_issue_disabled(
411411
# Assert everything was called correctly
412412
mock_update_jira.assert_not_called()
413413

414+
@mock.patch(PATH + "d_issue")
415+
def test_update_jira_service_unavailable(self, mock_d_issue):
416+
"""Test 'update_jira' when in development mode and the Jira service is unavailable."""
417+
418+
# Set up return values
419+
self.mock_config["sync2jira"]["develop"] = False
420+
mock_d_issue.check_jira_status.return_value = False
421+
422+
# Call the function
423+
with self.assertRaises(RuntimeError):
424+
d.update_jira(self.mock_client, self.mock_config, self.mock_pr)
425+
414426
@mock.patch(PATH + "_create_jira_issue_from_pr")
427+
@mock.patch(PATH + "update_jira_issue")
428+
@mock.patch(PATH + "matcher")
415429
@mock.patch(PATH + "d_issue")
416-
def test_update_jira_create_pr_issue_enabled(
417-
self, mock_d_issue, mock_create_jira_issue_from_pr
430+
def test_update_jira_with_no_key_create_enabled_no_issue(
431+
self,
432+
mock_d_issue,
433+
mock_matcher,
434+
mock_update_jira_issue,
435+
mock_create_jira_issue_from_pr,
418436
):
419-
"""
420-
Test 'update_jira' when create_pr_issue is enabled and no JIRA key is found.
437+
"""Test 'update_jira' when no JIRA key is found, issue creation is
438+
enabled, and the issue is not found.
421439
"""
422440
# Set up return values
423-
mock_client = MagicMock()
424441
mock_d_issue.check_jira_status.return_value = True
425-
self.mock_pr.jira_key = None
442+
mock_matcher.return_value = self.mock_pr.jira_key = None
426443
self.mock_pr.downstream = {"create_pr_issue": True}
444+
mock_d_issue.get_existing_jira_issue.return_value = None
427445

428446
# Call the function
429-
d.update_jira(mock_client, self.mock_config, self.mock_pr)
447+
d.update_jira(self.mock_client, self.mock_config, self.mock_pr)
430448

431449
# Assert everything was called correctly
432-
mock_d_issue.check_jira_status.assert_called_with(mock_client)
433-
mock_create_jira_issue_from_pr.assert_called_with(
434-
mock_client, self.mock_pr, self.mock_config
435-
)
436-
mock_client.search_issues.assert_not_called()
450+
mock_update_jira_issue.assert_not_called()
451+
mock_create_jira_issue_from_pr.assert_called()
437452

438453
@mock.patch(PATH + "_create_jira_issue_from_pr")
454+
@mock.patch(PATH + "update_jira_issue")
455+
@mock.patch(PATH + "matcher")
439456
@mock.patch(PATH + "d_issue")
440-
def test_update_jira_create_pr_issue_disabled(
441-
self, mock_d_issue, mock_create_jira_issue_from_pr
457+
def test_update_jira_with_no_key_create_enabled_issue_exists(
458+
self,
459+
mock_d_issue,
460+
mock_matcher,
461+
mock_update_jira_issue,
462+
mock_create_jira_issue_from_pr,
442463
):
443-
"""
444-
Test 'update_jira' when create_pr_issue is disabled and no JIRA key is found.
464+
"""Test 'update_jira' when no JIRA key is found, issue creation is
465+
enabled, and the issue exists.
445466
"""
446467
# Set up return values
447-
mock_client = MagicMock()
448468
mock_d_issue.check_jira_status.return_value = True
449-
self.mock_pr.jira_key = None
469+
mock_matcher.return_value = self.mock_pr.jira_key = None
470+
self.mock_pr.downstream = {"create_pr_issue": True}
471+
mock_d_issue.get_existing_jira_issue.return_value = self.mock_existing
472+
473+
# Call the function
474+
d.update_jira(self.mock_client, self.mock_config, self.mock_pr)
475+
476+
# Assert everything was called correctly
477+
mock_update_jira_issue.assert_called()
478+
mock_create_jira_issue_from_pr.assert_not_called()
479+
480+
@mock.patch(PATH + "_create_jira_issue_from_pr")
481+
@mock.patch(PATH + "update_jira_issue")
482+
@mock.patch(PATH + "matcher")
483+
@mock.patch(PATH + "d_issue")
484+
def test_update_jira_with_no_key_create_disabled(
485+
self,
486+
mock_d_issue,
487+
mock_matcher,
488+
mock_update_jira_issue,
489+
mock_create_jira_issue_from_pr,
490+
):
491+
"""Test 'update_jira' when no JIRA key is found and issue creation is disabled."""
492+
# Set up return values
493+
mock_d_issue.check_jira_status.return_value = True
494+
mock_matcher.return_value = self.mock_pr.jira_key = None
450495
self.mock_pr.downstream = {"create_pr_issue": False}
451496

452497
# Call the function
453-
d.update_jira(mock_client, self.mock_config, self.mock_pr)
498+
d.update_jira(self.mock_client, self.mock_config, self.mock_pr)
499+
500+
# Assert everything was (not) called correctly
501+
mock_update_jira_issue.assert_not_called()
502+
mock_create_jira_issue_from_pr.assert_not_called()
503+
504+
@mock.patch(PATH + "_create_jira_issue_from_pr")
505+
@mock.patch(PATH + "update_jira_issue")
506+
@mock.patch(PATH + "matcher")
507+
@mock.patch(PATH + "d_issue")
508+
def test_update_jira_with_no_key_create_unspecified(
509+
self,
510+
mock_d_issue,
511+
mock_matcher,
512+
mock_update_jira_issue,
513+
mock_create_jira_issue_from_pr,
514+
):
515+
"""Test 'update_jira' when no JIRA key is found and issue creation is unspecified."""
516+
# Set up return values
517+
mock_d_issue.check_jira_status.return_value = True
518+
mock_matcher.return_value = self.mock_pr.jira_key = None
519+
520+
# Call the function
521+
d.update_jira(self.mock_client, self.mock_config, self.mock_pr)
522+
523+
# Assert everything was (not) called correctly
524+
mock_update_jira_issue.assert_not_called()
525+
mock_create_jira_issue_from_pr.assert_not_called()
526+
527+
@mock.patch(PATH + "_create_jira_issue_from_pr")
528+
@mock.patch(PATH + "update_jira_issue")
529+
@mock.patch(PATH + "matcher")
530+
@mock.patch(PATH + "d_issue")
531+
def test_update_jira_with_key_found_and_unique_issue(
532+
self,
533+
mock_d_issue,
534+
mock_matcher,
535+
mock_update_jira_issue,
536+
mock_create_jira_issue_from_pr,
537+
):
538+
"""Test 'update_jira' when JIRA key is found and the issue is found."""
539+
# Set up return values
540+
mock_d_issue.check_jira_status.return_value = True
541+
mock_matcher.return_value = self.mock_pr.jira_key
542+
543+
# Call the function
544+
d.update_jira(self.mock_client, self.mock_config, self.mock_pr)
454545

455546
# Assert everything was called correctly
456-
mock_d_issue.check_jira_status.assert_called_with(mock_client)
547+
mock_update_jira_issue.assert_called()
548+
mock_create_jira_issue_from_pr.assert_not_called()
549+
550+
@mock.patch(PATH + "_create_jira_issue_from_pr")
551+
@mock.patch(PATH + "update_jira_issue")
552+
@mock.patch(PATH + "matcher")
553+
@mock.patch(PATH + "d_issue")
554+
def test_update_jira_with_key_found_and_no_issue(
555+
self,
556+
mock_d_issue,
557+
mock_matcher,
558+
mock_update_jira_issue,
559+
mock_create_jira_issue_from_pr,
560+
):
561+
"""Test 'update_jira' when JIRA key is found and the issue is not found."""
562+
# Set up return values
563+
mock_d_issue.check_jira_status.return_value = True
564+
mock_matcher.return_value = self.mock_pr.jira_key
565+
self.mock_client.search_issues.return_value = []
566+
567+
# Call the function
568+
d.update_jira(self.mock_client, self.mock_config, self.mock_pr)
569+
570+
# Assert everything was (not) called correctly
571+
mock_update_jira_issue.assert_not_called()
572+
mock_create_jira_issue_from_pr.assert_not_called()
573+
574+
@mock.patch(PATH + "_create_jira_issue_from_pr")
575+
@mock.patch(PATH + "update_jira_issue")
576+
@mock.patch(PATH + "matcher")
577+
@mock.patch(PATH + "d_issue")
578+
def test_update_jira_with_key_found_and_multiple_issues(
579+
self,
580+
mock_d_issue,
581+
mock_matcher,
582+
mock_update_jira_issue,
583+
mock_create_jira_issue_from_pr,
584+
):
585+
"""Test 'update_jira' when JIRA key is found and the search returns multiple issues."""
586+
# Set up return values
587+
mock_d_issue.check_jira_status.return_value = True
588+
mock_matcher.return_value = self.mock_pr.jira_key
589+
self.mock_client.search_issues.return_value = ["MOCK-123", "MOCK-456"]
590+
591+
# Call the function
592+
d.update_jira(self.mock_client, self.mock_config, self.mock_pr)
593+
594+
# Assert everything was called correctly: should exit without calling subroutines
595+
mock_update_jira_issue.assert_not_called()
457596
mock_create_jira_issue_from_pr.assert_not_called()
458-
mock_client.search_issues.assert_not_called()
459597

460598
def _setup_pr_for_issue_creation(self, **overrides):
461599
"""

0 commit comments

Comments
 (0)