diff --git a/cli/objects/failure.py b/cli/objects/failure.py
index 82bdb082..e7b12cbb 100644
--- a/cli/objects/failure.py
+++ b/cli/objects/failure.py
@@ -36,6 +36,7 @@ def __init__(
failure_type (str): The failure type
failed_test_name (Optional[str], optional): The failed test name. Defaults to None.
failed_test_junit_path (Optional[str], optional): The path to the failed test's junit file. Defaults to None.
+ ignore (bool): Flag to indicate that the failure should be ignored.
"""
self.logger = get_logger(__name__)
diff --git a/cli/objects/failure_rule.py b/cli/objects/failure_rule.py
index ff6d8679..41339aae 100644
--- a/cli/objects/failure_rule.py
+++ b/cli/objects/failure_rule.py
@@ -188,14 +188,9 @@ def _get_ignore(self, rule_dict: dict[Any, Any]) -> bool:
)
exit(1)
- def matches_failure(self, failure: "Failure") -> bool:
- if (
+ def matches_failure(self, failure: Failure) -> bool:
+ return (
hasattr(self, "step")
and fnmatch.fnmatch(failure.step, self.step)
- and (
- (failure.failure_type == self.failure_type)
- or self.failure_type == "all"
- )
- ):
- return True
- return False
+ and failure.failure_type in (self.failure_type, "all")
+ )
diff --git a/cli/objects/job.py b/cli/objects/job.py
index e4791f9c..93de03f6 100644
--- a/cli/objects/job.py
+++ b/cli/objects/job.py
@@ -14,7 +14,6 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
-import itertools
import json
import os
from typing import Any
@@ -324,13 +323,12 @@ def _find_failures(self, logs_dir: str, junit_dir: str) -> Optional[list[Failure
unique_steps_with_failures = set()
# Combine lists into one list
- for failure in itertools.chain(test_failures, pod_failures):
+ for failure in test_failures + pod_failures:
if failure.step not in unique_steps_with_failures:
- unique_steps_with_failures.update([failure.step])
- if self.firewatch_config.failure_rules:
- for rule in self.firewatch_config.failure_rules:
- if rule.matches_failure(failure) and rule.ignore:
- failure.ignore = True
+ unique_steps_with_failures.add(failure.step)
+ if failure_rules := self.firewatch_config.failure_rules:
+ for rule in failure_rules:
+ failure.ignore = rule.matches_failure(failure) and rule.ignore
failures_list.append(failure)
if len(failures_list) > 0:
diff --git a/tests/unittests/conftest.py b/tests/unittests/conftest.py
index 58024d04..14da98cb 100644
--- a/tests/unittests/conftest.py
+++ b/tests/unittests/conftest.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2023 Red Hat, Inc.
+# Copyright (C) 2024 Red Hat, Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -26,7 +26,7 @@
from cli.objects.job import Job
from cli.report import Report
-_logger = simple_logger.logger.get_logger(__name__)
+LOGGER = simple_logger.logger.get_logger(__name__)
BUILD_ID_ENV_VAR = "BUILD_ID"
FIREWATCH_DEFAULT_JIRA_PROJECT_ENV_VAR = "FIREWATCH_DEFAULT_JIRA_PROJECT"
@@ -54,6 +54,8 @@
["junit_install.xml", "junit_install_status.xml", "junit_symptoms.xml"],
]
+DEFAULT_MOCK_ISSUE_KEY = "LPTOCPCI-MOCK"
+
@pytest.fixture
def jira(jira_config_path):
@@ -85,18 +87,18 @@ def job(firewatch_config, build_id):
def patch_jira(monkeypatch):
@dataclass
class MockIssue:
- key: str = "LPTOCPCI-MOCK"
+ key: str = DEFAULT_MOCK_ISSUE_KEY
def create_jira_issue(*args, **kwargs):
- _logger.info("Patching Report.create_jira_issue")
- _logger.info(
+ LOGGER.info("Patching Report.create_jira_issue")
+ LOGGER.info(
f"Attempted call Report.create_issue with the following keywords: \n{pprint.pformat(kwargs)}",
)
return MockIssue()
def add_duplicate_comment(*args, **kwargs):
- _logger.info("Patching Report.add_duplicate_comment")
- _logger.info(
+ LOGGER.info("Patching Report.add_duplicate_comment")
+ LOGGER.info(
f"Attempted to call Report.add_duplicate_comment with the following keywords: \n{pprint.pformat(kwargs)}",
)
return
@@ -128,12 +130,12 @@ def __post_init__(self):
cap = CapJira()
def create_jira_issue(*args, **kwargs):
- _logger.info("Patching Report.create_jira_issue")
+ LOGGER.info("Patching Report.create_jira_issue")
cap.create_jira_issue.append(CapInputs(args, kwargs))
return MockIssue()
def add_duplicate_comment(*args, **kwargs):
- _logger.info("Patching Report.add_duplicate_comment")
+ LOGGER.info("Patching Report.add_duplicate_comment")
cap.add_duplicate_comment.append(CapInputs(args, kwargs))
monkeypatch.setattr(Report, "create_jira_issue", create_jira_issue)
@@ -143,7 +145,7 @@ def add_duplicate_comment(*args, **kwargs):
@pytest.fixture
def patch_job_log_dir(monkeypatch, job_log_dir):
- _logger.info("Patching Job log dir path")
+ LOGGER.info("Patching Job log dir path")
def _download_logs(*args, **kwargs):
return job_log_dir.as_posix()
@@ -152,36 +154,98 @@ def _download_logs(*args, **kwargs):
@pytest.fixture
-def patch_job_junit_dir(monkeypatch, job_junit_dir):
- _logger.info("Patching Job junit dir path")
+def patch_job_junit_dir(monkeypatch, job_artifacts_dir):
+ LOGGER.info("Patching Job junit dir path")
def _download_junit(*args, **kwargs):
- return job_junit_dir.as_posix()
+ return job_artifacts_dir.as_posix()
monkeypatch.setattr(Job, "_download_junit", _download_junit)
@pytest.fixture
-def assert_job_dir_exists(job_dir):
- job_dir.mkdir(exist_ok=True, parents=True)
- assert job_dir.is_dir()
+def fake_log_secret_path(job_log_dir):
+ yield job_log_dir.joinpath("fake-step/fake_build_log.txt")
@pytest.fixture
-def fake_log_secret_path(job_log_dir):
- yield job_log_dir.joinpath("fake-step/fake_build_log.txt")
+def fake_junit_secret_path(job_artifacts_dir):
+ yield job_artifacts_dir.joinpath("fake-step/fake_junit.xml")
@pytest.fixture
-def fake_junit_secret_path(job_junit_dir):
- yield job_junit_dir.joinpath("fake-step/fake_junit.xml")
+def firewatch_config_json(monkeypatch):
+ config_json = json.dumps(
+ {
+ "failure_rules": [
+ {
+ "step": "exact-step-name",
+ "failure_type": "pod_failure",
+ "classification": "Infrastructure",
+ "jira_project": "!default",
+ "jira_component": ["some-component"],
+ "jira_assignee": "some-user@redhat.com",
+ "jira_security_level": "Restricted",
+ },
+ {
+ "step": "*partial-name*",
+ "failure_type": "all",
+ "classification": "Misc.",
+ "jira_project": "OTHER",
+ "jira_component": ["component-1", "component-2", "!default"],
+ "jira_priority": "major",
+ "group": {"name": "some-group", "priority": 1},
+ },
+ {
+ "step": "*ends-with-this",
+ "failure_type": "test_failure",
+ "classification": "Test failures",
+ "jira_epic": "!default",
+ "jira_additional_labels": [
+ "test-label-1",
+ "test-label-2",
+ "!default",
+ ],
+ "group": {"name": "some-group", "priority": 2},
+ },
+ {
+ "step": "*ignore*",
+ "failure_type": "test_failure",
+ "classification": "NONE",
+ "jira_project": "NONE",
+ "ignore": "true",
+ },
+ {
+ "step": "affects-version",
+ "failure_type": "all",
+ "classification": "Affects Version",
+ "jira_project": "TEST",
+ "jira_epic": "!default",
+ "jira_affects_version": "4.14",
+ "jira_assignee": "!default",
+ },
+ {
+ "step": "affects-version",
+ "failure_type": "all",
+ "classification": "Affects Version",
+ "jira_project": "TEST",
+ "jira_epic": "!default",
+ "jira_affects_version": "4.14",
+ "jira_assignee": "!default",
+ },
+ ],
+ },
+ )
+ monkeypatch.setenv(FIREWATCH_CONFIG_ENV_VAR, config_json)
+ yield config_json
@pytest.fixture
-def assert_jira_config_file_exists(jira_config_path):
- if not jira_config_path.is_file():
- jira_config_path.parent.mkdir(exist_ok=True, parents=True)
- jira_config_path.write_text(
+def jira_config_path(tmp_path):
+ config_path = tmp_path.joinpath("jira_config.json")
+ if not config_path.is_file():
+ config_path.parent.mkdir(exist_ok=True, parents=True)
+ config_path.write_text(
json.dumps(
{
"token": os.getenv(JIRA_TOKEN_ENV_VAR),
@@ -193,87 +257,7 @@ def assert_jira_config_file_exists(jira_config_path):
},
),
)
- assert jira_config_path.is_file()
-
-
-@pytest.fixture
-def assert_firewatch_config_in_env(monkeypatch):
- monkeypatch.setenv(
- FIREWATCH_CONFIG_ENV_VAR,
- json.dumps(
- {
- "failure_rules": [
- {
- "step": "exact-step-name",
- "failure_type": "pod_failure",
- "classification": "Infrastructure",
- "jira_project": "!default",
- "jira_component": ["some-component"],
- "jira_assignee": "some-user@redhat.com",
- "jira_security_level": "Restricted",
- },
- {
- "step": "*partial-name*",
- "failure_type": "all",
- "classification": "Misc.",
- "jira_project": "OTHER",
- "jira_component": ["component-1", "component-2", "!default"],
- "jira_priority": "major",
- "group": {"name": "some-group", "priority": 1},
- },
- {
- "step": "*ends-with-this",
- "failure_type": "test_failure",
- "classification": "Test failures",
- "jira_epic": "!default",
- "jira_additional_labels": [
- "test-label-1",
- "test-label-2",
- "!default",
- ],
- "group": {"name": "some-group", "priority": 2},
- },
- {
- "step": "*ignore*",
- "failure_type": "test_failure",
- "classification": "NONE",
- "jira_project": "NONE",
- "ignore": "true",
- },
- {
- "step": "affects-version",
- "failure_type": "all",
- "classification": "Affects Version",
- "jira_project": "TEST",
- "jira_epic": "!default",
- "jira_affects_version": "4.14",
- "jira_assignee": "!default",
- },
- {
- "step": "affects-version",
- "failure_type": "all",
- "classification": "Affects Version",
- "jira_project": "TEST",
- "jira_epic": "!default",
- "jira_affects_version": "4.14",
- "jira_assignee": "!default",
- },
- ],
- },
- ),
- )
- assert os.getenv(FIREWATCH_CONFIG_ENV_VAR)
-
-
-@pytest.fixture
-def assert_artifact_dir_exists(artifact_dir):
- artifact_dir.mkdir(exist_ok=True, parents=True)
- assert artifact_dir.is_dir()
-
-
-@pytest.fixture
-def jira_config_path(tmp_path):
- yield tmp_path.joinpath("jira_config.json")
+ yield config_path
@pytest.fixture(params=JOB_STEP_DIRS_TO_TEST)
@@ -282,8 +266,8 @@ def job_log_step_dirs(request, job_log_dir):
@pytest.fixture(params=JOB_STEP_DIRS_TO_TEST)
-def job_junit_step_dirs(request, job_junit_dir):
- yield (job_junit_dir / p for p in request.param)
+def job_junit_step_dirs(request, job_artifacts_dir):
+ yield (job_artifacts_dir / p for p in request.param)
@pytest.fixture
@@ -292,7 +276,7 @@ def job_log_dir(job_dir):
@pytest.fixture
-def job_junit_dir(job_dir):
+def job_artifacts_dir(job_dir):
yield job_dir / "artifacts"
@@ -307,14 +291,11 @@ def job_dir_junit_artifact_paths(request, job_junit_step_dirs):
@pytest.fixture
-def assert_artifact_dir_in_env(monkeypatch, artifact_dir):
- monkeypatch.setenv(ARTIFACT_DIR_ENV_VAR, artifact_dir.as_posix())
- assert os.getenv(ARTIFACT_DIR_ENV_VAR)
-
-
-@pytest.fixture
-def artifact_dir(tmp_path):
- yield tmp_path / "artifacts"
+def artifact_dir(monkeypatch, tmp_path):
+ path = tmp_path / "artifacts"
+ monkeypatch.setenv(ARTIFACT_DIR_ENV_VAR, path.as_posix())
+ path.mkdir(exist_ok=True, parents=True)
+ yield path
@pytest.fixture
@@ -323,38 +304,28 @@ def job_dir(tmp_path, build_id):
@pytest.fixture(params=BUILD_IDS_TO_TEST, ids=BUILD_IDS_TO_TEST)
-def build_id(request):
- yield request.param
+def build_id(monkeypatch, request):
+ param = request.param
+ monkeypatch.setenv(BUILD_ID_ENV_VAR, param)
+ yield param
-@pytest.fixture(params=DEFAULT_JIRA_PROJECTS_TO_TEST, ids=DEFAULT_JIRA_PROJECTS_TO_TEST)
-def default_jira_project(request):
- yield request.param
+@pytest.fixture(params=DEFAULT_JIRA_PROJECTS_TO_TEST)
+def default_jira_project(monkeypatch, request):
+ param = request.param
+ monkeypatch.setenv(FIREWATCH_DEFAULT_JIRA_PROJECT_ENV_VAR, param)
+ yield param
@pytest.fixture(params=DEFAULT_JIRA_EPICS_TO_TEST, ids=DEFAULT_JIRA_EPICS_TO_TEST)
-def default_jira_epic(request):
- yield request.param
-
-
-@pytest.fixture
-def assert_build_id_in_env(build_id, monkeypatch):
- monkeypatch.setenv(BUILD_ID_ENV_VAR, build_id)
- assert os.getenv(BUILD_ID_ENV_VAR)
-
-
-@pytest.fixture
-def assert_default_jira_project_in_env(default_jira_project, monkeypatch):
- monkeypatch.setenv(FIREWATCH_DEFAULT_JIRA_PROJECT_ENV_VAR, default_jira_project)
- assert os.getenv(FIREWATCH_DEFAULT_JIRA_PROJECT_ENV_VAR)
-
-
-@pytest.fixture
-def assert_default_jira_epic_in_env(default_jira_epic, monkeypatch):
- monkeypatch.setenv(FIREWATCH_DEFAULT_JIRA_EPIC_ENV_VAR, default_jira_epic)
- assert os.getenv(FIREWATCH_DEFAULT_JIRA_EPIC_ENV_VAR)
+def default_jira_epic(monkeypatch, request):
+ param = request.param
+ monkeypatch.setenv(FIREWATCH_DEFAULT_JIRA_EPIC_ENV_VAR, param)
+ yield param
@pytest.fixture
-def assert_jira_token_in_env():
- assert os.getenv(JIRA_TOKEN_ENV_VAR)
+def jira_token():
+ token = os.getenv(JIRA_TOKEN_ENV_VAR)
+ assert token
+ yield token
diff --git a/tests/unittests/objects/failure_rule/test_firewatch_objects_failure_rule_matches_failure.py b/tests/unittests/objects/failure_rule/test_firewatch_objects_failure_rule_matches_failure.py
index b8cfdf0d..7bee003a 100644
--- a/tests/unittests/objects/failure_rule/test_firewatch_objects_failure_rule_matches_failure.py
+++ b/tests/unittests/objects/failure_rule/test_firewatch_objects_failure_rule_matches_failure.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2023 Red Hat, Inc.
+# Copyright (C) 2024 Red Hat, Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -19,29 +19,21 @@
from cli.objects.failure_rule import FailureRule
-@pytest.fixture(autouse=True)
-def setup_tests(assert_default_jira_project_in_env):
- ...
-
-
-@pytest.fixture
-def rule_dict():
- yield {
- "step": "gather-*",
- "failure_type": "test_failure",
- "classification": "NONE",
- "jira_project": "NONE",
- "ignore": "false",
- }
-
-
@pytest.fixture
-def failure_rule(rule_dict):
- yield FailureRule(rule_dict=rule_dict)
+def failure_rule():
+ yield FailureRule(
+ rule_dict={
+ "step": "gather-*",
+ "failure_type": "test_failure",
+ "classification": "NONE",
+ "jira_project": "NONE",
+ "ignore": "false",
+ },
+ )
@pytest.fixture
-def matching_failure():
+def failure():
yield Failure(
failure_type="test_failure",
failed_test_name="post",
@@ -49,9 +41,33 @@ def matching_failure():
)
-def test_init_failure_rule_from_fixtures(failure_rule):
- assert isinstance(failure_rule, FailureRule)
+def test_failure_rule_matches_failure_if_rule_pattern_equals_failure_step(
+ failure,
+ failure_rule,
+):
+ failure_rule.step = failure.step = "gather-must-gather"
+ assert failure_rule.matches_failure(
+ failure,
+ ), f"'{failure_rule.step}' should match '{failure.step}'"
+
+
+def test_failure_rule_matches_failure_if_glob_style_rule_pattern_matches_failure_step(
+ failure,
+ failure_rule,
+):
+ failure.step = "gather-must-gather"
+ failure_rule.step = "gather-*"
+ assert failure_rule.matches_failure(
+ failure,
+ ), f"'{failure_rule.step}' should match '{failure.step}'"
-def test_failure_rule_matches_failure(failure_rule, matching_failure):
- assert failure_rule.matches_failure(matching_failure)
+def test_failure_rule_does_not_match_non_glob_unequal_failure_step(
+ failure,
+ failure_rule,
+):
+ failure.step = "gather-must-gather"
+ failure_rule.step = "firewatch_report_issues"
+ assert not failure_rule.matches_failure(
+ failure,
+ ), f"'{failure_rule.step}' should NOT match '{failure.step}'"
diff --git a/tests/unittests/objects/fixtures/__init__.py b/tests/unittests/objects/fixtures/__init__.py
deleted file mode 100644
index b8c15d27..00000000
--- a/tests/unittests/objects/fixtures/__init__.py
+++ /dev/null
@@ -1,15 +0,0 @@
-# Copyright (C) 2023 Red Hat, Inc.
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-#
diff --git a/tests/unittests/objects/fixtures/test_fixtures.py b/tests/unittests/objects/fixtures/test_fixtures.py
deleted file mode 100644
index fcec2c8b..00000000
--- a/tests/unittests/objects/fixtures/test_fixtures.py
+++ /dev/null
@@ -1,48 +0,0 @@
-# Copyright (C) 2023 Red Hat, Inc.
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-#
-import pytest
-
-from cli.objects.configuration import Configuration
-from cli.objects.jira_base import Jira
-from cli.objects.job import Job
-
-
-@pytest.fixture(autouse=True)
-def setup_test_environment(
- assert_jira_token_in_env,
- assert_jira_config_file_exists,
- assert_default_jira_project_in_env,
- assert_firewatch_config_in_env,
- patch_job_junit_dir,
- patch_job_log_dir,
- assert_build_id_in_env,
- assert_artifact_dir_exists,
- assert_artifact_dir_in_env,
- assert_job_dir_exists,
-):
- ...
-
-
-def test_get_firewatch_config_instance_from_fixture(firewatch_config):
- assert isinstance(firewatch_config, Configuration)
-
-
-def test_get_job_instance_from_fixture(job):
- assert isinstance(job, Job)
-
-
-def test_get_jira_instance_from_fixture(jira):
- assert isinstance(jira, Jira)
diff --git a/tests/unittests/objects/job/test_fail_with_test_failures_should_not_cause_failure_for_ignored_step.py b/tests/unittests/objects/job/test_fail_with_test_failures_should_not_cause_failure_for_ignored_step.py
index 52133dc1..a3e75653 100644
--- a/tests/unittests/objects/job/test_fail_with_test_failures_should_not_cause_failure_for_ignored_step.py
+++ b/tests/unittests/objects/job/test_fail_with_test_failures_should_not_cause_failure_for_ignored_step.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2023 Red Hat, Inc.
+# Copyright (C) 2024 Red Hat, Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -20,18 +20,13 @@
from cli.objects.configuration import Configuration
from cli.objects.job import Job
+from conftest import FIREWATCH_CONFIG_ENV_VAR
-@pytest.fixture(autouse=True)
-def setup_tests(
- monkeypatch,
- assert_jira_config_file_exists,
- assert_default_jira_project_in_env,
- patch_job_log_dir,
- patch_job_junit_dir,
-):
+@pytest.fixture
+def firewatch_config(monkeypatch, jira, default_jira_project):
monkeypatch.setenv(
- "FIREWATCH_CONFIG",
+ FIREWATCH_CONFIG_ENV_VAR,
json.dumps(
{
"failure_rules": [
@@ -46,10 +41,6 @@ def setup_tests(
},
),
)
-
-
-@pytest.fixture
-def config(jira):
yield Configuration(
jira=jira,
fail_with_test_failures=True,
@@ -59,8 +50,8 @@ def config(jira):
@pytest.fixture
-def job(config, job_junit_dir):
- gather_must_gather_dir = job_junit_dir / "gather-must-gather"
+def job(firewatch_config, patch_job_log_dir, patch_job_junit_dir, job_artifacts_dir):
+ gather_must_gather_dir = job_artifacts_dir / "gather-must-gather"
gather_must_gather_dir.mkdir(exist_ok=True, parents=True)
(gather_must_gather_dir / "finished.json").write_text(
'{"timestamp":170340000,"passed":false,"result":"FAILURE","revision":"release-v1.11"}',
@@ -70,16 +61,16 @@ def job(config, job_junit_dir):
name_safe="mtr-interop-aws",
build_id="1739165508839673856",
gcs_bucket="test-platform-results",
- firewatch_config=config,
+ firewatch_config=firewatch_config,
)
-def test_init_job_from_fixtures(job):
- assert isinstance(job, Job)
-
-
-def test_fail_with_test_failures_should_not_cause_failure_for_ignored_step(config, job):
- rule = config.failure_rules[0]
+def test_fail_with_test_failures_should_not_cause_failure_for_ignored_step(
+ monkeypatch,
+ firewatch_config,
+ job,
+):
+ rule = firewatch_config.failure_rules[0]
assert rule.step == "gather-*"
assert rule.ignore
assert re.match(rule.step, "gather-must-gather")