diff --git a/tests/plugins/test_debug.py b/tests/plugins/test_debug.py new file mode 100644 index 0000000..355cc09 --- /dev/null +++ b/tests/plugins/test_debug.py @@ -0,0 +1,16 @@ +import toddlers.plugins.debug + +import fedora_messaging.api + + +class TestDebugToddler: + def test_accepts_topic(self): + assert toddlers.plugins.debug.DebugToddler.accepts_topic("foo.bar") == True + + def test_process(self): + msg = fedora_messaging.api.Message() + msg.id = 123 + msg.topic = "toddlers.test.topic" + assert ( + toddlers.plugins.debug.DebugToddler.process(config={}, message=msg) == None + ) diff --git a/tests/plugins/test_flag_ci_pr.py b/tests/plugins/test_flag_ci_pr.py new file mode 100644 index 0000000..0326bff --- /dev/null +++ b/tests/plugins/test_flag_ci_pr.py @@ -0,0 +1,159 @@ +import logging +from unittest.mock import patch, MagicMock + +import toddlers.plugins.flag_ci_pr +import pytest + +import fedora_messaging.api + + +class TestFlagCIPRToddler: + def test_accepts_topic_invalid(self): + assert toddlers.plugins.flag_ci_pr.FlagCIPR.accepts_topic("foo.bar") == False + + @pytest.mark.parametrize( + "topic", + [ + "org.centos.#.ci.dist-git-pr.test.error", + "org.centos.prod.ci.dist-git-pr.test.error", + "org.centos.stage.ci.dist-git-pr.test.error", + "org.centos.#.ci.dist-git-pr.test.complete" + "org.centos.prod.ci.dist-git-pr.test.complete" + "org.centos.stg.ci.dist-git-pr.test.complete" + "org.centos.#.ci.dist-git-pr.test.running", + "org.centos.prod.ci.dist-git-pr.test.running", + "org.centos.stg.ci.dist-git-pr.test.running", + ], + ) + def test_accepts_topic_valid(self, topic): + assert toddlers.plugins.flag_ci_pr.FlagCIPR.accepts_topic(topic) == True + + def test_process_invalid(self): + msg = fedora_messaging.api.Message() + msg.id = 123 + msg.topic = "toddlers.test.topic" + assert ( + toddlers.plugins.flag_ci_pr.FlagCIPR.process(config={}, message=msg) == None + ) + + def test_process_invalid_status(self, caplog): + caplog.set_level(logging.INFO) + msg = fedora_messaging.api.Message() + msg.id = 123 + msg.topic = "org.centos.stg.ci.dist-git-pr.test.invalid" + assert ( + toddlers.plugins.flag_ci_pr.FlagCIPR.process(config={}, message=msg) == None + ) + assert ( + caplog.records[-1].message + == "Pipeline state is not 'complete' or 'running' or 'error'." + ) + + def test_process_no_version(self, caplog): + caplog.set_level(logging.INFO) + msg = fedora_messaging.api.Message() + msg.id = 123 + msg.topic = "org.centos.stg.ci.dist-git-pr.test.complete" + assert ( + toddlers.plugins.flag_ci_pr.FlagCIPR.process(config={}, message=msg) == None + ) + assert caplog.records[-1].message == "Unsupported msg version, ignoring" + + def test_process_invalid_complete_status(self, caplog): + caplog.set_level(logging.INFO) + msg = fedora_messaging.api.Message() + msg.id = 123 + msg.topic = "org.centos.stg.ci.dist-git-pr.test.complete" + msg.body = {"version": "0.2.1", "test": {"result": "invalid",}} + assert ( + toddlers.plugins.flag_ci_pr.FlagCIPR.process(config={}, message=msg) == None + ) + assert ( + caplog.records[-1].message + == "Build is not in one of the expected status, ignoring" + ) + + def test_process_no_artifact(self, caplog): + caplog.set_level(logging.INFO) + msg = fedora_messaging.api.Message() + msg.id = 123 + msg.topic = "org.centos.stg.ci.dist-git-pr.test.complete" + msg.body = { + "version": "0.2.1", + "test": {"result": "passed",}, + "artifact": {"commit_hash": "abcdefghijklmnopqrst",}, + } + assert ( + toddlers.plugins.flag_ci_pr.FlagCIPR.process(config={}, message=msg) == None + ) + assert ( + caplog.records[-1].message + == "Invalid message: {'commit_hash': 'abcdefghijklmnopqrst'}, could not extract the PR id from it" + ) + + @patch("toddlers.plugins.flag_ci_pr.requests_session") + def test_process_request_failed(self, mock_requests, caplog): + mock_requests.request.return_value = MagicMock( + ok=False, status_code=401, text="invalid" + ) + caplog.set_level(logging.INFO) + msg = fedora_messaging.api.Message() + msg.id = 123 + msg.topic = "org.centos.stg.ci.dist-git-pr.test.running" + msg.body = { + "version": "0.2.1", + "test": {"result": "passed",}, + "artifact": { + "id": 456, + "commit_hash": "abcdefghijklmnopqrst", + "repository": "namespace/name", + }, + "run": {"url": "https://example.com/testing",}, + } + config = { + "pagure_token_seed": "example_seed", + "pagure_url": "https://pagure.io", + "pagure_token": "ahah", + } + + assert ( + toddlers.plugins.flag_ci_pr.FlagCIPR.process(config=config, message=msg) + == 1 + ) + assert ( + caplog.records[-1].message == "Request to https://pagure.io returned: 401" + ) + + @patch("toddlers.plugins.flag_ci_pr.requests_session") + def test_process_valid(self, mock_requests, caplog): + mock_requests.request.return_value = MagicMock( + ok=True, status_code=200, text="invalid" + ) + caplog.set_level(logging.INFO) + msg = fedora_messaging.api.Message() + msg.id = 123 + msg.topic = "org.centos.stg.ci.dist-git-pr.test.error" + msg.body = { + "version": "0.2.1", + "test": {"result": "passed",}, + "artifact": { + "id": 456, + "commit_hash": "abcdefghijklmnopqrst", + "repository": "namespace/name", + }, + "run": {"url": "https://example.com/testing",}, + } + config = { + "pagure_token_seed": "example_seed", + "pagure_url": "https://pagure.io", + "pagure_token": "ahah", + } + + assert ( + toddlers.plugins.flag_ci_pr.FlagCIPR.process(config=config, message=msg) + == None + ) + assert ( + caplog.records[-2].message == "Request to https://pagure.io returned: 200" + ) + assert caplog.records[-1].message == "All clear" diff --git a/tests/plugins/test_flag_commit_build.py b/tests/plugins/test_flag_commit_build.py new file mode 100644 index 0000000..6bb82b0 --- /dev/null +++ b/tests/plugins/test_flag_commit_build.py @@ -0,0 +1,292 @@ +import logging +from unittest.mock import patch, MagicMock, Mock + +import fedora_messaging.api +import pytest + +import toddlers.plugins.flag_commit_build + + +class TestFlagCommitBuildToddler: + def test_accepts_topic_invalid(self): + assert ( + toddlers.plugins.flag_commit_build.FlagCommitBuild.accepts_topic("foo.bar") + == False + ) + + @pytest.mark.parametrize( + "topic", + [ + "org.fedoraproject.#.buildsys.build.state.change", + "org.fedoraproject.stg.buildsys.build.state.change", + "org.fedoraproject.prod.buildsys.build.state.change", + ], + ) + def test_accepts_topic_valid(self, topic): + assert ( + toddlers.plugins.flag_commit_build.FlagCommitBuild.accepts_topic(topic) + == True + ) + + def test_process_containerbuild(self, caplog): + caplog.set_level(logging.INFO) + msg = fedora_messaging.api.Message() + msg.id = 123 + msg.topic = "org.fedoraproject.prod.buildsys.build.state.change" + msg.body = {"owner": "containerbuild"} + assert ( + toddlers.plugins.flag_commit_build.FlagCommitBuild.process( + config={}, message=msg + ) + == None + ) + assert caplog.records[-1].message == "Skipping container build" + + def test_process_mbs_build(self, caplog): + caplog.set_level(logging.INFO) + msg = fedora_messaging.api.Message() + msg.id = 123 + msg.topic = "org.fedoraproject.prod.buildsys.build.state.change" + msg.body = {"owner": "mbs/mbs.fedoraproject.org"} + assert ( + toddlers.plugins.flag_commit_build.FlagCommitBuild.process( + config={}, message=msg + ) + == None + ) + assert caplog.records[-1].message == "Skipping MBS builds" + + def test_process_secondary_instance(self, caplog): + caplog.set_level(logging.INFO) + msg = fedora_messaging.api.Message() + msg.id = 123 + msg.topic = "org.fedoraproject.prod.buildsys.build.state.change" + msg.body = { + "owner": "username", + "instance": "secondary", + } + assert ( + toddlers.plugins.flag_commit_build.FlagCommitBuild.process( + config={}, message=msg + ) + == None + ) + assert caplog.records[-1].message == "Ignoring secondary arch task..." + + def test_process_uninteresting_status(self, caplog): + caplog.set_level(logging.INFO) + msg = fedora_messaging.api.Message() + msg.id = 123 + msg.topic = "org.fedoraproject.prod.buildsys.build.state.change" + msg.body = { + "owner": "username", + "instance": "primary", + "new": 2, + } + assert ( + toddlers.plugins.flag_commit_build.FlagCommitBuild.process( + config={}, message=msg + ) + == None + ) + assert ( + caplog.records[-1].message + == "Build is not in a state we care about, skipping" + ) + + @patch("toddlers.plugins.flag_commit_build.koji") + def test_process_no_git_url(self, mock_koji, caplog): + client = Mock() + client.getBuild = Mock(return_value={}) + mock_koji.ClientSession = Mock(return_value=client) + caplog.set_level(logging.INFO) + msg = fedora_messaging.api.Message() + msg.id = 123 + msg.topic = "org.fedoraproject.prod.buildsys.build.state.change" + msg.body = { + "owner": "username", + "instance": "primary", + "new": 1, + "build_id": 42, + } + config = {"koji_url": "https://koji.fedoraproject.org"} + assert ( + toddlers.plugins.flag_commit_build.FlagCommitBuild.process( + config=config, message=msg + ) + == None + ) + assert ( + caplog.records[-1].message + == "No git url found in the extra information: None" + ) + + @patch("toddlers.plugins.flag_commit_build.koji") + def test_process_invalid_git_url(self, mock_koji, caplog): + client = Mock() + client.getBuild = Mock( + return_value={"extra": {"source": {"original_url": "foobar"}}} + ) + mock_koji.ClientSession = Mock(return_value=client) + caplog.set_level(logging.INFO) + msg = fedora_messaging.api.Message() + msg.id = 123 + msg.topic = "org.fedoraproject.prod.buildsys.build.state.change" + msg.body = { + "owner": "username", + "instance": "primary", + "new": 4, + "build_id": 42, + } + config = {"koji_url": "https://koji.fedoraproject.org"} + assert ( + toddlers.plugins.flag_commit_build.FlagCommitBuild.process( + config=config, message=msg + ) + == None + ) + assert caplog.records[-1].message == "No # in the git_url: foobar" + + @patch("toddlers.plugins.flag_commit_build.requests_session") + @patch("toddlers.plugins.flag_commit_build.koji") + def test_process_flag_failed(self, mock_koji, mock_requests, caplog): + mock_requests.request.return_value = MagicMock( + ok=False, status_code=401, text="invalid" + ) + client = Mock() + client.getBuild = Mock( + return_value={ + "extra": { + "source": { + "original_url": "https://src.fedoraproject.org/rpms/guake.git#commit_hash123" + } + } + } + ) + mock_koji.ClientSession = Mock(return_value=client) + caplog.set_level(logging.INFO) + msg = fedora_messaging.api.Message() + msg.id = 123 + msg.topic = "org.fedoraproject.prod.buildsys.build.state.change" + msg.body = { + "owner": "username", + "instance": "primary", + "new": 3, + "build_id": 42, + "epoch": "0", + "version": "1.0.0", + "name": "guake", + "release": "1", + "task_id": 84, + } + config = { + "koji_url": "https://koji.fedoraproject.org", + "pagure_url": "https://src.fedoraproject.org", + "pagure_token": "ahah", + } + assert ( + toddlers.plugins.flag_commit_build.FlagCommitBuild.process( + config=config, message=msg + ) + == None + ) + assert ( + caplog.records[-1].message + == "Request to https://src.fedoraproject.org returned: 401" + ) + + @patch("toddlers.plugins.flag_commit_build.requests_session") + @patch("toddlers.plugins.flag_commit_build.koji") + def test_process_flag_failed_cancel(self, mock_koji, mock_requests, caplog): + mock_requests.request.return_value = MagicMock( + ok=False, status_code=401, text="invalid" + ) + client = Mock() + client.getBuild = Mock( + return_value={ + "extra": { + "source": { + "original_url": "https://src.fedoraproject.org/rpms/guake.git#commit_hash123" + } + } + } + ) + mock_koji.ClientSession = Mock(return_value=client) + caplog.set_level(logging.INFO) + msg = fedora_messaging.api.Message() + msg.id = 123 + msg.topic = "org.fedoraproject.prod.buildsys.build.state.change" + msg.body = { + "owner": "username", + "instance": "primary", + "new": 4, + "build_id": 42, + "epoch": "0", + "version": "1.0.0", + "name": "guake", + "release": "1", + "task_id": 84, + } + config = { + "koji_url": "https://koji.fedoraproject.org", + "pagure_url": "https://src.fedoraproject.org", + "pagure_token": "ahah", + } + assert ( + toddlers.plugins.flag_commit_build.FlagCommitBuild.process( + config=config, message=msg + ) + == None + ) + assert ( + caplog.records[-1].message + == "Request to https://src.fedoraproject.org returned: 401" + ) + + @patch("toddlers.plugins.flag_commit_build.requests_session") + @patch("toddlers.plugins.flag_commit_build.koji") + def test_process_flag_pass(self, mock_koji, mock_requests, caplog): + mock_requests.request.return_value = MagicMock( + ok=True, status_code=200, text="woohoo!" + ) + client = Mock() + client.getBuild = Mock( + return_value={ + "extra": { + "source": { + "original_url": "https://src.fedoraproject.org/rpms/guake.git#commit_hash123" + } + } + } + ) + mock_koji.ClientSession = Mock(return_value=client) + caplog.set_level(logging.INFO) + msg = fedora_messaging.api.Message() + msg.id = 123 + msg.topic = "org.fedoraproject.prod.buildsys.build.state.change" + msg.body = { + "owner": "username", + "instance": "primary", + "new": 1, + "build_id": 42, + "version": "1.0.0", + "name": "guake", + "release": "1", + "task_id": 84, + } + config = { + "koji_url": "https://koji.fedoraproject.org", + "pagure_url": "https://src.fedoraproject.org", + "pagure_token": "ahah", + } + assert ( + toddlers.plugins.flag_commit_build.FlagCommitBuild.process( + config=config, message=msg + ) + == None + ) + assert ( + caplog.records[-2].message + == "Request to https://src.fedoraproject.org returned: 200" + ) + assert caplog.records[-1].message == "All clear" diff --git a/tests/plugins/test_plugins.py b/tests/plugins/test_plugins.py new file mode 100644 index 0000000..d3d9fd5 --- /dev/null +++ b/tests/plugins/test_plugins.py @@ -0,0 +1,14 @@ +import toddlers.plugins + + +def test_toddlers_plugins(): + variables = [var for var in dir(toddlers.plugins) if not var.startswith("__")] + assert sorted(variables) == [ + "debug", + "flag_ci_pr", + "flag_commit_build", + "here", + "importlib", + "name", + "os", + ] diff --git a/tests/test_base.py b/tests/test_base.py new file mode 100644 index 0000000..bb2ccb3 --- /dev/null +++ b/tests/test_base.py @@ -0,0 +1,15 @@ +import toddlers.base + + +class TestToddlerBase: + def test_name(self): + assert toddlers.base.ToddlerBase.name.fget() == "base" + + def test_amqp_topics(self): + assert toddlers.base.ToddlerBase.amqp_topics.fget() == [] + + def test_accepts_topic(self): + assert toddlers.base.ToddlerBase.accepts_topic("foo.bar") == None + + def test_process(self): + assert toddlers.base.ToddlerBase.process(config={}, message={}) == None diff --git a/tests/test_runner.py b/tests/test_runner.py new file mode 100644 index 0000000..394f13b --- /dev/null +++ b/tests/test_runner.py @@ -0,0 +1,86 @@ +import logging +from unittest.mock import patch, Mock + +import fedora_messaging.api +import fedora_messaging.exceptions +import pytest + +import toddlers.runner + + +class TestRunningToddler: + def test___init__(self): + runner = toddlers.runner.RunningToddler() + assert sorted([t.name for t in runner.toddlers]) == [ + "debug", + "flag_ci_pr", + "flag_commit_build", + ] + + @patch("toddlers.base.ToddlerBase") + def test___init__no_toddlers(self, mock_base, caplog): + mock_base.__subclasses__ = Mock(return_value=[]) + caplog.set_level(logging.INFO) + with pytest.raises( + fedora_messaging.exceptions.ConfigurationException, + match=r".* No toddlers found to run .*", + ): + runner = toddlers.runner.RunningToddler() + assert sorted([t.name for t in runner.toddlers]) == [ + "debug", + "flag_ci_pr", + "flag_commit_build", + ] + assert caplog.records[-2].message == "Toddlers processing: 123 -- foo.bar" + assert ( + caplog.records[-1].message + == "Toddler 'debug' accepted to process message id: 123" + ) + + @patch.dict( + "toddlers.runner.fedora_messaging.config.conf", + {"consumer_config": {"blocked_toddlers": ["debug", "flag_ci_pr"]}}, + ) + def test___init__blockedlist(self, caplog): + runner = toddlers.runner.RunningToddler() + assert sorted([t.name for t in runner.toddlers]) == ["flag_commit_build"] + + def test___call__(self, caplog): + caplog.set_level(logging.INFO) + msg = fedora_messaging.api.Message() + msg.id = 123 + msg.topic = "foo.bar" + msg.body = { + "owner": "username", + "instance": "secondary", + } + runner = toddlers.runner.RunningToddler() + runner.__call__(msg) + assert caplog.records[-3].message.startswith("Topics of interest:") + assert caplog.records[-2].message == "Toddlers processing: 123 -- foo.bar" + assert ( + caplog.records[-1].message + == "Toddler 'debug' accepted to process message id: 123" + ) + + @patch("toddlers.plugins.debug.DebugToddler.process", side_effect=Exception("haha")) + def test___call__exception(self, caplog): + caplog.set_level(logging.DEBUG) + msg = fedora_messaging.api.Message() + msg.id = 123 + msg.topic = "foo.bar" + msg.body = { + "owner": "username", + "instance": "secondary", + } + runner = toddlers.runner.RunningToddler() + with pytest.raises( + fedora_messaging.exceptions.Nack, match=r"A toddler tripped" + ): + runner.__call__(msg) + assert caplog.records[-3].message.startswith("Topics of interest:") + assert caplog.records[-2].message == "Toddlers processing: 123 -- foo.bar" + assert ( + caplog.records[-1].message + == "Toddler 'debug' accepted to process message id: 123" + )