def test_real_does_not_use_cached_httplib2_client(self): """ When we use this slumber in Wimbledon project, we found that the request is occationally error on IE9 (got 500 response in return bcoz of the empty request). We are not sure if the root cause was in IE9 or in the httplib2, but stop caching httplib2's client fixed it (reset client everytime). The downside is we cannot use caching features of the httplib2's client, but until we understand this problem in more details, we'll stick with resetting client every time. juacompe, 2nd November 2011 ref: http://proteus.eidos.proteus-tech.com/project/BBG/story/BBG-251/card/ """ self.callers = [] class response: status = 200 content = '''{"apps":{}}''' def _request(_self, url, headers={}): self.callers.append(_self) r = response() return r, r.content with patch('slumber.connector.ua.Http.request',_request): get(self.cache_url) get(self.cache_url) self.assertNotEqual(self.callers[0], self.callers[1])
def test_real_does_not_use_cached_httplib2_client(self): """ When we use this slumber in Wimbledon project, we found that the request is occationally error on IE9 (got 500 response in return bcoz of the empty request). We are not sure if the root cause was in IE9 or in the httplib2, but stop caching httplib2's client fixed it (reset client everytime). The downside is we cannot use caching features of the httplib2's client, but until we understand this problem in more details, we'll stick with resetting client every time. juacompe, 2nd November 2011 ref: http://proteus.eidos.proteus-tech.com/project/BBG/story/BBG-251/card/ """ self.callers = [] class response: status = 200 content = '''{"apps":{}}''' def _request(_self, url, headers={}): self.callers.append(_self) r = response() return r, r.content with patch('slumber.connector.ua.Http.request', _request): get(self.cache_url) get(self.cache_url) self.assertNotEqual(self.callers[0], self.callers[1])
def test_cache_ttl(self): def _request(_self, url, headers={}): r = _response_httplib2() return r, r.content with patch('slumber.connector.ua.Http.request', _request): get(self.cache_url, 2) with patch('slumber.connector.ua.Http.request', self.fail): get(self.cache_url, 2)
def test_retry_get_still_can_fail(self): class _response: content = '' status = 500 def _request(_self, url, headers={}): r = _response() return r, r.content with patch('slumber.connector.ua.Http.request',_request): with self.assertRaises(AssertionError): get(self.cache_url)
def test_get_retries(self): class _response: content = '''{"apps":{}}''' def __init__(self, counter): self.status = 200 if counter == 1 else 404 counter = [] def _request(_self, url, headers={}): r = _response(len(counter)) counter.append(True) return r, r.content with patch('slumber.connector.ua.Http.request', _request): get(self.cache_url) self.assertEqual(len(counter), 2)
def get_all_permissions(self): """Forward access to all of the permissions. """ # We're accessing attributes that are provided by the other types # pylint: disable = E1101 _, json = get(self._operations['get-permissions'], self._CACHE_TTL) return set(json['all_permissions'])
def __getattr__(self, attr_name): """Fetch the application list from the Slumber directory on request. """ _, json = get(self._directory) apps = {} for app in json['apps'].keys(): root = apps for k in app.split('.'): if not root.has_key(k): root[k] = {} root = root[k] def recurse_apps(loc, this_level, name): """Recursively build the application connectors. """ current_appname = '.'.join(name) if json['apps'].has_key(current_appname): loc._url = urljoin(self._directory, json['apps'][current_appname]) for k, v in this_level.items(): app_cnx = AppConnector() setattr(loc, k, app_cnx) recurse_apps(app_cnx, v, name + [k]) recurse_apps(self, apps, []) if attr_name in apps.keys(): return getattr(self, attr_name) else: raise AttributeError(attr_name)
def pull_monitor(model_url, callback, delay=dict(minutes=1), page_url=None, floor=0, pull_priority=5, job_priority=5): """Used to look for instances that need to be pulled. This only works with models who use an auto-incremented primary key. """ if not page_url: model = get_model(model_url) instances_url = model._operations['instances'] else: instances_url = page_url _, json = get(instances_url or page_url) latest, highest = None, floor for item in json['page']: highest = max(item['pk'], highest) latest = item['pk'] if latest > floor: schedule(callback, args=[urljoin(instances_url, item['data'])], priority=job_priority) if json.has_key('next_page') and latest > floor: schedule('pubsubpull.async.pull_monitor', args=[model_url, callback], kwargs=dict(delay=delay, floor=floor, page_url=urljoin(instances_url, json['next_page']), pull_priority=pull_priority, job_priority=job_priority), priority=pull_priority) print("Got another page to process", json['next_page'], floor) if not page_url: run_after = timezone.now() + timedelta(**delay) schedule('pubsubpull.async.pull_monitor', run_after=run_after, args=[model_url, callback], kwargs=dict(delay=delay, floor=highest, pull_priority=pull_priority, job_priority=job_priority), priority=pull_priority) print("Looking for new instances above", highest)
def test_fifteen_shops(self): for s in xrange(1, 16): Shop.objects.create(name="Shop %d" % s, slug="shop%2d" % s) response, json = get('/slumber/shops/mount2/') self.assertEqual(response.status_code, 200) self.assertFalse(json.has_key('instances')) self.assertTrue(json.has_key('_links')) links = json['_links'] self.assertEqual(links['self']['href'], '/slumber/shops/mount2/') self.assertEqual(links['model']['href'], '/slumber/slumber_examples/Shop/') self.assertTrue(links.has_key('next'), links) self.assertTrue(json.has_key('_embedded')) self.assertTrue(json['_embedded'].has_key('page')) page = json['_embedded']['page'] self.assertEqual(len(page), 10) self.assertEqual(page[0], dict( _links={'self': dict(href='/slumber/pizzas/shop/15/')}, display="Shop 15")) self.assertEqual(links['next']['href'], '/slumber/shops/mount2/?lpk=6')
def __getattr__(self, name): _, json = get(self._url) for k, v in json['fields'].items(): setattr(self, k, from_json_data(self._url, v)) if name in json['fields'].keys(): return getattr(self, name) return _return_data_array(self._url, json['data_arrays'], self, name)
def test_mount_point(self): response, json = get('/slumber/shops/mount1/') self.assertEqual(response.status_code, 200) self.assertEqual(json, dict(_meta={'message': 'OK', 'status': 200}, shops=[{ 'name': 'Hard Coded Pizza Parlour' }]))
def test_mount_point(self): shop = Shop.objects.create(name="Test One") with patch("slumber.connector._get_slumber_authn_name", lambda: "service"): response, json = get("/slumber/pizzas/shop/%s/" % shop.pk) self.assertEqual(response.status_code, 200) self.assertTrue(json["operations"].has_key("data"), json) self.assertEqual(json["operations"]["data"], "/slumber/pizzas/shop/%s/" % shop.pk)
def test_fifteen_shops(self): for s in xrange(1, 16): Shop.objects.create(name="Shop %d" % s, slug="shop%2d" % s) response, json = get('/slumber/shops/mount2/') self.assertEqual(response.status_code, 200) self.assertFalse(json.has_key('instances')) self.assertTrue(json.has_key('_links')) links = json['_links'] self.assertEqual(links['self']['href'], '/slumber/shops/mount2/') self.assertEqual(links['model']['href'], '/slumber/slumber_examples/Shop/') self.assertTrue(links.has_key('next'), links) self.assertTrue(json.has_key('_embedded')) self.assertTrue(json['_embedded'].has_key('page')) page = json['_embedded']['page'] self.assertEqual(len(page), 10) self.assertEqual( page[0], dict(_links={'self': dict(href='/slumber/pizzas/shop/15/')}, display="Shop 15")) self.assertEqual(links['next']['href'], '/slumber/shops/mount2/?lpk=6')
def test_mount_point(self): shop = Shop.objects.create(name='Test One') with patch('slumber.connector._get_slumber_authn_name', lambda: 'service'): response, json = get('/slumber/pizzas/shop/%s/' % shop.pk) self.assertEqual(response.status_code, 200) self.assertTrue(json['operations'].has_key('data'), json) self.assertEqual(json['operations']['data'], '/slumber/pizzas/shop/%s/' % shop.pk)
def get(self, **kwargs): """Implements the client side for the model 'get' operator. """ assert len(kwargs), \ "You must supply kwargs to filter on to fetch the instance" url = urljoin(self._url, 'get/') _, json = get(url + '?' + urlencode(kwargs), self._CACHE_TTL) return get_instance_from_data(url, json)
def test_model_operation_with_mock_ua(self, expect): expect.get('http://pizzas/app/Model/test-op/', {'test': 'item'}) expect.post('http://pizzas/app/Model/test-op/', {'test': 'item'}, {'item': 'test'}) self.assertEqual(len(expect.expectations), 2) response1, json1 = get(client.pizzas.app.Model._operations['test-op']) self.assertEqual(json1, dict(test='item')) response2, json2 = post(client.pizzas.app.Model._operations['test-op'], json1) self.assertEqual(json2, dict(item='test'))
def has_perm(self, permission): """Forward the permission check. """ # We're accessing attributes that are provided by the other types # pylint: disable = E1101 url = urljoin(self._operations['has-permission'], permission) _, json = get(url, self._CACHE_TTL) return json['is-allowed']
def test_directory(self): request, json = get('http://localhost:8000/slumber/') self.assertEquals( json['services'], { 'auth': 'http://localhost:8000/slumber/pizzas/django/contrib/auth/', 'pizzas': 'http://localhost:8000/slumber/pizzas/' })
def get_profile(self): """Forward access to the profile. """ # We're accessing attributes that are provided by the other types # pylint: disable = E1101 base_url = self._operations['get-profile'] _, json = get(base_url, self._CACHE_TTL) return get_instance_from_data(base_url, json)
def test_xml(self): response, _ = get('/slumber/shops/mount2/?lpk=6', headers=dict(Accept='application/xml')) self.assertEqual(response.status_code, 200) self.assertEqual(response['Content-Type'], 'application/xml; charset=utf-8') xml = parseString(response.content) self.assertEqual(xml.documentElement.tagName, 'instances')
def has_module_perms(self, module): """Forward the permission check. """ # We're accessing attributes that are provided by the other types # pylint: disable = E1101 _, json = get(urljoin(self._operations['module-permissions'], module), self._CACHE_TTL) return json['has_module_perms']
def test_cache(self): try: r1, j1 = get('http://urquell-fn.appspot.com/lib/echo/?__=', 2) r2, j2 = get('http://urquell-fn.appspot.com/lib/echo/?__=', 2) r3, j3 = get('http://urquell-fn.appspot.com/lib/echo/?dummy=&__=', 2) self.assertFalse(hasattr(r1, 'from_cache')) self.assertTrue(hasattr(r2, 'from_cache')) self.assertTrue(r2.from_cache) self.assertFalse(hasattr(r3, 'from_cache')) except ServerNotFoundError: # If we get a server error then we presume that there is no good # Internet connection and don't fail the test pass except socket.error: pass except Exception, e: print type(e) raise e
def test_cache(self): try: r1, j1 = get('http://httpbin.org/headers', 2) r2, j2 = get('http://httpbin.org/headers', 2) r3, j3 = get('http://httpbin.org/headers?dummy=', 2) self.assertFalse(hasattr(r1, 'from_cache')) self.assertTrue(hasattr(r2, 'from_cache')) self.assertTrue(r2.from_cache) self.assertFalse(hasattr(r3, 'from_cache')) except ServerNotFoundError: # If we get a server error then we presume that there is no good # Internet connection and don't fail the test pass except socket.error: pass except Exception, e: print type(e) raise e
def test_directory(self): request, json = get("http://localhost:8000/slumber/") self.assertEquals( json["services"], { "auth": "http://localhost:8000/slumber/pizzas/django/contrib/auth/", "pizzas": "http://localhost:8000/slumber/pizzas/", }, )
def has_module_perms(self, module): """Forward the permission check. """ # We're accessing attributes that are provided by the other types # pylint: disable = E1101 _, json = get( urljoin(self._operations['module-permissions'], module), self._CACHE_TTL) return json['has_module_perms']
def test_model_operation_with_mock_ua_no_post_data_match(self, expect): expect.get('http://pizzas/app/Model/test-op/', {'test': 'item'}) expect.post('http://pizzas/app/Model/test-op/', None, {'item': 'test'}) self.assertEqual(len(expect.expectations), 2) response1, json1 = get(client.pizzas.app.Model._operations['test-op']) self.assertEqual(json1, dict(test='item')) response2, json2 = post(client.pizzas.app.Model._operations['test-op'], json1) self.assertEqual(json2, dict(item='test'))
def __getattr__(self, name): attrs = ['name', 'module'] if name in attrs: _, json = get(self._url) for attr in attrs: setattr(self, attr, json[attr]) return getattr(self, name) else: raise AttributeError(name)
def test_real(self): class response: status = 200 content = '''{"apps":{}}''' def _request(_self, url, headers={}): r = response() return r, r.content with patch('slumber.connector.ua.Http.request', _request): response, json = get(self.cache_url) self.assertEqual(json, {"apps":{}})
def test_empty_hal_version(self): response, json = get('/slumber/shops/mount2/') self.assertEqual(response.status_code, 200) self.assertFalse(json.has_key('instances')) self.assertTrue(json.has_key('_links')) links = json['_links'] self.assertEqual(links['self']['href'], '/slumber/shops/mount2/') self.assertEqual(links['model']['href'], '/slumber/slumber_examples/Shop/')
def pull_monitor(model_url, callback, delay=None, page_url=None, floor=0, pull_priority=5, job_priority=5, callback_kwargs=None): """Used to look for instances that need to be pulled. This only works with models who use an auto-incremented primary key. """ if callback_kwargs is None: callback_kwargs = {} if delay is None: delay = dict(minutes=1) if not page_url: model = get_model(model_url) instances_url = model._operations['instances'] else: instances_url = page_url _, json = get(instances_url or page_url) latest, highest = None, floor for item in json['page']: highest = max(item['pk'], highest) latest = item['pk'] if latest > floor: schedule(callback, args=[urljoin(instances_url, item['data'])], kwargs=callback_kwargs, priority=job_priority) if 'next_page' in json and latest > floor: schedule('pubsubpull.async.pull_monitor', args=[model_url, callback], kwargs=dict(callback_kwargs=callback_kwargs, delay=delay, floor=floor, job_priority=job_priority, page_url=urljoin(instances_url, json['next_page']), pull_priority=pull_priority), priority=pull_priority) print("Got another page to process", json['next_page'], floor) if not page_url and delay: run_after = timezone.now() + timedelta(**delay) schedule('pubsubpull.async.pull_monitor', args=[model_url, callback], run_after=run_after, priority=pull_priority, kwargs=dict(callback_kwargs=callback_kwargs, delay=delay, floor=highest, job_priority=job_priority, pull_priority=pull_priority)) print("Looking for new instances above", highest)
def get(self, **kwargs): """Implements the client side for the model 'get' operator. """ assert len(kwargs), \ "You must supply kwargs to filter on to fetch the instance" url = urljoin(self._url, 'get/') _, json = get(url + '?' + urlencode(kwargs)) return get_instance(self, urljoin(self._url, json['identity']), json['display'], **dict([(k, from_json_data(self._url, j)) for k, j in json['fields'].items()]))
def pull_up(model, callback, callback_kwargs=None, **kwargs): """Start a job monitoring new instance from latest instance. """ if callback_kwargs is None: callback_kwargs = {} kwargs['callback_kwargs'] = callback_kwargs model_instance = get_model(model) instance_url = model_instance._operations['instances'] _, json_data = get(instance_url) kwargs['floor'] = json_data['page'][0]['pk'] if json_data['page'] else 0 schedule('pubsubpull.async.pull_monitor', args=[model, callback], kwargs=kwargs)
def _fetch_data(self): """Force fetching the data for this instance. """ _, json = get(self._url, self._CACHE_TTL) # We need to set this outside of __init__ for it to work correctly # pylint: disable = W0201 self._operations = dict([(o, urljoin(self._url, u)) for o, u in json['operations'].items()]) for k, v in json['fields'].items(): setattr(self, k, from_json_data(self._url, v)) self._display = json['display'] return json
def __getattr__(self, name): attrs = ['name', 'module'] if name in attrs + ['_operations']: _, json = get(self._url, self._CACHE_TTL) # We need to set this outside of __init__ for it to work correctly # pylint: disable = W0201 self._operations = dict([(o, urljoin(self._url, u)) for o, u in json['operations'].items()]) for attr in attrs: setattr(self, attr, json[attr]) return getattr(self, name) else: raise AttributeError(name)
def _return_data_array(base_url, arrays, instance, name): """Implement the lazy fetching of the instance data. """ # Pylint makes a bad type deduction # pylint: disable=E1103 if name in arrays.keys(): data_array = [] _, data = get(urljoin(base_url, arrays[name])) while True: for obj in data['page']: model_url = urljoin(base_url, obj['type']) model = MODEL_URL_TO_SLUMBER_MODEL[model_url] instance_url = urljoin(base_url, obj['data']) data_array.append( get_instance(model, instance_url, obj['display'])) if data.has_key('next_page'): _, data = get(urljoin(base_url, data['next_page'])) else: break setattr(instance, name, data_array) return data_array else: raise AttributeError(name)
def _return_data_array(base_url, arrays, instance, name, cache_ttl): """Implement the lazy fetching of the instance data. """ # Pylint makes a bad type deduction # pylint: disable=E1103 if name in arrays.keys(): data_array = [] _, data = get(urljoin(base_url, arrays[name]), cache_ttl) while True: for obj in data['page']: model_url = urljoin(base_url, obj['type']) model = get_model(model_url) instance_url = urljoin(base_url, obj['data']) data_array.append( get_instance(model, instance_url, obj['display'])) if data.has_key('next_page'): _, data = get(urljoin(base_url, data['next_page']), cache_ttl) else: break setattr(instance, name, data_array) return data_array else: raise AttributeError(name)
def __getattr__(self, name): if not self._url: raise AttributeError(name) _, json = get(self._url) models = json['models'] for model_name, url in models.items(): model_url = urljoin(self._url, url) model = MODEL_URL_TO_SLUMBER_MODEL.get(model_url, None) if not model: model = ModelConnector(model_url) setattr(self, model_name, model) if name in models.keys(): return getattr(self, name) else: raise AttributeError(name)
def __getattr__(self, attr_name): """Fetch the application list from the Slumber directory on request. """ logging.debug("Looking for attribute %s on %s for directory %s", attr_name, self, self._directory) if not self._directory: logging.debug("Raising AttributeError as _directory is falsey") raise AttributeError(attr_name) _, json = get(self._directory) logging.debug( "Looking for attribute %s on %s resulted in these applications %s", attr_name, self, json) # Pylint gets confused by the JSON object # pylint: disable=E1103 json_apps = json.get('apps', {}) apps = {} for app in json_apps.keys(): root = apps for k in app.split('.'): if not root.has_key(k): root[k] = {} root = root[k] def recurse_apps(loc, this_level, name): """Recursively build the application connectors. """ current_appname = '.'.join(name) if json_apps.has_key(current_appname): loc._directory = urljoin(self._directory, json_apps[current_appname]) for k, v in this_level.items(): app_cnx = ServiceConnector(None) setattr(loc, k, app_cnx) recurse_apps(app_cnx, v, name + [k]) recurse_apps(self, apps, []) models = json.get('models', {}) for model_name, url in models.items(): model_url = urljoin(self._directory, url) model = get_model(model_url) setattr(self, model_name, model) if attr_name in models.keys(): return getattr(self, attr_name) elif attr_name in apps.keys(): return getattr(self, attr_name) else: raise AttributeError(attr_name, json)
def check_request(request): class response: status = 200 content = '''null''' def _request(_self, url, headers={}): backend = FostBackend() authz = headers['Authorization'] key = authz[5:5 + len(self.service.username)] signature = authz[6 + len(self.service.username):] logging.info('Authorization %s %s', key, signature) request.META['HTTP_X_FOST_TIMESTAMP'] = headers[ 'X-FOST-Timestamp'] request.META['HTTP_X_FOST_HEADERS'] = headers['X-FOST-Headers'] user = backend.authenticate(request=request, key=key, hmac=signature) self.assertTrue(user) r = response() return r, r.content with patch('slumber.connector.ua.Http.request', _request): response, json = get('http://example.com/') return HttpResponse(response.content, 'text/plain')
def do_get(): with patch('slumber.connector.ua._calculate_signature', self.signature_with_username): get('/slumber/')
def test_slumber(self): response, json = get('/slumber/pizza/') self.assertEquals(response.status_code, 200, response) self.assertTrue(json.has_key('apps'), json) self.assertTrue(json['apps'].has_key('pubsubpull'), json)
def test_get_with_wrong_query(self, expect): expect.get('/?query1', {}) get('/?another')
def test_no_expectated_post_got_get(self, expect): expect.post('/', {}, {}) get('/')
def test_no_get_expectation(self, expect): get('/')