Source code for ska_ser_skallop.scripts.bdd_helper_scripts.xtp_push

"""This script uploads a cucumber feature file to a test set ticket in JIRA"""
import argparse
import getpass
import os
import sys

import pytest_bdd
import requests
from requests.auth import HTTPBasicAuth

JIRA_ISSUE_URL = "https://jira.skatelescope.org/browse/{}"
XTP_TESTSET_URL = "https://jira.skatelescope.org/rest/raven/1.0/api/testset/{}/test"
FEATURE_UPLOAD_URL = (
    "https://jira.skatelescope.org/rest/raven/1.0/import/feature?projectKey=XTP"
)
HEADERS = {"Accept": "application/json"}


[docs]def associate_test_with_test_set( args: argparse.Namespace, test_set_xtp_ticket: str, new_test_xtp_ticket: str, ): """Associate a Jira test ticket to a Jira test set ticket :param args: The passed paramaters :param test_set_xtp_ticket: The XTP ticket number of the test set :param new_test_xtp_ticket: The XTP ticket number of the test """ if args.verbose: print( f"Associating test ticket {new_test_xtp_ticket} to test set " f"{test_set_xtp_ticket}" ) data = {"add": [f"{new_test_xtp_ticket}"], "remove": []} url = XTP_TESTSET_URL.format(test_set_xtp_ticket) response = requests.post( url, auth=HTTPBasicAuth(args.username, args.password), json=data ) try: response.raise_for_status() except requests.exceptions.HTTPError as err: if response.status_code == 400: raise SystemExit(f"A bad request was made to {url}.") from err if response.status_code == 401: raise SystemExit( "Authentication failure, either the credentials are incorrect or the " "Xray license has expired." ) from err if response.status_code == 500: raise SystemExit( "An internal error occurred when associating the tests." ) from err raise SystemExit(err) from err if args.verbose: print(f"Result: {response.content}")
[docs]def upload_feature_file(xtp_ticket: str, args: argparse.Namespace) -> list: """POST the .feature file to JIRA If the `Scenario Outline` in the feature file matches the Summary of a Jira Test ticket, then the ticket will be updated. If not then a new ticket will be created. :param xtp_ticket: the test set XTP ticket :param args: the passed paramaters :return: details of the new/updated test ticket """ jira_issue_url = JIRA_ISSUE_URL.format(xtp_ticket) response = None if not args.dry_run: if args.verbose: print(f"Uploading feature file {args.feature_file.name}...") with open(args.feature_file.name, "rb") as open_file: files = {"file": open_file} response = requests.post( FEATURE_UPLOAD_URL, auth=HTTPBasicAuth(args.username, args.password), files=files, ) try: response.raise_for_status() except requests.exceptions.HTTPError as err: if response.status_code == 400: raise SystemExit("No .feature file was provided.") from err if response.status_code == 401: raise SystemExit( "Authentication failure, either the credentials are incorrect " "or the Xray license has expired." ) from err if response.status_code == 500: raise SystemExit( "An internal error occurred when uploading the .feature file." ) from err raise SystemExit(err) from err response = response.json() if args.verbose: print(f"Result: {response}") print( f"Uploaded feature file {args.feature_file.name} to " f"{jira_issue_url}." ) else: print(f"[DRY RUN] Uploading feature file {args.feature_file.name}...") print( f"[DRY RUN] Uploaded feature file {args.feature_file.name} to " f"{jira_issue_url}." ) return response
[docs]def fetch_test_set(test_set_xtp: str, args: argparse.Namespace) -> list: """Make sure a Jira test set with the parameter exists :param test_set_xtp: The test set XTP ticket :param args: The passed paramaters :return: Details of the tests assciated with the test set, if any """ url = XTP_TESTSET_URL.format(test_set_xtp) jira_issue_url = JIRA_ISSUE_URL.format(test_set_xtp) response = None if not args.dry_run: if args.verbose: print(f"Checking that test set {test_set_xtp} exists in {jira_issue_url}") try: response = requests.get( url, auth=HTTPBasicAuth(args.username, args.password), headers=HEADERS, ) response.raise_for_status() except requests.exceptions.HTTPError as err: if response.status_code == 404: raise SystemExit( f"No test set found in Jira with key {test_set_xtp}" ) from err if response.status_code == 401: raise SystemExit( "Authentication failure, either the credentials are incorrect or " "the Xray license has expired." ) from err if response.status_code == 400: raise SystemExit("Returns the error") from err if response.status_code == 500: raise SystemExit( "An internal error occurred when generating the feature file(s)." ) from err raise SystemExit(err) from err response = response.json() if args.verbose: print(f"OK. Result: {response}") else: print( f"[DRY RUN] Checking that test set {test_set_xtp} exists in " f"{jira_issue_url}" ) print(f"[DRY RUN] OK. Test set {test_set_xtp} found") return response
[docs]def get_xtp_ticket_tag( args: argparse.Namespace, feature_file: pytest_bdd.parser.Feature ) -> str: """Making use of the parsed feature file, get the test set XTP ticket Also ensures that there is only one XTP ticket tag :param args: The passed paramaters :param feature_file: The parsed feature file :return: The test set XTP ticket """ if args.verbose: print(f"\nGetting the test set XTP ticket from {feature_file.filename}") if args.dry_run: print( f"\n[DRY RUN] Getting the test set XTP ticket from {feature_file.filename}" ) xtp_ticket_tag = None tags = [tag for tag in feature_file.tags if tag.startswith("XTP")] if len(tags) == 0: if args.dry_run: print( "\n[DRY RUN] The feature file should have a tag with a XTP ticket " "number. E.g:\n\n\t@XTP-1234\n" "\tFeature: Some feature description\n\t...\n" ) else: print( "\nThe feature file should have a tag with a XTP ticket number. " "E.g:\n\n\t@XTP-1234\n\tFeature: Some feature description\n\t...\n" ) sys.exit(1) if len(tags) > 1: if args.dry_run: print("[DRY RUN] Only one XTP ticket tag is supported.") else: print("Only one XTP ticket tag is supported.") sys.exit(1) xtp_ticket_tag = tags[0] if args.verbose: print(f"OK. Test set XTP ticket is {xtp_ticket_tag}") if args.dry_run: print(f"[DRY RUN] OK. Test set XTP ticket is {xtp_ticket_tag}.") return xtp_ticket_tag
[docs]def parse_feature_file(args: argparse.Namespace): """Parse the feature file :param args: The passed paramaters :return: The parsed feature file """ if args.verbose: print(f"\nChecking feature file {args.feature_file.name}") feature_file = None try: feature_file = pytest_bdd.feature.get_feature("", args.feature_file.name) except pytest_bdd.exceptions.FeatureError as err: print("\nThere is a problem with the feature file:\n") print(err) sys.exit(1) if args.verbose: print("OK") return feature_file
[docs]def main(): """Script entrypoint""" parser = argparse.ArgumentParser( description=( "Uploads a cucumber test file to JIRA by way of the XRAY extension. Either " "use username/password or set the environment variable JIRA_AUTH for " "authentication." ) ) parser.add_argument( "-f", "--feature-file", type=argparse.FileType("r"), required=True, help="Path to the feature file.", ) parser.add_argument("-u", "--username", type=str, help="JIRA account username") parser.add_argument( "-p", "--password", type=str, default="", help=( "Password for specified user. If not specified you will be prompted for one" ), ) parser.add_argument( "-v", "--verbose", required=False, action="store_true", help="Verbose output", ) parser.add_argument( "--dry-run", required=False, action="store_true", help="Run the script without uploading the XTP-*.feature file.", ) args = parser.parse_args() setattr(args, "jira_auth_token", os.environ.get("JIRA_AUTH", None)) if not args.jira_auth_token: if not args.username: print( "A username is required when the environment variable JIRA_AUTH is not " "set" ) sys.exit(0) if not args.password: stdin_password = getpass.getpass() setattr(args, "password", stdin_password) feature_file = parse_feature_file(args) test_set_xtp = get_xtp_ticket_tag(args, feature_file) # If the test set has no associated tests, # then the response will be an empty list test_set = fetch_test_set(test_set_xtp, args) test_set_associated_tests = None if not args.dry_run: test_set_associated_tests = [ticket["key"] for ticket in test_set] new_test_result = upload_feature_file(test_set_xtp, args) if new_test_result: for test_xtp in new_test_result: new_test_xtp = test_xtp["key"] if new_test_xtp not in test_set_associated_tests: associate_test_with_test_set(args, test_set_xtp, new_test_xtp) if args.verbose: print(f"Test {new_test_xtp} associated with {test_set_xtp}") else: if args.verbose: print( f"Test {new_test_xtp} has already been associated with test " f"set {test_set_xtp}" )
if __name__ == "__main__": main()