1212from testgen .mcp .permissions import get_project_permissions , mcp_permission
1313from testgen .mcp .tools .common import (
1414 DocGroup ,
15+ FailureGroupBy ,
1516 format_page_footer ,
1617 format_page_info ,
18+ parse_failure_group_by ,
1719 parse_result_status ,
1820 parse_since_arg ,
1921 parse_uuid ,
22+ resolve_aggregate_scope ,
2023 resolve_test_type ,
2124 validate_limit ,
2225 validate_page ,
2730
2831_DEFAULT_SEARCH_STATUSES = [TestResultStatus .Failed , TestResultStatus .Warning ]
2932
33+ _MODEL_GROUP_COLUMN = {
34+ FailureGroupBy .TEST_TYPE : "test_type" ,
35+ FailureGroupBy .TABLE : "table_name" ,
36+ FailureGroupBy .COLUMN : "column_names" ,
37+ }
38+
3039
3140@with_database_session
3241@mcp_permission ("view" )
@@ -167,20 +176,20 @@ def get_failure_summary(
167176 group_by: Group failures by 'test_type', 'table', or 'column' (default: 'test_type').
168177 """
169178 perms = get_project_permissions ()
179+ group = parse_failure_group_by (group_by )
170180
171181 if not any ((job_execution_id , test_suite_id , since )):
172182 raise MCPUserError (
173183 "Provide 'job_execution_id' for a single run, or 'test_suite_id' or 'project_code' "
174184 "to aggregate across runs. 'since' is required when 'test_suite_id' is not provided."
175185 )
176- if group_by in ("table" , "column" ) and not (job_execution_id or test_suite_id ):
186+ if group in (FailureGroupBy . TABLE , FailureGroupBy . COLUMN ) and not (job_execution_id or test_suite_id ):
177187 raise MCPUserError (
178- f"'{ group_by } ' grouping requires a single-suite scope. "
188+ f"'{ group } ' grouping requires a single-suite scope. "
179189 "Provide 'job_execution_id' or 'test_suite_id'."
180190 )
181191
182- model_group_map = {"table" : "table_name" , "column" : "column_names" }
183- model_group_by = model_group_map .get (group_by , group_by )
192+ model_group_by = _MODEL_GROUP_COLUMN [group ]
184193
185194 scope_label : str
186195 test_run_id = None
@@ -197,15 +206,7 @@ def get_failure_summary(
197206 scope_label = f"run `{ job_execution_id } `"
198207 project_codes = perms .allowed_codes
199208 else :
200- if project_code :
201- perms .verify_access (project_code , not_found = MCPResourceNotAccessible ("Project" , project_code ))
202- project_codes = [project_code ]
203- else :
204- project_codes = perms .allowed_codes
205- if test_suite_uuid is not None :
206- suite = TestSuite .get_regular (test_suite_uuid )
207- if suite is None or not perms .has_access (suite .project_code ):
208- raise MCPResourceNotAccessible ("Test suite" , test_suite_id )
209+ project_codes = resolve_aggregate_scope (project_code , test_suite_id = test_suite_id )
209210 scope_parts = []
210211 if project_code :
211212 scope_parts .append (f"project `{ project_code } `" )
@@ -227,22 +228,22 @@ def get_failure_summary(
227228 return f"No confirmed failures found for { scope_label } ."
228229
229230 total = sum (row [- 1 ] for row in failures )
230- if group_by == "test_type" :
231+ if group is FailureGroupBy . TEST_TYPE :
231232 type_names = {tt .test_type : tt .test_name_short for tt in TestType .select_where (TestType .active == "Y" )}
232233
233234 doc = MdDoc ()
234235 doc .heading (1 , f"Failure Summary — { scope_label } " )
235236 doc .text (f"**Total confirmed failures (Failed + Warning):** { total } " )
236237
237- if group_by == "test_type" :
238+ if group is FailureGroupBy . TEST_TYPE :
238239 headers = ["Test Type" , "Severity" , "Count" ]
239240 rows = []
240241 for row in failures :
241242 code , status , count = row [0 ], row [1 ], row [- 1 ]
242243 name = type_names .get (code , code )
243244 severity = status .value if status else "Unknown"
244245 rows .append ([name , severity , count ])
245- elif group_by == "column" :
246+ elif group is FailureGroupBy . COLUMN :
246247 headers = ["Column" , "Count" ]
247248 rows = []
248249 for row in failures :
@@ -253,9 +254,9 @@ def get_failure_summary(
253254 headers = ["Table Name" , "Count" ]
254255 rows = [[row [0 ], row [- 1 ]] for row in failures ]
255256
256- doc .table (headers , rows , code = [0 ] if group_by == "table" else None )
257+ doc .table (headers , rows , code = [0 ] if group is FailureGroupBy . TABLE else None )
257258
258- if group_by == "test_type" :
259+ if group is FailureGroupBy . TEST_TYPE :
259260 doc .text (
260261 "Check `testgen://test-types` to understand what each test type checks "
261262 "and `get_test_type(test_type='...')` to fetch more details."
@@ -450,12 +451,9 @@ def get_failure_trend(
450451 valid = ", " .join (v .value for v in BucketInterval )
451452 raise MCPUserError (f"Invalid `bucket`: `{ bucket } `. Valid values: { valid } " ) from err
452453
453- perms = get_project_permissions ()
454- if project_code :
455- perms .verify_access (project_code , not_found = MCPResourceNotAccessible ("Project" , project_code ))
456- project_codes = [project_code ]
457- else :
458- project_codes = perms .allowed_codes
454+ project_codes = resolve_aggregate_scope (
455+ project_code , test_suite_id = test_suite_id , table_group_id = table_group_id
456+ )
459457
460458 anchor_today = datetime .now (UTC ).date ()
461459 if exclude_today :
0 commit comments