88import importlib .util
99import pathlib
1010import sys
11- from typing import Any
11+ from typing import TYPE_CHECKING , Any
1212from unittest .mock import Mock
1313
1414import pytest
1515from 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`)
1821rootpath = pathlib .Path (__file__ ).parent .parent .resolve ()
1922loader = importlib .machinery .SourceFileLoader ("bats_review" , f"{ rootpath } /openqa-bats-review" )
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.
0 commit comments