def test_extract_symlink_mode(self): # lp:973260 - charms packed by different tools that record symlink # mode permissions differently (ie the charm store) don't extract # correctly. charm_path = self.copy_charm() sym_path = os.path.join(charm_path, 'foobar') os.symlink('metadata.yaml', sym_path) charm_dir = CharmDirectory(charm_path) normal_path = charm_dir.as_bundle().path zf_src = zipfile.ZipFile(normal_path, "r") foreign_path = os.path.join(self.makeDir(), "store.charm") zf_dst = zipfile.ZipFile(foreign_path, "w") for info in zf_src.infolist(): if info.filename == "foobar": # This is what the charm store does: info.external_attr = (stat.S_IFLNK | 0777) << 16 zf_dst.writestr(info, zf_src.read(info.filename)) zf_src.close() zf_dst.close() bundle = CharmBundle(foreign_path) extract_dir = self.makeDir() bundle.extract_to(extract_dir) self.assertIn("foobar", os.listdir(extract_dir)) self.assertTrue(os.path.islink(os.path.join(extract_dir, "foobar"))) self.assertEqual(os.readlink(os.path.join(extract_dir, 'foobar')), 'metadata.yaml')
def test_charm_base_inheritance(self): """ get_sha256() should be implemented in the base class, and should use compute_sha256 to calculate the digest. """ directory = CharmDirectory(self.sample_dir1) bundle = directory.as_bundle() digest = compute_file_hash(hashlib.sha256, bundle.path) self.assertEquals(digest, directory.get_sha256())
def test_executable_extraction(self): sample_directory = os.path.join( repository_directory, "series", "varnish-alternative") charm_directory = CharmDirectory(sample_directory) source_hook_path = os.path.join(sample_directory, "hooks", "install") self.assertTrue(os.access(source_hook_path, os.X_OK)) bundle = charm_directory.as_bundle() directory = bundle.as_directory() hook_path = os.path.join(directory.path, "hooks", "install") self.assertTrue(os.access(hook_path, os.X_OK))
def test_executable_extraction(self): sample_directory = os.path.join(repository_directory, "series", "varnish-alternative") charm_directory = CharmDirectory(sample_directory) source_hook_path = os.path.join(sample_directory, "hooks", "install") self.assertTrue(os.access(source_hook_path, os.X_OK)) bundle = charm_directory.as_bundle() directory = bundle.as_directory() hook_path = os.path.join(directory.path, "hooks", "install") self.assertTrue(os.access(hook_path, os.X_OK))
def test_compute_sha256(self): """ Computing the sha256 of a directory will use the bundled charm, since the hash of the file itself is needed. """ directory = CharmDirectory(self.sample_dir1) sha256 = directory.compute_sha256() charm_bundle = directory.as_bundle() self.assertEquals(type(charm_bundle), CharmBundle) self.assertEquals(compute_file_hash(hashlib.sha256, charm_bundle.path), sha256)
def test_as_bundle_file_lifetime(self): """ The temporary bundle file created should have a life time equivalent to that of the directory object itself. """ directory = CharmDirectory(self.sample_dir1) charm_bundle = directory.as_bundle() gc.collect() self.assertTrue(os.path.isfile(charm_bundle.path)) del directory gc.collect() self.assertFalse(os.path.isfile(charm_bundle.path))
def test_as_bundle_with_relative_path(self): """ Ensure that as_bundle works correctly with relative paths. """ current_dir = os.getcwd() os.chdir(self.sample_dir2) self.addCleanup(os.chdir, current_dir) charm_dir = "../%s" % os.path.basename(self.sample_dir1) directory = CharmDirectory(charm_dir) charm_bundle = directory.as_bundle() self.assertEquals(type(charm_bundle), CharmBundle) self.assertEquals(charm_bundle.metadata.name, "sample")
def test_extract_symlink(self): extract_dir = self.makeDir() charm_path = self.copy_charm() sym_path = os.path.join(charm_path, 'foobar') os.symlink('metadata.yaml', sym_path) charm_dir = CharmDirectory(charm_path) bundle = charm_dir.as_bundle() bundle.extract_to(extract_dir) self.assertIn("foobar", os.listdir(extract_dir)) self.assertTrue(os.path.islink(os.path.join(extract_dir, "foobar"))) self.assertEqual(os.readlink(os.path.join(extract_dir, 'foobar')), 'metadata.yaml') # Verify we can extract it over again os.remove(sym_path) os.symlink('./config.yaml', sym_path) charm_dir = CharmDirectory(charm_path) bundle = charm_dir.as_bundle() bundle.extract_to(extract_dir) self.assertEqual(os.readlink(os.path.join(extract_dir, 'foobar')), './config.yaml')
def test_as_bundle(self): directory = CharmDirectory(self.sample_dir1) charm_bundle = directory.as_bundle() self.assertEquals(type(charm_bundle), CharmBundle) self.assertEquals(charm_bundle.metadata.name, "sample") self.assertIn("sample-1.charm", charm_bundle.path) total_compressed = 0 total_uncompressed = 0 zip_file = zipfile.ZipFile(charm_bundle.path) for n in zip_file.namelist(): info = zip_file.getinfo(n) total_compressed += info.compress_size total_uncompressed += info.file_size self.assertTrue(total_compressed < total_uncompressed)
class RemoteRepositoryTest(RepositoryTestBase): def setUp(self): super(RemoteRepositoryTest, self).setUp() self.cache_path = os.path.join( self.makeDir(), "notexistyet") self.download_path = os.path.join(self.cache_path, "downloads") def delete(): if os.path.exists(self.cache_path): shutil.rmtree(self.cache_path) self.addCleanup(delete) self.charm = CharmDirectory( os.path.join(self.unbundled_repo_path, "series", "dummy")) with open(self.charm.as_bundle().path, "rb") as f: self.bundle_data = f.read() self.sha256 = self.charm.as_bundle().get_sha256() self.getPage = self.mocker.replace("twisted.web.client.getPage") self.downloadPage = self.mocker.replace( "twisted.web.client.downloadPage") def repo(self, url_base): return RemoteCharmRepository(url_base, self.cache_path) def cache_location(self, url_str, revision): charm_url = CharmURL.parse(url_str) cache_key = under.quote( "%s.charm" % (charm_url.with_revision(revision))) return os.path.join(self.cache_path, cache_key) def charm_info(self, url_str, revision, warnings=None, errors=None): info = {"revision": revision, "sha256": self.sha256} if errors: info["errors"] = errors if warnings: info["warnings"] = warnings return json.dumps({url_str: info}) def mock_charm_info(self, url, result): self.getPage(url) self.mocker.result(result) def mock_download(self, url, error=None): self.downloadPage(url, ANY) if error: return self.mocker.result(fail(error)) def download(_, path): self.assertTrue(path.startswith(self.download_path)) with open(path, "wb") as f: f.write(self.bundle_data) return succeed(None) self.mocker.call(download) @inlineCallbacks def assert_find_uncached(self, dns_name, url_str, info_url, find_url): self.mock_charm_info(info_url, succeed(self.charm_info(url_str, 1))) self.mock_download(find_url) self.mocker.replay() repo = self.repo(dns_name) charm = yield repo.find(CharmURL.parse(url_str)) self.assertEquals(charm.get_sha256(), self.sha256) self.assertEquals(charm.path, self.cache_location(url_str, 1)) self.assertEquals(os.listdir(self.download_path), []) @inlineCallbacks def assert_find_cached(self, dns_name, url_str, info_url): os.makedirs(self.cache_path) cache_location = self.cache_location(url_str, 1) shutil.copy(self.charm.as_bundle().path, cache_location) self.mock_charm_info(info_url, succeed(self.charm_info(url_str, 1))) self.mocker.replay() repo = self.repo(dns_name) charm = yield repo.find(CharmURL.parse(url_str)) self.assertEquals(charm.get_sha256(), self.sha256) self.assertEquals(charm.path, cache_location) def assert_find_error(self, dns_name, url_str, err_type, message): self.mocker.replay() repo = self.repo(dns_name) d = self.assertFailure(repo.find(CharmURL.parse(url_str)), err_type) def verify(error): self.assertEquals(str(error), message) d.addCallback(verify) return d @inlineCallbacks def assert_latest(self, dns_name, url_str, revision): self.mocker.replay() repo = self.repo(dns_name) result = yield repo.latest(CharmURL.parse(url_str)) self.assertEquals(result, revision) def assert_latest_error(self, dns_name, url_str, err_type, message): self.mocker.replay() repo = self.repo(dns_name) d = self.assertFailure(repo.latest(CharmURL.parse(url_str)), err_type) def verify(error): self.assertEquals(str(error), message) d.addCallback(verify) return d def test_find_plain_uncached(self): return self.assert_find_uncached( "https://somewhe.re", "cs:series/name", "https://somewhe.re/charm-info?charms=cs%3Aseries/name", "https://somewhe.re/charm/series/name-1") def test_find_revision_uncached(self): return self.assert_find_uncached( "https://somewhe.re", "cs:series/name-1", "https://somewhe.re/charm-info?charms=cs%3Aseries/name-1", "https://somewhe.re/charm/series/name-1") def test_find_user_uncached(self): return self.assert_find_uncached( "https://somewhereel.se", "cs:~user/srs/name", "https://somewhereel.se/charm-info?charms=cs%3A%7Euser/srs/name", "https://somewhereel.se/charm/%7Euser/srs/name-1") def test_find_plain_cached(self): return self.assert_find_cached( "https://somewhe.re", "cs:series/name", "https://somewhe.re/charm-info?charms=cs%3Aseries/name") def test_find_revision_cached(self): return self.assert_find_cached( "https://somewhe.re", "cs:series/name-1", "https://somewhe.re/charm-info?charms=cs%3Aseries/name-1") def test_find_user_cached(self): return self.assert_find_cached( "https://somewhereel.se", "cs:~user/srs/name", "https://somewhereel.se/charm-info?charms=cs%3A%7Euser/srs/name") def test_find_info_http_error(self): self.mock_charm_info( "https://anoth.er/charm-info?charms=cs%3Aseries/name", fail(Error("500"))) return self.assert_find_error( "https://anoth.er", "cs:series/name", CharmNotFound, "Charm 'cs:series/name' not found in repository https://anoth.er") @inlineCallbacks def test_find_info_store_warning(self): self.mock_charm_info( "https://anoth.er/charm-info?charms=cs%3Aseries/name-1", succeed(self.charm_info( "cs:series/name-1", 1, warnings=["omg", "halp"]))) self.mock_download("https://anoth.er/charm/series/name-1") self.mocker.replay() repo = self.repo("https://anoth.er") log = self.capture_logging("juju.charm") charm = yield repo.find(CharmURL.parse("cs:series/name-1")) self.assertIn("omg", log.getvalue()) self.assertIn("halp", log.getvalue()) self.assertEquals(charm.get_sha256(), self.sha256) def test_find_info_store_error(self): self.mock_charm_info( "https://anoth.er/charm-info?charms=cs%3Aseries/name-101", succeed(self.charm_info( "cs:series/name-101", 101, errors=["oh", "noes"]))) return self.assert_find_error( "https://anoth.er", "cs:series/name-101", CharmError, "Error processing 'cs:series/name-101': oh; noes") def test_find_info_bad_revision(self): self.mock_charm_info( "https://anoth.er/charm-info?charms=cs%3Aseries/name-99", succeed(self.charm_info("cs:series/name-99", 1))) return self.assert_find_error( "https://anoth.er", "cs:series/name-99", AssertionError, "bad url revision") def test_find_download_error(self): self.mock_charm_info( "https://anoth.er/charm-info?charms=cs%3Aseries/name", succeed(json.dumps({"cs:series/name": {"revision": 123}}))) self.mock_download( "https://anoth.er/charm/series/name-123", Error("999")) return self.assert_find_error( "https://anoth.er", "cs:series/name", CharmNotFound, "Charm 'cs:series/name-123' not found in repository " "https://anoth.er") def test_find_charm_revision_mismatch(self): self.mock_charm_info( "https://anoth.er/charm-info?charms=cs%3Aseries/name", succeed(json.dumps({"cs:series/name": {"revision": 99}}))) self.mock_download("https://anoth.er/charm/series/name-99") return self.assert_find_error( "https://anoth.er", "cs:series/name", AssertionError, "bad charm revision") @inlineCallbacks def test_find_downloaded_hash_mismatch(self): cache_location = self.cache_location("cs:series/name-1", 1) self.mock_charm_info( "https://anoth.er/charm-info?charms=cs%3Aseries/name", succeed(json.dumps( {"cs:series/name": {"revision": 1, "sha256": "NO YUO"}}))) self.mock_download("https://anoth.er/charm/series/name-1") yield self.assert_find_error( "https://anoth.er", "cs:series/name", CharmError, "Error processing 'cs:series/name-1 (downloaded)': SHA256 " "mismatch") self.assertFalse(os.path.exists(cache_location)) @inlineCallbacks def test_find_cached_hash_mismatch(self): os.makedirs(self.cache_path) cache_location = self.cache_location("cs:series/name-1", 1) shutil.copy(self.charm.as_bundle().path, cache_location) self.mock_charm_info( "https://anoth.er/charm-info?charms=cs%3Aseries/name", succeed(json.dumps( {"cs:series/name": {"revision": 1, "sha256": "NO YUO"}}))) yield self.assert_find_error( "https://anoth.er", "cs:series/name", CharmError, "Error processing 'cs:series/name-1 (cached)': SHA256 mismatch") self.assertFalse(os.path.exists(cache_location)) def test_latest_plain(self): self.mock_charm_info( "https://somewhe.re/charm-info?charms=cs%3Afoo/bar", succeed(self.charm_info("cs:foo/bar", 99))) return self.assert_latest("https://somewhe.re", "cs:foo/bar-1", 99) def test_latest_user(self): self.mock_charm_info( "https://somewhereel.se/charm-info?charms=cs%3A%7Efee/foo/bar", succeed(self.charm_info("cs:~fee/foo/bar", 123))) return self.assert_latest( "https://somewhereel.se", "cs:~fee/foo/bar", 123) def test_latest_revision(self): self.mock_charm_info( "https://somewhereel.se/charm-info?charms=cs%3A%7Efee/foo/bar", succeed(self.charm_info("cs:~fee/foo/bar", 123))) return self.assert_latest( "https://somewhereel.se", "cs:~fee/foo/bar-99", 123) def test_latest_http_error(self): self.mock_charm_info( "https://andanoth.er/charm-info?charms=cs%3A%7Eblib/blab/blob", fail(Error("404"))) return self.assert_latest_error( "https://andanoth.er", "cs:~blib/blab/blob", CharmNotFound, "Charm 'cs:~blib/blab/blob' not found in repository " "https://andanoth.er") @inlineCallbacks def test_latest_store_warning(self): self.mock_charm_info( "https://anoth.er/charm-info?charms=cs%3Aseries/name", succeed(self.charm_info( "cs:series/name", 1, warnings=["eww", "yuck"]))) self.mocker.replay() repo = self.repo("https://anoth.er") log = self.capture_logging("juju.charm") revision = yield repo.latest(CharmURL.parse("cs:series/name-1")) self.assertIn("eww", log.getvalue()) self.assertIn("yuck", log.getvalue()) self.assertEquals(revision, 1) def test_latest_store_error(self): self.mock_charm_info( "https://anoth.er/charm-info?charms=cs%3Aseries/name", succeed(self.charm_info( "cs:series/name", 1, errors=["blam", "dink"]))) return self.assert_latest_error( "https://anoth.er", "cs:series/name-1", CharmError, "Error processing 'cs:series/name': blam; dink") def test_repo_type(self): self.mocker.replay() self.assertEqual(self.repo("http://fbaro.com").type, "store")
class RemoteRepositoryTest(RepositoryTestBase): def setUp(self): super(RemoteRepositoryTest, self).setUp() self.cache_path = os.path.join(tempfile.mkdtemp(), "notexistyet") self.download_path = os.path.join(self.cache_path, "downloads") def delete(): if os.path.exists(self.cache_path): shutil.rmtree(self.cache_path) self.addCleanup(delete) self.charm = CharmDirectory( os.path.join(self.unbundled_repo_path, "series", "dummy")) with open(self.charm.as_bundle().path, "rb") as f: self.bundle_data = f.read() self.sha256 = self.charm.as_bundle().get_sha256() self.getPage = self.mocker.replace("twisted.web.client.getPage") self.downloadPage = self.mocker.replace( "twisted.web.client.downloadPage") def repo(self, url_base): return RemoteCharmRepository(url_base, self.cache_path) def cache_location(self, url_str, revision): charm_url = CharmURL.parse(url_str) cache_key = under.quote("%s.charm" % (charm_url.with_revision(revision))) return os.path.join(self.cache_path, cache_key) def charm_info(self, url_str, revision, warnings=None, errors=None): info = {"revision": revision, "sha256": self.sha256} if errors: info["errors"] = errors if warnings: info["warnings"] = warnings return json.dumps({url_str: info}) def mock_charm_info(self, url, result): self.getPage(url) self.mocker.result(result) def mock_download(self, url, error=None): self.downloadPage(url, ANY) if error: return self.mocker.result(fail(error)) def download(_, path): self.assertTrue(path.startswith(self.download_path)) with open(path, "wb") as f: f.write(self.bundle_data) return succeed(None) self.mocker.call(download) @inlineCallbacks def assert_find_uncached(self, dns_name, url_str, info_url, find_url): self.mock_charm_info(info_url, succeed(self.charm_info(url_str, 1))) self.mock_download(find_url) self.mocker.replay() repo = self.repo(dns_name) charm = yield repo.find(CharmURL.parse(url_str)) self.assertEquals(charm.get_sha256(), self.sha256) self.assertEquals(charm.path, self.cache_location(url_str, 1)) self.assertEquals(os.listdir(self.download_path), []) @inlineCallbacks def assert_find_cached(self, dns_name, url_str, info_url): os.makedirs(self.cache_path) cache_location = self.cache_location(url_str, 1) shutil.copy(self.charm.as_bundle().path, cache_location) self.mock_charm_info(info_url, succeed(self.charm_info(url_str, 1))) self.mocker.replay() repo = self.repo(dns_name) charm = yield repo.find(CharmURL.parse(url_str)) self.assertEquals(charm.get_sha256(), self.sha256) self.assertEquals(charm.path, cache_location) def assert_find_error(self, dns_name, url_str, err_type, message): self.mocker.replay() repo = self.repo(dns_name) d = self.assertFailure(repo.find(CharmURL.parse(url_str)), err_type) def verify(error): self.assertEquals(str(error), message) d.addCallback(verify) return d @inlineCallbacks def assert_latest(self, dns_name, url_str, revision): self.mocker.replay() repo = self.repo(dns_name) result = yield repo.latest(CharmURL.parse(url_str)) self.assertEquals(result, revision) def assert_latest_error(self, dns_name, url_str, err_type, message): self.mocker.replay() repo = self.repo(dns_name) d = self.assertFailure(repo.latest(CharmURL.parse(url_str)), err_type) def verify(error): self.assertEquals(str(error), message) d.addCallback(verify) return d def test_find_plain_uncached(self): return self.assert_find_uncached( "https://somewhe.re", "cs:series/name", "https://somewhe.re/charm-info?charms=cs%3Aseries/name", "https://somewhe.re/charm/series/name-1") def test_find_revision_uncached(self): return self.assert_find_uncached( "https://somewhe.re", "cs:series/name-1", "https://somewhe.re/charm-info?charms=cs%3Aseries/name-1", "https://somewhe.re/charm/series/name-1") def test_find_user_uncached(self): return self.assert_find_uncached( "https://somewhereel.se", "cs:~user/srs/name", "https://somewhereel.se/charm-info?charms=cs%3A%7Euser/srs/name", "https://somewhereel.se/charm/%7Euser/srs/name-1") def test_find_plain_cached(self): return self.assert_find_cached( "https://somewhe.re", "cs:series/name", "https://somewhe.re/charm-info?charms=cs%3Aseries/name") def test_find_revision_cached(self): return self.assert_find_cached( "https://somewhe.re", "cs:series/name-1", "https://somewhe.re/charm-info?charms=cs%3Aseries/name-1") def test_find_user_cached(self): return self.assert_find_cached( "https://somewhereel.se", "cs:~user/srs/name", "https://somewhereel.se/charm-info?charms=cs%3A%7Euser/srs/name") def test_find_info_http_error(self): self.mock_charm_info( "https://anoth.er/charm-info?charms=cs%3Aseries/name", fail(Error("500"))) return self.assert_find_error( "https://anoth.er", "cs:series/name", CharmNotFound, "Charm 'cs:series/name' not found in repository https://anoth.er") @inlineCallbacks def test_find_info_store_warning(self): self.mock_charm_info( "https://anoth.er/charm-info?charms=cs%3Aseries/name-1", succeed( self.charm_info("cs:series/name-1", 1, warnings=["omg", "halp"]))) self.mock_download("https://anoth.er/charm/series/name-1") self.mocker.replay() repo = self.repo("https://anoth.er") log = self.capture_logging("juju.charm") charm = yield repo.find(CharmURL.parse("cs:series/name-1")) self.assertIn("omg", log.getvalue()) self.assertIn("halp", log.getvalue()) self.assertEquals(charm.get_sha256(), self.sha256) def test_find_info_store_error(self): self.mock_charm_info( "https://anoth.er/charm-info?charms=cs%3Aseries/name-101", succeed( self.charm_info("cs:series/name-101", 101, errors=["oh", "noes"]))) return self.assert_find_error( "https://anoth.er", "cs:series/name-101", CharmError, "Error processing 'cs:series/name-101': oh; noes") def test_find_info_bad_revision(self): self.mock_charm_info( "https://anoth.er/charm-info?charms=cs%3Aseries/name-99", succeed(self.charm_info("cs:series/name-99", 1))) return self.assert_find_error("https://anoth.er", "cs:series/name-99", AssertionError, "bad url revision") def test_find_download_error(self): self.mock_charm_info( "https://anoth.er/charm-info?charms=cs%3Aseries/name", succeed(json.dumps({"cs:series/name": { "revision": 123 }}))) self.mock_download("https://anoth.er/charm/series/name-123", Error("999")) return self.assert_find_error( "https://anoth.er", "cs:series/name", CharmNotFound, "Charm 'cs:series/name-123' not found in repository " "https://anoth.er") def test_find_charm_revision_mismatch(self): self.mock_charm_info( "https://anoth.er/charm-info?charms=cs%3Aseries/name", succeed(json.dumps({"cs:series/name": { "revision": 99 }}))) self.mock_download("https://anoth.er/charm/series/name-99") return self.assert_find_error("https://anoth.er", "cs:series/name", AssertionError, "bad charm revision") @inlineCallbacks def test_find_downloaded_hash_mismatch(self): cache_location = self.cache_location("cs:series/name-1", 1) self.mock_charm_info( "https://anoth.er/charm-info?charms=cs%3Aseries/name", succeed( json.dumps( {"cs:series/name": { "revision": 1, "sha256": "NO YUO" }}))) self.mock_download("https://anoth.er/charm/series/name-1") yield self.assert_find_error( "https://anoth.er", "cs:series/name", CharmError, "Error processing 'cs:series/name-1 (downloaded)': SHA256 " "mismatch") self.assertFalse(os.path.exists(cache_location)) @inlineCallbacks def test_find_cached_hash_mismatch(self): os.makedirs(self.cache_path) cache_location = self.cache_location("cs:series/name-1", 1) shutil.copy(self.charm.as_bundle().path, cache_location) self.mock_charm_info( "https://anoth.er/charm-info?charms=cs%3Aseries/name", succeed( json.dumps( {"cs:series/name": { "revision": 1, "sha256": "NO YUO" }}))) yield self.assert_find_error( "https://anoth.er", "cs:series/name", CharmError, "Error processing 'cs:series/name-1 (cached)': SHA256 mismatch") self.assertFalse(os.path.exists(cache_location)) def test_latest_plain(self): self.mock_charm_info( "https://somewhe.re/charm-info?charms=cs%3Afoo/bar", succeed(self.charm_info("cs:foo/bar", 99))) return self.assert_latest("https://somewhe.re", "cs:foo/bar-1", 99) def test_latest_user(self): self.mock_charm_info( "https://somewhereel.se/charm-info?charms=cs%3A%7Efee/foo/bar", succeed(self.charm_info("cs:~fee/foo/bar", 123))) return self.assert_latest("https://somewhereel.se", "cs:~fee/foo/bar", 123) def test_latest_revision(self): self.mock_charm_info( "https://somewhereel.se/charm-info?charms=cs%3A%7Efee/foo/bar", succeed(self.charm_info("cs:~fee/foo/bar", 123))) return self.assert_latest("https://somewhereel.se", "cs:~fee/foo/bar-99", 123) def test_latest_http_error(self): self.mock_charm_info( "https://andanoth.er/charm-info?charms=cs%3A%7Eblib/blab/blob", fail(Error("404"))) return self.assert_latest_error( "https://andanoth.er", "cs:~blib/blab/blob", CharmNotFound, "Charm 'cs:~blib/blab/blob' not found in repository " "https://andanoth.er") @inlineCallbacks def test_latest_store_warning(self): self.mock_charm_info( "https://anoth.er/charm-info?charms=cs%3Aseries/name", succeed( self.charm_info("cs:series/name", 1, warnings=["eww", "yuck"]))) self.mocker.replay() repo = self.repo("https://anoth.er") log = self.capture_logging("juju.charm") revision = yield repo.latest(CharmURL.parse("cs:series/name-1")) self.assertIn("eww", log.getvalue()) self.assertIn("yuck", log.getvalue()) self.assertEquals(revision, 1) def test_latest_store_error(self): self.mock_charm_info( "https://anoth.er/charm-info?charms=cs%3Aseries/name", succeed( self.charm_info("cs:series/name", 1, errors=["blam", "dink"]))) return self.assert_latest_error( "https://anoth.er", "cs:series/name-1", CharmError, "Error processing 'cs:series/name': blam; dink")