X-Git-Url: https://pwan.org/git/?p=margebot.git;a=blobdiff_plain;f=tests%2Ftest_marge.py;fp=tests%2Ftest_marge.py;h=bb53f9d459728a4cd33b5dadbf0a8867ddb2b6c0;hp=0000000000000000000000000000000000000000;hb=37a205f220a629d3a02dd1ce6a8dc75a33242de7;hpb=0471aaab125baa8828affab441ec72caa0e44bee diff --git a/tests/test_marge.py b/tests/test_marge.py new file mode 100644 index 0000000..bb53f9d --- /dev/null +++ b/tests/test_marge.py @@ -0,0 +1,322 @@ +""" +Mrgebot tests +""" +# pragma pylint: disable=invalid-name +# pragma pylint: disable=missing-docstring +# pragma pylint: disable=no-else-return +# pragma pylint: disable=protected-access +# pragma pylint: disable=redefined-outer-name +# pragma pylint: disable=too-many-public-methods +# pragma pylint: disable=unused-argument + +from datetime import datetime +import logging +import json + +import pytest +import errbot +from errbot.backends.test import testbot # pylint: disable=unused-import +import gitlab +from dateutil.relativedelta import relativedelta +from plugins.marge import deltastr + + +class TestMarge(object): + """ + Margebot Tests + + Run 'py.test' from the margebot root directory + """ + + extra_plugin_dir = "./plugins/" + loglevel = logging.INFO + + @pytest.fixture + def margebot(self, testbot, monkeypatch, mocker): + testbot.push_message("!plugin config Marge {'CHATROOM_HOST': 'conference.test.com', 'GITLAB_HOST': 'gitlab.test.com', 'GITLAB_ADMIN_TOKEN': 'fake-token', 'CRONTAB': '0 * * * *', 'VERIFY_SSL': True, 'CRONTAB_SOAK_HOURS': 1}") + testbot.pop_message() + testbot.push_message("!plugin config Webserver {'HOST': '0.0.0.0', 'PORT':3141, 'SSL': {'certificate': '', 'enabled': False, 'host': '0.0.0.0', 'key': '', 'port': 3142}}") + testbot.pop_message() + + def mock_keys(self): + return ['OPEN_MRS'] + monkeypatch.setattr(errbot.storage.StoreMixin, 'keys', mock_keys, raising=False) + + return testbot + + @pytest.fixture + def margebot_no_reviews(self, margebot, monkeypatch): + + def mock_get(self, key): + return {} + + monkeypatch.setattr(errbot.storage.StoreMixin, '__getitem__', mock_get, raising=False) + return margebot + + @pytest.fixture + def margebot_one_review(self, margebot, monkeypatch): + + def mock_get(self, key): + return {(1001, 2001, 'room1,room2'): True} # [(project, iid, notify_rooms)] + + monkeypatch.setattr(errbot.storage.StoreMixin, '__getitem__', mock_get, raising=False) + return margebot + + @pytest.fixture + def one_wip_review(self, margebot_one_review, monkeypatch): + + def mock_getmergerequest(self, project, iid): + return {'author': {'id': 2001}, + 'created_at': 'Oct 29, 2017 2:37am', + 'merge_status': 'can_be_merged', + 'state': 'opened', + 'upvotes': 0, + 'web_url': 'http://gitlab.example.com/sample/mr/2001', + 'work_in_progress': True} + monkeypatch.setattr(gitlab.Gitlab, 'getmergerequest', mock_getmergerequest) + return margebot_one_review + + @pytest.fixture + def one_waiting_review(self, margebot_one_review, monkeypatch): + + def mock_getmergerequest(self, project, iid): + return {'author': {'id': 2001}, + 'created_at': 'Oct 29, 2017 2:37am', + 'merge_status': 'can_be_merged', + 'state': 'opened', + 'upvotes': 1, + 'web_url': 'http://gitlab.example.com/sample/mr/2001', + 'work_in_progress': False} + monkeypatch.setattr(gitlab.Gitlab, 'getmergerequest', mock_getmergerequest) + return margebot_one_review + + @pytest.fixture + def one_conflicted_review(self, margebot_one_review, monkeypatch): + + def mock_getmergerequest(self, project, iid): + return {'author': {'id': 2001}, + 'created_at': 'Oct 29, 2017 2:37am', + 'merge_status': 'merge_conflicts', + 'state': 'opened', + 'upvotes': 2, + 'web_url': 'http://gitlab.example.com/sample/mr/2001', + 'work_in_progress': False} + monkeypatch.setattr(gitlab.Gitlab, 'getmergerequest', mock_getmergerequest) + return margebot_one_review + + @pytest.fixture + def one_mergable_review(self, margebot_one_review, monkeypatch): + + def mock_getmergerequest(self, project, iid): + return {'author': {'id': 2001}, + 'created_at': 'Oct 29, 2017 2:37am', + 'merge_status': 'can_be_merged', + 'state': 'opened', + 'upvotes': 2, + 'web_url': 'http://gitlab.example.com/sample/mr/2001', + 'work_in_progress': False} + monkeypatch.setattr(gitlab.Gitlab, 'getmergerequest', mock_getmergerequest) + return margebot_one_review + + @pytest.fixture + def gitlab(self, monkeypatch): + + def mock_getuser(self, user): + if user == '(missing)': + return False + else: + return {'username': user + ' username'} + monkeypatch.setattr(gitlab.Gitlab, 'getuser', mock_getuser) + + def mock_getusers(self, search=None): + if search: + ((_, val)) = search + if val == "(missing)": + return [] + else: + return [{'user': val, 'id': 3001}] + else: + return [{'user': 'user1', 'id': 3001}, {'user': 'user2', 'id': 3002}] + monkeypatch.setattr(gitlab.Gitlab, 'getusers', mock_getusers) + + # Waiting on Gitlab 8.9 or greater + # def mock_getapprovals(id): + # return [] + # monkeypatch.setattr(gitlab.Gitlab, 'getapprovals', mock_getapprovals) + + return gitlab.Gitlab + + @pytest.fixture + def gitlab_no_reviews(self, gitlab, monkeypatch): + def mock_getmergerequest(self, project, iid): + return {} + monkeypatch.setattr(gitlab, 'getuser', mock_getmergerequest) + + @pytest.fixture + def gitlab_one_review(self, gitlab, monkeypatch): + def mock_getmergerequest(self, project, iid): + return {'author': {'id': 2001}, + 'created_at': 'Oct 29, 2017 2:37am', + 'merge_status': 'can_be_merged', + 'state': 'opened', + 'upvotes': 0, + 'web_url': 'http://gitlab.example.com/sample/mr/2001', + 'work_in_progress': False} + monkeypatch.setattr(gitlab, 'getmergerequest', mock_getmergerequest) + + # ============================================================================ + + @pytest.mark.parametrize("rdelta,expected", [ + (relativedelta(days=1), "1 day ago"), + (relativedelta(days=2), "2 days ago"), + (relativedelta(days=1, hours=1), "1 day, 1 hour ago"), + (relativedelta(days=2, hours=2), "2 days, 2 hours ago"), + (relativedelta(hours=2), "2 hours ago"), + (relativedelta(days=1, minutes=23), "1 day, 23 minutes ago"), + (relativedelta(minutes=2), "2 minutes ago"), + (relativedelta(minutes=1), "1 minute ago"), + (relativedelta(minutes=0), "now"), ]) + def test_deltastr(self, rdelta, expected): + now = datetime.now() + tdelta = (now + rdelta) - now + assert deltastr(tdelta) == expected + + def test_help(self, margebot): + margebot.push_message('!help') + help_message = margebot.pop_message() + assert "Marge" in help_message + assert '!reviews' in help_message + assert '!hello' in help_message + assert '!xyzzy' in help_message + + def test_hello(self, margebot): + margebot.push_message('!hello') + assert 'Hi there' in margebot.pop_message() + +# def test_xyzzy(self, margebot): +# margebot.push_message('!xyzzy') +# assert 'All open MRs have been merged into master' in margebot.pop_message() +# time.sleep(6) +# assert 'just kidding' in margebot.pop_message() + + def test_webstatus(self, margebot): + margebot.push_message('!webstatus') + assert 'margebot/' in margebot.pop_message() + + def test_gitlab_hook(self, margebot, gitlab): + request = json.dumps({'object_kind': 'merge_request', + 'object_attributes': { + 'state': 'opened', + 'work_in_progress': '', + 'title': 'title', + 'author_id': 'author_id', + 'target_project_id': 'project_id', + 'iid': 'iid'}, + 'project': { + 'homepage': 'url'}}) + margebot.push_message("!webhook test /margebot/room1,room2 " + request) + assert 'New Review: author_id username has opened a new MR: \"title\"\nurl/merge_requests/iid' in margebot.pop_message() + margebot.push_message('!reviews') + assert 'New Review: author_id username has opened a new MR: \"title\"\nurl/merge_requests/iid' in margebot.pop_message() + + def test_gitlab_hook_wip(self, margebot, gitlab): + request = json.dumps({'object_kind': 'merge_request', + 'object_attributes': { + 'state': 'opened', + 'work_in_progress': 'true', + 'title': 'title', + 'author_id': 'author_id', + 'target_project_id': 'project_id', + 'iid': 'iid'}, + 'project': { + 'homepage': 'url'}}) + margebot.push_message("!webhook test /margebot/room1,room2 " + request) + assert 'New Review: author_id username has opened a new WIP MR: \"title\"\nurl/merge_requests/iid' in margebot.pop_message() + margebot.push_message('!reviews') + assert 'New Review: author_id username has opened a new WIP MR: \"title\"\nurl/merge_requests/iid' in margebot.pop_message() + + def test_gitlab_hook_unexpected_user(self, margebot, gitlab): + request = json.dumps({'object_kind': 'merge_request', + 'object_attributes': { + 'state': 'opened', + 'work_in_progress': '', + 'title': 'title', + 'author_id': '(missing)', + 'target_project_id': 'project_id', + 'iid': 'iid'}, + 'project': { + 'homepage': 'url'}}) + + margebot.push_message("!webhook test /margebot/room1,room2 " + request) + assert 'New Review: (missing) has opened a new MR: \"title\"\nurl/merge_requests/iid' in margebot.pop_message() + margebot.push_message('!reviews') + assert 'New Review: (missing) has opened a new MR: \"title\"\nurl/merge_requests/iid' in margebot.pop_message() + + def test_gitlab_hook_unexpected_object_kind(self, margebot_no_reviews, gitlab, caplog): + request = json.dumps({'object_kind': 'not_merge_request', + 'object_attributes': { + 'state': 'opened', + 'work_in_progress': '', + 'title': 'title', + 'author_id': 'author_id', + 'target_project_id': 'project_id', + 'iid': 'iid'}, + 'project': { + 'homepage': 'url'}}) + + margebot_no_reviews.push_message("!webhook test /margebot/room1,room2 " + request) + margebot_no_reviews.pop_message() + margebot_no_reviews.push_message('!reviews') + assert 'Hi gbin: I found no open MRs for you.' in margebot_no_reviews.pop_message() + assert 'unexpecting object_kind: not_merge_request' in caplog.text # Has to be at end of method + + def test_nothing_to_review(self, margebot_no_reviews, gitlab_no_reviews): + margebot_no_reviews.push_message('!reviews') + assert 'Hi gbin: I found no open MRs for you.' in margebot_no_reviews.pop_message() + + def test_get_one_open_mr(self, margebot_one_review, gitlab_one_review): + margebot_one_review.push_message('!reviews') + output = margebot_one_review.pop_message() + assert 'These MRs need some attention' in output + assert 'http://gitlab.example.com/sample/mr/2001' in output + assert 'No upvotes, please review.' in output + + def test_get_one_wip_mr(self, one_wip_review, gitlab): + one_wip_review.push_message('!reviews') + output = one_wip_review.pop_message() + assert 'These MRs need some attention' in output + assert 'http://gitlab.example.com/sample/mr/2001' in output + assert 'No upvotes, please review but still WIP.' in output + + def test_get_one_waiting_mr(self, margebot, one_waiting_review, gitlab): + one_waiting_review.push_message('!reviews') + output = margebot.pop_message() + assert 'These MRs need some attention' in output + assert 'http://gitlab.example.com/sample/mr/2001' in output + assert 'Waiting for another upvote.' in output + + def test_get_one_mergable_mr(self, margebot, one_mergable_review, gitlab): + one_mergable_review.push_message('!reviews') + output = margebot.pop_message() + assert 'These MRs need some attention' in output + assert 'http://gitlab.example.com/sample/mr/2001' in output + assert 'Has 2+ upvotes and could be merged in now.' in output + + def test_get_one_conflicted_mr(self, margebot, one_conflicted_review, gitlab): + one_conflicted_review.push_message('!reviews') + output = margebot.pop_message(timeout=1) + assert 'These MRs need some attention' in output + assert 'http://gitlab.example.com/sample/mr/2001' in output + assert 'Has 2+ upvotes and could be merged in now except there are merge conflicts.' in output + + def test_crontab_hook(self, one_waiting_review, gitlab, monkeypatch, mocker): + plugin = one_waiting_review._bot.plugin_manager.get_plugin_obj_by_name('Marge') + + def mock_rooms(): + return [mocker.MagicMock(node='room1'), mocker.MagicMock(node='room2')] + monkeypatch.setattr(plugin, 'rooms', mock_rooms) + + plugin.crontab_hook("unused") + output = one_waiting_review.pop_message() + assert 'These MRs need some attention' in output + assert 'http://gitlab.example.com/sample/mr/2001 (opened' in output