Skip to content

Commit 0509550

Browse files
committed
push_results: emit canonical test_suite + CI correlation fields for Reports drill-down
1 parent 4ed751e commit 0509550

1 file changed

Lines changed: 62 additions & 3 deletions

File tree

src/test/python/suites/agentic/reporting/push_results.py

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,19 +28,42 @@
2828
import argparse
2929
import datetime
3030
import json
31+
import os
32+
import re
3133
import sys
3234
import xml.etree.ElementTree as ET
3335
from pathlib import Path
3436

37+
# Canonical test-type taxonomy (mirrors agent's TEST_TYPES in
38+
# src/api/routes/reporting.py). Producers MUST emit one of these as
39+
# `test_suite` so the Reports drill-down can group by type.
40+
TEST_TYPES = ('unit', 'integration', 'ui', 'smoke', 'e2e', 'perf', 'chaos')
41+
42+
43+
def _derive_test_type(source_file: str, suite_name: str, override: str | None) -> str:
44+
"""Pick a canonical test type from CLI override, then filename, then suite."""
45+
if override:
46+
return override
47+
haystack = f'{source_file} {suite_name}'.lower()
48+
for t in TEST_TYPES:
49+
if re.search(rf'\b{t}\b', haystack):
50+
return t
51+
return 'unit'
52+
3553
try:
3654
import requests
3755
HAS_REQUESTS = True
3856
except ImportError:
3957
HAS_REQUESTS = False
4058

4159

42-
def parse_junit_xml(xml_path):
43-
"""Parse a JUnit XML file and return a list of test result dicts."""
60+
def parse_junit_xml(xml_path, test_type_override: str | None = None):
61+
"""Parse a JUnit XML file and return a list of test result dicts.
62+
63+
Enriches each doc with CI correlation fields (repo, pipeline_id,
64+
build_number, branch, commit_sha) from the Jenkins env so the
65+
Dashboard Reports drill-down can join against test-coverage-*.
66+
"""
4467
results = []
4568
tree = ET.parse(xml_path)
4669
root = tree.getroot()
@@ -50,9 +73,24 @@ def parse_junit_xml(xml_path):
5073
if not suites and root.tag == 'testsuite':
5174
suites = [root]
5275

76+
# Jenkins env correlation (no-op outside Jenkins). repo defaults to
77+
# the agentic-taf repo when JOB_NAME is unset.
78+
job = os.environ.get('JOB_NAME', '') or ''
79+
repo = job.split('/')[0] if job else 'agentic-taf'
80+
branch = os.environ.get('BRANCH_NAME') or os.environ.get('GIT_BRANCH') or 'main'
81+
commit_sha = os.environ.get('GIT_COMMIT', '')
82+
pipeline_id = os.environ.get('BUILD_TAG', '')
83+
build_number_raw = os.environ.get('BUILD_NUMBER', '0')
84+
try:
85+
build_number = int(build_number_raw)
86+
except ValueError:
87+
build_number = 0
88+
team = os.environ.get('TEAM', 'platform-team')
89+
5390
for suite in suites:
5491
suite_name = suite.get('name', 'unknown')
5592
suite_time = float(suite.get('time', 0))
93+
test_type = _derive_test_type(str(xml_path), suite_name, test_type_override)
5694

5795
for tc in suite.findall('testcase'):
5896
name = tc.get('name', 'unknown')
@@ -72,16 +110,30 @@ def parse_junit_xml(xml_path):
72110
message = tc.find('skipped').get('message', '')
73111

74112
results.append({
113+
# JUnit-derived fields (kept for backward compatibility)
75114
'suite': suite_name,
76115
'classname': classname,
77116
'name': name,
78117
'status': status,
79118
'duration_seconds': time_taken,
119+
'duration_ms': int(time_taken * 1000),
80120
'message': message,
81121
'source_file': str(xml_path),
82122
'timestamp': datetime.datetime.utcnow().isoformat() + 'Z',
83123
'framework': 'agentic-taf',
84124
'suite_duration': suite_time,
125+
# Canonical fields the agent reads for joining + grouping
126+
'test_suite': test_type, # canonical type taxonomy
127+
'test_name': name, # mirror of `name`
128+
'test_type': test_type, # explicit alias
129+
'repo': repo,
130+
'branch': branch,
131+
'commit_sha': commit_sha,
132+
'pipeline_id': pipeline_id,
133+
'build_number': build_number,
134+
'team': team,
135+
'is_flaky': False,
136+
'retry_count': 0,
85137
})
86138

87139
return results
@@ -159,6 +211,13 @@ def main():
159211
parser.add_argument('--opensearch-url', help='OpenSearch URL (e.g. http://opensearch:9200)')
160212
parser.add_argument('--agent-url', help='Agent API URL (e.g. http://agent:8000)')
161213
parser.add_argument('--index', default='test-results', help='OpenSearch index name')
214+
parser.add_argument(
215+
'--test-type',
216+
choices=TEST_TYPES,
217+
help='Override the canonical test type. If omitted, derived from the '
218+
'XML filename / suite name (regex against the TEST_TYPES list); '
219+
'falls back to "unit".',
220+
)
162221
args = parser.parse_args()
163222

164223
reports_dir = Path(args.reports_dir)
@@ -174,7 +233,7 @@ def main():
174233
all_results = []
175234
for xml_file in xml_files:
176235
try:
177-
results = parse_junit_xml(xml_file)
236+
results = parse_junit_xml(xml_file, test_type_override=args.test_type)
178237
all_results.extend(results)
179238
print(f'Parsed {len(results)} results from {xml_file.name}')
180239
except Exception as exc:

0 commit comments

Comments
 (0)