diff --git a/modules/testrail.py b/modules/testrail.py index 1a46bf90..14aaf170 100644 --- a/modules/testrail.py +++ b/modules/testrail.py @@ -104,7 +104,7 @@ def __send_request(self, method, uri, data): try: error = response.json() except ( - requests.exceptions.HTTPError + requests.exceptions.HTTPError ): # response.content not formatted as JSON error = str(response.content) raise APIError( @@ -176,7 +176,7 @@ def update_test_case(self, case_id, **kwargs): return self.client.send_post(f"update_case/{case_id}", **kwargs) def create_test_run_on_plan_entry( - self, plan_id, entry_id, config_ids, description=None, case_ids=None + self, plan_id, entry_id, config_ids, description=None, case_ids=None ): """ Add a test run on an entry (subplan) associated with a plan. @@ -259,12 +259,12 @@ def matching_custom_field(self, name): return None def create_new_plan( - self, - testrail_project_id, - name, - description=None, - milestone_id=None, - entries=None, + self, + testrail_project_id, + name, + description=None, + milestone_id=None, + entries=None, ): """ Create a new test plan (on a milestone). @@ -297,14 +297,14 @@ def create_new_plan( return self.client.send_post(f"/add_plan/{testrail_project_id}", payload) def create_new_plan_entry( - self, - plan_id, - suite_id, - name=None, - description=None, - case_ids=None, - config_ids=None, - runs=None, + self, + plan_id, + suite_id, + name=None, + description=None, + case_ids=None, + config_ids=None, + runs=None, ): """ Create a new entry (subplan) on a plan. @@ -380,12 +380,12 @@ def update_case_field(self, case_id, field_id, content): return self.client.send_post(f"update_case/{case_id}", data) def update_test_cases( - self, - testrail_project_id, - testrail_run_id, - testrail_suite_id, - test_case_ids=[], - status="passed", + self, + testrail_project_id, + testrail_run_id, + testrail_suite_id, + test_case_ids=[], + status="passed", ): """Given a project id, a run id, and a suite id, for each case given a status, update the test objects with the correct status code""" @@ -476,6 +476,10 @@ def _get_full_plan(self, plan_id): def _get_case_fields(self): return self.client.send_get("get_case_fields") + def get_suites(self, project_id): + """Get all suites for project""" + return self.client.send_get(f"get_suites/{project_id}") + def _retry_api_call(self, api_call, *args, max_retries=3, delay=5): """ Retries the given API call up to max_retries times with a delay between attempts. diff --git a/modules/testrail_integration.py b/modules/testrail_integration.py index a7f75af9..15dd40a0 100644 --- a/modules/testrail_integration.py +++ b/modules/testrail_integration.py @@ -172,11 +172,21 @@ def reportable(platform_to_test=None): def testrail_init() -> TestRail: """Connect to a TestRail API session""" - local = os.environ.get("TESTRAIL_BASE_URL").split("/")[2].startswith("127") - tr_session = tr.TestRail( - os.environ.get("TESTRAIL_BASE_URL"), - os.environ.get("TESTRAIL_USERNAME"), - os.environ.get("TESTRAIL_API_KEY"), + base_url = os.environ.get("TESTRAIL_BASE_URL") + username = os.environ.get("TESTRAIL_USERNAME") + api_key = os.environ.get("TESTRAIL_API_KEY") + + if not base_url or not username or not api_key: + raise ValueError( + "Missing TestRail credentials. Check your api_credentials.env file." + ) + + local = base_url.split("/")[2].startswith("127") + + tr_session = TestRail( + base_url, + username, + api_key, local, ) return tr_session diff --git a/modules/testrail_script.py b/modules/testrail_script.py new file mode 100644 index 00000000..c2ebe858 --- /dev/null +++ b/modules/testrail_script.py @@ -0,0 +1,74 @@ +import logging +import os +from dotenv import load_dotenv +from modules.testrail_integration import testrail_init + +# Set up logging +logging.basicConfig( + level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s" +) + +# Load env file from project root +script_dir = os.path.dirname(__file__) +project_root = os.path.abspath(os.path.join(script_dir, "..")) +env_file_path = os.path.join(project_root, "api_credentials.env") +load_dotenv(dotenv_path=env_file_path) + +MY_SUITES = ["Printing", "Startup and Profile", "Reader View"] +PROJECT_ID = 17 + +# Define what field to update and its new value +FIELD_TO_UPDATE = "custom_automation_coverage" +NEW_FIELD_VALUE = 3 # 3 might represent 'Covered' + +def main(): + # Read credentials from environment + base_url = os.environ.get("TESTRAIL_BASE_URL") + username = os.environ.get("TESTRAIL_USERNAME") + api_key = os.environ.get("TESTRAIL_API_KEY") + + if not all([base_url, username, api_key]): + logging.error("Missing TestRail credentials. Check your .env file.") + return + + logging.info(f"Loaded TestRail credentials for user: {username}") + logging.info(f"TestRail Base URL: {base_url}") + + tr = testrail_init() + + logging.info(f"Fetching suite IDs for suites: {MY_SUITES}") + suites = list(tr.get_suites(PROJECT_ID)) + suite_ids = [ + suite["id"] + for suite in suites + if suite["name"] in MY_SUITES + ] + logging.info(f"Suite IDs to process: {suite_ids}") + + case_ids = [] + for suite_id in suite_ids: + val = tr._get_test_cases(PROJECT_ID, suite_id) + if val["size"] < val["limit"]: + matching_cases = [ + case for case in val["cases"] + if case.get("custom_automated_test_names") and case.get("custom_automation_status") == 4 + ] + matching_case_ids = [case["id"] for case in matching_cases] + + case_ids.extend(matching_case_ids) + logging.info( + f"Suite {suite_id} processed and added case IDs: {matching_case_ids}" + ) + else: + logging.warning(f"Suite {suite_id} test cases exceed retrieval limit.") + + logging.info(f"Total collected case IDs: {case_ids}") + + # Perform updates to TestRail + for case_id in case_ids: + response = tr.update_case_field(case_id, FIELD_TO_UPDATE, NEW_FIELD_VALUE) + logging.info(f"Updated case ID {case_id}: {response}") + + +if __name__ == "__main__": + main()