Skip to content

Commit 6af4e9d

Browse files
authored
Merge pull request #565 from mozilla/as/none-to-untriaged-status
Anca/ Testrail script - Update null automation status to Untriaged
2 parents 9e7f76f + 5728930 commit 6af4e9d

File tree

2 files changed

+154
-0
lines changed

2 files changed

+154
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ tests/test_scratch.py
194194

195195
# Credentials
196196
credentials.json
197+
testrail_credentials.env
197198

198199
# Do not ignore
199200
!data/goomy.png
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
import logging
2+
import os
3+
4+
from dotenv import load_dotenv
5+
6+
from modules.testrail_integration import testrail_init
7+
8+
# Set up logging
9+
logging.basicConfig(
10+
level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s"
11+
)
12+
13+
# Load env file from project root
14+
script_dir = os.path.dirname(__file__)
15+
project_root = os.path.abspath(os.path.join(script_dir, ".."))
16+
env_file_path = os.path.join(project_root, "testrail_credentials.env")
17+
load_dotenv(dotenv_path=env_file_path)
18+
19+
# TestRail project ID (Fx Desktop)
20+
PROJECT_ID = 17
21+
22+
23+
def get_all_suites(tr, project_id):
24+
"""Get all suites from the project"""
25+
suites = tr.client.send_get(f"get_suites/{project_id}")
26+
logging.info(f"Found {len(suites)} suites in project {project_id}")
27+
return suites
28+
29+
30+
# Set limit below maximum value as a precaution to avoid API errors
31+
def get_all_test_cases(tr, project_id, suite_id):
32+
"""Fetch all test cases from a suite by handling pagination."""
33+
all_cases = []
34+
offset = 0
35+
limit = 240 # Default limit for TestRail API is 250
36+
37+
while True:
38+
# Build endpoint with pagination parameters
39+
endpoint = (
40+
f"get_cases/{project_id}&suite_id={suite_id}&limit={limit}&offset={offset}"
41+
)
42+
43+
response = tr.client.send_get(endpoint)
44+
cases = response.get("cases", [])
45+
if not cases:
46+
break
47+
all_cases.extend(cases)
48+
# If the number of cases returned is less than the limit, we've reached the last page.
49+
if len(cases) < limit:
50+
break
51+
offset += limit
52+
53+
logging.info(f"Total cases fetched from suite {suite_id}: {len(all_cases)}")
54+
return all_cases
55+
56+
57+
def update_null_automation_status(tr, project_id, dry_run=True):
58+
"""Update test cases with None automation status to Untriaged"""
59+
try:
60+
# Get all suites in the project
61+
suites = get_all_suites(tr, project_id)
62+
63+
# Track statistics
64+
total_null_cases = 0
65+
updated_count = 0
66+
67+
# Process each suite
68+
for suite in suites:
69+
suite_id = suite["id"]
70+
suite_name = suite["name"]
71+
logging.info(f"Processing suite {suite_name} (ID: {suite_id})...")
72+
73+
# Retrieve test cases for this suite
74+
try:
75+
cases = get_all_test_cases(tr, project_id, suite_id)
76+
77+
# Filter cases with null automation status
78+
null_status_cases = [
79+
case
80+
for case in cases
81+
if case.get("custom_automation_status") is None
82+
]
83+
84+
suite_null_count = len(null_status_cases)
85+
total_null_cases += suite_null_count
86+
87+
logging.info(
88+
f"Found {suite_null_count} cases with null automation status in suite {suite_name}"
89+
)
90+
91+
# Update each case that meets the criteria
92+
for case in null_status_cases:
93+
case_id = case["id"]
94+
try:
95+
if dry_run:
96+
logging.info(
97+
f"[DRY RUN] Would update case {case_id}: set automation status to Untriaged (1)"
98+
)
99+
else:
100+
# Perform the update
101+
tr.update_case_field(
102+
case_id, "custom_automation_status", "1"
103+
)
104+
logging.info(
105+
f"Updated case {case_id}: set automation status to Untriaged (1)"
106+
)
107+
updated_count += 1
108+
except Exception as e:
109+
logging.error(f"Error updating case {case_id}: {e}")
110+
except Exception as e:
111+
logging.error(f"Error processing suite {suite_id}: {e}")
112+
continue
113+
114+
# Log summary
115+
logging.info(
116+
f"Summary: Found {total_null_cases} cases with null automation status across all suites"
117+
)
118+
if not dry_run:
119+
logging.info(f"Updated {updated_count} cases to Untriaged")
120+
else:
121+
logging.info(
122+
f"Would update {total_null_cases} cases to Untriaged (dry run)"
123+
)
124+
125+
except Exception as e:
126+
logging.error(f"Error processing cases: {e}")
127+
128+
129+
def main():
130+
# Read credentials from environment
131+
base_url = os.environ.get("TESTRAIL_BASE_URL")
132+
username = os.environ.get("TESTRAIL_USERNAME")
133+
api_key = os.environ.get("TESTRAIL_API_KEY")
134+
135+
if not all([base_url, username, api_key]):
136+
logging.error("Missing TestRail credentials. Check your .env file.")
137+
return
138+
139+
logging.info(f"Loaded credentials for user: {username}")
140+
logging.info(f"Base URL: {base_url}")
141+
142+
tr = testrail_init()
143+
144+
# Safe approach to not accidentally update cases
145+
dry_run = True
146+
147+
# Process all cases in the project
148+
logging.info(f"Processing project ID: {PROJECT_ID}...")
149+
update_null_automation_status(tr, PROJECT_ID, dry_run)
150+
151+
152+
if __name__ == "__main__":
153+
main()

0 commit comments

Comments
 (0)