import time
from mock import patch, Mock
from nose.tools import assert_equal, assert_raises, assert_false
from pyrally.rally_access import RallyAccessor, get_accessor, MEM_CACHE
@patch('pyrally.rally_access.ACCESSOR')
@patch('pyrally.rally_access.RallyAccessor')
[docs]def test_get_accessor_returns_created_accessor_if_available(RallyAccessor,
ACCESSOR):
"""
Test ``get_accessor`` returns created.
Test that :py:func:`~pyrally.rally_access.get_accessor` returns the created
accessor if it is available.
"""
assert_equal(get_accessor(), ACCESSOR)
assert_false(RallyAccessor.called)
@patch('pyrally.rally_access.ACCESSOR', None)
@patch('pyrally.rally_access.RallyAccessor')
[docs]def test_get_accessor_creates_accessor_if_not_already_created(RallyAccessor):
"""
Test ``get_accessor`` returns created.
Test that :py:func:`~pyrally.rally_access.get_accessor` creates
and returns a :py:class:`~pyrally.rally_access.RallyAccessor` object if it
has not already been created.
"""
assert_equal(get_accessor('uname', 'pword', 'base_url'),
RallyAccessor.return_value)
assert_equal(RallyAccessor.call_args[0], ('uname', 'pword', 'base_url'))
@patch('pyrally.rally_access.ACCESSOR', None)
@patch('pyrally.rally_access.RallyAccessor')
[docs]def test_get_accessor_raises_exception_if_not_created_and_no_uname_password(
RallyAccessor):
"""
Test ``get_accessor`` raises Exception.
Test that :py:func:`~pyrally.rally_access.get_accessor` raises an Exception
if global ACCESSOR is None and called with no username and password
arguments.
"""
assert_raises(Exception, get_accessor)
assert_false(RallyAccessor.called)
[docs]def test_make_url_safe():
"""Test that :py:meth:`.RallyAccessor.make_url_safe` works correctly."""
my_accessor = RallyAccessor('uname', 'pword', 'base_url')
for url, expected_outcome in [
(' ', "%20"),
('(', "%28"),
(')', "%29"),
('"', "%22"),
('(Hello = "Fred")',
'%28Hello%20=%20%22Fred%22%29')]:
assert_equal(my_accessor.make_url_safe(url), expected_outcome)
@patch('pyrally.rally_access.urllib2')
[docs]def test_make_api_call_full_url_cached(urllib2):
"""
Test ``make_api_call`` with full, cached url.
Tests that :py:meth:`~.RallyAccessor.make_api_call`:
* looks in the cache first and does not make a urllib call.
* uses the url given without amendments.
* makes the url given safe.
"""
my_accessor = RallyAccessor('uname', 'pword', 'base_url')
my_accessor.get_from_cache = Mock()
my_accessor.make_url_safe = Mock()
my_accessor.make_url_safe.return_value = 'safe-url'
my_accessor.get_from_cache.return_value = 'Data'
response = my_accessor.make_api_call('some-url', full_url=True)
assert_equal(response, 'Data')
assert_equal(my_accessor.make_url_safe.call_args[0], ('some-url',))
assert_equal(my_accessor.get_from_cache.call_args[0], ('safe-url',))
assert_false(urllib2.urlopen.called)
@patch('pyrally.rally_access.urllib2')
[docs]def test_make_api_call_partial_url_not_cached(urllib2):
"""
Test ``make_api_call`` with partial url, not cached.
Tests that :py:meth:`~.RallyAccessor.make_api_call`:
* makes a call to the API via urllib.
* prepends the api_url to the partial url.
* makes the url given safe.
* stores the new data in the cache.
"""
MEM_CACHE.clear()
my_accessor = RallyAccessor('uname', 'pword', 'base_url')
my_accessor.api_url = 'http://dummy_url/'
my_accessor.get_from_cache = Mock()
my_accessor.set_to_cache = Mock()
my_accessor.make_url_safe = Mock()
my_accessor._get_json_response = Mock()
my_accessor.make_url_safe.return_value = 'safe-url'
my_accessor.get_from_cache.return_value = False
my_accessor._get_json_response.return_value = 'python_dict'
response = my_accessor.make_api_call('some-url', full_url=True)
assert_equal(response, 'python_dict')
assert_equal(my_accessor.make_url_safe.call_args[0], ('some-url',))
assert_equal(my_accessor._get_json_response.call_args[0],
(urllib2.Request.return_value,))
assert_equal(my_accessor.get_from_cache.call_args[0], ('safe-url',))
assert_equal(my_accessor.set_to_cache.call_args[0], ('safe-url',
'python_dict'))
[docs]def test_set_cache_timeout():
"""Test ``set_cache_timeout`` adds the timeout given correctly.
Tests :py:meth:`~.RallyAccessor.set_cache_timeout`.
"""
my_accessor = RallyAccessor('uname', 'pword', 'base_url')
my_accessor.set_cache_timeout('object_name', 10)
assert_equal(my_accessor.cache_timeouts, {'object_name': 10})
[docs]def test_delete_from_cache_removes_correctly():
"""Test ``delete_from_cache`` removes the correct key from the cache.
Tests :py:meth:`~.RallyAccessor.delete_from_cache`.
"""
MEM_CACHE.clear()
my_accessor = RallyAccessor('uname', 'pword', 'base_url')
MEM_CACHE['cache_key']['cache_lookup'] = 'some_test_data'
my_accessor.delete_from_cache('cache_key', 'cache_lookup')
assert_equal(MEM_CACHE, {'cache_key': {}})
[docs]def test_delete_from_cache_handles_missing_key():
"""Test ``delete_from_cache`` handles missing key being deleted.
Tests that :py:meth:`~.RallyAccessor.delete_from_cache` does not raise an
exception when a key is deleted that doesn't exist.
"""
MEM_CACHE.clear()
my_accessor = RallyAccessor('uname', 'pword', 'base_url')
my_accessor.delete_from_cache('story', 'key')
assert_equal(MEM_CACHE, {'story': {}})
[docs]def test_get_cacheable_info():
"""
Test ``get_cacheable_info``.
Tests that :py:meth:`~.RallyAccessor.get_cacheable_info`:
* returns the right cache key and index
"""
my_accessor = RallyAccessor('uname', 'pword', 'base_url')
my_accessor.api_url = 'http://dummy_url/'
for url, expected_tuple in [
('obj.js?query=something', ('obj_query', 'query=something')),
('obj/303923.js', ('obj', '303923')),
]:
full_url = "{0}{1}".format(my_accessor.api_url, url)
assert_equal(my_accessor.get_cacheable_info(full_url), expected_tuple)
[docs]def test_get_from_cache_retrieves_correctly():
"""
Test ``get_from_cache``.
Tests that :py:meth:`~.RallyAccessor.get_from_cache`:
* returns the data at the specified cache location if present
* returns False if not present
"""
MEM_CACHE.clear()
MEM_CACHE['cache_key']['cache_lookup'] = ('data', time.time())
my_accessor = RallyAccessor('uname', 'pword', 'base_url')
my_accessor.get_cacheable_info = Mock()
my_accessor.cache_timeouts = Mock()
my_accessor.cache_timeouts.get.return_value = 10
for (key, index), expected_result in [
(('cache_key', 'cache_lookup'), 'data'),
(('another_key', 'another_lookup'), False),
]:
my_accessor.get_cacheable_info.reset_mock()
my_accessor.get_cacheable_info.return_value = (key, index)
assert_equal(my_accessor.get_from_cache('url'), expected_result)
assert_equal(my_accessor.get_cacheable_info.call_args[0], ('url',))
assert_equal(my_accessor.cache_timeouts.get.call_args[0],
(key, my_accessor.default_cache_timeout))
[docs]def test_get_from_cache_returns_false_if_out_of_date():
"""
Test ``get_from_cache``.
Tests that :py:meth:`~.RallyAccessor.get_from_cache`:
* returns False if data is out of date
"""
MEM_CACHE.clear()
MEM_CACHE['cache_key']['cache_lookup'] = ('data', 0)
my_accessor = RallyAccessor('uname', 'pword', 'base_url')
my_accessor.get_cacheable_info = Mock()
my_accessor.cache_timeouts = Mock()
my_accessor.cache_timeouts.get.return_value = 0
my_accessor.get_cacheable_info.return_value = ('cache_key', 'cache_lookup')
assert_equal(my_accessor.get_from_cache('url'), False)
@patch('pyrally.rally_access.time')
[docs]def test_set_to_cache_adds_correctly(time_import):
"""
Test ``set_to_cache``.
Tests that :py:meth:`~.RallyAccessor.set_to_cache`:
* adds the given data to the cache.
"""
MEM_CACHE.clear()
my_accessor = RallyAccessor('uname', 'pword', 'base_url')
my_accessor.get_cacheable_info = Mock()
my_accessor.get_cacheable_info.return_value = ('cache_key', 'cache_lookup')
my_accessor.set_to_cache('url', 'set_to_cache_test')
assert_equal(my_accessor.get_cacheable_info.call_args[0], ('url',))
assert_equal(MEM_CACHE,
{'cache_key':
{'cache_lookup':
('set_to_cache_test',
time_import.time.return_value)}})