diff --git a/.github/workflows/build-and-push.yml b/.github/workflows/build-and-push.yml index 12b560d..91d8fc2 100644 --- a/.github/workflows/build-and-push.yml +++ b/.github/workflows/build-and-push.yml @@ -19,6 +19,6 @@ jobs: registry_username: ${{ secrets.QUAY_IMAGE_SCLORG_BUILDER_USERNAME }} registry_token: ${{ secrets.QUAY_IMAGE_SCLORG_BUILDER_TOKEN }} dockerfile: Dockerfile.daily-tests - tag: "0.8.11" + tag: "0.9.0" image_name: "upstream-daily-tests" quay_application_token: ${{ secrets.QUAY_IMAGE_SCLORG_UPDATE_DESC }} diff --git a/Dockerfile.daily-tests b/Dockerfile.daily-tests index c357664..ffd3ac9 100644 --- a/Dockerfile.daily-tests +++ b/Dockerfile.daily-tests @@ -2,7 +2,7 @@ FROM quay.io/fedora/fedora:42 ENV SHARED_DIR="/var/ci-scripts" \ VERSION="42" \ - RELEASE_UPSTREAM="0.8.11" \ + RELEASE_UPSTREAM="0.9.0" \ UPSTREAM_TMT_REPO="https://github.com/sclorg/sclorg-testing-farm" \ UPSTREAM_TMT_DIR="sclorg-testing-farm" \ HOME="/home/nightly" \ diff --git a/Makefile b/Makefile index d453505..cee6f25 100644 --- a/Makefile +++ b/Makefile @@ -7,4 +7,4 @@ shellcheck: ./run-shellcheck.sh `git ls-files *.sh` build_images: - podman build -t quay.io/sclorg/upstream-daily-tests:0.8.11 -f Dockerfile.daily-tests . + podman build -t quay.io/sclorg/upstream-daily-tests:0.9.0 -f Dockerfile.daily-tests . diff --git a/daily-tests/daily_tests/daily_nightly_tests_report.py b/daily-tests/daily_tests/daily_nightly_tests_report.py index e0525b5..eb5ee6c 100755 --- a/daily-tests/daily_tests/daily_nightly_tests_report.py +++ b/daily-tests/daily_tests/daily_nightly_tests_report.py @@ -160,35 +160,51 @@ def return_plan_name(self, item) -> str: [x[1] for x in self.available_test_case if item.startswith(x[0])] ) + def _get_env_variable(self, var_name: str, default_value: str = "") -> str: + """ + Get environment variable value or return default value if not set. + :param var_name: Name of the environment variable + :param default_value: Default value to return if environment variable is not set + :return: Value of the environment variable or default value + """ + if var_name in os.environ: + value = os.getenv(var_name, default_value) + print(f"Environment variable '{var_name}': '{value}'") + return value + return default_value + def load_mails_from_environment(self): """ Load email addresses from environment variables. """ print(os.environ) - if "DB_MAILS" in os.environ: - SCLORG_MAILS["mariadb-container"] = os.environ["DB_MAILS"].split(",") - SCLORG_MAILS["mysql-container"] = os.environ["DB_MAILS"].split(",") - SCLORG_MAILS["postgresql-container"] = os.environ["DB_MAILS"].split(",") - if "RUBY_MAILS" in os.environ: - SCLORG_MAILS["s2i-ruby-container"] = os.environ["RUBY_MAILS"].split(",") - if "PYTHON_MAILS" in os.environ: - SCLORG_MAILS["s2i-python-container"] = os.environ["PYTHON_MAILS"].split(",") - if "NODEJS_MAILS" in os.environ: - SCLORG_MAILS["s2i-nodejs-container"] = os.environ["NODEJS_MAILS"].split(",") - if "PERL_MAILS" in os.environ: - SCLORG_MAILS["s2i-perl-container"] = os.environ["PERL_MAILS"].split(",") - if "UPSTREAM_MAILS" in os.environ: - SCLORG_MAILS["upstream-tests"] = os.environ["UPSTREAM_MAILS"].split(",") - if "SMTP_SERVER" in os.environ: - self.smtp_server = os.getenv("SMTP_SERVER", "smtp.redhat.com") - if "SMTP_PORT" in os.environ: - self.smtp_port = int(os.getenv("SMTP_PORT", 25)) - if "DEFAULT_MAILS" in os.environ: - self.default_mails = os.environ["DEFAULT_MAILS"].split(",") - if "NIGHTLY_BUILDS_URL" in os.environ: - self.nightly_builds_url = os.environ("NIGHTLY_BUILDS_URL", "") - self.send_email = os.environ.get("SEND_EMAIL", False) - self.send_email = True + SCLORG_MAILS["mariadb-container"] = self._get_env_variable("DB_MAILS").split( + "," + ) + SCLORG_MAILS["mysql-container"] = self._get_env_variable("DB_MAILS").split(",") + SCLORG_MAILS["postgresql-container"] = self._get_env_variable("DB_MAILS").split( + "," + ) + SCLORG_MAILS["s2i-ruby-container"] = self._get_env_variable("RUBY_MAILS").split( + "," + ) + SCLORG_MAILS["s2i-python-container"] = self._get_env_variable( + "PYTHON_MAILS" + ).split(",") + SCLORG_MAILS["s2i-nodejs-container"] = self._get_env_variable( + "NODEJS_MAILS" + ).split(",") + SCLORG_MAILS["s2i-perl-container"] = self._get_env_variable("PERL_MAILS").split( + "," + ) + SCLORG_MAILS["upstream-tests"] = self._get_env_variable("UPSTREAM_MAILS").split( + "," + ) + self.smtp_server = self._get_env_variable("SMTP_SERVER", "smtp.redhat.com") + self.smtp_port = int(self._get_env_variable("SMTP_PORT", "25")) + self.default_mails = os.getenv("DEFAULT_MAILS").split(",") + self.nightly_builds_url = os.getenv("NIGHTLY_BUILDS_URL", "") + self.send_email = bool(os.getenv("SEND_EMAIL", "False")) print(f"Loaded mails from environment: '{SCLORG_MAILS}'") print(f"Default mails: '{self.default_mails}'") diff --git a/daily-tests/tests/test_reports.py b/daily-tests/tests/test_reports.py new file mode 100644 index 0000000..a06daad --- /dev/null +++ b/daily-tests/tests/test_reports.py @@ -0,0 +1,244 @@ +# pylint: disable=import-error,redefined-outer-name,protected-access +"""Tests for daily_nightly_tests_report.""" + +import sys +from pathlib import Path + +import pytest +from flexmock import flexmock + +from daily_tests import daily_nightly_tests_report as nightly_mod +from daily_tests.daily_nightly_tests_report import NightlyTestsReport + +TEST_DIR = Path(__file__).parent.absolute() +sys.path.insert(0, str(TEST_DIR.parent)) + + +@pytest.fixture +def nightly_report(monkeypatch): + """Build NightlyTestsReport with argv that avoids argparse errors.""" + monkeypatch.setattr(sys, "argv", ["daily_nightly_tests_report.py"]) + return NightlyTestsReport() + + +@pytest.fixture +def reset_sclorg_mails(): + """Isolate module-level SCLORG_MAILS across tests.""" + backup = dict(nightly_mod.SCLORG_MAILS) + nightly_mod.SCLORG_MAILS.clear() + yield + nightly_mod.SCLORG_MAILS.clear() + nightly_mod.SCLORG_MAILS.update(backup) + + +@pytest.fixture +def collect_report(nightly_report, monkeypatch, tmp_path): + """NightlyTestsReport with a single test case and tmp reports_dir.""" + nightly_report.reports_dir = tmp_path + monkeypatch.setattr( + nightly_report, + "available_test_case", + {("fedora-test", "nightly-fedora", "Fedora test results:")}, + ) + flexmock(nightly_report).should_receive("send_file_to_pastebin") + return nightly_report + + +class TestDailyNightlyTestsReport: + """Tests for NightlyTestsReport.""" + + def test_get_env_variable_missing_returns_default_empty( + self, nightly_report, monkeypatch + ): + monkeypatch.delenv("TEST_VAR_GET_ENV", raising=False) + assert nightly_report._get_env_variable("TEST_VAR_GET_ENV") == "" + + def test_get_env_variable_missing_returns_explicit_default( + self, nightly_report, monkeypatch + ): + monkeypatch.delenv("TEST_VAR_GET_ENV", raising=False) + got = nightly_report._get_env_variable("TEST_VAR_GET_ENV", "fallback") + assert got == "fallback" + + def test_get_env_variable_set_returns_value(self, nightly_report, monkeypatch): + monkeypatch.setenv("TEST_VAR_GET_ENV", "from-env") + assert nightly_report._get_env_variable("TEST_VAR_GET_ENV") == "from-env" + + def test_get_env_variable_set_empty_string_returns_empty( + self, nightly_report, monkeypatch + ): + monkeypatch.setenv("TEST_VAR_GET_ENV", "") + assert nightly_report._get_env_variable("TEST_VAR_GET_ENV", "ignored") == "" + + +def _env_for_load_mails(**overrides): + """Minimal env so load_mails_from_environment completes without error.""" + base = { + "DB_MAILS": "db1@example.com,db2@example.com", + "RUBY_MAILS": "ruby@example.com", + "PYTHON_MAILS": "py@example.com", + "NODEJS_MAILS": "node@example.com", + "PERL_MAILS": "perl@example.com", + "UPSTREAM_MAILS": "up@example.com", + "DEFAULT_MAILS": "def1@example.com,def2@example.com", + "NIGHTLY_BUILDS_URL": "https://nightly.example", + "SEND_EMAIL": "", + } + base.update(overrides) + return base + + +class TestLoadMailsFromEnvironment: + """Tests for NightlyTestsReport.load_mails_from_environment.""" + + def test_load_mails_from_environment_populates_sclorg_and_report_fields( + self, nightly_report, reset_sclorg_mails, monkeypatch + ): + env = _env_for_load_mails( + SMTP_SERVER="smtp.custom.example.com", SMTP_PORT="465" + ) + for key, val in env.items(): + monkeypatch.setenv(key, val, prepend=False) + + nightly_report.load_mails_from_environment() + + db_list = ["db1@example.com", "db2@example.com"] + assert nightly_mod.SCLORG_MAILS["mariadb-container"] == db_list + assert nightly_mod.SCLORG_MAILS["mysql-container"] == db_list + assert nightly_mod.SCLORG_MAILS["postgresql-container"] == db_list + assert nightly_mod.SCLORG_MAILS["s2i-ruby-container"] == ["ruby@example.com"] + assert nightly_mod.SCLORG_MAILS["s2i-python-container"] == ["py@example.com"] + assert nightly_mod.SCLORG_MAILS["s2i-nodejs-container"] == ["node@example.com"] + assert nightly_mod.SCLORG_MAILS["s2i-perl-container"] == ["perl@example.com"] + assert nightly_mod.SCLORG_MAILS["upstream-tests"] == ["up@example.com"] + assert nightly_report.smtp_server == "smtp.custom.example.com" + assert nightly_report.smtp_port == 465 + assert nightly_report.default_mails == ["def1@example.com", "def2@example.com"] + assert nightly_report.nightly_builds_url == "https://nightly.example" + assert nightly_report.send_email is False + + def test_load_mails_from_environment_smtp_defaults_when_unset( + self, nightly_report, reset_sclorg_mails, monkeypatch + ): + for key, val in _env_for_load_mails().items(): + monkeypatch.setenv(key, val, prepend=False) + monkeypatch.delenv("SMTP_SERVER", raising=False) + monkeypatch.delenv("SMTP_PORT", raising=False) + + nightly_report.load_mails_from_environment() + + assert nightly_report.smtp_server == "smtp.redhat.com" + assert nightly_report.smtp_port == 25 + + +class TestGetPastebinUrl: + """Tests for NightlyTestsReport.get_pastebin_url.""" + + def test_get_pastebin_url_returns_url_from_link_line( + self, nightly_report, tmp_path + ): + link_file = tmp_path / "paste.txt" + link_file.write_text( + "some noise\nLink: https://paste.example.com/abc \ntail\n", + encoding="utf-8", + ) + assert ( + nightly_report.get_pastebin_url(str(link_file)) + == "https://paste.example.com/abc" + ) + + def test_get_pastebin_url_returns_first_link_line(self, nightly_report, tmp_path): + link_file = tmp_path / "paste.txt" + link_file.write_text( + "Link: https://first.example\nLink: https://second.example\n", + encoding="utf-8", + ) + assert nightly_report.get_pastebin_url(str(link_file)) == ( + "https://first.example" + ) + + def test_get_pastebin_url_returns_empty_when_no_link_line( + self, nightly_report, tmp_path + ): + link_file = tmp_path / "paste.txt" + link_file.write_text("no url here\nhttps://ignored\n", encoding="utf-8") + assert nightly_report.get_pastebin_url(str(link_file)) == "" + + def test_get_pastebin_url_returns_empty_for_empty_file( + self, nightly_report, tmp_path + ): + link_file = tmp_path / "paste.txt" + link_file.write_text("", encoding="utf-8") + assert nightly_report.get_pastebin_url(str(link_file)) == "" + + +class TestCollectData: + """Tests for NightlyTestsReport.collect_data.""" + + def test_collect_data_full_success_when_no_failed_logs(self, collect_report): + case_dir = collect_report.reports_dir / "fedora-test" + (case_dir / "results").mkdir(parents=True) + + collect_report.collect_data() + + assert collect_report.full_success is True + assert collect_report.data_dict["SUCCESS"] == ["fedora-test"] + assert collect_report.data_dict["tmt"]["tmt_running"] == [] + assert collect_report.data_dict["tmt"]["tmt_failed"] == [] + + def test_collect_data_records_failed_container_logs(self, collect_report): + case_dir = collect_report.reports_dir / "fedora-test" + log_file = case_dir / "results" / "ns" / "failed.log" + log_file.parent.mkdir(parents=True) + log_file.write_text("err", encoding="utf-8") + + collect_report.collect_data() + + assert collect_report.full_success is False + assert "fedora-test" in collect_report.data_dict + assert collect_report.data_dict["fedora-test"] == [ + (str(log_file), "failed.log") + ] + + def test_collect_data_skips_when_reports_dir_missing( + self, nightly_report, monkeypatch, tmp_path + ): + nightly_report.reports_dir = tmp_path / "does_not_exist" + monkeypatch.setattr( + nightly_report, + "available_test_case", + {("fedora-test", "nightly-fedora", "Fedora test results:")}, + ) + + nightly_report.collect_data() + + assert nightly_report.full_success is True + assert nightly_report.data_dict["SUCCESS"] == [] + + def test_collect_data_skips_when_test_case_dir_missing(self, collect_report): + collect_report.collect_data() + + assert collect_report.full_success is True + assert collect_report.data_dict["SUCCESS"] == [] + + def test_collect_data_tmt_running(self, collect_report): + case_dir = collect_report.reports_dir / "fedora-test" + case_dir.mkdir(parents=True) + (case_dir / "tmt_running").write_text("", encoding="utf-8") + + collect_report.collect_data() + + assert collect_report.full_success is False + assert collect_report.data_dict["tmt"]["tmt_running"] == ["fedora-test"] + assert "fedora-test" not in collect_report.data_dict["SUCCESS"] + + def test_collect_data_tmt_failed(self, collect_report): + case_dir = collect_report.reports_dir / "fedora-test" + case_dir.mkdir(parents=True) + (case_dir / "tmt_failed").write_text("", encoding="utf-8") + + collect_report.collect_data() + + assert collect_report.full_success is False + assert collect_report.data_dict["tmt"]["tmt_failed"] == ["fedora-test"] + assert "fedora-test" not in collect_report.data_dict["SUCCESS"] diff --git a/daily-tests/tox.ini b/daily-tests/tox.ini index b89117b..bce35a3 100644 --- a/daily-tests/tox.ini +++ b/daily-tests/tox.ini @@ -6,6 +6,7 @@ commands = python3 -m pytest --color=yes -v --cov=daily_tests --cov-report=xml:c deps = pytest pytest-cov + flexmock PyYAML requests xmltodict