From 5516d499cdcf5162ddcb40da61729d8703ad228f Mon Sep 17 00:00:00 2001 From: Bohdan Iakymets Date: Mar 19 2019 16:37:25 +0000 Subject: [PATCH 1/9] Add comments synchronization for Github --- diff --git a/sync2jira/downstream.py b/sync2jira/downstream.py index 42ecc59..e818e6e 100644 --- a/sync2jira/downstream.py +++ b/sync2jira/downstream.py @@ -32,6 +32,15 @@ remote_link_title = "Upstream issue" jira_cache = {} +def find(f, seq): + """Return first item in sequence where f(item) == True.""" + for item in seq: + if f(item): + return item + return None + +def _comment_format(comment): + return "%s wrote: %s" % (comment['author'], comment['body']) def _get_jira_client(issue, config): @@ -68,6 +77,8 @@ def _matching_jira_issue_query(client, issue, config, free=False): log.debug("Found %i results for query %r", len(results), query) return results +def _comment_matching(g_comments, j_comments): + return list(filter(lambda x: find(lambda y: y.raw['body'] == "%s wrote: %s" % (x['author'], x['body']), j_comments) == None or x['changed'] != None, g_comments)) def _get_existing_jira_issue(client, issue, config): """ Get a jira issue by the linked remote issue. @@ -166,6 +177,8 @@ def _create_jira_issue(client, issue, config): remote_link = dict(url=issue.url, title=remote_link_title) _attach_link(client, downstream, remote_link) + for comment in issue['comments']: + client.add_comment(downstream, _comment_format(comment)) return downstream @@ -222,6 +235,7 @@ def close_duplicates(issue, config): def sync_with_jira(issue, config): log.info("Considering upstream %s, %s", issue.url, issue.title) + client = jira.client.JIRA(**config['sync2jira']['jira']) # Create a client connection for this issue client = _get_jira_client(issue, config) @@ -231,6 +245,21 @@ def sync_with_jira(issue, config): log.info(" Looking for matching downstream issue via new method.") existing = _get_existing_jira_issue(client, issue, config) if existing: + comments = client.comments(existing) + log.info(" Looking for relative complement between downstream comments and upstream") + comments_d = _comment_matching(issue.comments, comments) + for comment in comments_d: + if comment['changed'] == None: + comment_body = _comment_format(comment) + client.add_comment(existing, comment_body) + else: + j_comment = find(lambda y: y.raw['body'] == _comment_format(comment), comments) + comment_body = "%s wrote: %s" % (comment['author'], comment['changed']) + if j_comment == None: + client.add_comment(existing, comment_body) + else: + j_comment.update(body = comment_body) + log.info(" Comments synchronization done.") log.info(" Found existing, matching downstream %r.", existing.key) return diff --git a/sync2jira/intermediary.py b/sync2jira/intermediary.py index 2529fec..3ea36ee 100644 --- a/sync2jira/intermediary.py +++ b/sync2jira/intermediary.py @@ -20,11 +20,12 @@ class Issue(object): - def __init__(self, source, title, url, upstream, config): + def __init__(self, source, title, url, upstream, comments, config): self.source = source self._title = title self.url = url self.upstream = upstream + self.comments = comments self.downstream = config['sync2jira']['map'][self.source][upstream] @property @@ -44,10 +45,19 @@ class Issue(object): @classmethod def from_github(cls, upstream, issue, config): + comments = [] + for comment in issue['comments']: + print comment + comments.append({ + 'author': comment['user']['login'], + 'body': comment['body'], + 'changed': comment['changed'] if 'changed' in comment else None + }) return Issue( source='github', title=issue['title'], url=issue['html_url'], + comments=comments, upstream=upstream, config=config, ) diff --git a/sync2jira/main.py b/sync2jira/main.py index bf89755..9450f24 100644 --- a/sync2jira/main.py +++ b/sync2jira/main.py @@ -43,6 +43,7 @@ handlers = { 'github.issue.reopened': u.handle_github_message, # Example: https://apps.fedoraproject.org/datagrepper/id?id=2017-a053e0c2-f514-47d6-8cb2-f7b2858f7052&is_raw=true&size=extra-large 'github.issue.labeled': u.handle_github_message, + 'github.issue.comment': u.handle_github_comment, # Example: https://apps.fedoraproject.org/datagrepper/id?id=2016-d578d8f6-0c4c-493d-9535-4e138a03e197&is_raw=true&size=extra-large 'pagure.issue.new': u.handle_pagure_message, # Example: https://apps.fedoraproject.org/datagrepper/id?id=2017-c2e81259-8576-41a9-83c6-6db2cbcf67d3&is_raw=true&size=extra-large @@ -135,7 +136,8 @@ def initialize(config): def main(): config = load_config() - logging.config.dictConfig(config['logging']) + # logging.config.dictConfig(config['logging']) + logging.basicConfig(level=logging.INFO) warnings.simplefilter("ignore") if config['sync2jira'].get('initialize'): diff --git a/sync2jira/upstream.py b/sync2jira/upstream.py index 4798663..d22077a 100644 --- a/sync2jira/upstream.py +++ b/sync2jira/upstream.py @@ -152,22 +152,54 @@ def github_issues(upstream, config): for issue in issues: yield issue +def handle_github_comment(msg, config): + owner = msg['msg']['repository']['owner']['login'] + repo = msg['msg']['repository']['name'] + upstream = '{owner}/{repo}'.format(owner=owner, repo=repo) + mapped_repos = config['sync2jira']['map']['github'] + + token = config['sync2jira'].get('github_token') + if not token: + headers = {} + log.warning('No github_token found. We will be rate-limited...') + else: + headers = {'Authorization': 'token ' + token} + + if upstream not in mapped_repos: + log.info("%r not in github map: %r", upstream, mapped_repos.keys()) + return None + issue = msg['msg']['issue'] + + comment = msg['msg']['comment'] + log.info("Fetching comments for: %s", issue['url']) + issue['comments'] = _fetch_github_data("%s/comments" % (issue['url']), headers).json() + if msg['msg']['action'] == 'edited': + for c in issue['comments']: + if c['body'] == comment['body']: + c['body'] = msg['msg']['changes']['body']['from'] + c['changed'] = comment['body'] + return i.Issue.from_github(upstream, issue, config) def _get_all_github_issues(url, headers): """ Pagination utility. Obnoxious. """ link = dict(next=url) while 'next' in link: - response = requests.get(link['next'], headers=headers) - if not bool(response): - try: - reason = response.json() - except Exception: - reason = response.text - raise IOError("response: %r %r %r" % (response, reason, response.request.url)) + response = _fetch_github_data(link['next'], headers) for issue in response.json(): + comments = _fetch_github_data(issue['comments_url'], headers) + issue['comments'] = comments.json() yield issue link = _github_link_field_to_dict(response.headers.get('link', None)) +def _fetch_github_data(url, headers): + response = requests.get(url, headers=headers) + if not bool(response): + try: + reason = response.json() + except: + reason = response.text + raise IOError("response: %r %r %r" % (response, reason, response.request.url)) + return response def _github_link_field_to_dict(field): """ Utility for ripping apart github's Link header field. From ab265a0e60f91b235d494de9bee3f647a80b33a2 Mon Sep 17 00:00:00 2001 From: Bohdan Iakymets Date: Mar 19 2019 16:37:26 +0000 Subject: [PATCH 2/9] Split line in _comment_matching --- diff --git a/sync2jira/downstream.py b/sync2jira/downstream.py index e818e6e..c9faef1 100644 --- a/sync2jira/downstream.py +++ b/sync2jira/downstream.py @@ -78,7 +78,13 @@ def _matching_jira_issue_query(client, issue, config, free=False): return results def _comment_matching(g_comments, j_comments): - return list(filter(lambda x: find(lambda y: y.raw['body'] == "%s wrote: %s" % (x['author'], x['body']), j_comments) == None or x['changed'] != None, g_comments)) + return list( + filter(lambda x: + find(lambda y: + y.raw['body'] == "%s wrote: %s" % (x['author'], x['body']), j_comments) == None or + x['changed'] != None, g_comments + ) + ) def _get_existing_jira_issue(client, issue, config): """ Get a jira issue by the linked remote issue. From 4de21e1cc4c6e2d62b974f896a9a9becfc9b4d2e Mon Sep 17 00:00:00 2001 From: Bohdan Iakymets Date: Mar 19 2019 16:39:09 +0000 Subject: [PATCH 3/9] Added support comment sync for pagure --- diff --git a/sync2jira/downstream.py b/sync2jira/downstream.py index c9faef1..a2ba0fb 100644 --- a/sync2jira/downstream.py +++ b/sync2jira/downstream.py @@ -183,7 +183,7 @@ def _create_jira_issue(client, issue, config): remote_link = dict(url=issue.url, title=remote_link_title) _attach_link(client, downstream, remote_link) - for comment in issue['comments']: + for comment in issue.comments: client.add_comment(downstream, _comment_format(comment)) return downstream diff --git a/sync2jira/intermediary.py b/sync2jira/intermediary.py index 3ea36ee..2949a4f 100644 --- a/sync2jira/intermediary.py +++ b/sync2jira/intermediary.py @@ -35,12 +35,21 @@ class Issue(object): @classmethod def from_pagure(cls, upstream, issue, config): base = config['sync2jira'].get('pagure_url', 'https://pagure.io') + comments = [] + for comment in issue['comments']: + print comment + comments.append({ + 'author': comment['user']['name'], + 'body': comment['comment'], + 'changed': None + }) return Issue( source='pagure', title=issue['title'], url=base + '/%s/issue/%i' % (upstream, issue['id']), upstream=upstream, config=config, + comments=comments, ) @classmethod diff --git a/sync2jira/main.py b/sync2jira/main.py index 9450f24..abe06bf 100644 --- a/sync2jira/main.py +++ b/sync2jira/main.py @@ -48,6 +48,7 @@ handlers = { 'pagure.issue.new': u.handle_pagure_message, # Example: https://apps.fedoraproject.org/datagrepper/id?id=2017-c2e81259-8576-41a9-83c6-6db2cbcf67d3&is_raw=true&size=extra-large 'pagure.issue.tag.added': u.handle_pagure_message, + 'pagure.issue.comment.added': u.handle_pagure_comment, } diff --git a/sync2jira/upstream.py b/sync2jira/upstream.py index d22077a..78d6275 100644 --- a/sync2jira/upstream.py +++ b/sync2jira/upstream.py @@ -180,6 +180,20 @@ def handle_github_comment(msg, config): c['changed'] = comment['body'] return i.Issue.from_github(upstream, issue, config) +def handle_pagure_comment(msg, config): + upstream = msg['msg']['project']['name'] + ns = msg['msg']['project'].get('namespace') or None + if ns: + upstream = '{ns}/{upstream}'.format(ns=ns, upstream=upstream) + mapped_repos = config['sync2jira']['map']['pagure'] + + if upstream not in mapped_repos: + log.info("%r not in pagure map: %r", upstream, mapped_repos.keys()) + return None + + issue = msg['msg']['issue'] + return i.Issue.from_pagure(upstream, issue, config) + def _get_all_github_issues(url, headers): """ Pagination utility. Obnoxious. """ link = dict(next=url) From f34dcdc93d730b1741831071ca82bc3a6f4cd304 Mon Sep 17 00:00:00 2001 From: Bohdan Iakymets Date: Mar 19 2019 16:39:09 +0000 Subject: [PATCH 4/9] Made jira and github/pagure comments intersection more understandable, return logging level to previous value and remove print --- diff --git a/sync2jira/downstream.py b/sync2jira/downstream.py index a2ba0fb..8538b40 100644 --- a/sync2jira/downstream.py +++ b/sync2jira/downstream.py @@ -32,13 +32,6 @@ remote_link_title = "Upstream issue" jira_cache = {} -def find(f, seq): - """Return first item in sequence where f(item) == True.""" - for item in seq: - if f(item): - return item - return None - def _comment_format(comment): return "%s wrote: %s" % (comment['author'], comment['body']) @@ -77,12 +70,19 @@ def _matching_jira_issue_query(client, issue, config, free=False): log.debug("Found %i results for query %r", len(results), query) return results +def _find_comment_in_jira(comment, j_comments): + formated_comment = _comment_format(comment) + for item in j_comments: + if item.raw['body'] == formated_comment: + return item + return None + + def _comment_matching(g_comments, j_comments): return list( filter(lambda x: - find(lambda y: - y.raw['body'] == "%s wrote: %s" % (x['author'], x['body']), j_comments) == None or - x['changed'] != None, g_comments + _find_comment_in_jira(x, j_comments) == None or x['changed'] != None, + g_comments ) ) diff --git a/sync2jira/intermediary.py b/sync2jira/intermediary.py index 2949a4f..90b0f07 100644 --- a/sync2jira/intermediary.py +++ b/sync2jira/intermediary.py @@ -37,7 +37,6 @@ class Issue(object): base = config['sync2jira'].get('pagure_url', 'https://pagure.io') comments = [] for comment in issue['comments']: - print comment comments.append({ 'author': comment['user']['name'], 'body': comment['comment'], diff --git a/sync2jira/main.py b/sync2jira/main.py index abe06bf..3d8635a 100644 --- a/sync2jira/main.py +++ b/sync2jira/main.py @@ -137,8 +137,7 @@ def initialize(config): def main(): config = load_config() - # logging.config.dictConfig(config['logging']) - logging.basicConfig(level=logging.INFO) + logging.config.dictConfig(config['logging']) warnings.simplefilter("ignore") if config['sync2jira'].get('initialize'): From 153a19c4651f00affc83ea638abe016e3496d3ee Mon Sep 17 00:00:00 2001 From: Ralph Bean Date: Mar 19 2019 16:39:09 +0000 Subject: [PATCH 5/9] Remove print statement. --- diff --git a/sync2jira/intermediary.py b/sync2jira/intermediary.py index 90b0f07..5a10f2b 100644 --- a/sync2jira/intermediary.py +++ b/sync2jira/intermediary.py @@ -55,7 +55,6 @@ class Issue(object): def from_github(cls, upstream, issue, config): comments = [] for comment in issue['comments']: - print comment comments.append({ 'author': comment['user']['login'], 'body': comment['body'], From c8d73fcc8a49523036f38dd61ce0a630d68d68d2 Mon Sep 17 00:00:00 2001 From: Ralph Bean Date: Mar 19 2019 16:39:09 +0000 Subject: [PATCH 6/9] PEP8 futzing. --- diff --git a/tests/test_downstream.py b/tests/test_downstream.py index 08693c4..22cdf90 100644 --- a/tests/test_downstream.py +++ b/tests/test_downstream.py @@ -37,6 +37,7 @@ class TestDownstream(unittest.TestCase): @mock.patch('jira.client.JIRA') def test_get_existing_newstyle(self, client): config = self.config.copy() + class MockIssue(object): downstream = {'key': 'value'} title = 'A title, a title...' @@ -57,6 +58,7 @@ class TestDownstream(unittest.TestCase): @mock.patch('jira.client.JIRA') def test_upgrade_oldstyle_jira_issue(self, client): config = self.config.copy() + class MockIssue(object): downstream = {'key': 'value'} title = 'A title, a title...' @@ -73,7 +75,6 @@ class TestDownstream(unittest.TestCase): } client_obj.add_remote_link.assert_called_once_with(downstream.id, remote) - @mock.patch('jira.client.JIRA') def test_create_jira_issue(self, client): config = self.config.copy() From 77edb5855d5096392c4e3b6c748f36681d506c49 Mon Sep 17 00:00:00 2001 From: Ralph Bean Date: Mar 19 2019 16:39:09 +0000 Subject: [PATCH 7/9] Bring mocks in line with latest comment sync changes. --- diff --git a/tests/test_downstream.py b/tests/test_downstream.py index 22cdf90..7d64c8f 100644 --- a/tests/test_downstream.py +++ b/tests/test_downstream.py @@ -91,6 +91,10 @@ class TestDownstream(unittest.TestCase): } title = 'A title, a title...' url = 'http://threebean.org' + comments = [{ + 'author': 'Ralph', + 'body': 'Super duper.', + }] config['sync2jira']['testing'] = False result = d._create_jira_issue(jira.client.JIRA(), MockIssue(), config) @@ -119,6 +123,7 @@ class TestDownstream(unittest.TestCase): } title = 'A title, a title...' url = 'http://threebean.org' + comments = [] config['sync2jira']['testing'] = False result = d._create_jira_issue(jira.client.JIRA(), MockIssue(), config) diff --git a/tests/test_intermediary.py b/tests/test_intermediary.py index 6cadd71..dbb025c 100644 --- a/tests/test_intermediary.py +++ b/tests/test_intermediary.py @@ -24,17 +24,21 @@ class TestIntermediary(unittest.TestCase): } def test_issue_repr(self): - issue = i.Issue('pagure', 'title', 'url', 'koji', self.config) + issue = i.Issue('pagure', 'title', 'url', 'koji', [], self.config) eq_(repr(issue), '') def test_issue_title(self): - issue = i.Issue('pagure', 'title', 'url', 'koji', self.config) + issue = i.Issue('pagure', 'title', 'url', 'koji', [], self.config) eq_(issue.title, '[koji] title') def test_issue_from_pagure(self): upstream_issue = { 'title': 'title', 'id': 21, + 'comments': [{ + 'user': {'name': 'Ralph'}, + 'comment': 'This is fine.', + }], } issue = i.Issue.from_pagure('koji', upstream_issue, self.config) eq_(issue.title, '[koji] title') @@ -44,6 +48,10 @@ class TestIntermediary(unittest.TestCase): upstream_issue = { 'title': 'title', 'html_url': 'Some github url', + 'comments': [{ + 'user': {'login': 'ralphbean'}, + 'body': 'This is dandy.', + }], } repo = 'product-definition-center/product-definition-center' issue = i.Issue.from_github(repo, upstream_issue, self.config) diff --git a/tests/test_upstream.py b/tests/test_upstream.py index 2be4811..12c46d2 100644 --- a/tests/test_upstream.py +++ b/tests/test_upstream.py @@ -172,8 +172,9 @@ class TestUpstream(unittest.TestCase): @mock.patch('sync2jira.upstream.i.Issue.from_pagure') def test_get_all_pagure_issues(self, from_pagure, requests): response = mock.MagicMock() + mock_issue = {'comments_url': 'comment_url'} response.json.return_value = { - 'issues': ['some_issue_dict'], + 'issues': [mock_issue], } requests.get.return_value = response @@ -186,27 +187,30 @@ class TestUpstream(unittest.TestCase): params={'status': 'Open'}, ) from_pagure.assert_called_once_with( - 'some_repo', 'some_issue_dict', self.config) + 'some_repo', mock_issue, self.config) @mock.patch('sync2jira.upstream.requests') @mock.patch('sync2jira.upstream.i.Issue.from_github') def test_get_all_github_issues(self, from_github, requests): response = mock.MagicMock() - response.json.return_value = [ - 'some_issue_dict', - ] + mock_issue = {'comments_url': 'comment_url'} + response.json.return_value = [mock_issue] requests.get.return_value = response generator = u.github_issues('some_repo', self.config) # Step through that... list(generator) - requests.get.assert_called_once_with( + requests.get.assert_any_call( 'https://api.github.com/repos/some_repo/issues?state=open', headers={}, ) + requests.get.assert_any_call( + 'comment_url', + headers={}, + ) from_github.assert_called_once_with( - 'some_repo', 'some_issue_dict', self.config) + 'some_repo', mock_issue, self.config) @mock.patch('sync2jira.upstream.requests') @mock.patch('sync2jira.upstream.i.Issue.from_pagure') @@ -219,8 +223,9 @@ class TestUpstream(unittest.TestCase): }, } response = mock.MagicMock() + mock_issue = {'comments_url': 'comment_url'} response.json.return_value = { - 'issues': ['some_issue_dict'], + 'issues': [mock_issue], } requests.get.return_value = response @@ -233,7 +238,7 @@ class TestUpstream(unittest.TestCase): params={'some_value': 'present'}, ) from_pagure.assert_called_once_with( - 'some_repo', 'some_issue_dict', self.config) + 'some_repo', mock_issue, self.config) @mock.patch('sync2jira.upstream.requests') @mock.patch('sync2jira.upstream.i.Issue.from_github') @@ -246,18 +251,21 @@ class TestUpstream(unittest.TestCase): }, } response = mock.MagicMock() - response.json.return_value = [ - 'some_issue_dict', - ] + mock_issue = {'comments_url': 'comment_url'} + response.json.return_value = [mock_issue] requests.get.return_value = response generator = u.github_issues('some_repo', self.config) # Step through that... list(generator) - requests.get.assert_called_once_with( + requests.get.assert_any_call( 'https://api.github.com/repos/some_repo/issues?some_value=present', headers={}, ) + requests.get.assert_any_call( + 'comment_url', + headers={}, + ) from_github.assert_called_once_with( - 'some_repo', 'some_issue_dict', self.config) + 'some_repo', mock_issue, self.config) From 3633086a0f19d9e6f9a6086a5fc59875686165fb Mon Sep 17 00:00:00 2001 From: Bohdan Iakymets Date: Mar 19 2019 16:39:09 +0000 Subject: [PATCH 8/9] Made jira and github/pagure comments intersection more understandable, return logging level to previous value and remove print --- diff --git a/sync2jira/downstream.py b/sync2jira/downstream.py index 8538b40..a62cf95 100644 --- a/sync2jira/downstream.py +++ b/sync2jira/downstream.py @@ -33,7 +33,7 @@ remote_link_title = "Upstream issue" jira_cache = {} def _comment_format(comment): - return "%s wrote: %s" % (comment['author'], comment['body']) + return "Upstream, %s wrote:\n\n{quote}\n%s\n{quote}" % (comment['author'], comment['body']) def _get_jira_client(issue, config): From bd5c54a1e5536b7933eb10aa65e972e64c2f7268 Mon Sep 17 00:00:00 2001 From: Bohdan Iakymets Date: Mar 19 2019 16:39:09 +0000 Subject: [PATCH 9/9] Added tests for comments sync --- diff --git a/tests/test_intermediary.py b/tests/test_intermediary.py index dbb025c..4ef85f2 100644 --- a/tests/test_intermediary.py +++ b/tests/test_intermediary.py @@ -43,6 +43,11 @@ class TestIntermediary(unittest.TestCase): issue = i.Issue.from_pagure('koji', upstream_issue, self.config) eq_(issue.title, '[koji] title') eq_(issue.url, 'https://pagure.io/koji/issue/21') + eq_(issue.comments, [{ + 'author': 'Ralph', + 'body': 'This is fine.', + 'changed': None + }]) def test_issue_from_github(self): upstream_issue = { @@ -57,3 +62,8 @@ class TestIntermediary(unittest.TestCase): issue = i.Issue.from_github(repo, upstream_issue, self.config) eq_(issue.title, '[product-definition-center/product-definition-center] title') eq_(issue.url, 'Some github url') + eq_(issue.comments, [{ + 'author': 'ralphbean', + 'body': 'This is dandy.', + 'changed': None + }]) diff --git a/tests/test_upstream.py b/tests/test_upstream.py index 12c46d2..e2a7a7b 100644 --- a/tests/test_upstream.py +++ b/tests/test_upstream.py @@ -1,5 +1,6 @@ import mock import unittest +from nose.tools import eq_ import sync2jira.upstream as u @@ -59,6 +60,112 @@ class TestUpstream(unittest.TestCase): 'some_repo', issue_dict, self.config) @mock.patch('sync2jira.upstream.i.Issue.from_github') + @mock.patch('sync2jira.upstream._fetch_github_data') + def test_handle_github_comment(self, _fetch_github_data, from_github): + comment = { + 'user': {'login': 'threebean'}, + 'body': 'This is fine.', + } + comments = [comment] + issue_dict = { + 'state': 'Open', + 'title': 'title', + 'url': 'some_issue_url', + } + message = { + 'msg': { + 'action': 'created', + 'repository': { + 'name': 'repo', + 'owner': { + 'login': 'org', + }, + }, + 'issue': issue_dict, + 'comment': comment, + }, + } + githubResponse = mock.MagicMock() + githubResponse.json.return_value = comments + _fetch_github_data.return_value = githubResponse + u.handle_github_comment(message, self.config) + + from_github.assert_called_once_with( + 'org/repo', issue_dict, self.config) + + @mock.patch('sync2jira.upstream.i.Issue.from_github') + @mock.patch('sync2jira.upstream._fetch_github_data') + def test_handle_github_change_comment(self, _fetch_github_data, from_github): + comment = { + 'user': {'login': 'threebean'}, + 'body': 'This is fine.', + } + comments = [ + comment, + { + 'user': {'login': 'threebean'}, + 'body': 'Some other text', + } + ] + issue_dict = { + 'state': 'Open', + 'title': 'title', + 'url': 'some_issue_url', + } + message = { + 'msg': { + 'action': 'edited', + 'changes': { + 'body': { + 'from': 'Some text', + }, + }, + 'repository': { + 'name': 'repo', + 'owner': { + 'login': 'org', + }, + }, + 'issue': issue_dict, + 'comment': comment, + }, + } + githubResponse = mock.MagicMock() + githubResponse.json.return_value = comments + _fetch_github_data.return_value = githubResponse + u.handle_github_comment(message, self.config) + + from_github.assert_called_once_with( + 'org/repo', issue_dict, self.config) + + + @mock.patch('sync2jira.upstream.i.Issue.from_pagure') + def test_handle_pagure_comment(self, from_pagure): + issue_dict = { + 'status': 'Open', + 'title': 'title', + 'id': 21, + 'comments': [{ + 'user': {'name': 'Ralph'}, + 'comment': 'This is fine.', + }], + } + message = { + 'msg': { + 'project': { + 'name': 'some_repo', + 'owner': { + 'login': 'org', + }, + }, + 'issue': issue_dict, + }, + } + u.handle_pagure_comment(message, self.config) + from_pagure.assert_called_once_with( + 'some_repo', issue_dict, self.config) + + @mock.patch('sync2jira.upstream.i.Issue.from_github') def test_handle_github_filter_positive(self, from_github): self.config['sync2jira']['filters'] = { 'github': {