270 lines
8.8 KiB
Python
270 lines
8.8 KiB
Python
import six
|
|
import os
|
|
import yaml
|
|
|
|
from testtools import TestCase
|
|
from mock import (
|
|
patch,
|
|
MagicMock,
|
|
call,
|
|
)
|
|
|
|
from charmhelpers import fetch
|
|
|
|
if six.PY3:
|
|
from urllib.parse import urlparse
|
|
builtin_open = 'builtins.open'
|
|
else:
|
|
from urlparse import urlparse
|
|
builtin_open = '__builtin__.open'
|
|
|
|
|
|
FAKE_APT_CACHE = {
|
|
# an installed package
|
|
'vim': {
|
|
'current_ver': '2:7.3.547-6ubuntu5'
|
|
},
|
|
# a uninstalled installation candidate
|
|
'emacs': {
|
|
}
|
|
}
|
|
|
|
|
|
def fake_apt_cache(in_memory=True, progress=None):
|
|
def _get(package):
|
|
pkg = MagicMock()
|
|
if package not in FAKE_APT_CACHE:
|
|
raise KeyError
|
|
pkg.name = package
|
|
if 'current_ver' in FAKE_APT_CACHE[package]:
|
|
pkg.current_ver.ver_str = FAKE_APT_CACHE[package]['current_ver']
|
|
else:
|
|
pkg.current_ver = None
|
|
return pkg
|
|
cache = MagicMock()
|
|
cache.__getitem__.side_effect = _get
|
|
return cache
|
|
|
|
|
|
def getenv(update=None):
|
|
# return a copy of os.environ with update applied.
|
|
# this was necessary because some modules modify os.environment directly
|
|
copy = os.environ.copy()
|
|
if update is not None:
|
|
copy.update(update)
|
|
return copy
|
|
|
|
|
|
class FetchTest(TestCase):
|
|
|
|
@patch('charmhelpers.fetch.log')
|
|
@patch.object(fetch, 'config')
|
|
@patch.object(fetch, 'add_source')
|
|
def test_configure_sources_single_source(self, add_source, config, log):
|
|
config.side_effect = ['source', 'key']
|
|
fetch.configure_sources()
|
|
add_source.assert_called_with('source', 'key')
|
|
|
|
@patch.object(fetch, 'config')
|
|
@patch.object(fetch, 'add_source')
|
|
def test_configure_sources_null_source(self, add_source, config):
|
|
config.side_effect = [None, None]
|
|
fetch.configure_sources()
|
|
self.assertEqual(add_source.call_count, 0)
|
|
|
|
@patch.object(fetch, 'config')
|
|
@patch.object(fetch, 'add_source')
|
|
def test_configure_sources_empty_source(self, add_source, config):
|
|
config.side_effect = ['', '']
|
|
fetch.configure_sources()
|
|
self.assertEqual(add_source.call_count, 0)
|
|
|
|
@patch.object(fetch, 'config')
|
|
@patch.object(fetch, 'add_source')
|
|
def test_configure_sources_single_source_no_key(self, add_source, config):
|
|
config.side_effect = ['source', None]
|
|
fetch.configure_sources()
|
|
add_source.assert_called_with('source', None)
|
|
|
|
@patch.object(fetch, 'config')
|
|
@patch.object(fetch, 'add_source')
|
|
def test_configure_sources_multiple_sources(self, add_source, config):
|
|
sources = ["sourcea", "sourceb"]
|
|
keys = ["keya", None]
|
|
config.side_effect = [
|
|
yaml.dump(sources),
|
|
yaml.dump(keys)
|
|
]
|
|
fetch.configure_sources()
|
|
add_source.assert_has_calls([
|
|
call('sourcea', 'keya'),
|
|
call('sourceb', None)
|
|
])
|
|
|
|
@patch.object(fetch, 'config')
|
|
@patch.object(fetch, 'add_source')
|
|
def test_configure_sources_missing_keys(self, add_source, config):
|
|
sources = ["sourcea", "sourceb"]
|
|
keys = ["keya"] # Second key is missing
|
|
config.side_effect = [
|
|
yaml.dump(sources),
|
|
yaml.dump(keys)
|
|
]
|
|
self.assertRaises(fetch.SourceConfigError, fetch.configure_sources)
|
|
|
|
@patch.object(fetch, '_fetch_update')
|
|
@patch.object(fetch, 'config')
|
|
@patch.object(fetch, 'add_source')
|
|
def test_configure_sources_update_called_ubuntu(self, add_source, config,
|
|
update):
|
|
config.side_effect = ['source', 'key']
|
|
fetch.configure_sources(update=True)
|
|
add_source.assert_called_with('source', 'key')
|
|
self.assertTrue(update.called)
|
|
|
|
|
|
class InstallTest(TestCase):
|
|
|
|
def setUp(self):
|
|
super(InstallTest, self).setUp()
|
|
self.valid_urls = (
|
|
"http://example.com/foo.tar.gz",
|
|
"http://example.com/foo.tgz",
|
|
"http://example.com/foo.tar.bz2",
|
|
"http://example.com/foo.tbz2",
|
|
"http://example.com/foo.zip",
|
|
"http://example.com/foo.zip?bar=baz&x=y#whee",
|
|
"ftp://example.com/foo.tar.gz",
|
|
"https://example.com/foo.tgz",
|
|
"file://example.com/foo.tar.bz2",
|
|
"bzr+ssh://example.com/branch-name",
|
|
"bzr+ssh://example.com/branch-name/",
|
|
"lp:branch-name",
|
|
"lp:example/branch-name",
|
|
)
|
|
self.invalid_urls = (
|
|
"git://example.com/foo.tar.gz",
|
|
"http://example.com/foo",
|
|
"http://example.com/foobar=baz&x=y#tar.gz",
|
|
"http://example.com/foobar?h=baz.zip",
|
|
"abc:example",
|
|
"file//example.com/foo.tar.bz2",
|
|
"garbage",
|
|
)
|
|
|
|
@patch('charmhelpers.fetch.log')
|
|
@patch('charmhelpers.fetch.plugins')
|
|
def test_installs_remote(self, _plugins, _log):
|
|
h1 = MagicMock(name="h1")
|
|
h1.can_handle.return_value = "Nope"
|
|
|
|
h2 = MagicMock(name="h2")
|
|
h2.can_handle.return_value = True
|
|
h2.install.side_effect = fetch.UnhandledSource()
|
|
|
|
h3 = MagicMock(name="h3")
|
|
h3.can_handle.return_value = True
|
|
h3.install.return_value = "foo"
|
|
|
|
_plugins.return_value = [h1, h2, h3]
|
|
for url in self.valid_urls:
|
|
result = fetch.install_remote(url)
|
|
|
|
h1.can_handle.assert_called_with(url)
|
|
h2.can_handle.assert_called_with(url)
|
|
h3.can_handle.assert_called_with(url)
|
|
|
|
h1.install.assert_not_called()
|
|
h2.install.assert_called_with(url)
|
|
h3.install.assert_called_with(url)
|
|
|
|
self.assertEqual(result, "foo")
|
|
|
|
fetch.install_remote('url', extra_arg=True)
|
|
h2.install.assert_called_with('url', extra_arg=True)
|
|
|
|
@patch('charmhelpers.fetch.install_remote')
|
|
@patch('charmhelpers.fetch.config')
|
|
def test_installs_from_config(self, _config, _instrem):
|
|
for url in self.valid_urls:
|
|
_config.return_value = {"foo": url}
|
|
fetch.install_from_config("foo")
|
|
_instrem.assert_called_with(url)
|
|
|
|
|
|
class PluginTest(TestCase):
|
|
|
|
@patch('charmhelpers.fetch.importlib.import_module')
|
|
def test_imports_plugins(self, import_):
|
|
fetch_handlers = ['a.foo', 'b.foo', 'c.foo']
|
|
module = MagicMock()
|
|
import_.return_value = module
|
|
plugins = fetch.plugins(fetch_handlers)
|
|
|
|
self.assertEqual(len(fetch_handlers), len(plugins))
|
|
module.foo.assert_has_calls(([call()] * len(fetch_handlers)))
|
|
|
|
@patch('charmhelpers.fetch.importlib.import_module')
|
|
def test_imports_plugins_default(self, import_):
|
|
module = MagicMock()
|
|
import_.return_value = module
|
|
plugins = fetch.plugins()
|
|
|
|
self.assertEqual(len(fetch.FETCH_HANDLERS), len(plugins))
|
|
for handler in fetch.FETCH_HANDLERS:
|
|
classname = handler.rsplit('.', 1)[-1]
|
|
getattr(module, classname).assert_called_with()
|
|
|
|
@patch('charmhelpers.fetch.log')
|
|
@patch('charmhelpers.fetch.importlib.import_module')
|
|
def test_skips_and_logs_missing_plugins(self, import_, log_):
|
|
fetch_handlers = ['a.foo', 'b.foo', 'c.foo']
|
|
import_.side_effect = (NotImplementedError, NotImplementedError,
|
|
MagicMock())
|
|
plugins = fetch.plugins(fetch_handlers)
|
|
|
|
self.assertEqual(1, len(plugins))
|
|
self.assertEqual(2, log_.call_count)
|
|
|
|
@patch('charmhelpers.fetch.log')
|
|
@patch.object(fetch.importlib, 'import_module')
|
|
def test_plugins_are_valid(self, import_module, log_):
|
|
plugins = fetch.plugins()
|
|
self.assertEqual(len(fetch.FETCH_HANDLERS), len(plugins))
|
|
|
|
|
|
class BaseFetchHandlerTest(TestCase):
|
|
|
|
def setUp(self):
|
|
super(BaseFetchHandlerTest, self).setUp()
|
|
self.test_urls = (
|
|
"http://example.com/foo?bar=baz&x=y#blarg",
|
|
"https://example.com/foo",
|
|
"ftp://example.com/foo",
|
|
"file://example.com/foo",
|
|
"git://github.com/foo/bar",
|
|
"bzr+ssh://bazaar.launchpad.net/foo/bar",
|
|
"bzr+http://bazaar.launchpad.net/foo/bar",
|
|
"garbage",
|
|
)
|
|
self.fh = fetch.BaseFetchHandler()
|
|
|
|
def test_handles_nothing(self):
|
|
for url in self.test_urls:
|
|
self.assertNotEqual(self.fh.can_handle(url), True)
|
|
|
|
def test_install_throws_unhandled(self):
|
|
for url in self.test_urls:
|
|
self.assertRaises(fetch.UnhandledSource, self.fh.install, url)
|
|
|
|
def test_parses_urls(self):
|
|
sample_url = "http://example.com/foo?bar=baz&x=y#blarg"
|
|
p = self.fh.parse_url(sample_url)
|
|
self.assertEqual(p, urlparse(sample_url))
|
|
|
|
def test_returns_baseurl(self):
|
|
sample_url = "http://example.com/foo?bar=baz&x=y#blarg"
|
|
expected_url = "http://example.com/foo"
|
|
u = self.fh.base_url(sample_url)
|
|
self.assertEqual(u, expected_url)
|