From 0eb4a722cbec2885bec65b2b8add8c1a5ea6fe97 Mon Sep 17 00:00:00 2001 From: Sujay Date: Thu, 5 Dec 2024 01:40:35 +0530 Subject: [PATCH] fix: unit tests --- src/github_services.py | 2 - src/main.py | 13 +- tests/github_services_test.py | 230 ++++++++++++++-------------------- tests/main_test.py | 164 +++++++++++------------- 4 files changed, 176 insertions(+), 233 deletions(-) diff --git a/src/github_services.py b/src/github_services.py index ed5219c..34adc6d 100644 --- a/src/github_services.py +++ b/src/github_services.py @@ -293,8 +293,6 @@ def _get_category_id( assert category_id is not None return category_id - - @check_token def _get_discussion_ids( org_name: str, diff --git a/src/main.py b/src/main.py index 91f6362..1b0bc56 100644 --- a/src/main.py +++ b/src/main.py @@ -80,11 +80,12 @@ def generate_message( """ pr_list_messages: List[str] = [] for pull_request in pull_requests: - assignee = pull_request.get_assignee(username) - assert assignee is not None - pr_list_messages.append( - f'- [#{pull_request.pr_number}]({pull_request.url}) [Waiting for the ' - f'last {assignee.get_waiting_time()}]') + assignee: Optional[github_domain.Assignee] = pull_request.get_assignee(username) + + if assignee is not None: + pr_list_messages.append( + f'- [#{pull_request.pr_number}]({pull_request.url}) [Waiting for the ' + f'last {assignee.get_waiting_time()}]') if not os.path.exists(template_path): @@ -114,7 +115,7 @@ def main(args: Optional[List[str]]=None) -> None: max_wait_hours = parsed_args.max_wait_hours # Raise error if any of the required arguments are not provided. - required_args = ['max_wait_hours', 'discussion_category', 'discussion_title'] + required_args = ['max_wait_hours', 'discussion_category'] for arg in required_args: if arg is None: raise builtins.BaseException(f'Please provide {arg} argument.') diff --git a/tests/github_services_test.py b/tests/github_services_test.py index 2e596eb..660552f 100644 --- a/tests/github_services_test.py +++ b/tests/github_services_test.py @@ -62,71 +62,63 @@ def _get_past_time(self, hours: int=0) -> str: def setUp(self) -> None: self.org_name = 'orgName' self.repo_name = 'repo' - self.discussion_category = 'category' - self.discussion_title = 'title' - self.response_for_get_categories = { - "data": { - "repository": { - "discussionCategories": { - "nodes": [ - { - "id": "test_category_id_1", - "name": "test_category_name_1" - }, + self.discussion_category = 'test_category_name_1' + self.discussion_title = 'Pending Reviews: User-1' + self.discussion_body = 'body' + self.response_for_get_repository_id = { + 'data': { + 'repository': { + 'id': 'test_repository_id' + } + } + } + self.response_for_get_category_id = { + 'data': { + 'repository': { + 'discussionCategories': { + 'nodes': [ { - "id": "test_category_id_2", - "name": "test_category_name_2" + 'id': 'test_category_id_1', + 'name': 'test_category_name_1' + },{ + 'id': 'test_category_id_2', + 'name': 'test_category_name_2' } ] } } } } - self.response_for_get_discussion = { + self.response_for_get_discussion_ids = { 'data': { 'repository': { 'discussions': { 'nodes': [ { 'id': 'test_discussion_id_1', - 'title': 'test_discussion_title_1', - 'number': 12345 + 'title': 'Pending Reviews: User-1', + 'number': 65 } ] } } } } - self.response_for_get_old_comment_ids = { + self.response_for_delete_discussion = { 'data': { - 'repository': { + 'deleteDiscussion': { + 'clientMutationId': 'null', 'discussion': { - 'comments': { - 'nodes': [ - { - 'id': 'test_comment_id_2', - 'createdAt': '2022-05-05T11:44:00Z' - } - ] - } + 'title': 'Pending Reviews: User-1' } } } } - self.response_for_delete_comment = { + self.response_for_create_discussion = { 'data': { - 'deleteDiscussionComment': { - 'clientMutationId': 'test_id' - } - } - } - # Here we use type Any because this response is hard to annotate in a typedDict. - self.response_for_post_comment: Dict[str, Any] = { - 'data': { - 'addDiscussionComment': { - 'clientMutationId': 'test_id', - 'comment': { - 'id': 'test_discussion_id_1' + 'createDiscussion': { + 'discussion': { + 'id': 'D_kwDOJclmXc4AdCMs' } } } @@ -292,19 +284,15 @@ def test_get_prs_assigned_to_reviewers(self) -> None: self.assertEqual(mock_request.call_count, 6) - def test_get_discussion_data(self) -> None: - """Test _get_discussion_data.""" + def test_get_repository_id(self) -> None: + """Test _get_repository_id.""" - mock_response_for_get_categories = self.mock_post_requests( - self.response_for_get_categories) - mock_response_for_get_discussion = self.mock_post_requests( - self.response_for_get_discussion) + mock_response_for_get_repository_id = self.mock_post_requests( + self.response_for_get_repository_id) - self.assertTrue(mock_response_for_get_categories.assert_not_called) - self.assertTrue(mock_response_for_get_discussion.assert_not_called) + self.assertTrue(mock_response_for_get_repository_id.assert_not_called) mock_response = [ - mock_response_for_get_categories, - mock_response_for_get_discussion + mock_response_for_get_repository_id ] with requests_mock.Mocker() as mock_requests: @@ -313,22 +301,19 @@ def test_get_discussion_data(self) -> None: with mock.patch('requests.post', side_effect=mock_response) as mock_post: - mocked_response = github_services._get_discussion_data( + mocked_response = github_services._get_repository_id( self.org_name, - self.repo_name, - 'test_category_name_1', - 'test_discussion_title_1' + self.repo_name ) - self.assertTrue(mock_response_for_get_categories.assert_called_once) - self.assertTrue(mock_response_for_get_discussion.assert_called_once) - self.assertEqual(mock_post.call_count, 2) - self.assertEqual(mocked_response, ('test_discussion_id_1', 12345)) + self.assertTrue(mock_response_for_get_repository_id.assert_called_once) + self.assertEqual(mock_post.call_count, 1) + self.assertEqual(mocked_response, 'test_repository_id') - def test_get_old_comment_ids(self) -> None: - """Test _get_old_comment_ids.""" + def test_get_category_id(self) -> None: + """Test _get_category_id.""" mock_response = mock.Mock() - mock_response.json.return_value = self.response_for_get_old_comment_ids + mock_response.json.return_value = self.response_for_get_category_id self.assertTrue(mock_response.assert_not_called) with requests_mock.Mocker() as mock_requests: @@ -337,40 +322,51 @@ def test_get_old_comment_ids(self) -> None: with mock.patch('requests.post', side_effect=[mock_response]) as mock_post: - mocked_response = github_services._get_old_comment_ids( + mocked_response = github_services._get_category_id( self.org_name, self.repo_name, - 12345 + 'test_category_name_1' ) self.assertTrue(mock_response.assert_called_once) self.assertEqual(mock_post.call_count, 1) - self.assertEqual(mocked_response, ['test_comment_id_2']) + self.assertEqual(mocked_response, 'test_category_id_1') - def test_delete_comment(self) -> None: - """Test delete_comment.""" + def test_get_discussion_ids(self) -> None: + """Test _get_discussion_ids.""" - token = 'my_github_token' - github_services.init_service(token) - - mock_response = mock.Mock() - mock_response.json.return_value = self.response_for_delete_comment - self.assertTrue(mock_response.assert_not_called) + mock_response_1 = mock.Mock() + mock_response_1.json.return_value = self.response_for_get_category_id + mock_response_2 = mock.Mock() + mock_response_2.json.return_value = self.response_for_get_discussion_ids + self.assertTrue(mock_response_1.assert_not_called) + self.assertTrue(mock_response_2.assert_not_called) with requests_mock.Mocker() as mock_requests: self.mock_all_get_requests(mock_requests) - with mock.patch('requests.post', side_effect=[mock_response]) as mock_post: + with mock.patch('requests.post', side_effect=[ + mock_response_1, mock_response_2]) as mock_post: - github_services._delete_comment('test_comment_id_2') - self.assertTrue(mock_response.assert_called) - self.assertEqual(mock_post.call_count, 1) + mocked_response = github_services._get_discussion_ids( + self.org_name, + self.repo_name, + 'test_category_name_1' + ) + self.assertTrue(mock_response_1.assert_called_once) + self.assertTrue(mock_response_2.assert_called_once) + self.assertEqual(mock_post.call_count, 2) + self.assertEqual( + mocked_response, [ + 'test_discussion_id_1' + ] + ) - def test_post_comment(self) -> None: - """Test post comment.""" + def test_delete_discussion(self) -> None: + """Test _delete_discussion.""" mock_response = mock.Mock() - mock_response.json.return_value = self.response_for_post_comment + mock_response.json.return_value = self.response_for_delete_discussion self.assertTrue(mock_response.assert_not_called) with requests_mock.Mocker() as mock_requests: @@ -379,70 +375,40 @@ def test_post_comment(self) -> None: with mock.patch('requests.post', side_effect=[mock_response]) as mock_post: - github_services._post_comment( - 'test_discussion_id_1', - 'test_message' - ) + github_services._delete_discussion('test_discussion_id_1') self.assertTrue(mock_response.assert_called_once) self.assertEqual(mock_post.call_count, 1) - def test_delete_discussion_comments(self) -> None: - """Test delete_discussion_comments function.""" + def test_delete_discussions(self) -> None: + """Test _delete_discussions.""" - token = 'my_github_token' - github_services.init_service(token) - - mock_response_1 = mock.Mock() - mock_response_1.json.return_value = self.response_for_get_categories - - mock_response_2 = mock.Mock() - mock_response_2.json.return_value = self.response_for_get_discussion - - mock_response_3 = mock.Mock() - mock_response_3.json.return_value = self.response_for_get_old_comment_ids - - mock_response_4 = mock.Mock() - mock_response_4.json.return_value = self.response_for_delete_comment - - self.assertTrue(mock_response_1.assert_not_called) - self.assertTrue(mock_response_2.assert_not_called) - self.assertTrue(mock_response_3.assert_not_called) - self.assertTrue(mock_response_4.assert_not_called) + mock_response = mock.Mock() + mock_response.json.return_value = self.response_for_delete_discussion + self.assertTrue(mock_response.assert_not_called) + self.assertTrue(mock_response.assert_not_called) with requests_mock.Mocker() as mock_requests: self.mock_all_get_requests(mock_requests) - with mock.patch('requests.post', side_effect=[ - mock_response_1, mock_response_2, mock_response_3, mock_response_4]) as mock_post: + with mock.patch('requests.post', side_effect=[mock_response]) as mock_post: - github_services.delete_discussion_comments( - self.org_name, - self.repo_name, - 'test_category_name_1', - 'test_discussion_title_1' - ) - self.assertTrue(mock_response_1.assert_called) - self.assertTrue(mock_response_2.assert_called) - self.assertTrue(mock_response_3.assert_called) - self.assertTrue(mock_response_4.assert_called) - self.assertEqual(mock_post.call_count, 4) + github_services._delete_discussion('test_discussion_id_1') + self.assertTrue(mock_response.assert_called_once) + self.assertEqual(mock_post.call_count, 1) - def test_add_discussion_comments(self) -> None: - """Test discussion comments.""" + def test_create_discussion(self) -> None: + """Test create discussion.""" token = 'my_github_token' github_services.init_service(token) mock_response_1 = mock.Mock() - mock_response_1.json.return_value = self.response_for_get_categories - + mock_response_1.json.return_value = self.response_for_get_category_id mock_response_2 = mock.Mock() - mock_response_2.json.return_value = self.response_for_get_discussion - + mock_response_2.json.return_value = self.response_for_get_repository_id mock_response_3 = mock.Mock() - mock_response_3.json.return_value = self.response_for_post_comment - + mock_response_3.json.return_value = self.response_for_create_discussion self.assertTrue(mock_response_1.assert_not_called) self.assertTrue(mock_response_2.assert_not_called) self.assertTrue(mock_response_3.assert_not_called) @@ -454,22 +420,14 @@ def test_add_discussion_comments(self) -> None: with mock.patch('requests.post', side_effect=[ mock_response_1, mock_response_2, mock_response_3]) as mock_post: - github_services.add_discussion_comments( + github_services.create_discussion( self.org_name, self.repo_name, - 'test_category_name_1', - 'test_discussion_title_1', - 'test_message' + self.discussion_category, + self.discussion_title, + self.discussion_body ) self.assertTrue(mock_response_1.assert_called) self.assertTrue(mock_response_2.assert_called) self.assertTrue(mock_response_3.assert_called) self.assertEqual(mock_post.call_count, 3) - - -_get_repository_id -_get_category_id -_get_discussion_ids -_delete_discussion -delete_discussions -create_discussion diff --git a/tests/main_test.py b/tests/main_test.py index fbdeb13..0e2c2d7 100644 --- a/tests/main_test.py +++ b/tests/main_test.py @@ -23,6 +23,7 @@ from unittest import mock from src import github_services +from src import github_domain from src import main import requests @@ -42,13 +43,16 @@ def test_generate_message(self) -> None: file_data = mock.mock_open(read_data=self.test_template) template_path = '.github/PENDING_REVIEW_NOTIFICATION_TEMPLATE.md' with mock.patch('builtins.open', file_data): - pr_list = ( - '- [#123](https://githuburl.pull/123) [Waiting for the last 2 days, 8' - ' hours]') - response = main.generate_message('reviewerName1', pr_list, template_path) - expected_response = ( - '@reviewerName1\n- [#123](https://githuburl.pull/123) [Waiting for the last 2' - ' days, 8 hours]') + pull_requests = github_domain.PullRequest( + 'https://githuburl.pull/123', + 123, + 'user-1', + 'test-title', + [github_domain.Assignee('user-2', datetime.datetime.now())] + ) + response = main.generate_message('reviewerName1', [pull_requests], template_path) + expected_response = '@reviewerName1\n' + self.assertEqual(expected_response, response) def test_generate_message_raises_template_not_found_error(self) -> None: @@ -57,12 +61,12 @@ def test_generate_message_raises_template_not_found_error(self) -> None: file_data = mock.mock_open(read_data=self.test_template) template_path = 'invalid_path' with mock.patch('builtins.open', file_data): - pr_list = ( - '- [#123](https://githuburl.pull/123) [Waiting for the last 2 days, 8 ' - 'hours]') + pull_requests = github_domain.PullRequest( + 'https://githuburl.pull/123',123, 'user-1', 'test-title', [github_domain.Assignee('user-2', (datetime.datetime.now()))] + ) with self.assertRaisesRegex( builtins.BaseException, f'Please add a template file at: {template_path}'): - main.generate_message('reviewerName1', pr_list, template_path) + main.generate_message('reviewerName1', [pull_requests], template_path) class ModuleIntegrationTest(unittest.TestCase): @@ -80,69 +84,58 @@ def setUp(self) -> None: self.repo_name = 'repo' self.discussion_category = 'category' self.discussion_title = 'title' - self.response_for_get_categories = { - "data": { - "repository": { - "discussionCategories": { - "nodes": [ - { - "id": "test_category_id_1", - "name": "test_category_name_1" - }, + self.discussion_body = 'body' + self.response_for_get_repository_id = { + 'data': { + 'repository': { + 'id': 'test_repository_id' + } + } + } + self.response_for_get_category_ids = { + 'data': { + 'repository': { + 'discussionCategories': { + 'nodes': [ { - "id": "test_category_id_2", - "name": "test_category_name_2" + 'id': 'test_category_id_1', + 'name': 'test_category_name_1' } ] } } } } - self.response_for_get_discussion = { + self.response_for_get_discussion_ids = { 'data': { 'repository': { 'discussions': { 'nodes': [ { 'id': 'test_discussion_id_1', - 'title': 'test_discussion_title_1', - 'number': 12345 + 'title': 'Pending Reviews: User-1', + 'number': 65 } ] } } } } - self.response_for_get_old_comment_ids = { + self.response_for_delete_discussion = { 'data': { - 'repository': { + 'deleteDiscussion': { + 'clientMutationId': 'null', 'discussion': { - 'comments': { - 'nodes': [ - { - 'id': 'test_comment_id_2', - 'createdAt': '2022-05-05T11:44:00Z' - } - ] - } + 'title': 'Pending Reviews: User-1' } } } } - self.response_for_delete_comment = { - 'data': { - 'deleteDiscussionComment': { - 'clientMutationId': 'test_id' - } - } - } - # Here we use type Any because this response is hard to annotate in a typedDict. - self.response_for_post_comment: Dict[str, Any] = { + self.response_for_create_discussion = { 'data': { - 'addDiscussionComment': { - 'clientMutationId': 'test_id', - 'comment': { - 'id': 'test_discussion_id_1' + 'createDiscussion': { + 'discussion': { + 'id': 'D_kwDOJclmXc4AdCMs' } } } @@ -246,22 +239,30 @@ def mock_post_requests(self, response: Dict[str, Any]) -> mock.Mock: mocked_response.json.return_value = response return mocked_response - def test_executing_main_function_sends_notification(self) -> None: + def test_main_function(self) -> None: """Test main function to send notification.""" # Here we are mocking the POST requests that we will use in the test below. # and they are listed in the particular order they will be called. - post_requests_side_effect: List[mock.Mock] = [ - self.mock_post_requests(self.response_for_get_categories), - self.mock_post_requests(self.response_for_get_discussion), - self.mock_post_requests(self.response_for_get_old_comment_ids), - self.mock_post_requests(self.response_for_delete_comment), - self.mock_post_requests(self.response_for_get_categories), - self.mock_post_requests(self.response_for_get_discussion), - self.mock_post_requests(self.response_for_post_comment), - self.mock_post_requests(self.response_for_get_categories), - self.mock_post_requests(self.response_for_get_discussion), - self.mock_post_requests(self.response_for_post_comment) + post_requests_side_effect_1: List[mock.Mock] = [ + self.mock_post_requests(self.response_for_get_category_ids), + self.mock_post_requests(self.response_for_get_discussion_ids), + self.mock_post_requests(self.response_for_delete_discussion), + self.mock_post_requests(self.response_for_get_category_ids), + self.mock_post_requests(self.response_for_get_repository_id), + self.mock_post_requests(self.response_for_create_discussion), + ] + + post_requests_side_effect_2: List[mock.Mock] = [ + self.mock_post_requests(self.response_for_get_category_ids), + self.mock_post_requests(self.response_for_get_repository_id), + self.mock_post_requests(self.response_for_delete_discussion), + self.mock_post_requests(self.response_for_get_category_ids), + self.mock_post_requests(self.response_for_get_discussion_ids), + self.mock_post_requests(self.response_for_delete_discussion), + self.mock_post_requests(self.response_for_get_category_ids), + self.mock_post_requests(self.response_for_get_repository_id), + self.mock_post_requests(self.response_for_create_discussion), ] with requests_mock.Mocker() as mock_request: @@ -275,8 +276,7 @@ def test_executing_main_function_sends_notification(self) -> None: # to assert the response. with mock.patch( 'requests.post', side_effect=( - post_requests_side_effect + post_requests_side_effect - )) as mock_post: + post_requests_side_effect_1 + post_requests_side_effect_2)) as mock_post: self.assertEqual(mock_request.call_count, 0) self.assertEqual(mock_post.call_count, 0) @@ -286,68 +286,54 @@ def test_executing_main_function_sends_notification(self) -> None: main.main([ '--repo', 'orgName/repo', '--category', 'test_category_name_1', - '--title', 'test_discussion_title_1', + '--title', 'title', '--max-wait-hours', '20', '--token', 'githubTokenForApiRequest' ]) - response_for_get_categories = requests.post( + response_for_get_category_ids = requests.post( github_services.GITHUB_GRAPHQL_URL, timeout=( github_services.TIMEOUT_SECS)) - response_for_get_discussion = requests.post( + response_for_get_discussion_ids = requests.post( github_services.GITHUB_GRAPHQL_URL, timeout=( github_services.TIMEOUT_SECS)) - response_for_get_old_comment_ids = requests.post( + response_for_delete_discussion = requests.post( github_services.GITHUB_GRAPHQL_URL, timeout=( github_services.TIMEOUT_SECS)) - response_for_delete_comment = requests.post( + response_for_get_category_ids = requests.post( github_services.GITHUB_GRAPHQL_URL, timeout=( github_services.TIMEOUT_SECS)) - response_for_get_categories = requests.post( + response_for_get_repository_id = requests.post( github_services.GITHUB_GRAPHQL_URL, timeout=( github_services.TIMEOUT_SECS)) - response_for_get_discussion = requests.post( + response_for_create_discussion = requests.post( github_services.GITHUB_GRAPHQL_URL, timeout=( github_services.TIMEOUT_SECS)) - response_for_post_comment = requests.post( - github_services.GITHUB_GRAPHQL_URL, timeout=( - github_services.TIMEOUT_SECS)) - - self.assertEqual(mock_post.call_count, 17) + self.assertEqual(mock_post.call_count, 15) self.assertEqual(mock_request.call_count, 6) # Here we use MyPy ignore because the response is of Mock type and # Mock does not contain return_value attribute, so because of this MyPy throws an # error. Thus to avoid the error, we used ignore here. self.assertEqual( - response_for_get_categories.json.return_value, self.response_for_get_categories) # type: ignore[attr-defined] - # Here we use MyPy ignore because the response is of Mock type and - # Mock does not contain return_value attribute, so because of this MyPy throws an - # error. Thus to avoid the error, we used ignore here. - self.assertEqual( - response_for_get_discussion.json.return_value, self.response_for_get_discussion) # type: ignore[attr-defined] - # Here we use MyPy ignore because the response is of Mock type and - # Mock does not contain return_value attribute, so because of this MyPy throws an - # error. Thus to avoid the error, we used ignore here. - self.assertEqual( - response_for_get_old_comment_ids.json.return_value, self.response_for_get_old_comment_ids) # type: ignore[attr-defined] + response_for_get_category_ids.json.return_value, self.response_for_get_category_ids) # type: ignore[attr-defined] # Here we use MyPy ignore because the response is of Mock type and # Mock does not contain return_value attribute, so because of this MyPy throws an # error. Thus to avoid the error, we used ignore here. self.assertEqual( - response_for_delete_comment.json.return_value, self.response_for_delete_comment) # type: ignore[attr-defined] + response_for_get_discussion_ids.json.return_value, self.response_for_get_discussion_ids) # type: ignore[attr-defined] # Here we use MyPy ignore because the response is of Mock type and # Mock does not contain return_value attribute, so because of this MyPy throws an # error. Thus to avoid the error, we used ignore here. self.assertEqual( - response_for_get_categories.json.return_value, self.response_for_get_categories) # type: ignore[attr-defined] + response_for_delete_discussion.json.return_value, self.response_for_delete_discussion) # type: ignore[attr-defined] # Here we use MyPy ignore because the response is of Mock type and # Mock does not contain return_value attribute, so because of this MyPy throws an # error. Thus to avoid the error, we used ignore here. self.assertEqual( - response_for_get_discussion.json.return_value, self.response_for_get_discussion) # type: ignore[attr-defined] + response_for_get_repository_id.json.return_value, self.response_for_get_repository_id) # type: ignore[attr-defined] # Here we use MyPy ignore because the response is of Mock type and # Mock does not contain return_value attribute, so because of this MyPy throws an # error. Thus to avoid the error, we used ignore here. self.assertEqual( - response_for_post_comment.json.return_value, self.response_for_post_comment) # type: ignore[attr-defined] + response_for_create_discussion.json.return_value, self.response_for_create_discussion) # type: ignore[attr-defined]