diff --git a/conftest.py b/conftest.py index 38a53b1..00c385d 100644 --- a/conftest.py +++ b/conftest.py @@ -12,9 +12,13 @@ def kb(mocker): This mock is used by various tests and can be extended with more methods should they be needed. """ kb = mocker.Mock(kanboard.Client) + kb.add_group_member = mocker.Mock() kb.create_comment = mocker.Mock() + kb.create_task = mocker.Mock() + kb.create_task_file = mocker.Mock() kb.create_user = mocker.Mock() kb.get_all_users = mocker.Mock() + kb.get_project_by_name = mocker.Mock() kb.get_task = mocker.Mock() kb.open_task = mocker.Mock() kb.update_task = mocker.Mock() @@ -25,6 +29,6 @@ def email_message(mocker): """ Mock an email.message.EmailMessage object """ - mail = mocker.Mock(EmailMessage) + mail = mocker.MagicMock(EmailMessage) mail.walk.return_value = [] return mail diff --git a/src/tasks_from_email.py b/src/tasks_from_email.py index 95a2946..4476c03 100755 --- a/src/tasks_from_email.py +++ b/src/tasks_from_email.py @@ -40,7 +40,7 @@ sys.path.append('/etc/tasks_from_email') try: from tasks_from_email_config import * -except ImportError: +except ImportError: # pragma: no cover if os.path.exists('/etc/tasks_from_email'): print('ERROR: Make sure tasks_from_email_config.py exists in "/etc/task_from_email", are readable and contain valid settings.') else: @@ -203,7 +203,7 @@ def main(): kb_user_id = create_user_for_sender(kb, email_address) """ add user to group """ - if KANBOARD_GROUP_ID > 0: + if KANBOARD_GROUP_ID > 0: # pragma: no cover - will get tested once config is refactored kb.add_group_member(group_id=KANBOARD_GROUP_ID, user_id=kb_user_id) """ get id from project specified """ @@ -235,5 +235,5 @@ def main(): imap_close(imap_connection) -if __name__ == "__main__": +if __name__ == "__main__": # pragma: no cover main() diff --git a/src/test_main.py b/src/test_main.py new file mode 100644 index 0000000..5c73efe --- /dev/null +++ b/src/test_main.py @@ -0,0 +1,111 @@ +import pytest + +import email +import kanboard + +import tasks_from_email + + +class TestMain: + def test_call_no_results(self, mocker): + mocker.patch("tasks_from_email.imap_connect") + mocker.patch("tasks_from_email.imap_search_unseen") + mocker.patch("tasks_from_email.imap_close") + + tasks_from_email.imap_search_unseen.return_value = ("typ", [""]) + + tasks_from_email.main() + + tasks_from_email.imap_connect.assert_called_once() + tasks_from_email.imap_search_unseen.assert_called_once() + tasks_from_email.imap_close.assert_called_once() + + @pytest.mark.parametrize("create", [(True), (False)]) + def test_call_with_mocks(self, mocker, kb, email_message, create): + mocker.patch("tasks_from_email.imap_connect") + mocker.patch("tasks_from_email.imap_search_unseen") + mocker.patch("tasks_from_email.imap_close") + mocker.patch("tasks_from_email.convert_to_kb_date") + mocker.patch("tasks_from_email.create_user_for_sender") + mocker.patch("tasks_from_email.get_task_if_subject_matches") + mocker.patch("tasks_from_email.reopen_and_update") + mocker.patch("email.message_from_bytes") + mocker.patch("email.header.make_header") + mocker.patch("kanboard.Client") + + imap_connection = mocker.Mock() + imap_connection.fetch.return_value = ("typ", [(None, b"raw")]) + tasks_from_email.imap_connect.return_value = imap_connection + tasks_from_email.imap_search_unseen.return_value = ("typ", ["a"]) + email.message_from_bytes.return_value = email_message + mock_data = { + "Date": "rfcdate", + "From": "from@example.org", + "To": "to@example.org", + "Subject": "subject", + } + + def getitem(name): + return mock_data[name] + + email_message.__getitem__.side_effect = getitem + tasks_from_email.convert_to_kb_date.return_value = "converted-date" + tasks_from_email.create_user_for_sender.return_value = 2 + kanboard.Client.return_value = kb + kb.get_project_by_name.return_value = {"id": 1} + if create: + tasks_from_email.get_task_if_subject_matches.return_value = (None, None) + else: + tasks_from_email.get_task_if_subject_matches.return_value = ( + 1, + {"is_active": True}, + ) + email.header.make_header.return_value = "ExampleHeader" + kb.create_task.return_value = 1 + + tasks_from_email.main() + + tasks_from_email.imap_connect.assert_called_once() + tasks_from_email.imap_search_unseen.assert_called_once() + imap_connection.fetch.assert_called_once_with("a", "(RFC822)") + email.message_from_bytes.assert_called_once_with(b"raw") + tasks_from_email.convert_to_kb_date.assert_has_calls( + [mocker.call("rfcdate"), mocker.call("rfcdate", 48)] + ) + assert email_message.__getitem__.call_args_list == [ + mocker.call("Date"), + mocker.call("Date"), + mocker.call("From"), + mocker.call("To"), + mocker.call("Subject"), + ] + kanboard.Client.assert_called_once() + tasks_from_email.create_user_for_sender.called_once_with(kb, "from@example.org") + kb.add_group_member.assert_not_called() + kb.get_project_by_name.assert_called_once_with(name="Support") + tasks_from_email.get_task_if_subject_matches.assert_called_once_with( + kb, "ExampleHeader" + ) + if create: + kb.create_task.assert_called_once_with( + project_id="1", + title="ExampleHeader", + creator_id=2, + date_started="converted-date", + date_due="converted-date", + description="From: from@example.org\n\nTo: to@example.org\n\nDate: converted-date\n\nSubject: ExampleHeader\n\nNone", + ) + tasks_from_email.reopen_and_update.assert_not_called() + else: + kb.create_task.assert_not_called() + tasks_from_email.reopen_and_update.called_once_with( + kb, {"is_active": True}, 1, 2, "None", "converted-date" + ) + kb.create_task_file.assert_called_once_with( + project_id="1", + task_id="1", + filename="ExampleHeader.mbox", + blob="cmF3", # None + ) + + tasks_from_email.imap_close.assert_called_once()