Getting all the test cases inside the test folder using Pyral api - python

I have used pyral api
rally = Rally(server, apikey=api_key, workspace=workspace, project=project)
to connect to rally
Than ,
testfolders = rally.get('TestFolder', fetch=True, query=query_criteria)
I need to extract all the test cases defined in the rally as:
TF*****
-TC1
-TC2
-TC3
I need to get all the test cases from the formattedID returned from
testfolders.
for tc in testfolders:
print(tc.Name)
print(tc.FormattedID)# this represents the TF***
tc_link=tc._ref#url for TC's https://rally1.rallydev.com/slm/webservice
query_criteria = 'TestSets = "%s"' % tp_name
testfolders = rally.get('TestFolder', fetch=True, query=query_criteria)
I have tried looking for different combination sets to extract test cases
The tc._ref is actually a
https://rally1.rallydev.com/slm/webservice/v2.0/TestFolder/XXXXX/TestCases
when I tried to open it in browser it gave the entire Test cases list.
I need to extract those test case list .
I tried accessing using urllib to acess the data using python ,but said unauthorized access, I think if I am on the same session authorization shouldnt be an issue since rally object is already instantiated.
Any help on this will be much appreciated!!
Thanks in advance
rally = Rally(server, apikey=api_key, workspace=workspace, project=project)
tp_name = "specific test case "
query_criteria = 'FormattedID = "%s"' % tp_name
testfolders = rally.get('TestFolder', fetch=True, query=query_criteria)
for tc in testfolders:
print(tc.Name)
print(tc.FormattedID)
query_criteria = 'TestSets = "%s"' % tc._ref
response = rally.get('TestCase', fetch=True, query=query_criteria)
print(response)
422 Could not read: could not read all instances of class com.f4tech.slm.domain.TestCase for class TestCase

It's not clear what you are trying to achieve but please take a look at the code below. Probably, it will solve your issue:
query = 'FormattedID = %s'
test_folder_req = rally.get('TestFolder', fetch=True, projectScopeDown=True,
query=query % folder_id) # folder_id == "TF111"
if test_folder_req.resultCount == 0:
print('Rally returned nothing for folder: %s', folder_id)
sys.exit(1)
test_folder = test_folder_req.next()
test_cases = test_folder.TestCases
print('Start working with %s' % folder_id)
print('Test Folder %s contains %s TestCases' % (folder_id, len(test_cases)))
for test_case in test_cases:
print(test_case.FormattedID)
print(test_case.Name)

Related

InvalidSchema("No connection adapters were found for '%s'" % url) - Have tried a lot

I understood that the topic already exists, but I cannot figure out what is the issue.
This is the code ( I believe it is straight forward )
import requests
import config
import time
dataID = "111111"
# If reportis not generated will return 'None'
def get_data():
dataChecking = None
checking_url = 'https://api.example.com/v1.1/reports/{0}'.format(dataID)
responseCheck = requests.get(checking_url, params=(('fields',
'generated_date'), ), auth=(''
+ config.authUsername + '', ''
+ config.authPassword + ''))
report_url = responseCheck.json()['report']['generated_date']
dataChecking = requests.get(report_url).content
return dataChecking
def download_report(dataChecking):
urlDownload = 'https://api.example.com/v1.1/reports/{0}'.format(dataID)
responseDownload = requests.get(urlDownload, params=(('fields',
'download'), ), auth=(''
+ config.authUsername + '', ''
+ config.authPassword + ''))
report_url = responseDownload.json()['report']['download']
dataDownload = requests.get(report_url).content
with open('' + config.fileDest + '\exportReport.json', 'w') as f:
f.write(dataDownload)
pass
# Checking report is generated
generatedData = get_data()
# Wating for report to generated
while generatedData == None:
# Check again if report is generated
print("Report is generating, Please wait")
generatedData = get_data()
# Wait 0.25 seconds between each check
time.sleep(0.25)
# Report generated, need to download him
download_report(dataChecking)
Error is :
raise InvalidSchema("No connection adapters were found for '%s'" % url)
InvalidSchema: No connection adapters were found for '2018-06-15T10:37:50'
I have tried to change the URL part, using different tutorials, with no success.
'2018-06-15T10:37:50' - This is the date when the report is generated, so what I currently do, is trying to check if the data is empty and keep checking it it filled in (with date, as I demonstrate in the example) and then it will run the download part.
Your error seems to be in these lines in the get_data function:
report_url = responseCheck.json()['report']['generated_date']
dataChecking = requests.get(report_url).content
The data structure implies that what you're storing in the variable report_url is actually a date, and then you're trying to retrieve that as a url, which throws the error. Figure out where the actual report url is stored and fetch that instead.

Bug in python code preventing successful recursion?

I have been working on a script to ingest a file (accounts.txt) which contains email addresses, for which each will then be verified against an API to see if they appear in a data dump. The script appears to work, however there is a bug present whereby once it finds a positive hit, it will disregard any other match...
For example;
If my "accounts.txt" file contains the following entries:
a#a.com
b#b.com
Even though both of those should return results, as soon as the script is run, the match on a#a.com will be found however b#b.com will not return anything.
I cannot seem to figure out why this is happening, ideally I want all of the hits outputted.
FYI, the script is querying 'haveibeenpwned' which is a site that locates email addresses found in credential dumps.
Any help finding my bug would be greatly appreciated. Below is my current script.
#!/usr/bin/env python
import argparse
import json
import requests
import time
breaches_by_date = {}
breaches_by_account = {}
breaches_by_name = {}
class Breach(object):
def __init__(self, e, n, d):
self.email = e
self.name = n
self.date = d
def __repr__(self):
return "%s: %s breached on %s" % (self.email, self.name, self.date)
def accCheck(acc):
global breaches_by_date, breaches_by_account, breaches_by_name
r = requests.get('https://haveibeenpwned.com/api/v2/breachedaccount/%s?truncateResponse=false' % acc)
try:
data = json.loads(r.text)
except ValueError:
print("No breach information for %s" % acc)
return
for i in data:
name, date = (i['Name'], i['BreachDate'])
breach = Breach(acc, name, date)
try: breaches_by_account[acc].append(breach)
except: breaches_by_account[acc] = [breach]
try: breaches_by_name[name].append(breach)
except: breaches_by_name[name] = [breach]
try: breaches_by_date[date].append(breach)
except: breaches_by_date[date] = [breach]
def readFromFile(fname="accounts.txt"):
accounts=[]
with open(fname, "r+") as f:
accounts = [l.strip() for l in f.readlines()]
return accounts
if __name__ == '__main__':
accounts = readFromFile()
for email_addr in accounts:
accCheck(email_addr)
print
print("Breaches by date")
for date, breaches in breaches_by_date.items():
for breach in breaches:
print(breach)
print
print("Breaches by account")
for acc, breaches in breaches_by_account.items():
print(acc)
for breach in breaches:
print("%s breached on %s" % (breach.name, breach.date))
print
print("Breaches by name")
for name, breaches in breaches_by_name.items():
print("%s breached for the following accounts:" % name)
for breach in breaches:
print("%s on %s" % (breach.email, breach.date))
print
I am not 100% sure to know where your problem comes from, but I would opt for a code like:
emails_to_check = open("/path/to/yourfile").read().split("\n")
for email in emails_to_check:
if is_email_blacklisted(email):
do_something()

MySQL Python connector does not commit

I have this function that it's supposed should receive json and store the values on a RDS MySQL db.
def saveMetric(metrics):
cnx = RDS_Connect()
cursor = cnx.cursor()
jsonMetrics = json.loads(metrics)
#print type(jsonMetrics['Metrics'])
# Every 2000 registries, the script will start overriding values
persistance = 2000
save_metrics_query = (
"REPLACE INTO metrics "
"SET metric_seq = (SELECT COALESCE(MAX(row_id), 0) %% %(persistance)d + 1 FROM metrics AS m), "
"instance_id = \'%(instance_id)s\', "
"service = \'%(service)s\' , "
"metric_name = \'%(metric_name)s\', "
"metric_value = %(metric_value)f"
)
for metric in jsonMetrics['Metrics']:
formatData = {}
formatData['persistance'] = persistance
formatData['instance_id'] = arguments.dimensionValue
formatData['service'] = jsonMetrics['Service']
formatData['metric_name'] = metric
formatData['metric_value'] = jsonMetrics['Metrics'][metric]
print save_metrics_query % formatData
try:
cursor.execute(save_metrics_query, formatData, multi=True)
logger('info','Metrics were saved successfully!')
cnx.commit()
except mysql.connector.Error as err:
logger('error', "Something went wrong: %s" % err)
cursor.close()
cnx.close()
RDS_Connect() was already tested and it works just fine. The problem is that after running the function, the data is not saved to the DB. I think there is a problem with the commit but I don't see any errors or warning message. If I run the query manually, the data gets stored.
Here is the query that runs after parsing the json:
REPLACE INTO metrics SET metric_seq = (SELECT COALESCE(MAX(row_id), 0) % 2000 + 1 FROM metrics AS m), instance_id = 'i-03932937bd67622c4', service = 'AWS/EC2' , metric_name = 'CPUUtilization', metric_value = 0.670000
If it helps, this is the json that the function receives:
{
"Metrics": {
"CPUUtilization": 1.33,
"NetworkIn": 46428.0,
"NetworkOut": 38772.0
},
"Id": "i-03932937bd67622c4",
"Service": "AWS/EC2"
}
I'd appreciate some help.
Regards!
UPDATE:
I found that the problem was related to the formatting codes on the query template.
I re wrote the function like this:
def saveMetric(metrics):
cnx = RDS_Connect()
jsonMetrics = json.loads(metrics)
print json.dumps(jsonMetrics,indent=4)
persistance = 2000
row_id_query_template = "SELECT COALESCE(MAX(row_id), 0) % {} + 1 FROM metrics AS m"
row_id_query = row_id_query_template.format(persistance)
save_metrics_query = (
"REPLACE INTO metrics "
"SET metric_seq = (" + row_id_query + "),"
"instance_id = %(instance_id)s,"
"service = %(service)s,"
"metric_name = %(metric_name)s,"
"metric_value = %(metric_value)s"
)
for metric in jsonMetrics['Metrics']:
formatData = {}
formatData['instance_id'] = arguments.dimensionValue
formatData['service'] = jsonMetrics['Service']
formatData['metric_name'] = metric
formatData['metric_value'] = jsonMetrics['Metrics'][metric]
if arguments.verbose == True:
print "Data: ",formatData
print "Query Template: ",save_metrics_query.format(**formatData)
try:
cursor = cnx.cursor()
cursor.execute(save_metrics_query, formatData)
logger('info','Metrics were saved successfully!')
cnx.commit()
cursor.close()
except mysql.connector.Error as err:
logger('error', "Something went wrong: %s" % err)
cnx.close()
As you can see, I format the SELECT outside. I believe the whole problem was due to this line:
"metric_value = %(metric_value)f"
I changed to:
"metric_value = %(metric_value)s"
and now it works. I think the formatting was wrong, given a syntax error (tho I don't know how the exception was never thrown).
Thanks to everyone who took time to help me!
I haven't actually used MySQL, but the docs seem to indicate that calling cursor.execute with multi=True just returns an iterator. If that is true, then it wouldn't actually insert anything - you'd need to call .next() on the iterator (or just iterate over it) to actually insert the record.
It also goes on to advise against using parameters with multi=True:
If multi is set to True, execute() is able to execute multiple statements specified in the operation string. It returns an iterator that enables processing the result of each statement. However, using parameters does not work well in this case, and it is usually a good idea to execute each statement on its own.
tl;dr: remove that parameter, as the default is False.
This was the solution. I changed:
"metric_value = %(metric_value)f"
To:
"metric_value = %(metric_value)s"
Doing some troubleshooting I found a syntax error on the SQL. Somehow the exception didn't show.

How to get user story points using pyral?

I am trying to modify the statecounts.py from pyral examples to pull all the defined/accepted/in progress/completed stories point for each release. I am keep getting query exception error. Is there anything that I have missed?
state = 'ScheduleState'
state_values = rally.getAllowedValues('HierarchicalRequirement', state)
output = []
for rel in sorted(release_names):
for state_value in sorted(state_values):
response = rally.get(artifact_type, fetch="FormattedID", query='(Release.Name= %s ) AND %s = %s' % (rel, state, state_value),
projectScopeUp=False, projectScopeDown=False)
output.append("%20s : %16s : %5d" % (rel, state, state_value, response.resultCount))
Thanks!!!
If you are ANDing multiple terms it's easiest to create a list of terms like so:
query=['Release.Name = %s' % (rel), '%s = %s' % (state, state_value)]
Also, whitespace matters. You were missing a space before the first =.

Rally APIs: How to copy Test Folder and member Test Cases

This question was asked by a different user earlier:
Copying Test Cases and Test Folder using Rally Python or Ruby API [closed]
but closed by moderators as being an overly broad question. However, given the inability to copy Test Folders and their member Test Cases within the Rally UI, this is a common need for Rally Users.
Thus - I'll re-pose the question, hopefully with enough detail to stand as a valid question. I'll also re-post the answers that I developed for the original question.
Question: As a Rally user and developer in the Rally Python and Ruby REST APIs: how can I leverage the Rally API toolkits to accomplish this task?
Python:
Here is a script that performs this task - it will copy all Test Cases from a Source Test Folder identified by FormattedID, to a Target Test Folder, also identified by FormattedID. It will copy all Test Steps and Attachments as well. The Target Test Folder must exist, i.e. the script will not create a Test Folder for you if the Target is not found.
The script does not associate the new Test Case to original Test Case's Work Product (i.e. Defect, User Story), or copy Discussion items, Test Case Results, or Last Build, Verdict, etc. as it is assumed the new Test Case is desired to be in a "blank" state.
For those needing to install and configure the Rally Python REST Library:
Rally Developer Portal: Pyral Download and Installation
Pyral Documentation
#!/usr/bin/env python
#################################################################################################
#
# copy_test_folder.py -- Copy all Test Cases in Source Test Folder to Target. Includes Test Steps
# and attachments. Target Test Folder must exist (i.e. the script will not
# create a new targeet Test Folder for you)
#
USAGE = """
Usage: copy_test_folder.py
"""
#################################################################################################
# import needed python libs
import sys, os
import re
import string
import base64
from pprint import pprint
# import needed pyral libs
from pyral import Rally, rallySettings, RallyRESTAPIError
errout = sys.stderr.write
my_server = "rally1.rallydev.com"
my_user = "user#company.com"
my_password = "topsecret"
my_workspace = "My Workspace"
my_project = "My Project"
source_test_folder_formatted_id = "TF1"
target_test_folder_formatted_id = "TF4"
rally = Rally(my_server, my_user, my_password, workspace=my_workspace, project=my_project)
# rally = Rally(my_server, my_user, my_password, workspace=my_workspace, project=my_project, debug=True)
rally.enableLogging('copy_test_folder.log')
# Query for source and target test folders
source_test_folder_response = rally.get('TestFolder', fetch=True, query='FormattedID = %s' % source_test_folder_formatted_id)
target_test_folder_response = rally.get('TestFolder', fetch=True, query='FormattedID = %s' % target_test_folder_formatted_id)
# Check to make sure folders exist
if source_test_folder_response.resultCount == 0:
errout('No Source Test Folder Found matching Formatted ID: %s\n' % (source_test_folder_formatted_id))
sys.exit(4)
if target_test_folder_response.resultCount == 0:
errout('No Target Test Folder Found matching Formatted ID: %s\n. Target Test Folder must be created before copying.' % (target_test_folder_formatted_id))
sys.exit(4)
# Get references to source Test Folder and Test Cases, etc.
source_test_folder = source_test_folder_response.next()
source_test_cases = source_test_folder.TestCases
# Get reference to target Test Folder
target_test_folder = target_test_folder_response.next()
for source_test_case in source_test_cases:
# Create update fields for target Test Case
# Does NOT associate new Test Case to original Test Case's work product (i.e. Defect, User Story)
# Does NOT copy Discussion items - as the old Discussions are likely not desired on new Test Case
# Does NOT copy Last Build, Last Run, Last Update Date, Last Verdict as new Test Case will effectively
# be "blank" and not have any results associated to it
if source_test_case.Owner != None:
target_owner = source_test_case.Owner.ref
else:
target_owner = None
target_test_case_fields = {
"Package": source_test_case.Package,
"Description": source_test_case.Description,
"Method": source_test_case.Method,
"Name": source_test_case.Name,
"Objective": source_test_case.Objective,
"Owner": target_owner,
"PostConditions": source_test_case.PostConditions,
"PreConditions": source_test_case.PreConditions,
"Priority": source_test_case.Priority,
"Project": source_test_case.Project.ref,
"Risk": source_test_case.Risk,
"ValidationInput": source_test_case.ValidationInput,
"ValidationExpectedResult": source_test_case.ValidationExpectedResult,
"TestFolder": target_test_folder.ref,
}
# Create the target test case
try:
target_test_case = rally.create("TestCase", target_test_case_fields)
message = "Copied Source Test Case: " + source_test_case.FormattedID + \
" To: " + target_test_folder.FormattedID + ": " + target_test_folder.Name + \
": " + target_test_case.FormattedID
print message
except RallyRESTAPIError, details:
sys.stderr.write('ERROR: %s \n' % details)
sys.exit(2)
# Copy Test Steps
# Add Test Case Steps
source_test_case_steps = source_test_case.Steps
for source_step in source_test_case_steps:
target_step_fields = {
"TestCase" : target_test_case.ref,
"StepIndex" : source_step.StepIndex,
"Input" : source_step.Input,
"ExpectedResult" : source_step.ExpectedResult
}
target_test_case_step = rally.put('TestCaseStep', target_step_fields)
print "===> Copied TestCaseStep: %s OID: %s" % (target_test_case_step.StepIndex, target_test_case_step.oid)
# Copy Attachments
source_attachments = rally.getAttachments(source_test_case)
for source_attachment in source_attachments:
# First copy the content
source_attachment_content = source_attachment.Content
target_attachment_content_fields = {
"Content": base64.encodestring(source_attachment_content)
}
try:
target_attachment_content = rally.put('AttachmentContent', target_attachment_content_fields)
print "===> Copied AttachmentContent: %s" % target_attachment_content.ref
except RallyRESTAPIError, details:
sys.stderr.write('ERROR: %s \n' % details)
sys.exit(2)
# Next copy the attachment object
target_attachment_fields = {
"Name": source_attachment.Name,
"Description": source_attachment.Description,
"Content": target_attachment_content.ref,
"ContentType": source_attachment.ContentType,
"Size": source_attachment.Size,
"Artifact": target_test_case.ref,
"User": source_attachment.User.ref
}
try:
target_attachment = rally.put('Attachment', target_attachment_fields)
print "===> Copied Attachment: %s" % target_attachment.ref
except RallyRESTAPIError, details:
sys.stderr.write('ERROR: %s \n' % details)
sys.exit(2)
# Copy Tags
source_tags = source_test_case.Tags;
target_tags = list()
for source_tag in source_tags:
target_tags.append({"_ref":source_tag.ref})
target_test_case_fields = {
"FormattedID": target_test_case.FormattedID,
"Tags": target_tags
}
try:
update_response = rally.update('TestCase', target_test_case_fields)
except RallyRESTAPIError, details:
sys.stderr.write('ERROR: %s \n' % details)
sys.exit(2)
Ruby:
This Ruby script will copy all Test Cases from a Source Test Folder identified by FormattedID, to a Target Test Folder, also identified by FormattedID. It will copy all Test Steps and Attachments as well. The Target Test Folder must exist, i.e. the script will not create a Test Folder for you if the Target is not found.
The script does not associate the new Test Case to original Test Case's Work Product (i.e. Defect, User Story), or copy Discussion items, Test Case Results, or Last Build, Verdict, etc. as it is assumed the new Test Case is desired to be in a "blank" state.
For those needing to install and configure the Ruby REST Toolkit, links are here:
Developer Portal: Rally REST API for Ruby
Github
# Copyright 2002-2012 Rally Software Development Corp. All Rights Reserved.
require 'rally_api'
$my_base_url = "https://rally1.rallydev.com/slm"
$my_username = "user#company.com"
$my_password = "password"
$my_workspace = "My Workspace"
$my_project = "My Project"
$wsapi_version = "1.37"
# Test Folders
$source_test_folder_formatted_id = "TF4"
$target_test_folder_formatted_id = "TF8"
# Load (and maybe override with) my personal/private variables from a file...
my_vars= File.dirname(__FILE__) + "/my_vars.rb"
if FileTest.exist?( my_vars ) then require my_vars end
#==================== Make a connection to Rally ====================
config = {:base_url => $my_base_url}
config[:username] = $my_username
config[:password] = $my_password
config[:workspace] = $my_workspace
config[:project] = $my_project
config[:version] = $wsapi_version
#rally = RallyAPI::RallyRestJson.new(config)
begin
# Lookup source Test Folder
source_test_folder_query = RallyAPI::RallyQuery.new()
source_test_folder_query.type = :testfolder
source_test_folder_query.fetch = true
source_test_folder_query.query_string = "(FormattedID = \"" + $source_test_folder_formatted_id + "\")"
source_test_folder_result = #rally.find(source_test_folder_query)
# Lookup Target Test Folder
target_test_folder_query = RallyAPI::RallyQuery.new()
target_test_folder_query.type = :testfolder
target_test_folder_query.fetch = true
target_test_folder_query.query_string = "(FormattedID = \"" + $target_test_folder_formatted_id + "\")"
target_test_folder_result = #rally.find(target_test_folder_query)
if source_test_folder_result.total_result_count == 0
puts "Source Test Folder: " + $source_test_folder_formatted_id + "not found. Exiting."
exit
end
if target_test_folder_result.total_result_count == 0
puts "Target Test Folder: " + $target_test_folder_formatted_id + "not found. Target must exist before copying."
exit
end
source_test_folder = source_test_folder_result.first()
target_test_folder = target_test_folder_result.first()
# Populate full object for Target Test Folder
full_target_test_folder = target_test_folder.read
# Get Target Project
target_project = full_target_test_folder["Project"]
# Grab collection of Source Test Cases
source_test_cases = source_test_folder["TestCases"]
# Loop through Source Test Cases and Copy to Target
source_test_cases.each do |source_test_case|
# Get full object for Source Test Case
full_source_test_case = source_test_case.read
# Check if there's an Owner
if !full_source_test_case["Owner"].nil?
source_owner = full_source_test_case["Owner"]
else
source_owner = nil
end
# Populate field data from Source to Target
target_test_case_fields = {}
target_test_case_fields["Package"] = full_source_test_case["Package"]
target_test_case_fields["Description"] = full_source_test_case["Description"]
target_test_case_fields["Method"] = full_source_test_case["Method"]
target_test_case_fields["Name"] = full_source_test_case["Name"]
target_test_case_fields["Objective"] = full_source_test_case["Objective"]
target_test_case_fields["Owner"] = source_owner
target_test_case_fields["PostConditions"] = full_source_test_case["PostConditions"]
target_test_case_fields["PreConditions"] = full_source_test_case["PreConditions"]
target_test_case_fields["Priority"] = full_source_test_case["Priority"]
target_test_case_fields["Project"] = target_project
target_test_case_fields["Risk"] = full_source_test_case["Risk"]
target_test_case_fields["ValidationInput"] = full_source_test_case["ValidationInput"]
target_test_case_fields["ValidationExpectedResult"] = full_source_test_case["ValidationExpectedResult"]
target_test_case_fields["Tags"] = full_source_test_case["Tags"]
target_test_case_fields["TestFolder"] = target_test_folder
# Create the Target Test Case
begin
target_test_case = #rally.create(:testcase, target_test_case_fields)
puts "Test Case: #{full_source_test_case["FormattedID"]} successfully copied to #{full_target_test_folder["FormattedID"]}"
rescue => ex
puts "Test Case: #{full_source_test_case["FormattedID"]} not copied due to error"
puts ex
end
# Now Copy Test Steps
# Add Test Case Steps
source_test_case_steps = full_source_test_case["Steps"]
source_test_case_steps.each do |source_test_case_step|
full_source_step = source_test_case_step.read
target_step_fields = {}
target_step_fields["TestCase"] = target_test_case
target_step_fields["StepIndex"] = full_source_step["StepIndex"]
target_step_fields["Input"] = full_source_step["Input"]
target_step_fields["ExpectedResult"] = full_source_step["ExpectedResult"]
begin
target_test_case_step = #rally.create(:testcasestep, target_step_fields)
puts "===> Copied TestCaseStep: #{target_test_case_step["_ref"]}"
rescue => ex
puts "Test Case Step not copied due to error:"
puts ex
end
end
# Now Copy Attachments
source_attachments = full_source_test_case["Attachments"]
source_attachments.each do |source_attachment|
full_source_attachment = source_attachment.read
source_attachment_content = full_source_attachment["Content"]
full_source_attachment_content = source_attachment_content.read
# Create AttachmentContent Object for Target
target_attachment_content_fields = {}
target_attachment_content_fields["Content"] = full_source_attachment_content["Content"]
begin
target_attachment_content = #rally.create(:attachmentcontent, target_attachment_content_fields)
puts "===> Copied AttachmentContent: #{target_attachment_content["_ref"]}"
rescue => ex
puts "AttachmentContent not copied due to error:"
puts ex
end
# Now Create Attachment Container
target_attachment_fields = {}
target_attachment_fields["Name"] = full_source_attachment["Name"]
target_attachment_fields["Description"] = full_source_attachment["Description"]
target_attachment_fields["Content"] = target_attachment_content
target_attachment_fields["ContentType"] = full_source_attachment["ContentType"]
target_attachment_fields["Size"] = full_source_attachment["Size"]
target_attachment_fields["Artifact"] = target_test_case
target_attachment_fields["User"] = full_source_attachment["User"]
begin
target_attachment = #rally.create(:attachment, target_attachment_fields)
puts "===> Copied Attachment: #{target_attachment["_ref"]}"
rescue => ex
puts "Attachment not copied due to error:"
puts ex
end
end
end
end
An improvement of the same Python script that adds the following features
Test Folders (source and destination) are passed in arguments instead of hard coded
Creation is done recursively (i.e. copy Test Folders and Test Cases in it)
Test Cases are replaced if they already exist (based on the Name)
Usage of API Key to connect rather than password
Try 5 times to connect to Rally before giving up
To use it to copy TF10 to TF99 (which already exists): python copy_test_folder TF10 TF99
#!/usr/bin/env python
#################################################################################################
#
# copy_test_folder.py -- Copy all Test Cases in Source Test Folder to Target. Includes Test Steps
# and attachments. Target Test Folder must exist (i.e. the script will not
# create a new targeet Test Folder for you)
#
USAGE = """
Usage: copy_test_folder.py TF_src TF_dest
"""
#################################################################################################
# import needed python libs
import sys, os
import re
import string
import base64
from pprint import pprint
# import needed pyral libs
from pyral import Rally, rallySettings, RallyRESTAPIError
errout = sys.stderr.write
my_server = "rally1.rallydev.com"
my_user = "name#domain.com"
my_password = "<PASSWORD>"
my_workspace = "<WORKSPACE>"
my_project = "<PROJECT>"
my_key = "<API KEY>"
def connect(nb_attempts=5):
'''Connect to Rally'''
global is_connected
global rally
#Connect
if not is_connected and nb_attempts>0:
print "...Attempting to connect to Rally for project %s..." % my_project
try:
# rally = Rally(my_server, my_user, my_password, workspace=my_workspace, project=my_project)
rally = Rally(my_server, apikey=my_key, workspace=my_workspace, project=my_project)
print "Connected to Rally for project %s (%s)" % (my_project, my_workspace)
is_connected=True
rally.enableLogging('copy_test_folder.log')
#Errors during connection (attempting to connect again)
# except Exception, details:
except AttributeError:
if nb_attempts>1:
connect(nb_attempts-1)
else:
errout('Error during connection to Rally (%s)\n' % details)
exit(4)
else: pass
def copyTF(src_TF, dest_TF):
'''Copy Test Cases from one folder into another (including children Test Folders)'''
# List names and FormattedID of existing Test Cases in destination
dest_TCs = dest_TF.TestCases
existing_dest_TC = {tc.Name:tc.FormattedID for tc in dest_TCs}
# Copy Test Cases to destination folder (replace if Test Case with same name exists)
src_TCs = src_TF.TestCases
for src_TC in src_TCs:
# Create update fields for target Test Case
# Does NOT associate new Test Case to original Test Case's WorkProduct (i.e. Defect, User Story)
# Does NOT copy Discussion items - as the old Discussions are likely not desired on new Test Case
# Does NOT copy Last Build, Last Run, Last Update Date, Last Verdict as new Test Case will effectively
# be "blank" and not have any results associated to it
tcName = src_TC.Name
dest_TC_fields = {
"Package": src_TC.Package,
"Description": src_TC.Description,
"Method": src_TC.Method,
"Name": tcName,
"Objective": src_TC.Objective,
"Owner": getattr(src_TC.Owner, 'ref', None),
"PostConditions": src_TC.PostConditions,
"PreConditions": src_TC.PreConditions,
"Priority": src_TC.Priority,
"Project": src_TC.Project.ref,
"Risk": src_TC.Risk,
"ValidationInput": src_TC.ValidationInput,
"ValidationExpectedResult": src_TC.ValidationExpectedResult,
"TestFolder": dest_TF.ref,
}
# Create/Update the target test case
try:
if existing_dest_TC.has_key(tcName):
operation = "Update"
dest_TC_fields['FormattedID'] = existing_dest_TC[tcName]
dest_TC = rally.update("TestCase", dest_TC_fields)
else:
#Create
operation = "Create"
dest_TC = rally.create("TestCase", dest_TC_fields)
message = operation + "d Source Test Case: " + src_TC.FormattedID + \
" To: " + dest_TF.FormattedID + ": " + dest_TF.Name + \
": " + dest_TC.FormattedID
print message
except RallyRESTAPIError, details:
sys.stderr.write('ERROR: %s \n' % details)
sys.exit(2)
# Copy Test Steps
#Cleared-up all Test Steps in destination if Test Case is updated
if operation == "Update":
dest_TC_steps = dest_TC.Steps
for dest_step in dest_TC_steps:
rally.delete('TestCaseStep', dest_step.oid)
print "===> Cleared all Test Steps"
# Add Test Case Steps
src_TC_steps = src_TC.Steps
for src_step in src_TC_steps:
target_step_fields = {
"TestCase" : dest_TC.ref,
"StepIndex" : src_step.StepIndex,
"Input" : src_step.Input,
"ExpectedResult" : src_step.ExpectedResult
}
dest_TC_step = rally.put('TestCaseStep', target_step_fields)
print "===> Copied TestCaseStep: %s OID: %s" % (dest_TC_step.StepIndex, dest_TC_step.oid)
# Copy Attachments
source_attachments = rally.getAttachments(src_TC)
for source_attachment in source_attachments:
# First copy the content
source_attachment_content = source_attachment.Content
target_attachment_content_fields = {
"Content": base64.encodestring(source_attachment_content)
}
try:
target_attachment_content = rally.put('AttachmentContent', target_attachment_content_fields)
print "===> Copied AttachmentContent: %s" % target_attachment_content.ref
except RallyRESTAPIError, details:
sys.stderr.write('ERROR: %s \n' % details)
sys.exit(2)
# Next copy the attachment object
target_attachment_fields = {
"Name": source_attachment.Name,
"Description": source_attachment.Description,
"Content": target_attachment_content.ref,
"ContentType": source_attachment.ContentType,
"Size": source_attachment.Size,
"Artifact": dest_TC.ref,
"User": source_attachment.User.ref
}
try:
target_attachment = rally.put('Attachment', target_attachment_fields)
print "===> Copied Attachment: %s" % target_attachment.ref
except RallyRESTAPIError, details:
sys.stderr.write('ERROR: %s \n' % details)
sys.exit(2)
# Copy Tags
source_tags = src_TC.Tags;
target_tags = list()
for source_tag in source_tags:
target_tags.append({"_ref":source_tag.ref})
dest_TC_fields = {
"FormattedID": dest_TC.FormattedID,
"Tags": target_tags
}
try:
update_response = rally.update('TestCase', dest_TC_fields)
except RallyRESTAPIError, details:
sys.stderr.write('ERROR: %s \n' % details)
sys.exit(2)
# Recursive call for each child Test Folder (after creating it)
src_children_TFs = src_TF.Children
dest_children_TFs = dest_TF.Children
existing_dest_TF = {tf.Name:tf for tf in dest_children_TFs}
for src_child_TF in src_children_TFs:
tfName = src_child_TF.Name
# Create Test Folder if needed
if existing_dest_TF.has_key(tfName):
dest_child_TF = existing_dest_TF[tfName]
else:
target_TF_fields = {
'Name': tfName,
'Parent': dest_TF.ref
}
dest_child_TF = rally.put('TestFolder', target_TF_fields)
print "Created Test folder %s (%s)" % (tfName, dest_child_TF.FormattedID)
# Copy Test Cases of this folder
copyTF(src_child_TF, dest_child_TF)
if '__main__' in __name__:
# Get source and destination test folders
src_TF_formatted_id = sys.argv[1]
dest_TF_formatted_id = sys.argv[2]
# Connect to Rally
global is_connected
is_connected = False
connect(5)
# Query for source and target test folders
global rally
src_TF_response = rally.get('TestFolder', fetch=True, query='FormattedID = %s' % src_TF_formatted_id)
dest_TF_response = rally.get('TestFolder', fetch=True, query='FormattedID = %s' % dest_TF_formatted_id)
# Check to make sure folders exist
if src_TF_response.resultCount == 0:
errout('No Source Test Folder Found matching Formatted ID: %s\n' % (src_TF_formatted_id))
sys.exit(4)
if dest_TF_response.resultCount == 0:
errout('No Target Test Folder Found matching Formatted ID: %s\n. Target Test Folder must be created before copying.' % (dest_TF_formatted_id))
sys.exit(4)
# Get Objects of source and target Test Folders
src_TF = src_TF_response.next()
dest_TF = dest_TF_response.next()
# Copy file
copyTF(src_TF, dest_TF)

Categories

Resources