diff --git a/tests/api_tests.py b/tests/api_tests.py index 0daf051b..30656ded 100644 --- a/tests/api_tests.py +++ b/tests/api_tests.py @@ -32,17 +32,31 @@ if PY3: else: string_types = basestring, -class TestApiCalls(unittest.TestCase): +class OBS: """ - Tests for various api calls to ensure we return expected content + Class trying to simulate a simple OBS """ - responses = { 'GET': {}, 'PUT': {}, 'POST': {}, 'ALL': {} } + responses = { } + + def __init__(self): + """ + Initialize the configuration and create basic OBS instance + """ + + self.reset_config() + + def reset_config(self): + self._clear_responses() def _clear_responses(self): """ Reset predefined responses """ self.responses = { 'GET': {}, 'PUT': {}, 'POST': {}, 'ALL': {} } + # Add methods to manipulate reviews + self._request_review() + # Add methods to search requests + self._request_search() def _pretty_callback(self, request, uri, headers): """ @@ -55,31 +69,147 @@ class TestApiCalls(unittest.TestCase): :param uri: uri as provided to callback function by HTTPretty :param headers: headers as provided to callback function by HTTPretty """ + + # Get path path = re.match( r'.*localhost([^?]*)(\?.*)?',uri).group(1) reply = None + # Try to find a fallback if self.responses['ALL'].has_key(path): reply = self.responses['ALL'][path] + # Try to find a specific method if self.responses[request.method].has_key(path): reply = self.responses[request.method][path] + # We have something to reply with if reply: + # It's a list, so take the first if isinstance(reply, list): reply = reply.pop(0) + # It's string if isinstance(reply, string_types): - return (200, headers, reply) + # It's XML + if reply.startswith('<'): + return (200, headers, reply) + # It's fixture + else: + return (200, headers, _get_fixture_content(reply)) + # All is left is callback function else: return (200, headers, reply(self.responses, request, uri)) + # No possible response found else: if len(path) == 0: path = uri raise BaseException("No response for {0} on {1} provided".format(request.method,path)) - def _register_pretty(self): + # Initial request data + requests_data = { '123': { 'request': 'new', 'review': 'accepted', + 'who': 'Admin', 'by': 'group', 'id': '123', + 'by_who': 'opensuse-review-team', + 'package': 'gcc' }, + '321': { 'request': 'review', 'review': 'new', + 'who': 'Admin', 'by': 'group', 'id': '321', + 'by_who': 'factory-staging', + 'package': 'puppet' } + } + + def _request_review(self): + """ + Register requests methods + """ + + # Load template + tmpl = Template(self._get_fixture_content('request_review.xml')) + + # What happens when we try to change the review + def review_change(responses, request, uri): + rq_id = re.match( r'.*/([0-9]+)',uri).group(1) + args = self.requests_data[rq_id] + # Adding review + if request.querystring.has_key(u'cmd') and request.querystring[u'cmd'] == [u'addreview']: + self.requests_data[rq_id]['request'] = 'review' + self.requests_data[rq_id]['review'] = 'new' + # Changing review + if request.querystring.has_key(u'cmd') and request.querystring[u'cmd'] == [u'changereviewstate']: + self.requests_data[rq_id]['request'] = 'new' + self.requests_data[rq_id]['review'] = request.querystring[u'newstate'][0] + # Project review + if request.querystring.has_key(u'by_project'): + self.requests_data[rq_id]['by'] = 'project' + self.requests_data[rq_id]['by_who'] = request.querystring[u'by_project'][0] + # Group review + if request.querystring.has_key(u'by_group'): + self.requests_data[rq_id]['by'] = 'group' + self.requests_data[rq_id]['by_who'] = request.querystring[u'by_group'][0] + responses['GET']['/request/' + rq_id] = tmpl.substitute(self.requests_data[rq_id]) + return responses['GET']['/request/' + rq_id] + + # Register methods for all requests + for rq in self.requests_data: + self.responses['GET']['/request/' + rq] = tmpl.substitute(self.requests_data[rq]) + self.responses['ALL']['/request/' + rq] = review_change + + def _request_search(self): + """ + Allows searching for requests + """ + def request_search(responses, request, uri): + # Searching for requests that has open review for staging group + if request.querystring.has_key(u'match') and request.querystring[u'match'][0] == u"state/@name='review' and review[@by_group='factory-staging' and @state='new']": + rqs = [] + # Itereate through all requests + for rq in self.requests_data: + # Find the ones matching the condition + if self.requests_data[rq]['request'] == 'review' and self.requests_data[rq]['review'] == 'new' and self.requests_data[rq]['by'] == 'group' and self.requests_data[rq]['by_who'] == 'factory-staging': + rqs.append(rq) + # Create response + ret_str = '' + for rq in rqs: + ret_str += responses['GET']['/request/' + rq] + ret_str += '' + return ret_str + # We are searching for something else, we don't know the answer + raise BaseException("No search results defined for " + pprint.pformat(request.querystring)) + self.responses['GET']['/search/request'] = request_search + + def register_obs(self): """ Register custom callback for HTTPretty """ httpretty.register_uri(httpretty.GET,re.compile(r'/.*localhost.*/'),body=self._pretty_callback) httpretty.register_uri(httpretty.PUT,re.compile(r'/.*localhost.*/'),body=self._pretty_callback) httpretty.register_uri(httpretty.POST,re.compile(r'/.*localhost.*/'),body=self._pretty_callback) + self.reset_config() + # Initiate the api with mocked rings + with mock_generate_ring_packages(): + self.api = oscs.StagingAPI('http://localhost') + + def _get_fixtures_dir(self): + """ + Return path for fixtures + """ + return os.path.join(os.getcwd(), 'tests/fixtures') + + def _get_fixture_path(self, filename): + """ + Return path for fixture + """ + return os.path.join(self._get_fixtures_dir(), filename) + + def _get_fixture_content(self, filename): + """ + Return content of fixture + """ + response = open(self._get_fixture_path(filename), 'r') + content = response.read() + response.close() + return content + +class TestApiCalls(unittest.TestCase): + """ + Tests for various api calls to ensure we return expected content + """ + + obs = OBS() def _get_fixtures_dir(self): """ @@ -165,22 +295,17 @@ class TestApiCalls(unittest.TestCase): Test dispatching and closure of non-ring packages """ - # Initiate the pretty overrides - self._register_pretty_url_get('http://localhost/search/request?match=state/@name=\'review\'+and+review[@by_group=\'factory-staging\'+and+@state=\'new\']', - 'open-requests.xml') - - # There should be just one request that gets closed - # We don't care about the return so just reuse the above :P - # If there is bug in the function we get assertion about closing more issues than we should - self._register_pretty_url_post('http://localhost/request/220956?comment=No+need+for+staging%2C+not+in+tested+ring+project.&newstate=accepted&by_group=factory-staging&cmd=changereviewstate', - 'open-requests.xml') - - # Initiate the api with mocked rings - with mock_generate_ring_packages(): - api = oscs.StagingAPI('http://localhost') - - # get the open requests - requests = api.dispatch_open_requests() + # Register OBS + self.obs.register_obs() + # Get rid of open requests + self.obs.api.dispatch_open_requests() + # Check that we tried to close it + self.assertEqual(httpretty.last_request().method, 'POST') + self.assertEqual(httpretty.last_request().querystring[u'cmd'], [u'changereviewstate']) + # Try it again + self.obs.api.dispatch_open_requests() + # This time there should be nothing to close + self.assertEqual(httpretty.last_request().method, 'GET') @httpretty.activate def test_pseudometa_get_prj(self): @@ -306,41 +431,25 @@ class TestApiCalls(unittest.TestCase): Test whether accepting/creating reviews behaves correctly """ - with mock_generate_ring_packages(): - api = oscs.StagingAPI('http://localhost') - - tmpl = Template(self._get_fixture_content('request_review.xml')) - - self._clear_responses() - self.responses['GET']['/request/123'] = tmpl.substitute(request='new', review='accepted', review_project="openSUSE:Factory") - - def review_change(responses, request, uri): - if request.querystring.has_key(u'cmd') and request.querystring[u'cmd'] == [u'addreview']: - responses['GET']['/request/123'] = tmpl.substitute(request='review', review='new', review_project=request.querystring[u'by_project'][0]) - if request.querystring.has_key(u'cmd') and request.querystring[u'cmd'] == [u'changereviewstate']: - responses['GET']['/request/123'] = tmpl.substitute(request='new', review=request.querystring[u'newstate'][0], review_project=request.querystring[u'by_project'][0]) - return responses['GET']['/request/123'] - - self.responses['ALL']['/request/123'] = review_change - - self._register_pretty() + # Register OBS + self.obs.register_obs() # Add review - api.add_review('123', 'openSUSE:Factory:Staging:A') + self.obs.api.add_review('123', 'openSUSE:Factory:Staging:A') self.assertEqual(httpretty.last_request().method, 'POST') self.assertEqual(httpretty.last_request().querystring[u'cmd'], [u'addreview']) # Try to readd, should do anything - api.add_review('123', 'openSUSE:Factory:Staging:A') + self.obs.api.add_review('123', 'openSUSE:Factory:Staging:A') self.assertEqual(httpretty.last_request().method, 'GET') # Accept review - api.set_review('123', 'openSUSE:Factory:Staging:A') + self.obs.api.set_review('123', 'openSUSE:Factory:Staging:A') self.assertEqual(httpretty.last_request().method, 'POST') self.assertEqual(httpretty.last_request().querystring[u'cmd'], [u'changereviewstate']) # Try to accept it again should do anything - api.set_review('123', 'openSUSE:Factory:Staging:A') + self.obs.api.set_review('123', 'openSUSE:Factory:Staging:A') self.assertEqual(httpretty.last_request().method, 'GET') # But we should be able to reopen it - api.add_review('123', 'openSUSE:Factory:Staging:A') + self.obs.api.add_review('123', 'openSUSE:Factory:Staging:A') self.assertEqual(httpretty.last_request().method, 'POST') self.assertEqual(httpretty.last_request().querystring[u'cmd'], [u'addreview']) diff --git a/tests/fixtures/open-requests.xml b/tests/fixtures/open-requests.xml deleted file mode 100644 index 90133c4b..00000000 --- a/tests/fixtures/open-requests.xml +++ /dev/null @@ -1,255 +0,0 @@ - - - - - - - - Check Staging Project - - - {"approve": "preliminary, version number changed"} <!-- { - "dest": { - "ldb": { - "prodonly": "", - "review": "done", - "rpm_license": "{\"python3.spec\":{\"curses\":[\"Python-2.0\"],\"python3\":[\"Python-2.0\"],\"dbm\":[\"Python-2.0\"],\"tk\":[\"Python-2.0\"]},\"python3-base.spec\":{\"-n python3-tools\":[\"Python-2.0\"],\"-n python3-idle\":[\"Python-2.0\"],\"python3-base\":[\"Python-2.0\"],\"-n python3-testsuite\":[\"Python-2.0\"],\"-n libpython%{so_version}\":[\"Python-2.0\"],\"-n python3-devel\":[\"Python-2.0\"]},\"python3-doc.spec\":{\"python3-doc\":[\"Python-2.0\"],\"pdf\":[\"Python-2.0\"]}}", - "status": "production", - "version": "3.3.3" - }, - "license": { - "python3-base.spec": { - "-n libpython%{so_version}": [ - "Python-2.0" - ], - "-n python3-devel": [ - "Python-2.0" - ], - "-n python3-idle": [ - "Python-2.0" - ], - "-n python3-testsuite": [ - "Python-2.0" - ], - "-n python3-tools": [ - "Python-2.0" - ], - "python3-base": [ - "Python-2.0" - ] - }, - "python3-doc.spec": { - "pdf": [ - "Python-2.0" - ], - "python3-doc": [ - "Python-2.0" - ] - }, - "python3.spec": { - "curses": [ - "Python-2.0" - ], - "dbm": [ - "Python-2.0" - ], - "python3": [ - "Python-2.0" - ], - "tk": [ - "Python-2.0" - ] - } - }, - "version": "3.3.3" - }, - "hint": [ - "version changed: src('3.3.3') differs from dest('3.4.0~b3')" - ], - "plugin": "0.60", - "src": { - "auto-co": "/api.opensuse.org/devel:languages:python:Factory/python3%3.4.0~b3%r125", - "kiwi_only": false, - "license": { - "python3-base.spec": { - "-n libpython%{so_version}": [ - "Python-2.0" - ], - "-n python3-devel": [ - "Python-2.0" - ], - "-n python3-idle": [ - "Python-2.0" - ], - "-n python3-testsuite": [ - "Python-2.0" - ], - "-n python3-tools": [ - "Python-2.0" - ], - "python3-base": [ - "Python-2.0" - ] - }, - "python3-doc.spec": { - "pdf": [ - "Python-2.0" - ], - "python3-doc": [ - "Python-2.0" - ] - }, - "python3.spec": { - "curses": [ - "Python-2.0" - ], - "dbm": [ - "Python-2.0" - ], - "python3": [ - "Python-2.0" - ], - "tk": [ - "Python-2.0" - ] - } - }, - "rev": "125", - "version": "3.4.0~b3", - "version_diff": "src('3.3.3') differs from dest('3.4.0~b3')" - } -} --> - - - Check script succeeded - - - ok - - - Builds for repo openSUSE_Factory - - - Check Staging Project - - - - Please review sources - - - Please review build success - - - initial commit of 3.4.0 beta 3 - * new stdlib modules: pathlib, enum, statistics, tracemalloc - * asynchronous IO with new asyncio module - * introspection data for builtins - * subprocesses no longer inherit open file descriptors - * standardized metadata for packages - * internal hashing changed to SipHash - * new pickle protocol - * improved handling of codecs - * TLS 1.2 support - * major speed improvements for internal unicode handling - * many bugfixes and optimizations -- see porting guide at: - http://docs.python.org/3.4/whatsnew/3.4.html#porting-to-python-3-4 -- moved several modules to -testsuite subpackage -- updated list of binary extensions, refreshed patches -- tracemalloc_gcov.patch fixes profile-based optimization build -- updated packages and pre_checkin.sh to use ~-version notation - for prereleases -- fix-shebangs part of build process moved to common %prep -- drop python-3.3.2-no-REUSEPORT.patch (upstreamed) -- update baselibs for new soname -- TODOs: - * require python-pip, make ensurepip work with zypper - -- initial commit of 3.4.0 beta2 - * for full changelog, see python3-base - -- initial commit of 3.4.0 beta 3 - * for full changelog, see python3-base - - - - - - - - Check Staging Project - - - {"approve": "preliminary, version number changed"} <!-- { - "dest": { - "ldb": { - "review": "never", - "rpm_license": "{\"xf86-video-intel.spec\":{\"xf86-video-intel\":[\"MIT\"]}}", - "status": "production", - "version": "2.99.909" - }, - "license": { - "xf86-video-intel.spec": { - "xf86-video-intel": [ - "MIT" - ] - } - }, - "version": "2.99.909" - }, - "hint": [ - "no ldb.risk defined, okay as per bnc#771677. ", - "ldb.review was (apparently) never done" - ], - "plugin": "0.60", - "src": { - "auto-co": "/api.opensuse.org/X11:XOrg/xf86-video-intel%2.99.910%r110", - "kiwi_only": false, - "license": { - "xf86-video-intel.spec": { - "xf86-video-intel": [ - "MIT" - ] - } - }, - "rev": "110", - "version": "2.99.910", - "version_diff": null - } -} --> - - - Check script succeeded - - - Please review sources - - - Please review build success - - - Check Staging Project - - - - Please review sources - - - Please review build success - - - Update to 3.0 prerelease 2.99.910 - * Only discard damage when overwriting the dirty CPU bo, instead - of discarding damage that will be shown! - * Reset operation state when switching between glyph caches. - https://bugs.freedesktop.org/show_bug.cgi?id=74494 - * Fully reinitialise pixmaps allocated from the freed cache. Fixes - a potential issue (crash or misrendering) when using some compositors. - https://bugs.freedesktop.org/show_bug.cgi?id=74550 - * Do not expose the TexturedVideo adaptor in UXA when it is disabled - either due to a hung GPU or explicitly disabled by the user. - * Restore the pipe stall when changing CC state on gen6, otherwise - the GPU may not flush intermediate results from all EU resulting - in render corruption (usually the occasional black box). - Regression from 2.99.906 - https://bugs.freedesktop.org/show_bug.cgi?id=7237 - - diff --git a/tests/fixtures/request_review.xml b/tests/fixtures/request_review.xml index 21360bfc..e774c24b 100644 --- a/tests/fixtures/request_review.xml +++ b/tests/fixtures/request_review.xml @@ -1,14 +1,13 @@ - - + - - + + - Passed staging project "openSUSE:Factory:Staging:test" + ... - - Passed staging project "openSUSE:Factory:Staging:test" + + ... test