diff --git a/PKG-INFO b/PKG-INFO index 7ad472e..478fd5d 100644 --- a/PKG-INFO +++ b/PKG-INFO @@ -1,21 +1,21 @@ Metadata-Version: 1.1 Name: pyral -Version: 1.4.2 -Summary: Python toolkit for Agile Central (Rally) REST API +Version: 1.5.0 +Summary: Python toolkit for Rally REST API Home-page: https://github.com/RallyTools/RallyRestToolkitForPython -Author: Kip Lehman (CA Technologies, AgileCentral Business Unit) +Author: Kip Lehman (Broadcom / Rally Software) License: BSD -Download-URL: https://github.com/RallyTools/RallyRestToolkitForPython/raw/master/dists/pyral-1.4.2.zip +Download-URL: https://github.com/RallyTools/RallyRestToolkitForPython/raw/master/dists/pyral-1.5.0.zip Description: This is the README file for pyral, a package implementing a - Pythonic interface to the Agile Central (formerly Rally) REST API + Pythonic interface to the Rally REST API Description ----------- The Rally REST Tookit for Python consists of a package that provides a means to interact with entities and artifacts in your Rally subscription. The full - spectrum of CRUD operations is available in your Agile Central (Rally) Workspaces and Projects, - providing of course that your Agile Central (Rally) subscription credentials enable you to perform + spectrum of CRUD operations is available in your Rally Workspaces and Projects, + providing of course that your Rally subscription credentials enable you to perform those operations. The toolkit insulates the package user from having to make multiple requests to obtain the full set of qualifying items by performing any subsequent "page" requests automatically. @@ -25,10 +25,10 @@ Description: This is the README file for pyral, a package implementing a Requirements ------------ - The pyral package requires the use of Kenneth Reitz's requests package using version 2.8.0 or better. + The pyral package requires the use of Kenneth Reitz's requests package using version 2.12.0 or better. As of requests version 2.0.0, there is support for HTTPS over HTTP proxy via the CONNECT request. The requests package can be found via the Python Package Index site (http://pypi/python.org/index). - The most recent release of pyral (1.4.2) has been tested with requests 2.19.1. + The most recent release of pyral (1.5.0) has been tested with requests 2.21.0. The six module is also required. @@ -71,9 +71,10 @@ Description: This is the README file for pyral, a package implementing a and http://pythonhosted.org/pyral -Keywords: rally,agilecentral,api +Keywords: rally,api Requires: six -Requires: requests>=2.12.5 +#Requires: requests>=2.12.5 +Requires: requests>=2.21.0 Platform: any Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: Web Environment @@ -81,9 +82,9 @@ Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 Classifier: Topic :: Internet :: WWW/HTTP Classifier: Topic :: Software Development :: Libraries diff --git a/current-issues b/current-issues new file mode 100644 index 0000000..4ce5648 --- /dev/null +++ b/current-issues @@ -0,0 +1,62 @@ + class importance repro'ed status +#138 Impossible to retrieve attachments Bug High yes Fixed in 1.5.0 + opened on Apr 20, 2018 by jfthuong + +#150 getAttachment*() functionality generally broken on pyral 1.4.2 and Python 3.6 Bug High yes Fixed in 1.5.0 + opened on Dec 6, 2018 by pgacv2 + same as #138 + + +#151 post() failure 'No classFor item for EngrFeature' Bug High yes + opened on Apr 11, 2019 by NCarteazy + +#158 Keyerror while hydrating custom PortfolioItem after a post() or put() Bug Med yes + opened on Oct 28, 2019 by Sssnek1 + +#147 Can't parse portfolio_item Bug High no + opened on Aug 24, 2018 by tsal + probably needs doc update to make clear what works + for a PortfolioItem sub-type + +#157 rally.get() returns wrong results for a query with char "&" Bug Med yes + opened on Aug 28, 2019 by Kimudai94 + +#154 Could not get Release... Bug Med no + opened on Jul 19, 2019 by KapsGit + +#134 LDAP username vs. authentication username Feature Low has PR that could be accepted + opened on Jan 29, 2018 by carlsonp + +#143 Unable to specify path to certfile in Rally() kwarg verify_ssl_cert Feature Med + opened on Jul 5, 2018 by Meussdorffer + +#169 Error trying to add a feature to collection (milestone) Bug Low + opened 2 days ago by reddevil311 + Milestone has a collection of Artifacts, Feature is a derivate of Artifact + +#167 get User call doesn't fetch all user fields using pyral Bug? Low no + opened on Jun 17 by Pravesh-dot-com + +#164 Workspace name includes a space Bug? Low another customer has no problem with this + opened on May 21 by johnhaprian (user error?) + +#159 How to call the service with an URL Bug? Low no + opened on Jan 20 by KapsGit + does a get for a PortfolioItem subtype and wants to + get a ref attribute's sub-attribute + +#152 Fails Initializing Rally when performed more than once in an application UserBad Low yes (have an entity PortfolioItem/Project) + opened on Jul 7, 2019 by jonvadney + +#149 Cannot add custom multi-select field to Feature Bug Low no + opened on Nov 19, 2018 by damon-11 + +#144 No apparent way to fetch data about closed projects NonBug Low yes + opened on Jul 11, 2018 by pgacv2 + +#137 addCollectionItems - TypeError: 'Tag' object is not iterable UserError Med + opened on Mar 23, 2018 by ssah-blr + used tag instance as arg to addCollectionItems when shoudl be a list of tags + +#110 Cannot get all attachments if several attachments have the same name --- Low + opened on Jan 29, 2017 by jfthuong diff --git a/doc/source/conf.py b/doc/source/conf.py index 54200d6..c6ba2b3 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -41,16 +41,16 @@ # General information about the project. project = 'pyral' -copyright = 'CA Technologies 2015-2017, Rally Software Development 2010-2015' +copyright = 'Broadcom 2018-2020, CA Technologies 2015-2017, Rally Software Development 2010-2015' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. -version = '1.4.2' +version = '1.5.0' # The full version, including alpha/beta/rc tags. -release = '1.4.2' +release = '1.5.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -188,7 +188,7 @@ # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ - ('index', 'pyral.tex', 'pyral Documentation', 'CA Technologies', 'manual'), + ('index', 'pyral.tex', 'pyral Documentation', 'Broadcom', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of @@ -220,5 +220,5 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - ('index', 'pyral', 'pyral Documentation', ['CA Technologies'], 1) + ('index', 'pyral', 'pyral Documentation', ['Broadcom'], 1) ] diff --git a/examples/addtags.py b/examples/addtags.py index f6dc614..9997765 100644 --- a/examples/addtags.py +++ b/examples/addtags.py @@ -67,7 +67,7 @@ def main(args): query="FormattedID = %s" % story_id, server_ping=False, isolated_workspace=True, instance=True) #print(story.details()) - print "story tags after deleting the '%s' Tag" % (droppable_tags[0].Name) + print("story tags after deleting the '%s' Tag" % (droppable_tags[0].Name)) story_tags = [str(tag.Name) for tag in story.Tags] print(story_tags) diff --git a/examples/allowedValues.py b/examples/allowedValues.py index 297be6e..bdfe1fa 100644 --- a/examples/allowedValues.py +++ b/examples/allowedValues.py @@ -8,7 +8,7 @@ # NOTE: Be aware as of Oct 2016 that the only attributes that have _meaningful_ values # are those of type RATING, STATE and some whose type STRING (but not all whose # type is STRING). -# There are numerous standard AgileCentral entity attributes of type STRING +# There are numerous standard Rally entity attributes of type STRING # whose allowedValues reference url returns a True value upon hitting the endpoint. # As it that is the only value, it's not really useful as compared to the other # attributes that do have multiple allowed values. diff --git a/examples/crtask.py b/examples/crtask.py index eca1b87..f880ffa 100644 --- a/examples/crtask.py +++ b/examples/crtask.py @@ -30,7 +30,7 @@ def main(args): if apikey: rally = Rally(server, apikey=apikey, workspace=workspace, project=project) else: - rally = Rally(server, user=username, password=password, workspace=workspace, project=project) + rally = Rally(server, user=user, password=password, workspace=workspace, project=project) rally.enableLogging("rally.history.crtask") # For a task: Workspace, Project, WorkProduct, Name, State, TaskIndex are required; diff --git a/examples/jpwhiny.py b/examples/jpwhiny.py new file mode 100644 index 0000000..e1849f1 --- /dev/null +++ b/examples/jpwhiny.py @@ -0,0 +1,55 @@ +import sys, os + +from pyral import Rally, rallyWorkset, RallyRESTAPIError + +server = 'demo-west.rallydev.com' +username = 'jpkole@rallydev.com' +authy = 'Kanban!!' + +foozy_workspace = 'Workspace #1' +std_project = 'Online Store' + +rally = Rally(server, username, authy, workspace=foozy_workspace, project=std_project) +#print(rally) + +#my_workspace = 'zJP-Test-WS-1' +#valid_project = 'Sample Project' +#rally2 = Rally(server, username, authy, workspace=my_workspace, project=valid_project) +#print(rally2) + +#for my_workspace in ['zJP-Test-WS-1']: +# print ("Connecting to Workspace: '%s'" % my_workspace) + ##print ("Connecting to Rally ... not specifying a Workspace/Project combo") + #try: + # #rally = Rally(server, username, authy, workspace=my_workspace, project="Online Store") + # rally = Rally(server, username, authy, workspace=my_workspace) + # #rally = Rally(server, username, authy) + #except RallyRESTAPIError as ex: + # print(ex) + # sys.exit(1) + +# current_workspace = rally.getWorkspace() +# print(current_workspace.Name) +# +# projects = rally.getProjects() +# for project in projects: +# print(" %s" % project.Name) +# +# print ("\tsuccess!") +# #rally.setProject("Online Store") +# +# break +# +# +all_workspaces = rally.getWorkspaces() +print(" All Workspaces ...") +for wksp in all_workspaces: + print(wksp.Name) + +#wacky_workspace_1 = 'Workspace #1' +#rally.setWorkspace(wacky_workspace_1) +#current_workspace = rally.getWorkspace() +#print("\n\nset the workspace to %s" % current_workspace.Name) +#wacky_projects = rally.getProjects() +#for proj in wacky_projects: +# print(" %s" % proj.Name) \ No newline at end of file diff --git a/examples/periscope.py b/examples/periscope.py index a21be06..0703732 100644 --- a/examples/periscope.py +++ b/examples/periscope.py @@ -17,7 +17,8 @@ def main(args): args = [arg for arg in args if arg not in options] server, username, password, apikey, workspace, project = rallyWorkset(options) if apikey: - rally = Rally(server, apikey=apikey, workspace=workspace, project=project) + #rally = Rally(server, apikey=apikey, workspace=workspace, project=project) + rally = Rally(server, apikey=apikey) else: rally = Rally(server, user=username, password=password, workspace=workspace, project=project) rally.enableLogging('rally.hist.periscope') # name of file for logging content diff --git a/examples/typedef.py b/examples/typedef.py index cffcf07..0d3f848 100644 --- a/examples/typedef.py +++ b/examples/typedef.py @@ -74,10 +74,12 @@ def main(args): server, username, password, apikey, workspace, project = rallyWorkset(options) print(" ".join(["|%s|" % item for item in [server, username, password, apikey, workspace, project]])) try: - rally = Rally(server, username, password, apikey=apikey, workspace=workspace, server_ping=False) + rally = Rally(server, username, password, apikey=apikey, server_ping=False) except Exception as ex: errout(str(ex.args[0])) sys.exit(1) + + rally.setWorkspace(workspace) sub_name = rally.subscriptionName() print("Subscription Name: %s" % sub_name) @@ -94,8 +96,8 @@ def main(args): print("") print("-" * 64) print("") - for ix, ancestor in enumerate(typedef.inheritanceChain()): - print("%s %s" % (" " * (ix*4), ancestor)) +# for ix, ancestor in enumerate(typedef.inheritanceChain()): +# print("%s %s" % (" " * (ix*4), ancestor)) ################################################################################################# @@ -130,7 +132,7 @@ def showAttributes(rally, target, attributes): tank.append(" ... %d more values not shown" % (len(allowed_values) - av_limit)) for item in required + optional: - print(item.encode('utf-8')) + print(item) ################################################################################################# ################################################################################################# diff --git a/pyral/__init__.py b/pyral/__init__.py index 0f8d247..d9080c1 100644 --- a/pyral/__init__.py +++ b/pyral/__init__.py @@ -1,7 +1,6 @@ __version__ = (1, 4, 2) from .config import rallySettings, rallyWorkset from .restapi import Rally, RallyRESTAPIError, RallyUrlBuilder -from .restapi import AgileCentral, AgileCentralRESTAPIError, AgileCentralUrlBuilder +from .restapi import AgileCentral from .rallyresp import RallyRESTResponse -from .rallyresp import AgileCentralRESTResponse diff --git a/pyral/config.py b/pyral/config.py index 0c92a31..41d4ccc 100644 --- a/pyral/config.py +++ b/pyral/config.py @@ -34,12 +34,12 @@ { #'X-RallyIntegrationName' : 'Python toolkit for Rally REST API', # although syntactically this is the more correct 'X-RallyIntegrationName' : 'Rally REST API toolkit for Python', # this matches the format of the other language toolkits - 'X-RallyIntegrationVendor' : 'CA Technologies', + 'X-RallyIntegrationVendor' : 'Broadcom / Rally', 'X-RallyIntegrationVersion' : '%s.%s.%s' % __version__, 'X-RallyIntegrationLibrary' : 'pyral-%s.%s.%s' % __version__, 'X-RallyIntegrationPlatform' : 'Python %s' % platform.python_version(), 'X-RallyIntegrationOS' : platform.platform(), - 'User-Agent' : 'Pyral Agile Central WebServices Agent', + 'User-Agent' : 'Pyral Rally WebServices Agent', 'Content-Type' : 'application/json', 'Accept-Encoding' : 'gzip' } @@ -52,10 +52,10 @@ def timestamp(): ################################################################################################## -CONFIG_SETTING_PATT = re.compile('^([A-Z]+)\s*=\s*(.+)$') -RALLY_ARG_SETTING_PATT1 = re.compile('^--(rally[SUPW][a-z]+)=(.+)\s*$') -RALLY_ARG_SETTING_PATT2 = re.compile('^--([ASUPWasupw][a-z]+)=(.+)\s*$') -RALLY_CONFIG_FILE_PATT = re.compile('^--(cfg|conf|config|rallyConfig)=(\S+)$') +CONFIG_SETTING_PATT = re.compile(r'^([A-Z]+)\s*=\s*(.+)$') +RALLY_ARG_SETTING_PATT1 = re.compile(r'^--(rally[SUPW][a-z]+)=(.+)\s*$') +RALLY_ARG_SETTING_PATT2 = re.compile(r'^--([ASUPWasupw][a-z]+)=(.+)\s*$') +RALLY_CONFIG_FILE_PATT = re.compile(r'^--(cfg|conf|config|rallyConfig)=(\S+)$') TRUTHY_VALUES = ['t', 'true', 'y', 'yes', '1'] FALSEY_VALUES = ['f', 'false', 'n', 'no', '0'] diff --git a/pyral/context.py b/pyral/context.py index 2f5b503..ad27b0c 100644 --- a/pyral/context.py +++ b/pyral/context.py @@ -751,7 +751,7 @@ def identifyContext(self, **kwargs): if workspace not in eligible_workspace_names: problem = 'Workspace specified: "%s" not accessible with current credentials' - raise RallyRESTAPIError(problem % workspace.Name) + raise RallyRESTAPIError(problem % workspace) if workspace not in self._workspaces and self._inflated != 'wide': ec_kwargs = {'workspace' : workspace} self._establishContext(ec_kwargs) @@ -775,7 +775,7 @@ def identifyContext(self, **kwargs): proj_path_leaf = self._findMultiElementPathToProject(project) prj_ref = proj_path_leaf.ref project = proj_path_leaf.Name - elif re.search('project/\d+$', project): + elif re.search(r'project/\d+$', project): prj_ref = project else: problem = 'Project specified: "%s" (in workspace: "%s") not accessible with current credentials' % \ diff --git a/pyral/entity.py b/pyral/entity.py index c8f2ca9..41d3835 100644 --- a/pyral/entity.py +++ b/pyral/entity.py @@ -844,7 +844,7 @@ def resolveAllowedValues(self, context, getCollection): by chasing the collection URL left from initialization, does this method issue a call to resolve the collection URL via the getCollection callable parm. The need to use getCollection is based on whether the AllowedValues value - is a string that matches the regex '^https?://.*/attributedefinition/-\d+/AllowedValues' + is a string that matches the regex r'^https?://.*/attributedefinition/-\\d+/AllowedValues' """ ## ## print("in resolveAllowedValues for |%s| is a %s" % (self.Name, type(self.Name))) @@ -857,7 +857,7 @@ def resolveAllowedValues(self, context, getCollection): return True if type(self.AllowedValues) != str: #previously was != bytes return True - std_av_ref_pattern = '^https?://.*/\w+/-?\d+/AllowedValues$' + std_av_ref_pattern = r'^https?://.*/\w+/-?\d+/AllowedValues$' mo = re.match(std_av_ref_pattern, self.AllowedValues) if not mo: anomaly = "Standard AllowedValues ref pattern |%s| not matched by candidate |%s|" % \ diff --git a/pyral/hydrate.py b/pyral/hydrate.py index 7e17f77..5b50625 100644 --- a/pyral/hydrate.py +++ b/pyral/hydrate.py @@ -17,7 +17,8 @@ import six six.PY2 and sys.setdefaultencoding('UTF8') # not required in python 3 -from .entity import classFor, VERSION_ATTRIBUTES, MINIMAL_ATTRIBUTES, PORTFOLIO_ITEM_SUB_TYPES +from .entity import classFor, getSchemaItem, \ + VERSION_ATTRIBUTES, MINIMAL_ATTRIBUTES, PORTFOLIO_ITEM_SUB_TYPES ################################################################################################## @@ -107,6 +108,11 @@ def _basicInstance(self, item): bonked = False except KeyError as e: raise + else: + wksp_oid = item['Workspace']['_ref'].split('/')[-1] + workspace = (self.context.workspace, f'workspace/{wksp_oid}') + si = getSchemaItem(workspace, str(itemType)) + print(si) if bonked: print("No classFor item for |%s|" % itemType) raise KeyError(itemType) diff --git a/pyral/query_builder.py b/pyral/query_builder.py index fd8bf0c..a47d059 100644 --- a/pyral/query_builder.py +++ b/pyral/query_builder.py @@ -108,17 +108,15 @@ def augmentScoping(self, augments): def beautifyResponse(self): self.pretty = True -AgileCentralUrlBuilder = RallyUrlBuilder - ################################################################################################## class RallyQueryFormatter(object): CONJUNCTIONS = ['and', 'AND', 'or', 'OR'] - CONJUNCTION_PATT = re.compile('\s+(AND|OR)\s+', re.I | re.M) + CONJUNCTION_PATT = re.compile(r'\s+(AND|OR)\s+', re.I | re.M) ATTR_IDENTIFIER = r'[\w\.]+[a-zA-Z0-9]' RELATIONSHIP = r'=|!=|>|<|>=|<=|contains|!contains' ATTR_VALUE = r'"[^"]+"|[^ ]+' - QUERY_CRITERIA_PATTERN = re.compile('^(%s) (%s) (%s)$' % (ATTR_IDENTIFIER, RELATIONSHIP, ATTR_VALUE), re.M) + QUERY_CRITERIA_PATTERN = re.compile(r'^(%s) (%s) (%s)$' % (ATTR_IDENTIFIER, RELATIONSHIP, ATTR_VALUE), re.M) @staticmethod def parenGroups(criteria): @@ -232,9 +230,9 @@ def validatePartsSyntax(parts): attr_ident = r'[\w\.]+[a-zA-Z0-9]' relationship = r'=|!=|>|<|>=|<=|contains|!contains' attr_value = r'"[^"]+"|[^" ]+' - criteria_pattern = re.compile('^(%s) (%s) (%s)$' % (attr_ident, relationship, attr_value)) - quoted_value_pattern = re.compile('^(%s) (%s) ("[^"]+")$' % (attr_ident, relationship)) - unquoted_value_pattern = re.compile('^(%s) (%s) ([^"].+[^"])$' % (attr_ident, relationship)) + criteria_pattern = re.compile(r'^(%s) (%s) (%s)$' % (attr_ident, relationship, attr_value)) + quoted_value_pattern = re.compile(r'^(%s) (%s) ("[^"]+")$' % (attr_ident, relationship)) + unquoted_value_pattern = re.compile(r'^(%s) (%s) ([^"].+[^"])$' % (attr_ident, relationship)) valid_parts = [] front = "" @@ -260,6 +258,4 @@ def validatePartsSyntax(parts): return valid_parts -AgileCentralQueryFormatter = RallyQueryFormatter - ################################################################################################## diff --git a/pyral/rallyresp.py b/pyral/rallyresp.py index 4d3333f..0c721a7 100644 --- a/pyral/rallyresp.py +++ b/pyral/rallyresp.py @@ -21,8 +21,7 @@ from .hydrate import EntityHydrator from .cargotruck import CargoTruck -__all__ = ['RallyRESTResponse', 'ErrorResponse', 'RallyResponseError', - 'AgileCentralRESTResponse', 'AgileCentralResponseError'] +__all__ = ['RallyRESTResponse', 'ErrorResponse', 'RallyResponseError'] ################################################################################################## @@ -31,8 +30,7 @@ ################################################################################################## class RallyResponseError(Exception): pass -AgileCentralResponseError = RallyResponseError - + class ErrorResponse(object): SECURITY_ERROR = 'An Authentication object was not found in the SecurityContext' @@ -83,7 +81,7 @@ def __init__(self, session, context, request, response, hydration, limit, **kwar ## print("RRR.init request_path_elements = %s " % repr(request_path_elements)) ## self.target = request_path_elements[-1] - if re.match('^\d+$', self.target): + if re.match(r'^\d+$', self.target): self.target = request_path_elements[-2] # this happens on request for RevisionHistory if self.target.lower() == 'revisionhistory': self.target = 'RevisionHistory' @@ -336,7 +334,7 @@ def __next__(self): item = self.page[self._curIndex] except: exception_type, value, traceback = sys.exc_info() - exc_name = re.search("'exceptions\.(.+)'", str(exception_type)).group(1) + exc_name = re.search(r'\'exceptions\.(.+)\'', str(exception_type)).group(1) problem = '%s: %s for response from request to get next data page for %s' % (exc_name, value, self.target) errout("ERROR: %s\n" % problem) if item is None: @@ -411,7 +409,7 @@ def __retrieveNextPage(self): return chapter self.startIndex += self.pageSize - nextPageUrl = re.sub('&start=\d+', '&start=%s' % self.startIndex, self.resource) + nextPageUrl = re.sub(r'&start=\d+', '&start=%s' % self.startIndex, self.resource) if not nextPageUrl.startswith('http'): nextPageUrl = '%s/%s' % (self.context.serviceURL(), nextPageUrl) ## @@ -435,7 +433,7 @@ def __retrievePages(self): """ Use self._served, self._servable, self.resource, self.max_threads, self.pageSize and self.startIndex to come up with suitable thread count to grab the - next group of pages from AgileCentral. + next group of pages from Rally. There will be a thread per page still to be retrieved (including the ending partial page) up to self.max_threads. For the number of threads that will actually be used, populate a page_urls list @@ -452,7 +450,7 @@ def __retrievePages(self): full, leftovers = divmod(items_remaining, self.pageSize) num_threads = full + (1 if leftovers else 0) stixes = [i+1 for i in range(num_threads)] - page_urls = [re.sub('&start=\d+', '&start=%s' % (self.startIndex + (i * self.pageSize)), self.resource) + page_urls = [re.sub(r'&start=\d+', '&start=%s' % (self.startIndex + (i * self.pageSize)), self.resource) for i in stixes] success = False @@ -504,6 +502,5 @@ def __repr__(self): (self.request_type, self.resultCount, self.content['QueryResult']['Results']) return "%s %s" % (self.status_code, blurb) -AgileCentralRESTResponse = RallyRESTResponse ################################################################################################## diff --git a/pyral/restapi.py b/pyral/restapi.py index f23f57c..79badfc 100644 --- a/pyral/restapi.py +++ b/pyral/restapi.py @@ -1,11 +1,11 @@ -#!/usr/local/bin/python3.5 +#!/usr/local/bin/python3.7 ################################################################################################### # # pyral.restapi - Python Rally REST API module -# round 16 moar movement towards parallel Rally/AgileCentral name access foar all the things +# round 17 strip out AgileCentral naming except for synonym to Rally class # notable dependencies: -# requests v2.12.5 or better +# requests v2.21.0 or better # six # ################################################################################################### @@ -57,9 +57,7 @@ def warning(message): ################################################################################################### class RallyRESTAPIError(Exception): pass -AgileCentralRESTAPIError = RallyRESTAPIError class RallyAttributeNameError(Exception): pass -AgileCentralAttributeNameError = RallyAttributeNameError # # define a couple of entry point functions for use by other pkg modules and import the modules @@ -141,9 +139,8 @@ def getCollection(context, collection_url, **kwargs): from .context import RallyContext, RallyContextHelper from .entity import validRallyType, DomainObject from .query_builder import RallyUrlBuilder -from .query_builder import AgileCentralUrlBuilder -__all__ = ["Rally", "getResourceByOID", "getCollection", "hydrateAnInstance", "RallyUrlBuilder", "AgileCentralUrlBuilder"] +__all__ = ["Rally", "getResourceByOID", "getCollection", "hydrateAnInstance", "RallyUrlBuilder"] def _createShellInstance(context, entity_name, item_name, item_ref): @@ -799,7 +796,7 @@ def _greased(self, item_data): for value in item_data[attr_name]: # is value like "someentityname/34223214" ? if (type(value) == str or type(value) == bytes) and '/' in value \ - and re.match('^\d+$', value.split('/')[-1]): + and re.match(r'^\d+$', value.split('/')[-1]): obj_list.append({"_ref" : value}) # transform to a dict instance elif issubclass(value.__class__, DomainObject): obj_list.append({"_ref" : value.ref}) # put the ref in a dict instance @@ -1112,7 +1109,7 @@ def delete(self, entityName, itemIdent, workspace='current', project='current', # guess at whether itemIdent is an ObjectID or FormattedID via # regex matching (all digits or 1-2 upcase chars + digits) objectID = itemIdent # at first assume itemIdent is the ObjectID - if re.match('^[A-Z]{1,2}\d+$', str(itemIdent)): + if re.match(r'^[A-Z]{1,2}\d+$', str(itemIdent)): fmtIdQuery = 'FormattedID = "%s"' % itemIdent response = self.get(entityName, fetch="ObjectID", query=fmtIdQuery, workspace=workspace, project=project) @@ -1561,7 +1558,7 @@ def addAttachment(self, artifact, filename, mime_type='text/plain'): contents of filename into Rally and associate that Attachment with the Artifact. Upon the successful creation of the Attachment and linkage to the artifact, - return an instance of the succesfully added Attachment. + return an instance of the successfully added Attachment. Exceptions are raised for other error conditions, such as the filename identified by the filename parm not existing, or not being a file, or the attachment file exceeding the maximum allowed size, or failure @@ -1696,8 +1693,7 @@ def getAttachment(self, artifact, filename): if response.errors or response.resultCount != 1: return None att_content = response.next() - #att.Content = base64.decodestring(att_content.Content) # maybe further txfm to Unicode ? - att.Content = base64.decodebytes(att_content.Content) # maybe further txfm to Unicode ? + att.Content = base64.b64decode(att_content.Content) # contrib by jfthuong return att @@ -1739,7 +1735,11 @@ def deleteAttachment(self, artifact, filename): # get the target Attachment and the associated AttachmentContent item attachment = hits.pop(0) - if attachment.Content and attachment.Content.oid: + if isinstance(attachment.Content, (bytes, bytearray)): + # have to query for the attachment again so that when we access attachment.Content the + # Content attribute is an instance of AttachmentContent (with the attendant oid attribute) + attachment = self.get('Attachment', query=f'ObjectID = {attachment.oid}', instance=True) + if attachment and attachment.Content and attachment.Content.oid: success = self.delete('AttachmentContent', attachment.Content.oid, project=None) if not success: print("ERROR: Unable to delete AttachmentContent item for %s" % attachment.Name) @@ -1751,7 +1751,7 @@ def deleteAttachment(self, artifact, filename): return False remaining_attachments = [att for att in current_attachments if att.ref != attachment.ref] att_refs = [dict(_ref=str(att.ref)) for att in remaining_attachments] - artifact_info = { 'ObjectID' : artifact.ObjectID, + artifact_info = { 'ObjectID' : artifact.oid, 'Attachments' : att_refs, } updated = self.update(art_type, artifact_info, project=None) @@ -1851,9 +1851,9 @@ def _rankTo(self, target_artifact, location): def _postRankRequest(self, target_artifact, resource, update_item): """ - Given an AgileCentral target Artifact and a resource URI (sans the self.service_url prefix) + Given an Rally target Artifact and a resource URI (sans the self.service_url prefix) and a dict that serves as a "container" for the target item's _ref value, - obtain the security token we need to post to AgileCentral, construct the + obtain the security token we need to post to Rally, construct the full url along with the query string containing the workspace ref and the security token. POST to the resource supplying the update_item "container" and catch any non success status code returned from the operation. @@ -1891,6 +1891,6 @@ def _ensureRankItemSanity(self, target_artifact, reference_artifact=None): return target_artifact.__class__.__name__.lower() -AgileCentral = Rally +AgileCentral = Rally # for backwards compatibility sake #################################################################################################### diff --git a/test/alpine-snow-glen-plake-quote.png b/test/alpine-snow-glen-plake-quote.png new file mode 100644 index 0000000..83c4d8d Binary files /dev/null and b/test/alpine-snow-glen-plake-quote.png differ diff --git a/test/test_allowed_values.py b/test/test_allowed_values.py index d498686..9544f1d 100644 --- a/test/test_allowed_values.py +++ b/test/test_allowed_values.py @@ -11,7 +11,7 @@ ################################################################################################## -from rally_targets import AGICEN, AGICEN_USER, AGICEN_PSWD +from rally_targets import RALLY, RALLY_USER, RALLY_PSWD from rally_targets import API_KEY from rally_targets import LARGE_WORKSPACE, LARGE_PROJECT_TREE_BASE @@ -23,7 +23,7 @@ def test_getAllowedValues_query(): request the allowed value information for the State field of the Defect entity and request the allowed value information for the PrimaryColor field of the Defect entity. """ - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD) avs = rally.getAllowedValues('Defect', 'State') assert len(avs) > 0 assert len(avs) >= 4 @@ -53,7 +53,7 @@ def test_getAllowedValues_for_UserStory_Milestone(): with the same sort of semantic that are excluded from chasing the COLLECTION url and returning some list of values. (like, Changesets, Discussions, Tags, etc.) """ - rally = Rally(server=AGICEN, apikey=API_KEY) + rally = Rally(server=RALLY, apikey=API_KEY) avs = rally.getAllowedValues('Story', 'Milestones') assert avs == [True] @@ -69,7 +69,7 @@ def test_getAllowedValues_for_UserStory_Milestone(): def test_getAllowedValues_for_custom_collections_field_Defect(): - rally = Rally(server=AGICEN, apikey=API_KEY, workspace='Rally', project='Rally') + rally = Rally(server=RALLY, apikey=API_KEY, workspace='Rally', project='Rally') avs = rally.getAllowedValues('Defect', 'MobileOS') assert len(avs) > 0 target_value = 'Android' diff --git a/test/test_attachments.py b/test/test_attachments.py index 4fdb081..438a2bd 100644 --- a/test/test_attachments.py +++ b/test/test_attachments.py @@ -6,15 +6,16 @@ import pyral from pyral import Rally +from pyral.config import timestamp RallyRESTAPIError = pyral.context.RallyRESTAPIError ################################################################################################## -from rally_targets import AGICEN, AGICEN_USER, AGICEN_PSWD +from rally_targets import RALLY, RALLY_USER, RALLY_PSWD, API_KEY from rally_targets import DEFAULT_WORKSPACE, DEFAULT_PROJECT -EXAMPLE_ATTACHMENT_CONTENT = "The quck brown fox eluded the lumbering sloth\n" +EXAMPLE_ATTACHMENT_CONTENT = "The quick brown fox eluded the lumbering sloth\n" ################################################################################################## @@ -36,50 +37,70 @@ def retrieveAttachment(rally, artifact, attachmentFileName): def test_add_attachment(): """ """ - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD) - # find a Project with some US artifacts - # pick one with no attachments + #rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD) + # find a Project with some US artifacts, pick one with no attachments + target_workspace = 'Yeti Rally Workspace' + target_project = 'Anti-Cyclone' + target_story = 'US6099' + target_attachment_file = 'test/alpine-snow-glen-plake-quote.png' + attachment_name = os.path.basename(target_attachment_file) + attachment_type = 'image/png' + rally = Rally(server=RALLY, user=RALLY_USER, apikey=API_KEY, workspace=target_workspace, project=target_project) # create an attachment file (or choose a smallish file with a commonly used suffix) # create the attachment in Rally and link it to the US artifact - wksp = rally.getWorkspace() - assert wksp.Name == DEFAULT_WORKSPACE + assert wksp.Name == target_workspace response = rally.get('Project', fetch=False, limit=10) assert response != None assert response.status_code == 200 - proj = rally.getProject() # proj.Name == DEFAULT_PROJECT - assert proj.Name == DEFAULT_PROJECT + proj = rally.getProject() # proj.Name == target_project + assert proj.Name == target_project #response = rally.get("UserStory", fetch="FormattedID,Name,Attachments") #for story in response: # print "%s %-48.48s %d" % (story.FormattedID, story.Name, len(story.Attachments)) - candidate_story = "US1" # was "US96" in trial - response = rally.get("UserStory", fetch="FormattedID,Name,Attachments", - query='FormattedID = "%s"' % candidate_story) + criteria = f'FormattedID = "{target_story}"' + response = rally.get("UserStory", fetch="FormattedID,Name,Attachments", query=criteria) ##print(response.resultCount) story = response.next() + if len(story.Attachments): + for att in story.Attachments: + rally.deleteAttachment(story, att.Name) + response = rally.get("UserStory", fetch="FormattedID,Name,Attachments", + query='FormattedID = "%s"' % target_story) + story = response.next() + ##print(response.resultCount) assert len(story.Attachments) == 0 - attachment_name = "Addendum.txt" - att_ok = conjureUpAttachmentFile(attachment_name) - assert att_ok == True + #attachment_name = "Addendum.txt" + # + #att_ok = conjureUpAttachmentFile(attachment_name) + #assert att_ok == True + #att = rally.addAttachment(story, attachment_name) - att = rally.addAttachment(story, attachment_name) + att = rally.addAttachment(story, target_attachment_file, mime_type=attachment_type) assert att.Name == attachment_name - response = rally.get("UserStory", fetch="FormattedID,Name,Attachments", - query='FormattedID = "%s"' % candidate_story) + criteria = f'FormattedID = "{target_story}"' + response = rally.get("UserStory", fetch="FormattedID,Name,Attachments", query=criteria) story = response.next() assert len(story.Attachments) == 1 + attachment = story.Attachments[0] + assert attachment.Name == attachment_name + + #result = rally.deleteAttachment(story, attachment_name) + #assert result != False + #assert len(result.Attachments) == 0 def test_get_attachment(): """ """ - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD) + #rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD) + rally = Rally(server=RALLY, user=RALLY_USER, apikey=API_KEY) candidate_story = "US2" # was this in trial -> "US80" target = 'FormattedID = "%s"' % candidate_story response = rally.get("UserStory", fetch=True, query=target, project=None) @@ -98,6 +119,34 @@ def test_get_attachment(): assert attachment.Name == expected_attachment_name assert attachment.Content == EXAMPLE_ATTACHMENT_CONTENT +def test_get_image_binary_attachment(): + # Use prior testing outcome for the story and attachment target + target_workspace = 'Yeti Rally Workspace' + target_project = 'Anti-Cyclone' + target_story = 'US6099' + target_attachment = 'alpine-snow-glen-plake-quote.png' + attachment_type = 'image/png' + clone_file = 'test/plakism.png' + rally = Rally(server=RALLY, user=RALLY_USER, apikey=API_KEY, workspace=target_workspace, project=target_project) + criteria = f'FormattedID = "{target_story}"' + response = rally.get("UserStory", fetch='ObjectID,Name,Description,Attachments', query=criteria) + assert response.resultCount == 1 + story = response.next() + assert len(story.Attachments) == 1 + attachment_from_collection = story.Attachments[0] + attachment_specific = rally.getAttachment(story, target_attachment) + assert attachment_from_collection.Name == attachment_specific.Name + assert attachment_from_collection.Content == attachment_specific.Content + assert len(attachment_specific.Content) > 950000 + + with open(clone_file, 'wb') as imf: + imf.write(attachment_specific.Content) + assert os.path.exists(clone_file) + import subprocess + process = subprocess.run(['file', clone_file], stdout=subprocess.PIPE, universal_newlines=True) + assert 'plakism.png: PNG image data, 1074 x 538, 8-bit/color RGBA, non-interlaced' in process.stdout + os.remove(clone_file) + def test_add_tcr_attachment(): """ @@ -108,7 +157,8 @@ def test_add_tcr_attachment(): Create an attachment Attach the Attachment to the TestCaseResult item """ - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD) + #rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD) + rally = Rally(server=RALLY, user=RALLY_USER, apikey=API_KEY, workspace=DEFAULT_WORKSPACE, project=DEFAULT_PROJECT) wksp = rally.getWorkspace() assert wksp.Name == DEFAULT_WORKSPACE @@ -125,11 +175,13 @@ def test_add_tcr_attachment(): } test_case = rally.create('TestCase', tc_info) assert int(test_case.oid) > 0 + + current = timestamp()[:-4].replace(' ', 'T') + "Z" tcr_info = { "Workspace" : wksp.ref, "TestCase" : test_case.ref, - "Date" : "2016-05-17T14:30:28.000Z", - "Build" : 17, + "Date" : current, + "Build" : 27, "Verdict" : "Pass" } tcr = rally.create('TestCaseResult', tcr_info) @@ -141,27 +193,42 @@ def test_add_tcr_attachment(): att = rally.addAttachment(tcr, attachment_name) assert att.Name == attachment_name - - -def test_detach_attachment(): - """ - This is the counterpart test for test_add_attachment - """ - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD) - candidate_story = "US1" # "US96" - target = 'FormattedID = "%s"' % candidate_story - - response = rally.get("UserStory", fetch=True, query=target, project=None) + target = ['Build = 27', f'TestCase = {test_case.ref}'] + response = rally.get("TestCaseResult", fetch='ObjectID,FormattedID,Attachments', query=target, project=None) assert response.resultCount == 1 - story = response.next() - assert len(story.Attachments) == 1 - attachment = story.Attachments[0] - expected_attachment_name = "Addendum.txt" - assert attachment.Name == expected_attachment_name - - result = rally.deleteAttachment(story, expected_attachment_name) - assert result != False - assert len(result.Attachments) == (len(story.Attachments) - 1) + tcr = response.next() + attachment = rally.getAttachment(tcr, attachment_name) + assert attachment.Name == attachment_name + att_type = attachment.ContentType + assert att_type == 'text/plain' + att = tcr.Attachments[0] + assert attachment.Content.decode('UTF-8') == EXAMPLE_ATTACHMENT_CONTENT + assert att.Content.decode('UTF-8') == EXAMPLE_ATTACHMENT_CONTENT + rally.deleteAttachment(tcr, attachment_name) + rally.delete('TestCaseResult', tcr) + rally.delete('TestCase', test_case) + + +#def test_detach_attachment(): +# """ +# This is the counterpart test for test_add_attachment +# """ +# #rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD) +# rally = Rally(server=RALLY, user=RALLY_USER, apikey=API_KEY, workspace=DEFAULT_WORKSPACE, project=DEFAULT_PROJECT) +# candidate_story = "US1" # "US96" +# target = 'FormattedID = "%s"' % candidate_story +# +# response = rally.get("UserStory", fetch=True, query=target, project=None) +# assert response.resultCount == 1 +# story = response.next() +# assert len(story.Attachments) == 1 +# attachment = story.Attachments[0] +# expected_attachment_name = "Addendum.txt" +# assert attachment.Name == expected_attachment_name +# +# result = rally.deleteAttachment(story, expected_attachment_name) +# assert result != False +# assert len(result.Attachments) == (len(story.Attachments) - 1) def x_test_replace_attachment(): @@ -190,10 +257,8 @@ def x_test_replace_attachments(): #expectedErrMsg = "hostname '%s' non-existent or unreachable" % bogus_server #with py.test.raises(RallyRESTAPIError) as excinfo: - # rally = Rally(server=bogus_server, - # user=AGICEN_USER, - # password=AGICEN_PSWD) - #actualErrVerbiage = excinfo.value.args[0] # becuz Python2.6 deprecates message :-( + # rally = Rally(server=bogus_server, apikey=API_KEY) + #actualErrVerbiage = excinfo.value.args[0] #assert excinfo.value.__class__.__name__ == 'RallyRESTAPIError' #assert actualErrVerbiage == expectedErrMsg diff --git a/test/test_big_query.py b/test/test_big_query.py index 01bcabb..6cd157b 100644 --- a/test/test_big_query.py +++ b/test/test_big_query.py @@ -16,7 +16,7 @@ ################################################################################################## -from rally_targets import AGICEN, AGICEN_USER, AGICEN_PSWD, API_KEY +from rally_targets import RALLY, RALLY_USER, RALLY_PSWD, API_KEY #from rally_targets import DEFAULT_WORKSPACE, DEFAULT_PROJECT, NON_DEFAULT_PROJECT #from rally_targets import ORG_LEVEL_PROJECT ORG_LEVEL_PROJECT = 'AC Engineering' @@ -32,7 +32,7 @@ def test_multiple_page_response_query(): """ WORKSPACE = 'Rally' PROJECT = 'AC Engineering' - rally = Rally(server=AGICEN, apikey=API_KEY, workspace=WORKSPACE, project=PROJECT) + rally = Rally(server=RALLY, apikey=API_KEY, workspace=WORKSPACE, project=PROJECT) """ response = rally.get('Story', fetch='ObjectID,FormattedID,Name', pagesize=100, limit=1500, projectScopeDown=True) count = 0 diff --git a/test/test_conn.py b/test/test_conn.py index d670c96..5d571af 100644 --- a/test/test_conn.py +++ b/test/test_conn.py @@ -14,7 +14,7 @@ ################################################################################################## -from rally_targets import AGICEN, AGICEN_USER, AGICEN_PSWD, HTTPS_PROXY +from rally_targets import RALLY, RALLY_USER, RALLY_PSWD, HTTPS_PROXY from rally_targets import PROD, API_KEY from rally_targets import PROD_USER, PROD_PSWD @@ -25,7 +25,7 @@ def test_basic_connection(): Using a known valid Rally server and access credentials, issue a simple query request against a known valid Rally entity. """ - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD) response = rally.get('Project', fetch=False, limit=10) assert response != None assert response.status_code == 200 @@ -38,7 +38,7 @@ def test_basic_connection(): # """ # os.environ['https_proxy'] = "http://%s" % HTTPS_PROXY # -# rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD) +# rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD) # response = rally.get('Project', fetch=False, limit=10) # assert response != None # assert response.status_code == 200 @@ -54,7 +54,7 @@ def test_basic_connection(): # """ # os.environ['https_proxy'] = "http://%s" % HTTPS_PROXY # -# rally = Rally(server=AGICEN, apikey=API_KEY, server_ping=False) +# rally = Rally(server=RALLY, apikey=API_KEY, server_ping=False) # rally.setWorkspace('Rally') # projects = rally.getProjects() # project_names = sorted([proj.Name for proj in projects]) @@ -228,8 +228,8 @@ def test_bad_server_spec(): #with py.test.raises(RallyRESTAPIError) as excinfo: # rally = Rally(server=bad_server, - # user=AGICEN_USER, - # password=AGICEN_PSWD, timeout=3) + # user=RALLY_USER, + # password=RALLY_PSWD, timeout=3) # response = rally.get('Project', fetch=False, limit=5) #actualErrVerbiage = excinfo.value.args[0] # becuz Python2.6 deprecates message :-( #assert excinfo.value.__class__.__name__ == 'RallyRESTAPIError' @@ -252,7 +252,7 @@ def test_insuff_credentials(): expectedErrMsg = 'Invalid credentials' with py.test.raises(RallyRESTAPIError) as excinfo: - rally = Rally(server=AGICEN, user=AGICEN_USER, password="") + rally = Rally(server=RALLY, user=RALLY_USER, password="") response = rally.get('Project', fetch=False, limit=10) actualErrVerbiage = excinfo.value.args[0] # becuz Python2.6 deprecates message :-( assert excinfo.value.__class__.__name__ == 'RallyRESTAPIError' @@ -261,7 +261,7 @@ def test_insuff_credentials(): time.sleep(1) with py.test.raises(RallyRESTAPIError) as excinfo: - rally = Rally(server=AGICEN, user="", password="doofus") + rally = Rally(server=RALLY, user="", password="doofus") response = rally.get('Project', fetch=False, limit=10) actualErrVerbiage = excinfo.value.args[0] # becuz Python2.6 deprecates message :-( assert excinfo.value.__class__.__name__ == 'RallyRESTAPIError' @@ -270,7 +270,7 @@ def test_insuff_credentials(): time.sleep(1) with py.test.raises(RallyRESTAPIError) as excinfo: - rally = Rally(server=AGICEN, user="", password="") + rally = Rally(server=RALLY, user="", password="") response = rally.get('Project', fetch=False, limit=10) actualErrVerbiage = excinfo.value.args[0] # becuz Python2.6 deprecates message :-( assert excinfo.value.__class__.__name__ == 'RallyRESTAPIError' @@ -279,7 +279,7 @@ def test_insuff_credentials(): time.sleep(1) with py.test.raises(RallyRESTAPIError) as excinfo: - rally = Rally(server=AGICEN, user="guest", password="") + rally = Rally(server=RALLY, user="guest", password="") response = rally.get('Project', fetch=False, limit=10) actualErrVerbiage = excinfo.value.args[0] # becuz Python2.6 deprecates message :-( assert excinfo.value.__class__.__name__ == 'RallyRESTAPIError' @@ -288,7 +288,7 @@ def test_insuff_credentials(): time.sleep(1) with py.test.raises(RallyRESTAPIError) as excinfo: - rally = Rally(server=AGICEN, user="guest", password="doofus") + rally = Rally(server=RALLY, user="guest", password="doofus") response = rally.get('Project', fetch=False, limit=10) actualErrVerbiage = excinfo.value.args[0] # becuz Python2.6 deprecates message :-( assert excinfo.value.__class__.__name__ == 'RallyRESTAPIError' @@ -297,7 +297,7 @@ def test_insuff_credentials(): time.sleep(1) with py.test.raises(RallyRESTAPIError) as excinfo: - rally = Rally(server=AGICEN, user="guest") + rally = Rally(server=RALLY, user="guest") response = rally.get('Project', fetch=False, limit=10) actualErrVerbiage = excinfo.value.args[0] # becuz Python2.6 deprecates message :-( assert excinfo.value.__class__.__name__ == 'RallyRESTAPIError' diff --git a/test/test_conn_headers.py b/test/test_conn_headers.py new file mode 100644 index 0000000..bb8017e --- /dev/null +++ b/test/test_conn_headers.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python + +import sys, os +import types +import py +import time +import re + +import pyral +from pyral import Rally + +################################################################################################## + +from rally_targets import RALLY, API_KEY + +################################################################################################## + +def test_basic_connection_with_a_header(): + """ + Using a known valid Rally server and access credentials, issue a simple query + request against a known valid Rally entity. + """ + headers = {'name': 'Fungibles Goods Burn Up/Down', 'vendor': 'Archimedes', 'version': '1.2.3'} + rally = Rally(RALLY, apikey=API_KEY, headers=headers) + response = rally.get('Project', fetch=False, limit=10) + assert response != None + assert response.status_code == 200 + time.sleep(1) + + diff --git a/test/test_context.py b/test/test_context.py index 0923b03..ed24697 100644 --- a/test/test_context.py +++ b/test/test_context.py @@ -8,7 +8,7 @@ ################################################################################################## -from rally_targets import AGICEN, AGICEN_USER, AGICEN_PSWD +from rally_targets import RALLY, RALLY_USER, RALLY_PSWD from rally_targets import DEFAULT_WORKSPACE, DEFAULT_PROJECT, NON_DEFAULT_PROJECT from rally_targets import ALTERNATE_WORKSPACE, ALTERNATE_PROJECT @@ -52,7 +52,7 @@ def test_default_context(): Furthermore the construction of a GET related URL will contain the correct workspace and project specifications in the QUERY_STRING. """ - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD) context1 = rally.contextHelper.currentContext() workspace = rally.getWorkspace() project = rally.getProject() @@ -71,7 +71,7 @@ def test_default_context(): # 2 def test_explictly_set_workspace_as_default_context(): - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD, workspace=DEFAULT_WORKSPACE) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD, workspace=DEFAULT_WORKSPACE) workspace = rally.getWorkspace() assert workspace.Name == DEFAULT_WORKSPACE project = rally.getProject() @@ -85,7 +85,7 @@ def test_explictly_set_workspace_as_default_context(): # 3 def test_initial_workspace_not_default(): - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD, + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD, workspace=ALTERNATE_WORKSPACE, warn=False) # Because no project=name arg was supplied, the project will be the User's default project @@ -108,7 +108,7 @@ def test_initial_workspace_not_default(): # 4 def test_explicitly_set_workspace_and_project_as_default_context(): - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD, + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD, workspace=DEFAULT_WORKSPACE, project=DEFAULT_PROJECT) workspace = rally.getWorkspace() @@ -124,7 +124,7 @@ def test_explicitly_set_workspace_and_project_as_default_context(): # 5 def test_set_default_workspace_non_default_project_context(): - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD, + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD, workspace=DEFAULT_WORKSPACE, project=NON_DEFAULT_PROJECT) workspace = rally.getWorkspace() @@ -140,7 +140,7 @@ def test_set_default_workspace_non_default_project_context(): # 6 def test_set_non_default_workspace_and_project_context(): - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD, + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD, workspace=ALTERNATE_WORKSPACE, project=ALTERNATE_PROJECT) workspace = rally.getWorkspace() @@ -156,7 +156,7 @@ def test_set_non_default_workspace_and_project_context(): # 7 def test_default_wksprj_with_set_workspace_with_default_context(): - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD) workspace = rally.getWorkspace() assert workspace.Name == DEFAULT_WORKSPACE project = rally.getProject() @@ -175,7 +175,7 @@ def test_default_wksprj_with_set_workspace_with_default_context(): #8 def test_default_wksprj_with_set_non_default_workspace_context(): - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD) workspace = rally.getWorkspace() assert workspace.Name == DEFAULT_WORKSPACE project = rally.getProject() @@ -203,7 +203,7 @@ def test_default_wksprj_with_set_non_default_workspace_context(): #9 def test_default_workspace_with_set_non_default_workspace_context(): - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD, workspace=DEFAULT_WORKSPACE) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD, workspace=DEFAULT_WORKSPACE) workspace = rally.getWorkspace() assert workspace.Name == DEFAULT_WORKSPACE project = rally.getProject() @@ -226,7 +226,7 @@ def test_default_workspace_with_set_non_default_workspace_context(): #10 def test_default_workspace_with_set_non_default_workspace_and_project_context(): - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD, workspace=DEFAULT_WORKSPACE) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD, workspace=DEFAULT_WORKSPACE) workspace = rally.getWorkspace() assert workspace.Name == DEFAULT_WORKSPACE project = rally.getProject() @@ -249,7 +249,7 @@ def test_default_workspace_with_set_non_default_workspace_and_project_context(): #11 def test_default_workspace_project_specify_project_equal_None_context(): - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD) workspace = rally.getWorkspace() assert workspace.Name == DEFAULT_WORKSPACE project = rally.getProject() @@ -263,7 +263,7 @@ def test_default_workspace_project_specify_project_equal_None_context(): #12 def test_non_default_workspace_project_specify_project_equal_None_context(): - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD, + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD, workspace=ALTERNATE_WORKSPACE, project=ALTERNATE_PROJECT) workspace = rally.getWorkspace() @@ -279,7 +279,7 @@ def test_non_default_workspace_project_specify_project_equal_None_context(): #13 def test_default_wksprj_with_set_non_default_workspace_specify_project_equal_None_context(): - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD) workspace = rally.getWorkspace() assert workspace.Name == DEFAULT_WORKSPACE project = rally.getProject() @@ -296,7 +296,7 @@ def test_default_wksprj_with_set_non_default_workspace_specify_project_equal_Non #14 def test_default_wksprj_with_set_non_default_workspace_and_project_specify_project_equal_None_context(): - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD) workspace = rally.getWorkspace() assert workspace.Name == DEFAULT_WORKSPACE project = rally.getProject() @@ -317,7 +317,7 @@ def test_default_wksprj_with_set_non_default_workspace_and_project_specify_proje #15 def test_default_wksprj_specify_workspace_and_project_equal_None_context(): - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD) workspace = rally.getWorkspace() assert workspace.Name == DEFAULT_WORKSPACE project = rally.getProject() @@ -330,7 +330,7 @@ def test_default_wksprj_specify_workspace_and_project_equal_None_context(): #16 def test_non_default_wksprj_specify_workspace_and_project_equal_None_context(): - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD, + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD, workspace=ALTERNATE_WORKSPACE, project=ALTERNATE_PROJECT) workspace = rally.getWorkspace() @@ -345,7 +345,7 @@ def test_non_default_wksprj_specify_workspace_and_project_equal_None_context(): #17 def test_default_wksprj_set_non_default_wksprj_specify_workspace_and_project_equal_None_context(): - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD) workspace = rally.getWorkspace() assert workspace.Name == DEFAULT_WORKSPACE project = rally.getProject() diff --git a/test/test_convenience.py b/test/test_convenience.py index fda472f..17952db 100644 --- a/test/test_convenience.py +++ b/test/test_convenience.py @@ -8,8 +8,8 @@ ################################################################################################## -from rally_targets import AGICEN, AGICEN_USER, AGICEN_PSWD -from rally_targets import AGICEN_NICKNAME, DEFAULT_WORKSPACE +from rally_targets import RALLY, RALLY_USER, RALLY_PSWD +from rally_targets import RALLY_NICKNAME, DEFAULT_WORKSPACE ################################################################################################## @@ -19,7 +19,7 @@ def test_getSchemaInfo(): obtain a Rally instance and call the getSchemaInfo method for the default workspace. """ - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD) schema_info = rally.getSchemaInfo(rally.getWorkspace()) assert type(schema_info) == list assert len(schema_info) > 50 @@ -37,7 +37,7 @@ def test_getWorkspace(): Rally entity. The fetch specifies a small number of known valid attributes on the Rally entity. """ - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD) workspace = rally.getWorkspace() assert int(workspace.oid) > 10000 assert len(workspace.Name) > 6 @@ -49,7 +49,7 @@ def test_getProject(): issue a simple query (no qualifying criteria) for a known valid Rally entity. """ - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD, version="v2.0") + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD, version="v2.0") response = rally.get('Project', fetch=False, limit=10) assert response.status_code == 200 assert response.errors == [] @@ -67,12 +67,12 @@ def test_getUserInfo_query(): Using a known valid Rally server and known valid access credentials, request the information associated with a single username. """ - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD) - qualifiers = rally.getUserInfo(username=AGICEN_USER) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD) + qualifiers = rally.getUserInfo(username=RALLY_USER) assert len(qualifiers) == 1 user = qualifiers.pop() - assert user.Name == AGICEN_NICKNAME - assert user.UserName == AGICEN_USER + assert user.Name == RALLY_NICKNAME + assert user.UserName == RALLY_USER assert user.UserProfile.DefaultWorkspace.Name == DEFAULT_WORKSPACE #assert user.Role == 'CONTRIBUTOR' # or this may be set to ORGANIZER #assert user.Role == 'Developer' # not set for yeti@rallydev.com on the trial instance... @@ -86,7 +86,7 @@ def test_getAllUsers_query(): Using a known valid Rally server and known valid access credentials, request information about every user associated with the current subscription. """ - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD) everybody = rally.getAllUsers() assert len(everybody) > 0 assert len([user for user in everybody if user.DisplayName == 'da Kipster']) == 1 @@ -97,7 +97,7 @@ def test_typedef(): exercise the Rally.typedef convenience method using 'Portfolio/Feature' as a target. """ - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD) target_type = 'PortfolioItem/Feature' td = rally.typedef(target_type) assert td != None @@ -111,7 +111,7 @@ def test_getStates(): get all the State entity instances for Initiative via the Rally.getStates convenience method. """ - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD) target_entity = 'Initiative' states = rally.getStates(target_entity) assert len(states) == 4 @@ -125,10 +125,10 @@ def test_getCollection(): url using the workspace.ObjectID. Call the rally.getCollection with the confabulated collection url. """ - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD) workspace = rally.getWorkspace() assert workspace is not None - proj_collection_url = "http://%s/slm/webservice/v2.0/Workspace/%s/Projects" % (AGICEN, workspace.ObjectID) + proj_collection_url = "http://%s/slm/webservice/v2.0/Workspace/%s/Projects" % (RALLY, workspace.ObjectID) response = rally.getCollection(proj_collection_url) assert response.__class__.__name__ == 'RallyRESTResponse' projects = [proj for proj in response] diff --git a/test/test_field_access.py b/test/test_field_access.py index 378f759..bb0b666 100644 --- a/test/test_field_access.py +++ b/test/test_field_access.py @@ -8,7 +8,7 @@ ################################################################################################## -from rally_targets import AGICEN, AGICEN_USER, AGICEN_PSWD +from rally_targets import RALLY, RALLY_USER, RALLY_PSWD ################################################################################################## @@ -19,7 +19,7 @@ def test_story_fields(): Rally entity, and observe that you can access both standard and custom fields by the field Display Name. """ - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD) response = rally.get('Story', fetch=True, query=['NumberofCases = 9', 'AffectedCustomers = "abc, xyz"']) assert response.status_code == 200 story = response.next() @@ -35,7 +35,7 @@ def test_defect_fields(): Rally entity, and observe that you can access both standard and custom fields by the field Display Name. """ - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD) cust_field_criteria = {"BugzillaID" : 7224, "JiraKey" : "SLO-109", "QCDefectID" : 5724} response = rally.get('Defect', fetch=True, query=cust_field_criteria) assert response.status_code == 200 diff --git a/test/test_inflation.py b/test/test_inflation.py index 93bba6d..82a6b52 100644 --- a/test/test_inflation.py +++ b/test/test_inflation.py @@ -9,7 +9,7 @@ ################################################################################################## -from rally_targets import AGICEN, AGICEN_USER, AGICEN_PSWD, DEFAULT_WORKSPACE, DEFAULT_PROJECT +from rally_targets import RALLY, RALLY_USER, RALLY_PSWD, DEFAULT_WORKSPACE, DEFAULT_PROJECT from rally_targets import ALTERNATE_WORKSPACE ################################################################################################## @@ -21,7 +21,7 @@ def test_default_connection(): Return status should be OK, Rally._wpCacheStatus should be 'minimal' """ - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD) response = rally.get('Project', fetch=False, limit=10) assert response != None assert response.status_code == 200 @@ -37,7 +37,7 @@ def test_default_workspace_with_named_default_project(): _inflated value should be 'minimal' """ project = 'Sample Project' - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD, + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD, project=project) response = rally.get('Project', fetch=False) assert response != None @@ -53,7 +53,7 @@ def test_default_workspace_non_default_valid_project(): _inflated value should be 'minimal' """ project = 'My Project' - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD, + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD, project=project) response = rally.get('Project', fetch=False) assert response != None @@ -71,7 +71,7 @@ def test_default_workspace_non_valid_project(): problem = "The current Workspace '%s' does not contain an accessible Project with the name of '%s'" expectedErrMsg = problem % (DEFAULT_WORKSPACE, project) with py.test.raises(Exception) as excinfo: - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD, + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD, project=project) actualErrVerbiage = excinfo.value.args[0] assert excinfo.value.__class__.__name__ == 'RallyRESTAPIError' @@ -84,7 +84,7 @@ def test_named_default_workspace_use_default_project(): without specifying the name of project. Inquire after connecting as to what the current project is. """ - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD, + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD, workspace=DEFAULT_WORKSPACE) project = rally.getProject() assert project != None @@ -99,7 +99,7 @@ def test_named_default_workspace_named_default_project(): Return status should be OK, the Rally instance's RallyContextHelper _inflated value should be 'minimal' """ - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD, + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD, workspace=DEFAULT_WORKSPACE, project=DEFAULT_PROJECT) response = rally.get('Project') assert response != None @@ -115,7 +115,7 @@ def test_named_default_workspace_named_valid_project(): _inflated value should be 'minimal' """ project = 'My Project' - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD, + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD, workspace=DEFAULT_WORKSPACE, project=project) response = rally.get('Project') assert response != None @@ -133,7 +133,7 @@ def test_named_default_workspace_named_invalid_project(): problem = "The current Workspace '%s' does not contain an accessible Project with the name of '%s'" expectedErrMsg = problem % (DEFAULT_WORKSPACE, project) with py.test.raises(Exception) as excinfo: - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD, + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD, workspace=DEFAULT_WORKSPACE, project=project) actualErrVerbiage = excinfo.value.args[0] assert excinfo.value.__class__.__name__ == 'RallyRESTAPIError' @@ -153,14 +153,14 @@ def test_named_non_default_workspace_use_default_project(): in the named non-default workspace """ - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD, + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD, workspace=ALTERNATE_WORKSPACE, warn=False) ai_proj = rally.getProject() assert str(ai_proj.Name) == 'Sample Project' assert rally._wpCacheStatus() == 'narrow' #alt_project = "Modus Operandi" - #rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD, + #rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD, # workspace=ALTERNATE_WORKSPACE, project=alt_project, warn=False) #ai_proj = rally.getProject() #assert str(ai_proj.Name) == alt_project # is valid only in ALTERNATE_WORKSPACE @@ -175,7 +175,7 @@ def test_named_non_default_workspace_named_valid_project(): """ workspace = "Kip's Playground" alt_project = 'Modus Operandi' - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD, workspace=workspace, warn=False) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD, workspace=workspace, warn=False) response = rally.get('Project') assert response != None assert response.status_code == 200 @@ -197,7 +197,7 @@ def test_named_non_default_workspace_named_invalid_project(): problem = "The current Workspace '%s' does not contain an accessible Project with the name of '%s'" expectedErrMsg = problem % (ALTERNATE_WORKSPACE, project) with py.test.raises(Exception) as excinfo: - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD, + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD, workspace=workspace, project=project, timeout=10) response = rally.get('Project', fetch=False, limit=5) actualErrVerbiage = excinfo.value.args[0] diff --git a/test/test_portfolio_items.py b/test/test_portfolio_items.py new file mode 100644 index 0000000..0578432 --- /dev/null +++ b/test/test_portfolio_items.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python + +import sys, os +import types +import pprint + +from pyral import Rally + +################################################################################################## + +from rally_targets import RALLY, RALLY_USER, RALLY_PSWD, API_KEY +from rally_targets import RALLY_NICKNAME, DEFAULT_WORKSPACE + +################################################################################################## + +def test_getSchemaInfo(): + """ + Using a known valid Rally server and known valid access credentials, + obtain a Rally instance and call the getSchemaInfo method for the + default workspace. + """ + rally = Rally(server=RALLY, apikey=API_KEY) + rally.setWorkspace('NMTest') + schema_info = rally.getSchemaInfo(rally.getWorkspace()) + assert type(schema_info) == list + assert len(schema_info) > 50 + subs_schema = [item for item in schema_info if item['Name'] == 'Subscription'] + assert subs_schema != None + assert len(subs_schema) == 1 + assert type(subs_schema) == list + assert u'Attributes' in subs_schema[0] + assert len(subs_schema[0][u'Attributes']) > 15 + + epic_schema = [item for item in schema_info if item['Name'] == 'Epic'] + assert epic_schema != None + + +def test_typedef(): + """ + Using a known valid Rally server and known valid access credentials, + exercise the Rally.typedef convenience method using 'Portfolio/Epic' + as a target. + """ + rally = Rally(server=RALLY, apikey=API_KEY) + rally.setWorkspace('NMTest') + target_type = 'PortfolioItem/Epic' + td = rally.typedef(target_type) + assert td != None + assert td._type == 'TypeDefinition' + assert td.TypePath == 'PortfolioItem/Epic' + assert td.ref.startswith('typedefinition/') + +def test_epic_creation(): + """ + Using a known valid Rally server and known valid access credentials, + exercise the Rally.create method to create an Epic instance and be handed + back a usable pyral.entity representing the newly created Epic PortfoloItem instance. + """ + rally = Rally(server=RALLY, apikey=API_KEY) + rally.setWorkspace('NMTest') + target_workspace = rally.getWorkspace() + rally.setProject('Unbearable MFA music') + target_project = rally.getProject() + #response = rally.get('Feature', fetch=True, workspace=target_workspace.Name, project=None, instance=True) + #if response.resultCount == 0: + # raise Exception("Unable to retrieve any Feature items... so sad") + #features = [f for f in response] + #feature = [f for f in features if f.FormattedID == 'F1'] + + feature = rally.get('Feature', fetch=True, query='FormattedID = F1', workspace=target_workspace.Name, project=None, instance=True) + info = { + "Workspace" : target_workspace._ref, + "Project" : target_project._ref, + "Name" : "VacuumHajCracklings", + "Description" : "420 CuHectare capacity. It is a big place and partygoers are sloppy. Big fast clean mandatory." + #"Notes" : "Hoover or Dustmatic it makes no difference", + #"Ready" : False, + #"Parent" : feature._ref + } + + print("Creating Epic ...") + epic = rally.put('Epic', info) + assert epic + assert epic.Name == 'VacuumHajCracklings' + print("Created Epic: %s OID: %s Name: %s Description: %s" % (epic.FormattedID, epic.oid, epic.Name, epic.Description)) + assert epic.Ready == False diff --git a/test/test_project_pathing.py b/test/test_project_pathing.py index a27fe09..c338938 100644 --- a/test/test_project_pathing.py +++ b/test/test_project_pathing.py @@ -9,7 +9,7 @@ ################################################################################################## -from rally_targets import AGICEN, AGICEN_USER, AGICEN_PSWD +from rally_targets import RALLY, RALLY_USER, RALLY_PSWD from rally_targets import YETI_USER, YETI_PSWD from rally_targets import BOONDOCKS_WORKSPACE, BOONDOCKS_PROJECT from rally_targets import API_KEY @@ -63,7 +63,7 @@ def test_mep_project_as_payload_project_value(): The resulting created item must have a Project value that is a ref to the m-e-p Project. """ - rally = Rally(server=AGICEN, user=YETI_USER, password=YETI_PSWD) + rally = Rally(server=RALLY, user=YETI_USER, password=YETI_PSWD) def test_obtain_instance_using_mep_project(): @@ -74,7 +74,7 @@ def test_obtain_instance_using_mep_project(): A subsequent call on the instance to obtain the current Project should return a pyral entity for Project that has a ref for the correct m-e-p Project. """ - rally = Rally(server=AGICEN, user=YETI_USER, password=YETI_PSWD, + rally = Rally(server=RALLY, user=YETI_USER, password=YETI_PSWD, workspace=BOONDOCKS_WORKSPACE, project=DEEP_DUPE) cur_project = rally.getProject() assert cur_project.Name == DEEP_DUPE @@ -86,7 +86,7 @@ def test_get_duplicated_project(): issue a query to retrieve Project items that match a specific project name that occurs in more than one location in the tree of Projects under the target Workspace. """ - rally = Rally(server=AGICEN, user=YETI_USER, password=YETI_PSWD, + rally = Rally(server=RALLY, user=YETI_USER, password=YETI_PSWD, workspace=BOONDOCKS_WORKSPACE, project=BOONDOCKS_PROJECT) response = rally.get('Project', fetch=False, query='Name = "%s"' % DUPLICATED_PROJECT, limit=10) assert response.status_code == 200 @@ -109,7 +109,7 @@ def test_get_distant_duplicated_project(): multi element path for the Project it should do so and should return the correct value when asked for the current Project. """ - rally = Rally(server=AGICEN, user=YETI_USER, password=YETI_PSWD, + rally = Rally(server=RALLY, user=YETI_USER, password=YETI_PSWD, workspace=BOONDOCKS_WORKSPACE, project=BOONDOCKS_PROJECT) dupey_project = rally.getProject(DEEP_DUPE) @@ -139,7 +139,7 @@ def test_use_mep_project_as_default_scoped_project(): A subsequent call on the instance to obtain the current Project should return a pyral entity for Project that has a ref for the correct m-e-p Project. """ - rally = Rally(server=AGICEN, user=YETI_USER, password=YETI_PSWD, + rally = Rally(server=RALLY, user=YETI_USER, password=YETI_PSWD, workspace=BOONDOCKS_WORKSPACE, project=DEEP_DUPE) target_story = 'US3' result = rally.get('Story', fetch="FormattedID,Name,Description,State,Project", projectScopeUp=False) @@ -162,7 +162,7 @@ def test_use_mep_project_as_project_keyword_scoped(): A subsequent request the instance specifying a valid m-e-p Project in the Workspace should return an item scoped to that m-e-p Project. """ - rally = Rally(server=AGICEN, user=YETI_USER, password=YETI_PSWD, + rally = Rally(server=RALLY, user=YETI_USER, password=YETI_PSWD, workspace=BOONDOCKS_WORKSPACE, project=BOONDOCKS_PROJECT) target_story = 'US3' result = rally.get('Story', fetch="FormattedID,Name,Description,State,Project", @@ -191,7 +191,7 @@ def test_set_mep_project_used_as_default_in_request_operation(): The result should be an Artifact whose projet matches the target duplicate Project leaf name and ref exactly. """ - rally = Rally(server=AGICEN, user=YETI_USER, password=YETI_PSWD, + rally = Rally(server=RALLY, user=YETI_USER, password=YETI_PSWD, workspace=BOONDOCKS_WORKSPACE, project=BOONDOCKS_PROJECT) rally.setProject(DEEP_DUPE) target_story = 'US3' @@ -218,7 +218,7 @@ def test_mep_project_in_request_payload(): The result should be the creation of the BuildDefinition item for which the Project attribute has a ref to the targeted m-e-p Project. """ - rally = Rally(server=AGICEN, user=YETI_USER, password=YETI_PSWD, + rally = Rally(server=RALLY, user=YETI_USER, password=YETI_PSWD, workspace=BOONDOCKS_WORKSPACE, project=BOONDOCKS_PROJECT) workspace = rally.getWorkspace() dd_project = rally.getProject(DEEP_DUPE) diff --git a/test/test_pull_request_copied.py b/test/test_pull_request_copied.py index cd321d0..7ee2ff0 100644 --- a/test/test_pull_request_copied.py +++ b/test/test_pull_request_copied.py @@ -10,14 +10,14 @@ ################################################################################################## -from rally_targets import AGICEN, AGICEN_USER, AGICEN_PSWD +from rally_targets import RALLY, RALLY_USER, RALLY_PSWD from rally_targets import DEFAULT_WORKSPACE, DEFAULT_PROJECT, NON_DEFAULT_PROJECT from rally_targets import ALTERNATE_WORKSPACE, ALTERNATE_PROJECT from internal_rally_targets import APIKEY -AGICEN_SUB_100_USER = '' -AGICEN_SUB_100_PSWD = '' -AGICEN_SUB_100_API_KEY = APIKEY +RALLY_SUB_100_USER = '' +RALLY_SUB_100_PSWD = '' +RALLY_SUB_100_API_KEY = APIKEY REFABLE_ARTIFACT = 'hierarchicalrequirement/141184568124' @@ -46,7 +46,7 @@ # def test_create_local_pull_request_instance(): # fake_oid = 12345698765 # -# rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD) +# rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD) # context1 = rally.contextHelper.currentContext() # pr_class = classFor['PullRequest'] # pr = pr_class(fake_oid, dummy_data['Name'], dummy_data['Url'], context1) @@ -64,8 +64,8 @@ def test_post_pull_request(): expectedErrMsg = '422 Requested type name "pullrequest" is unknown.' #rally = Rally(server=TESTN, user=TESTN_USER, password=TESTN_PSWD) - #rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD) - rally = Rally(server=AGICEN, apikey=AGICEN_SUB_100_API_KEY) + #rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD) + rally = Rally(server=RALLY, apikey=RALLY_SUB_100_API_KEY) #with py.test.raises(RallyRESTAPIError) as excinfo: pr = rally.create('PullRequest', dummy_data, project=None) assert pr is not None @@ -75,9 +75,9 @@ def test_post_pull_request(): #assert expectedErrMsg == actualErrVerbiage def test_query_pull_requests(): - #rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD) + #rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD) #rally = Rally(server=TESTN, user=TESTN_USER, password=TESTN_PSWD) - rally = Rally(server=AGICEN, apikey=AGICEN_SUB_100_API_KEY) + rally = Rally(server=RALLY, apikey=RALLY_SUB_100_API_KEY) attrs = "ExternalId,ExternalFormattedId,Artifact,Name,Url,Description" response = rally.get('PullRequest', fetch=attrs, project=None) assert response.status_code == 200 @@ -91,7 +91,7 @@ def test_query_pull_requests(): print(prs[0].details()) # def test_creation_date_query(): -# rally = Rally(server=AGICEN, apikey=AGICEN_SUB_100_API_KEY) +# rally = Rally(server=RALLY, apikey=RALLY_SUB_100_API_KEY) # ref_time_iso = '2017-08-30T14:00:00.000Z' # attrs = "ExternalId,ExternalFormattedId,Artifact,Name,Url,Description" # selectors = ['CreationDate >= %s' % ref_time_iso] @@ -112,7 +112,7 @@ def test_query_pull_requests(): def test_delete_pull_request(): #rally = Rally(server=TESTN, user=TESTN_USER, password=TESTN_PSWD) - rally = Rally(server=AGICEN, apikey=AGICEN_SUB_100_API_KEY) + rally = Rally(server=RALLY, apikey=RALLY_SUB_100_API_KEY) attrs = "ExternalId,ExternalFormattedId,Artifact,Name,Url,Description" response = rally.get('PullRequest', fetch=attrs, project=None) assert response.status_code == 200 diff --git a/test/test_query.py b/test/test_query.py index c920ff6..4a79d26 100644 --- a/test/test_query.py +++ b/test/test_query.py @@ -18,7 +18,7 @@ ################################################################################################## -from rally_targets import AGICEN, AGICEN_USER, AGICEN_PSWD +from rally_targets import RALLY, RALLY_USER, RALLY_PSWD from rally_targets import DEFAULT_WORKSPACE, DEFAULT_PROJECT, NON_DEFAULT_PROJECT from rally_targets import BOONDOCKS_WORKSPACE, BOONDOCKS_PROJECT from rally_targets import PROJECT_SCOPING_TREE @@ -31,7 +31,7 @@ def test_basic_query(): issue a simple query (no qualifying criteria) for a known valid Rally entity. """ - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD) response = rally.get('Project', fetch=False, limit=10) assert response.status_code == 200 assert response.errors == [] @@ -45,7 +45,7 @@ def test_simple_named_fields_query(): Rally entity. The fetch specifies a small number of known valid attributes on the Rally entity. """ - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD) response = rally.get('Project', fetch="Owner,State", limit=10) assert response.status_code == 200 assert len(response.errors) == 0 @@ -58,7 +58,7 @@ def test_all_fields_query(): Rally entity. The fetch value is True so each entity returned in the response (data) will have its _hydrated attribute value set to True. """ - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD) response = rally.get('Project', fetch=True, limit=10) assert response.status_code == 200 assert len(response.errors) == 0 @@ -75,7 +75,7 @@ def test_bogus_query(): The status_code in the response must not indicate a valid request/response and the errors attribute must have some descriptive info about the error. """ - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD) bogus_entity = "Payjammas" expectedErrMsg = "not a valid Rally entity: %s" % bogus_entity with py.test.raises(InvalidRallyTypeNameError) as excinfo: @@ -93,7 +93,7 @@ def test_good_and_bad_fields_query(): in the response data should not have the invalid attribute names, but should have attribute names/values for the correctly specified entity attributes. """ - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD) response = rally.get('Project', fetch="Owner,State,Fabulote,GammaRays", limit=10) project = response.next() name = None @@ -130,7 +130,7 @@ def test_multiple_entities_query(): As of Rally WSAPI 1.x, this is an invalid request; only a single Rally entity can be specified. """ - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD) multiple_entities = "Project,Workspace" with py.test.raises(InvalidRallyTypeNameError) as excinfo: response = rally.get(multiple_entities, fetch=False, limit=10) @@ -145,7 +145,7 @@ def test_multiple_page_response_query(): (Defect) known to have more than 5 items. Set the pagesize to 5 to force pyral to retrieve multiple pages to satisfy the query. """ - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD) response = rally.get('Defect', fetch=False, pagesize=5, limit=15) count = 0 for ix, bugger in enumerate(response): @@ -165,7 +165,7 @@ def test_defects_revision_history(): Ultimately, the attributes deeper than the first level must be obtained and have their attributes filled out completely (_hydrated == True). """ - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD) response = rally.get('Defect', fetch=True, limit=10) defect1 = response.next() @@ -198,7 +198,7 @@ def test_single_condition_query_plain_expression(): one or more Defects. The qualifying criterion is a string that is _not_ surrounded with paren chars. """ - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD) workspace = rally.getWorkspace() project = rally.getProject() qualifier = 'State = "Submitted"' @@ -214,7 +214,7 @@ def test_single_condition_query_parenned(): one or more Defects. The qualifying criterion is a string that _is_ surrounded with paren chars. """ - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD) qualifier = "(State = Submitted)" #qualifier = '(FormattedID = "US100")' response = rally.get('Defect', fetch=True, query=qualifier, limit=10) @@ -229,7 +229,7 @@ def test_two_condition_query_in_unparenned_string(): two condition strings, each condition string does _not_ have any surrounding paren chars. """ - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD) double_qualifier = "State = Submitted AND FormattedID != US100" response = rally.get('Defect', fetch=True, query=double_qualifier, limit=10) assert response.resultCount > 0 @@ -243,7 +243,7 @@ def test_two_condition_query_parenned(): surrounded with paren chars and each condition itself is surrounded by parens.. """ - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD) qualifiers = "((State = Submitted) AND (FormattedID != US100))" response = rally.get('Defect', fetch=True, query=qualifiers, limit=10) assert response.resultCount > 0 @@ -251,9 +251,9 @@ def test_two_condition_query_parenned(): def test_four_ored_conditions_in_parrened_string(): """ Take a user query with OR conditions in which the parenneg groups are - already supplied in AgileCentral conformant "binary" condition style + already supplied in Rally conformant "binary" condition style """ - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD, + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD, workspace=BOONDOCKS_WORKSPACE, project=BOONDOCKS_PROJECT) qualifiers = '((((Name = "Brazen%20Milliwogs") OR (Name = "Jenkins")) OR (Name = "Refusnik")) OR (Name = "Salamandra"))' response = rally.get('Project', fetch=True, query=qualifiers, limit=10) @@ -263,7 +263,7 @@ def test_four_ored_conditions_in_parrened_string(): assert response.resultCount == 2 # Only Jenkins and Salamandra exist or or accessible to the accessing account def test_single_condition_query_as_list(): - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD) qualifier = ['State != Open'] response = rally.get('Defect', fetch=True, query=qualifier, limit=10) assert response.resultCount > 0 @@ -277,7 +277,7 @@ def test_two_condition_query_in_list(): two condition strings, each condition string does _not_ have any surrounding paren chars. """ - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD) qualifiers = ["State = Submitted", "FormattedID != US100"] response = rally.get('Defect', fetch=True, query=qualifiers, limit=10) assert response.resultCount > 0 @@ -291,14 +291,14 @@ def test_three_condition_query_in_list(): three condition strings, each condition string does _not_ have any surrounding paren chars. """ - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD) #qualifiers = ["State = Submitted", "FormattedID != DE100", "Owner.UserName != horsefeathers"] qualifiers = ["State = Submitted", "FormattedID != DE100", "Severity != UltraMegaHurt"] response = rally.get('Defect', fetch=True, query=qualifiers, limit=10) assert response.resultCount > 0 def test_five_condition_query_in_list(): - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD) qualifiers = ["State = Submitted", "FormattedID < DE22", "FormattedID != DE17", @@ -309,13 +309,13 @@ def test_five_condition_query_in_list(): assert response.resultCount > 0 def test_single_condition_query_as_dict(): - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD) qualifier = {'State' : 'Submitted'} response = rally.get('Defect', fetch=True, query=qualifier, limit=10) assert response.resultCount > 0 def test_two_conditions_query_as_dict(): - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD) qualifiers = {'State' : 'Submitted', 'Ready' : 'False' } @@ -328,7 +328,7 @@ def test_three_conditions_query_as_dict(): """ # TODO: note that an attribute value containing a '/' char will fail # have yet to determine how to get this to work with Rally WSAPI ... - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD) qualifiers = {"State" : "Submitted", "Priority" : "High Attention", "Ready" : "False" @@ -342,7 +342,7 @@ def test_limit_query(): """ Use a pagesize of 200 and a limit of 80 in the params in the URL """ - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD) qualifier = "State = Submitted" response = rally.get('Defect', fetch=True, query=qualifier, pagesize=100, limit=30) items = [item for item in response] @@ -352,7 +352,7 @@ def test_start_value_query(): """ Use a pagesize of 200 and a start index value of 10 in the params in the URL """ - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD) qualifier = "State = Submitted" response = rally.get('Defect', fetch=True, query=qualifier, pagesize=200, start=10) items = [item for item in response] @@ -363,7 +363,7 @@ def test_start_and_limit_query(): """ Use a pagesize of 50 and a start index value of 10 and a limit of 40 in the params in the URL """ - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD) qualifier = "State = Submitted" response = rally.get('Defect', fetch=True, query=qualifier, pagesize=50, start=10,limit=40) items = [item for item in response] @@ -408,7 +408,7 @@ def test_query_with_special_chars_in_criteria(): DE3228 in DEFAULT_WORKSPACE / DEFAULT_PROJECT has Name = Special chars:/!@#$%^&*()-=+[]{};:./<>?/ query for it by looking for it by the name value """ - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD) rally.setWorkspace(DEFAULT_WORKSPACE) rally.setProject(DEFAULT_PROJECT) #rally.enableLogging('spec_char_query') @@ -451,7 +451,7 @@ def test_query_with_matched_parens_in_condition_value(): The default workspace and project has a Release in it whose name contains a matched paren pair make sure a query containing a condition looking for the Release by this name succeeds. """ - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD) #rally.enableLogging('query_condition_value_has_matched_internal_parens') criteria = 'Name = "8.5 (Blah and Stuff)"' @@ -472,7 +472,7 @@ def test_query_using_project_scoping_options(): The query specifying projectScopeDown=True should return more Stories than the initial query """ SCOPE_ROOT_PROJECT = 'Arctic Elevation' - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD) projects = rally.getProjects(workspace=DEFAULT_WORKSPACE) rally.setProject(SCOPE_ROOT_PROJECT) diff --git a/test/test_ranking.py b/test/test_ranking.py index 6608f71..d44c00d 100644 --- a/test/test_ranking.py +++ b/test/test_ranking.py @@ -7,7 +7,7 @@ ################################################################################################## -from rally_targets import AGICEN, AGICEN_USER, AGICEN_PSWD +from rally_targets import RALLY, RALLY_USER, RALLY_PSWD ################################################################################################## @@ -16,7 +16,7 @@ def test_rank_story_above(): Using a known valid Rally server and known valid access credentials, obtain a Rally instance and issue a get for Story items ordered by Rank ASC. """ - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD, server_ping=False, isolated_workspace=True) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD, server_ping=False, isolated_workspace=True) response = rally.get('Story', fetch="FormattedID,Name,Description,State,DragAndDropRank", order="Rank ASC", limit=100) stories = [story for story in response] @@ -35,7 +35,7 @@ def test_rank_story_above(): assert stories[0].DragAndDropRank < story2.DragAndDropRank def test_rank_story_below(): - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD, server_ping=False, isolated_workspace=True) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD, server_ping=False, isolated_workspace=True) response = rally.get('Story', fetch="FormattedID,Name,Description,State,DragAndDropRank", order="Rank ASC", limit=100) stories = [story for story in response] @@ -59,7 +59,7 @@ def test_rank_story_below(): def test_rank_story_to_top(): """ """ - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD, server_ping=False, isolated_workspace=True) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD, server_ping=False, isolated_workspace=True) response = rally.get('Story', fetch="FormattedID,Name,Description,State,DragAndDropRank", order="Rank ASC", limit=100) stories = [story for story in response] @@ -80,7 +80,7 @@ def test_rank_story_to_top(): assert top_story.DragAndDropRank < stories[1].DragAndDropRank def test_rank_story_to_bottom(): - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD, server_ping=False, isolated_workspace=True) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD, server_ping=False, isolated_workspace=True) response = rally.get('Story', fetch="FormattedID,Name,Description,State,DragAndDropRank", order="Rank ASC", limit=100) stories = [story for story in response] diff --git a/test/test_recyclebin.py b/test/test_recyclebin.py index 3af586d..d14c989 100644 --- a/test/test_recyclebin.py +++ b/test/test_recyclebin.py @@ -13,7 +13,7 @@ ################################################################################################## -from rally_targets import AGICEN, AGICEN_USER, AGICEN_PSWD +from rally_targets import RALLY, RALLY_USER, RALLY_PSWD from rally_targets import DEFAULT_WORKSPACE, NON_DEFAULT_PROJECT ################################################################################################## @@ -24,7 +24,7 @@ def test_basic_query(): issue a simple filtering query targeting RecycleBinEntry items whose Name value does not contain a specific value. """ - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD) response = rally.get('RecycleBinEntry', fetch="ObjectID,ID,Name", query='Name = "Gone but not forgotten with the wind"', limit=100) diff --git a/test/test_update.py b/test/test_update.py index fd84832..1e7a19e 100644 --- a/test/test_update.py +++ b/test/test_update.py @@ -17,7 +17,7 @@ ################################################################################################## -from rally_targets import AGICEN, AGICEN_USER, AGICEN_PSWD +from rally_targets import RALLY, RALLY_USER, RALLY_PSWD from rally_targets import YETI_USER, YETI_PSWD from rally_targets import DEFAULT_WORKSPACE, DEFAULT_PROJECT, NON_DEFAULT_PROJECT from rally_targets import BOONDOCKS_WORKSPACE, BOONDOCKS_PROJECT @@ -32,7 +32,7 @@ def test_update_defect_in_other_project(): Update the State value of a Defect identified by a FormattedID that is associated with a non-DEFAULT_PROJECT project in the DEFAULT_WORKSPACE. """ - rally = Rally(server=AGICEN, user=YETI_USER, password=YETI_PSWD, + rally = Rally(server=RALLY, user=YETI_USER, password=YETI_PSWD, workspace=DEFAULT_WORKSPACE, project=DEFAULT_PROJECT) response = rally.get('Project', fetch=False, limit=100) proj = response.next() diff --git a/test/test_wksprj_setting.py b/test/test_wksprj_setting.py index 44c70e4..f3cf0dc 100644 --- a/test/test_wksprj_setting.py +++ b/test/test_wksprj_setting.py @@ -10,7 +10,7 @@ ################################################################################################## -from rally_targets import AGICEN, AGICEN_USER, AGICEN_PSWD +from rally_targets import RALLY, RALLY_USER, RALLY_PSWD from rally_targets import DEFAULT_WORKSPACE, DEFAULT_PROJECT from rally_targets import ALTERNATE_WORKSPACE, ALTERNATE_PROJECT from rally_targets import API_KEY @@ -25,10 +25,10 @@ def test_get_default_workspace(): calling the getWorkspace method returns info for the default workspace. """ - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD) workspace = rally.getWorkspace() assert workspace.Name == DEFAULT_WORKSPACE - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD, workspace=DEFAULT_WORKSPACE) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD, workspace=DEFAULT_WORKSPACE) workspace = rally.getWorkspace() assert workspace.Name == DEFAULT_WORKSPACE @@ -40,7 +40,7 @@ def test_get_non_default_workspace(): workspace value, the getWorkspace call correctly returns the newly set workspace value. """ - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD) workspace = rally.getWorkspace() assert workspace.Name == DEFAULT_WORKSPACE rally.setWorkspace(ALTERNATE_WORKSPACE) @@ -55,7 +55,7 @@ def test_warn_on_setting_invalid_workspace(): something like a warning occurs and that a subsequent call to getWorkspace returns the default workspace. """ - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD) workspace = rally.getWorkspace() assert workspace.Name == DEFAULT_WORKSPACE py.test.raises(Exception, "rally.setWorkspace('Constant Misbehavior')") @@ -70,7 +70,7 @@ def test_warn_on_setting_invalid_project(): something like a warning occurs and that a subsequent call to getProject returns the default project. """ - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD) project = rally.getProject() assert project.Name == DEFAULT_PROJECT py.test.raises(Exception, "rally.setProject('Thorny Buxcuit Weevilz')") @@ -85,7 +85,7 @@ def test_disallow_project_value_invalid_for_workspace(): """ problem_text = "The current Workspace '%s' does not contain an accessible Project with the name of '%s'" % (DEFAULT_WORKSPACE, ALTERNATE_PROJECT) with py.test.raises(Exception) as excinfo: - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD, + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD, workspace=DEFAULT_WORKSPACE, project=ALTERNATE_PROJECT, server_ping=False) actualErrVerbiage = excinfo.value.args[0] assert excinfo.value.__class__.__name__ == 'RallyRESTAPIError' @@ -97,7 +97,7 @@ def test_get_project(): issue a simple query (no qualifying criteria) for a known valid Rally entity. """ - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD, workspace=DEFAULT_WORKSPACE, project='Sample Project') + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD, workspace=DEFAULT_WORKSPACE, project='Sample Project') response = rally.get('Project', fetch=False, limit=10) assert response.status_code == 200 assert response.errors == [] @@ -112,7 +112,7 @@ def test_get_named_project(): call the Rally.getProject using a valid (but non-default and non-current) project name as a parameter to the call. """ - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD, workspace=DEFAULT_WORKSPACE, project='Sample Project') + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD, workspace=DEFAULT_WORKSPACE, project='Sample Project') response = rally.get('Project', fetch=False, limit=10) assert response.status_code == 200 assert response.errors == [] @@ -129,7 +129,7 @@ def test_no_defaults_good_workspace_none_project(): problem_text = problem % (nd_workspace, none_project) with py.test.raises(Exception) as excinfo: - agicen = Rally(AGICEN, no_defaults_user, no_defaults_password, + rally = Rally(RALLY, no_defaults_user, no_defaults_password, workspace=nd_workspace, project=none_project, server_ping=False) actualErrVerbiage = excinfo.value.args[0] assert excinfo.value.__class__.__name__ == 'RallyRESTAPIError' @@ -142,7 +142,7 @@ def test_no_defaults_good_workspace_bad_project(): problem_text = problem % (nd_workspace, bad_project) with py.test.raises(Exception) as excinfo: - agicen = Rally(AGICEN, no_defaults_user, no_defaults_password, + rally = Rally(RALLY, no_defaults_user, no_defaults_password, workspace=nd_workspace, project=bad_project, server_ping=False) actualErrVerbiage = excinfo.value.args[0] assert excinfo.value.__class__.__name__ == 'RallyRESTAPIError' @@ -153,7 +153,7 @@ def test_ignore_defaults_use_good_workspace_none_project(): good_project = "Integrations Project" none_project = None - rally = Rally(server=AGICEN, username=AGICEN_USER, password=AGICEN_PSWD, apikey=API_KEY, + rally = Rally(server=RALLY, username=RALLY_USER, password=RALLY_PSWD, apikey=API_KEY, workspace=good_workspace, project=good_project, server_ping=False) workspace = rally.getWorkspace() project = rally.getProject() @@ -163,7 +163,7 @@ def test_ignore_defaults_use_good_workspace_none_project(): problem = "The current Workspace '%s' does not contain a Project with the name of '%s'" problem_text = problem % (good_workspace, none_project) with py.test.raises(Exception) as excinfo: - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD, apikey=API_KEY, + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD, apikey=API_KEY, workspace=good_workspace, project=none_project, server_ping=False) actualErrVerbiage = excinfo.value.args[0] @@ -180,7 +180,7 @@ def test_ignore_defaults_use_good_workspace_bad_project(): problem = "The current Workspace '%s' does not contain an accessible Project with the name of '%s'" problem_text = problem % (good_workspace, bad_project) with py.test.raises(Exception) as excinfo: - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD, apikey=API_KEY, + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD, apikey=API_KEY, workspace=good_workspace, project=bad_project, server_ping=False) actualErrVerbiage = excinfo.value.args[0] diff --git a/test/test_workspaces.py b/test/test_workspaces.py index 429bbea..1991796 100644 --- a/test/test_workspaces.py +++ b/test/test_workspaces.py @@ -8,7 +8,7 @@ ################################################################################################## -from rally_targets import AGICEN, AGICEN_USER, AGICEN_PSWD +from rally_targets import RALLY, RALLY_USER, RALLY_PSWD from rally_targets import DEFAULT_WORKSPACE, DEFAULT_PROJECT, NON_DEFAULT_PROJECT from rally_targets import ALTERNATE_WORKSPACE, ALTERNATE_PROJECT @@ -52,7 +52,7 @@ def test_default_context(): Furthermore the construction of a GET related URL will contain the correct workspace and project specifications in the QUERY_STRING. """ - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD, server_ping=False) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD, server_ping=False) context1 = rally.contextHelper.currentContext() workspace = rally.getWorkspace() project = rally.getProject() @@ -82,7 +82,7 @@ def test_default_isolated_workspace(): And any attempt to change the workspace via rally.setWorkspace(some_name) will result in a Exception being raised """ - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD, server_ping=False, isolated_workspace=True) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD, server_ping=False, isolated_workspace=True) context1 = rally.contextHelper.currentContext() workspace = rally.getWorkspace() project = rally.getProject() @@ -106,7 +106,7 @@ def test_default_isolated_workspace(): # 3 def test_explictly_set_workspace_as_default_context(): - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD, workspace=DEFAULT_WORKSPACE) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD, workspace=DEFAULT_WORKSPACE) workspace = rally.getWorkspace() assert workspace.Name == DEFAULT_WORKSPACE project = rally.getProject() @@ -123,7 +123,7 @@ def test_explictly_set_workspace_as_default_context(): # 4 def test_explictly_set_workspace_as_isolated_workspace(): - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD, workspace=DEFAULT_WORKSPACE, isolated_workspace=True) + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD, workspace=DEFAULT_WORKSPACE, isolated_workspace=True) workspace = rally.getWorkspace() assert workspace.Name == DEFAULT_WORKSPACE project = rally.getProject() @@ -143,7 +143,7 @@ def test_explictly_set_workspace_as_isolated_workspace(): # 5 def test_initial_workspace_not_default(): - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD, + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD, workspace=ALTERNATE_WORKSPACE, warn=False) # Because no project=name arg was supplied, the project will be the User's default project @@ -181,7 +181,7 @@ def test_initial_workspace_not_default(): # 6 def test_initial_non_default_workspace_as_isolated(): - rally = Rally(server=AGICEN, user=AGICEN_USER, password=AGICEN_PSWD, + rally = Rally(server=RALLY, user=RALLY_USER, password=RALLY_PSWD, workspace=ALTERNATE_WORKSPACE, warn=False, isolated_workspace=True) # Because no project=name arg was supplied, the project will be the User's default project