Skip to content

Commit 7dd93e5

Browse files
Fix static-checks loading and add error handling
1 parent 8157d0b commit 7dd93e5

4 files changed

Lines changed: 80 additions & 12 deletions

File tree

operatorcert/operator_repo/checks/__init__.py

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ def get_checks(
103103
"""
104104
if skip_tests is None:
105105
skip_tests = []
106+
log.debug("Loading checks from suite: %s", suite_name)
106107
result: dict[str, list[Check]] = {}
107108
for module_name, _ in SUPPORTED_TYPES:
108109
result[module_name] = []
@@ -120,8 +121,36 @@ def get_checks(
120121
log.debug("Skipping %s check", check_name)
121122
continue
122123
result[module_name].append(check)
123-
except ModuleNotFoundError:
124-
pass
124+
except ModuleNotFoundError as e:
125+
# ModuleNotFoundError can be caused by two different reasons:
126+
# - file doesn't exist (e.g. community/operator_catalogs.py) - OK
127+
# - module exists but has a missing import dependency - SHOULD FAIL
128+
129+
module_path = f"{suite_name}.{module_name}"
130+
error_message = str(e)
131+
if f"No module named '{module_path}'" in error_message:
132+
# Nonexistent module
133+
log.debug("Module %s not found: %s", module_path, e)
134+
else:
135+
# Issue with missing dependency
136+
error_msg = f"Failed to import {module_path} due to error: {e}"
137+
log.debug(error_msg)
138+
traceback.print_exc()
139+
raise RuntimeError(error_msg) from e
140+
except Exception as e:
141+
# Unexpected error - SHOULD FAIL
142+
error_msg = (
143+
f"Unexpected error loading module {suite_name}.{module_name}: {e}"
144+
)
145+
log.debug(error_msg)
146+
traceback.print_exc()
147+
raise RuntimeError(error_msg) from e
148+
149+
num_loaded = len([checks for checks in result.values() if checks])
150+
log.debug("Loaded %d check types from %s", num_loaded, suite_name)
151+
for module_type, checks in result.items():
152+
if checks:
153+
log.debug(" %s: %d checks", module_type, len(checks))
125154
return result
126155

127156

poetry.lock

Lines changed: 10 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ dependencies = [
3434
"httpx (>=0.28.1)",
3535
"kubernetes (>=33.1.0)",
3636
"cel-python (>=0.4.0)",
37+
"jsonschema (>=4.0.0)",
3738
]
3839

3940
[project.scripts]

tests/operator_repo/checks/test_checks.py

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,10 @@ def check_fake(_something): # type: ignore
6666

6767
@patch("importlib.import_module")
6868
def test_get_checks_missing_modules(mock_import_module: MagicMock) -> None:
69-
mock_import_module.side_effect = ModuleNotFoundError()
69+
def side_effect(module_name: str) -> None:
70+
raise ModuleNotFoundError(f"No module named '{module_name}'")
71+
72+
mock_import_module.side_effect = side_effect
7073
assert get_checks("suite.name") == {
7174
"operator": [],
7275
"bundle": [],
@@ -77,6 +80,40 @@ def test_get_checks_missing_modules(mock_import_module: MagicMock) -> None:
7780
)
7881

7982

83+
@patch("importlib.import_module")
84+
def test_get_checks_missing_dependency(mock_import_module: MagicMock) -> None:
85+
"""Test that missing dependencies cause a RuntimeError"""
86+
87+
def side_effect(module_name: str) -> None:
88+
# Simulate a missing dependency (e.g., jsonschema)
89+
raise ModuleNotFoundError("No module named 'jsonschema'")
90+
91+
mock_import_module.side_effect = side_effect
92+
try:
93+
get_checks("suite.name")
94+
assert False, "Expected RuntimeError to be raised"
95+
except RuntimeError as e:
96+
assert "Failed to import suite.name.operator due to error" in str(e)
97+
assert "jsonschema" in str(e)
98+
99+
100+
@patch("importlib.import_module")
101+
def test_get_checks_unexpected_error(mock_import_module: MagicMock) -> None:
102+
"""Test that unexpected errors during module loading cause a RuntimeError"""
103+
104+
def side_effect(module_name: str) -> None:
105+
# Simulate an unexpected error (e.g., SyntaxError, AttributeError)
106+
raise ValueError("Unexpected error during import")
107+
108+
mock_import_module.side_effect = side_effect
109+
try:
110+
get_checks("suite.name")
111+
assert False, "Expected RuntimeError to be raised"
112+
except RuntimeError as e:
113+
assert "Unexpected error loading module suite.name.operator" in str(e)
114+
assert "Unexpected error during import" in str(e)
115+
116+
80117
def test_run_check(mock_bundle: Bundle) -> None:
81118
def check_fake(_something): # type: ignore
82119
yield Warn("foo")

0 commit comments

Comments
 (0)