|
1 | 1 | import pytest |
2 | 2 | import copy |
3 | | -from azure.ai.evaluation._evaluate._evaluate_aoai import _combine_item_schemas |
| 3 | +from unittest.mock import MagicMock, patch |
| 4 | +from azure.ai.evaluation._evaluate._evaluate_aoai import _combine_item_schemas, _get_single_run_results |
| 5 | +from azure.ai.evaluation._exceptions import ErrorBlame, EvaluationException |
4 | 6 |
|
5 | 7 |
|
6 | 8 | @pytest.fixture |
@@ -119,3 +121,85 @@ def test_combine_item_schemas_with_external_properties_without_required(self, de |
119 | 121 |
|
120 | 122 | assert data_source_config["item_schema"]["properties"] == expected_properties |
121 | 123 | assert data_source_config["item_schema"]["required"] == expected_required |
| 124 | + |
| 125 | + |
| 126 | +class TestGetSingleRunResultsBlame: |
| 127 | + """Unit tests for blame attribution in _get_single_run_results.""" |
| 128 | + |
| 129 | + def _make_run_info(self, client): |
| 130 | + return { |
| 131 | + "client": client, |
| 132 | + "eval_group_id": "group-1", |
| 133 | + "eval_run_id": "run-1", |
| 134 | + "grader_name_map": {}, |
| 135 | + } |
| 136 | + |
| 137 | + @patch("azure.ai.evaluation._evaluate._evaluate_aoai._wait_for_run_conclusion") |
| 138 | + @pytest.mark.parametrize("code", ["UserError", "usererror", "USERERROR", "uSeReRrOr"]) |
| 139 | + def test_user_error_code_sets_user_blame(self, mock_wait, code): |
| 140 | + """When run fails with error.code matching 'usererror' (case-insensitive), blame should be USER_ERROR.""" |
| 141 | + run_result = MagicMock() |
| 142 | + run_result.status = "failed" |
| 143 | + run_result.error.code = code |
| 144 | + mock_wait.return_value = run_result |
| 145 | + client = MagicMock() |
| 146 | + |
| 147 | + with pytest.raises(EvaluationException) as exc_info: |
| 148 | + _get_single_run_results(self._make_run_info(client)) |
| 149 | + |
| 150 | + assert exc_info.value.blame == ErrorBlame.USER_ERROR |
| 151 | + |
| 152 | + @patch("azure.ai.evaluation._evaluate._evaluate_aoai._wait_for_run_conclusion") |
| 153 | + def test_non_user_error_code_sets_unknown_blame(self, mock_wait): |
| 154 | + """When run fails with a non-UserError code, blame should be UNKNOWN.""" |
| 155 | + run_result = MagicMock() |
| 156 | + run_result.status = "failed" |
| 157 | + run_result.error.code = "SystemError" |
| 158 | + mock_wait.return_value = run_result |
| 159 | + client = MagicMock() |
| 160 | + |
| 161 | + with pytest.raises(EvaluationException) as exc_info: |
| 162 | + _get_single_run_results(self._make_run_info(client)) |
| 163 | + |
| 164 | + assert exc_info.value.blame == ErrorBlame.UNKNOWN |
| 165 | + |
| 166 | + @patch("azure.ai.evaluation._evaluate._evaluate_aoai._wait_for_run_conclusion") |
| 167 | + def test_missing_error_attribute_sets_unknown_blame(self, mock_wait): |
| 168 | + """When run fails and error attribute is absent, blame should be UNKNOWN.""" |
| 169 | + run_result = MagicMock(spec=["status"]) |
| 170 | + run_result.status = "failed" |
| 171 | + mock_wait.return_value = run_result |
| 172 | + client = MagicMock() |
| 173 | + |
| 174 | + with pytest.raises(EvaluationException) as exc_info: |
| 175 | + _get_single_run_results(self._make_run_info(client)) |
| 176 | + |
| 177 | + assert exc_info.value.blame == ErrorBlame.UNKNOWN |
| 178 | + |
| 179 | + @patch("azure.ai.evaluation._evaluate._evaluate_aoai._wait_for_run_conclusion") |
| 180 | + def test_error_present_but_code_missing_sets_unknown_blame(self, mock_wait): |
| 181 | + """When error object exists but has no code attribute, blame should be UNKNOWN.""" |
| 182 | + run_result = MagicMock() |
| 183 | + run_result.status = "failed" |
| 184 | + run_result.error = MagicMock(spec=[]) # error object without 'code' |
| 185 | + mock_wait.return_value = run_result |
| 186 | + client = MagicMock() |
| 187 | + |
| 188 | + with pytest.raises(EvaluationException) as exc_info: |
| 189 | + _get_single_run_results(self._make_run_info(client)) |
| 190 | + |
| 191 | + assert exc_info.value.blame == ErrorBlame.UNKNOWN |
| 192 | + |
| 193 | + @patch("azure.ai.evaluation._evaluate._evaluate_aoai._wait_for_run_conclusion") |
| 194 | + def test_error_is_none_sets_unknown_blame(self, mock_wait): |
| 195 | + """When error attribute is None, blame should be UNKNOWN.""" |
| 196 | + run_result = MagicMock() |
| 197 | + run_result.status = "failed" |
| 198 | + run_result.error = None |
| 199 | + mock_wait.return_value = run_result |
| 200 | + client = MagicMock() |
| 201 | + |
| 202 | + with pytest.raises(EvaluationException) as exc_info: |
| 203 | + _get_single_run_results(self._make_run_info(client)) |
| 204 | + |
| 205 | + assert exc_info.value.blame == ErrorBlame.UNKNOWN |
0 commit comments