Skip to content

Commit 5fb35c4

Browse files
committed
fix: resolve MockerFixture attribute from pytest_mock directly
Issue: https://progress.opensuse.org/issues/198026
1 parent 031caee commit 5fb35c4

2 files changed

Lines changed: 34 additions & 28 deletions

File tree

tests/test_openqa_bats_review.py

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,15 @@
88
import importlib.util
99
import pathlib
1010
import sys
11-
from typing import Any
11+
from typing import TYPE_CHECKING, Any
1212
from unittest.mock import Mock
1313

1414
import pytest
1515
from requests.exceptions import RequestException
1616

17+
if TYPE_CHECKING:
18+
from pytest_mock import MockerFixture
19+
1720
# Load the script as module "bats_review" (the file is named `openqa-bats-review`)
1821
rootpath = pathlib.Path(__file__).parent.parent.resolve()
1922
loader = importlib.machinery.SourceFileLoader("bats_review", f"{rootpath}/openqa-bats-review")
@@ -29,7 +32,7 @@
2932
#
3033

3134

32-
def test_get_file_success(mocker: pytest.MockerFixture) -> None:
35+
def test_get_file_success(mocker: MockerFixture) -> None:
3336
mock_session = mocker.patch("bats_review.session")
3437
resp = Mock()
3538
resp.text = "hello"
@@ -46,7 +49,7 @@ def test_get_file_success(mocker: pytest.MockerFixture) -> None:
4649
resp.raise_for_status.assert_called_once()
4750

4851

49-
def test_get_file_request_exception(mocker: pytest.MockerFixture) -> None:
52+
def test_get_file_request_exception(mocker: MockerFixture) -> None:
5053
mock_session = mocker.patch("bats_review.session")
5154
mock_log = mocker.patch("bats_review.log")
5255
mock_session.get.side_effect = RequestException("network")
@@ -56,7 +59,7 @@ def test_get_file_request_exception(mocker: pytest.MockerFixture) -> None:
5659
mock_log.exception.assert_called_once()
5760

5861

59-
def test_get_job_success(mocker: pytest.MockerFixture) -> None:
62+
def test_get_job_success(mocker: MockerFixture) -> None:
6063
with contextlib.suppress(Exception):
6164
bats_review.get_job.cache_clear()
6265
mock_session = mocker.patch("bats_review.session")
@@ -74,7 +77,7 @@ def test_get_job_success(mocker: pytest.MockerFixture) -> None:
7477
)
7578

7679

77-
def test_get_job_request_exception(mocker: pytest.MockerFixture) -> None:
80+
def test_get_job_request_exception(mocker: MockerFixture) -> None:
7881
with contextlib.suppress(Exception):
7982
bats_review.get_job.cache_clear()
8083
mock_session = mocker.patch("bats_review.session")
@@ -86,7 +89,7 @@ def test_get_job_request_exception(mocker: pytest.MockerFixture) -> None:
8689
mock_log.exception.assert_called_once()
8790

8891

89-
def test_grep_failures_success(mocker: pytest.MockerFixture) -> None:
92+
def test_grep_failures_success(mocker: MockerFixture) -> None:
9093
mock_get_file = mocker.patch("bats_review.get_file")
9194
# one passing, one failing testcase (with classname)
9295
mock_get_file.return_value = """
@@ -101,7 +104,7 @@ def test_grep_failures_success(mocker: pytest.MockerFixture) -> None:
101104
assert result == {"suite1:failing_test"}
102105

103106

104-
def test_grep_failures_malformed(mocker: pytest.MockerFixture) -> None:
107+
def test_grep_failures_malformed(mocker: MockerFixture) -> None:
105108
mock_get_file = mocker.patch("bats_review.get_file")
106109
mock_log = mocker.patch("bats_review.log")
107110
mock_get_file.return_value = "<this is not xml"
@@ -112,15 +115,15 @@ def test_grep_failures_malformed(mocker: pytest.MockerFixture) -> None:
112115
mock_log.exception.assert_called_once()
113116

114117

115-
def test_process_logs_single_file(mocker: pytest.MockerFixture) -> None:
118+
def test_process_logs_single_file(mocker: MockerFixture) -> None:
116119
mock_grep = mocker.patch("bats_review.grep_failures")
117120
mock_grep.return_value = {"a", "b"}
118121
res = bats_review.process_logs(["http://example.com/a.xml"])
119122
assert res == {"a", "b"}
120123
mock_grep.assert_called_once_with("http://example.com/a.xml")
121124

122125

123-
def test_process_logs_multiple_files(mocker: pytest.MockerFixture) -> None:
126+
def test_process_logs_multiple_files(mocker: MockerFixture) -> None:
124127
mock_executor_class = mocker.patch("bats_review.ThreadPoolExecutor")
125128
# build fake executor that returns map -> iterator of sets
126129
fake_executor = Mock()
@@ -134,7 +137,7 @@ def test_process_logs_multiple_files(mocker: pytest.MockerFixture) -> None:
134137
fake_executor.map.assert_called_once()
135138

136139

137-
def test_resolve_clone_chain_single(mocker: pytest.MockerFixture) -> None:
140+
def test_resolve_clone_chain_single(mocker: MockerFixture) -> None:
138141
with contextlib.suppress(Exception):
139142
bats_review.get_job.cache_clear()
140143
mock_get_job = mocker.patch("bats_review.get_job")
@@ -144,7 +147,7 @@ def test_resolve_clone_chain_single(mocker: pytest.MockerFixture) -> None:
144147
mock_get_job.assert_called_once_with("http://openqa/api/v1/jobs/123/details")
145148

146149

147-
def test_resolve_clone_chain_multiple(mocker: pytest.MockerFixture) -> None:
150+
def test_resolve_clone_chain_multiple(mocker: MockerFixture) -> None:
148151
with contextlib.suppress(Exception):
149152
bats_review.get_job.cache_clear()
150153
mock_get_job = mocker.patch("bats_review.get_job")
@@ -167,7 +170,7 @@ def side(url: str) -> dict[str, Any] | None:
167170
# main
168171

169172

170-
def test_main_no_clones(mocker: pytest.MockerFixture) -> None:
173+
def test_main_no_clones(mocker: MockerFixture) -> None:
171174
with contextlib.suppress(Exception):
172175
bats_review.get_job.cache_clear()
173176
mock_resolve = mocker.patch("bats_review.resolve_clone_chain")
@@ -179,7 +182,7 @@ def test_main_no_clones(mocker: pytest.MockerFixture) -> None:
179182
mock_log.info.assert_called_with("No clones. Exiting")
180183

181184

182-
def test_main_no_common_failures(mocker: pytest.MockerFixture) -> None:
185+
def test_main_no_common_failures(mocker: MockerFixture) -> None:
183186
"""Two jobs in chain; each produces different failures -> no common failures.
184187
185188
main should log Tagging as PASSED.
@@ -208,7 +211,7 @@ def job_resp(url: str) -> dict[str, Any]:
208211
mock_log.info.assert_called_with("No common failures across clone chain. Tagging as PASSED.")
209212

210213

211-
def test_main_insufficient_logs(mocker: pytest.MockerFixture) -> None:
214+
def test_main_insufficient_logs(mocker: MockerFixture) -> None:
212215
"""If jobs do not have the expected number of logs (e.g. podman expects 4 but provides 2).
213216
214217
main should log the 'only X logs' messages for each job and eventually exit(0).
@@ -235,13 +238,13 @@ def job_resp(url: str) -> dict[str, Any]:
235238
mock_log.info.assert_any_call("No logs found in chain. Exiting")
236239

237240

238-
def test_parse_args_success(mocker: pytest.MockerFixture) -> None:
241+
def test_parse_args_success(mocker: MockerFixture) -> None:
239242
mocker.patch("sys.argv", ["script.py", "http://example.com/tests/123"])
240243
args = bats_review.parse_args()
241244
assert args.url == "http://example.com/tests/123"
242245

243246

244-
def test_parse_args_missing_url(mocker: pytest.MockerFixture) -> None:
247+
def test_parse_args_missing_url(mocker: MockerFixture) -> None:
245248
mocker.patch("sys.argv", ["script.py"])
246249
with pytest.raises(SystemExit):
247250
bats_review.parse_args()
@@ -250,7 +253,7 @@ def test_parse_args_missing_url(mocker: pytest.MockerFixture) -> None:
250253
# Integration test
251254

252255

253-
def test_full_workflow_no_common_failures(mocker: pytest.MockerFixture) -> None:
256+
def test_full_workflow_no_common_failures(mocker: MockerFixture) -> None:
254257
"""Patch session.get to simulate two jobs each with a different failing testcase.
255258
256259
Asserts that the script decides to tag as PASSED (dry_run) when there are no common failures.

tests/test_openqa_llm_investigate.py

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,15 @@
88
import logging
99
import pathlib
1010
import sys
11-
from typing import Any
11+
from typing import TYPE_CHECKING, Any
1212
from unittest.mock import MagicMock, Mock
1313

1414
import httpx
1515
import pytest
1616

17+
if TYPE_CHECKING:
18+
from pytest_mock import MockerFixture
19+
1720
# Load the script as module "llm_investigate" (the file is named `openqa-llm-investigate`)
1821
rootpath = pathlib.Path(__file__).parent.parent.resolve()
1922
loader = importlib.machinery.SourceFileLoader("llm_investigate", f"{rootpath}/openqa-llm-investigate")
@@ -72,7 +75,7 @@ def test_fetch_text_failure() -> None:
7275
assert not res
7376

7477

75-
def test_post_comment(mocker: pytest.MockerFixture) -> None:
78+
def test_post_comment(mocker: MockerFixture) -> None:
7679
mock_run = mocker.patch("llm_investigate.subprocess.run")
7780
llm_investigate.post_comment("http://base", "123", "test comment")
7881
mock_run.assert_called_once()
@@ -82,7 +85,7 @@ def test_post_comment(mocker: pytest.MockerFixture) -> None:
8285
assert "text=test comment" in args
8386

8487

85-
def setup_mock_client(mocker: pytest.MockerFixture, overrides: dict[str, Any] | None = None) -> MagicMock:
88+
def setup_mock_client(mocker: MockerFixture, overrides: dict[str, Any] | None = None) -> MagicMock:
8689
mock_client_class = mocker.patch("llm_investigate.httpx.Client")
8790
mock_client = MagicMock()
8891
mock_client_class.return_value.__enter__.return_value = mock_client
@@ -143,7 +146,7 @@ def mock_post_call(url: str, json: dict[str, Any] | None = None, *args: Any, **k
143146
return mock_client
144147

145148

146-
def test_investigate_cmd(mocker: pytest.MockerFixture) -> None:
149+
def test_investigate_cmd(mocker: MockerFixture) -> None:
147150
mock_post = mocker.patch("llm_investigate.post_comment")
148151
mock_print = mocker.patch("builtins.print")
149152
setup_mock_client(mocker)
@@ -153,7 +156,7 @@ def test_investigate_cmd(mocker: pytest.MockerFixture) -> None:
153156
assert "BISECT: YES" in mock_post.call_args[0][2]
154157

155158

156-
def test_investigate_cmd_passed_job(mocker: pytest.MockerFixture) -> None:
159+
def test_investigate_cmd_passed_job(mocker: MockerFixture) -> None:
157160
mock_print = mocker.patch("builtins.print")
158161
setup_mock_client(mocker, overrides={"api/v1/jobs": {"job": {"id": 123, "result": "passed"}}})
159162

@@ -164,7 +167,7 @@ def test_investigate_cmd_passed_job(mocker: pytest.MockerFixture) -> None:
164167
mock_print.assert_not_called()
165168

166169

167-
def test_investigate_cmd_softfailed_job(mocker: pytest.MockerFixture) -> None:
170+
def test_investigate_cmd_softfailed_job(mocker: MockerFixture) -> None:
168171
mock_print = mocker.patch("builtins.print")
169172
setup_mock_client(mocker, overrides={"api/v1/jobs": {"job": {"id": 123, "result": "softfailed"}}})
170173

@@ -175,7 +178,7 @@ def test_investigate_cmd_softfailed_job(mocker: pytest.MockerFixture) -> None:
175178
mock_print.assert_not_called()
176179

177180

178-
def test_investigate_cmd_already_commented(mocker: pytest.MockerFixture) -> None:
181+
def test_investigate_cmd_already_commented(mocker: MockerFixture) -> None:
179182
mock_log = mocker.patch("llm_investigate.log")
180183
setup_mock_client(mocker, overrides={"comments": [{"text": "**LLM Investigation summary:** already done"}]})
181184

@@ -187,7 +190,7 @@ def test_investigate_cmd_already_commented(mocker: pytest.MockerFixture) -> None
187190
assert "already has an LLM investigation summary" in mock_log.info.call_args[0][0]
188191

189192

190-
def test_investigate_cmd_already_commented_with_force(mocker: pytest.MockerFixture) -> None:
193+
def test_investigate_cmd_already_commented_with_force(mocker: MockerFixture) -> None:
191194
mock_post = mocker.patch("llm_investigate.post_comment")
192195
mock_print = mocker.patch("builtins.print")
193196
setup_mock_client(mocker, overrides={"comments": [{"text": "**LLM Investigation summary:** already done"}]})
@@ -199,7 +202,7 @@ def test_investigate_cmd_already_commented_with_force(mocker: pytest.MockerFixtu
199202
assert "BISECT: YES" in mock_post.call_args[0][2]
200203

201204

202-
def test_investigate_cmd_dry_run(mocker: pytest.MockerFixture) -> None:
205+
def test_investigate_cmd_dry_run(mocker: MockerFixture) -> None:
203206
mock_post = mocker.patch("llm_investigate.post_comment")
204207
mock_print = mocker.patch("builtins.print")
205208
client = setup_mock_client(mocker)
@@ -214,7 +217,7 @@ def test_investigate_cmd_dry_run(mocker: pytest.MockerFixture) -> None:
214217
mock_print.assert_any_call("**LLM Investigation summary:**\n\nBISECT: NO. Already known.")
215218

216219

217-
def test_investigate_cmd_connection_error(mocker: pytest.MockerFixture) -> None:
220+
def test_investigate_cmd_connection_error(mocker: MockerFixture) -> None:
218221
mock_log = mocker.patch("llm_investigate.log")
219222
client = setup_mock_client(mocker)
220223
client.post.side_effect = httpx.ConnectError("Connection refused")
@@ -227,7 +230,7 @@ def test_investigate_cmd_connection_error(mocker: pytest.MockerFixture) -> None:
227230
assert "Could not connect to LLM server" in mock_log.error.call_args[0][0]
228231

229232

230-
def test_investigate_logging_levels(mocker: pytest.MockerFixture) -> None:
233+
def test_investigate_logging_levels(mocker: MockerFixture) -> None:
231234
mock_basic_config = mocker.patch("llm_investigate.logging.basicConfig")
232235
mocker.patch("llm_investigate.httpx.Client")
233236
mock_fetch = mocker.patch("llm_investigate.fetch_json")

0 commit comments

Comments
 (0)