100
@mock.patch('utils.run')
101
@mock.patch('utils.log')
102
@mock.patch('utils.cmd_log', mock.Mock())
103
class TestDownloadRelease(unittest.TestCase):
105
def test_download(self, mock_log, mock_run):
106
# A release is properly downloaded using curl.
107
url = 'http://download.example.com/release.tgz'
108
filename = 'local-release.tgz'
109
destination = download_release(url, filename)
110
expected_destination = os.path.join(os.getcwd(), 'releases', filename)
111
self.assertEqual(expected_destination, destination)
112
expected_log = 'Downloading release file: {} --> {}.'.format(
113
url, expected_destination)
114
mock_log.assert_called_once_with(expected_log)
115
mock_run.assert_called_once_with(
116
'curl', '-L', '-o', expected_destination, url)
119
@mock.patch('utils.log', mock.Mock())
120
class TestFetchGuiRelease(unittest.TestCase):
122
release_path = '/my/release.tgz'
125
def patch_launchpad(self, origin, version):
126
"""Mock the functions used to download a release from Launchpad.
128
Ensure all the functions are called correctly.
130
url = 'http://launchpad.example.com/release.tgz/file'
131
filename = 'release.tgz'
132
patch_launchpad = mock.patch('utils.Launchpad')
133
patch_get_launchpad_release = mock.patch(
134
'utils.get_launchpad_release',
135
mock.Mock(return_value=(url, filename)),
137
patch_download_release = mock.patch(
138
'utils.download_release',
139
mock.Mock(return_value=self.release_path),
141
with patch_launchpad as mock_launchpad:
142
with patch_get_launchpad_release as mock_get_launchpad_release:
143
with patch_download_release as mock_download_release:
145
login = mock_launchpad.login_anonymously
146
login.assert_called_once_with('Juju GUI charm', 'production')
147
mock_get_launchpad_release.assert_called_once_with(
148
login().projects['juju-gui'], origin, version)
149
mock_download_release.assert_called_once_with(url, filename)
151
@mock.patch('utils.download_release')
152
def test_url(self, mock_download_release):
153
# The release is retrieved from an URL.
154
mock_download_release.return_value = self.release_path
155
url = 'http://download.example.com/release.tgz'
156
path = fetch_gui_release('url', url)
157
self.assertEqual(self.release_path, path)
158
mock_download_release.assert_called_once_with(url, 'url-release.tgz')
160
@mock.patch('utils.get_release_file_path')
161
def test_local(self, mock_get_release_file_path):
162
# The last local release is requested.
163
mock_get_release_file_path.return_value = self.release_path
164
path = fetch_gui_release('local', None)
165
self.assertEqual(self.release_path, path)
166
mock_get_release_file_path.assert_called_once_with()
168
@mock.patch('utils.get_release_file_path')
169
def test_version_found(self, mock_get_release_file_path):
170
# A release version is specified and found locally.
171
mock_get_release_file_path.return_value = self.release_path
172
path = fetch_gui_release('stable', '0.1.42')
173
self.assertEqual(self.release_path, path)
174
mock_get_release_file_path.assert_called_once_with('0.1.42')
176
@mock.patch('utils.get_release_file_path')
177
def test_version_not_found(self, mock_get_release_file_path):
178
# A release version is specified but not found locally.
179
mock_get_release_file_path.return_value = None
180
with self.patch_launchpad('stable', '0.1.42'):
181
path = fetch_gui_release('stable', '0.1.42')
182
self.assertEqual(self.release_path, path)
183
mock_get_release_file_path.assert_called_once_with('0.1.42')
185
@mock.patch('utils.get_release_file_path')
186
def test_stable(self, mock_get_release_file_path):
187
# The last stable release is requested.
188
with self.patch_launchpad('stable', None):
189
path = fetch_gui_release('stable', None)
190
self.assertEqual(self.release_path, path)
191
self.assertFalse(mock_get_release_file_path.called)
193
@mock.patch('utils.get_release_file_path')
194
def test_trunk(self, mock_get_release_file_path):
195
# The last development release is requested.
196
with self.patch_launchpad('trunk', None):
197
path = fetch_gui_release('trunk', None)
198
self.assertEqual(self.release_path, path)
199
self.assertFalse(mock_get_release_file_path.called)
97
202
class TestFirstPathInDir(unittest.TestCase):
184
289
self.assertRaises(IOError, get_api_address, unit_dir)
292
class TestGetReleaseFilePath(unittest.TestCase):
295
self.playground = tempfile.mkdtemp()
296
self.addCleanup(shutil.rmtree, self.playground)
298
def mock_releases_dir(self):
299
"""Mock the releases directory."""
300
return mock.patch('utils.RELEASES_DIR', self.playground)
302
def assert_path(self, filename, path):
303
"""Ensure the absolute path of filename equals the given path."""
304
expected = os.path.join(self.playground, filename)
305
self.assertEqual(expected, path)
308
def assert_error(self):
309
"""Ensure the code executed in the context block raises a ValueError.
311
Also check the error message.
313
with self.assertRaises(ValueError) as context_manager:
315
error = str(context_manager.exception)
316
self.assertEqual('Error: no releases found in the charm.', error)
318
def add(self, filename):
319
"""Create a release file in the playground directory."""
320
path = os.path.join(self.playground, filename)
321
open(path, 'w').close()
323
def test_last_release(self):
324
# The last release is correctly retrieved.
325
self.add('juju-gui-0.12.1.tgz')
326
self.add('juju-gui-1.2.3.tgz')
327
self.add('juju-gui-2.0.0+build.42.tgz')
328
self.add('juju-gui-2.0.1.tgz')
329
with self.mock_releases_dir():
330
path = get_release_file_path()
331
self.assert_path('juju-gui-2.0.1.tgz', path)
333
def test_ordering(self):
334
# Release versions are correctly ordered.
335
self.add('juju-gui-0.12.1.tgz')
336
self.add('juju-gui-0.9.1.tgz')
337
with self.mock_releases_dir():
338
path = get_release_file_path()
339
self.assert_path('juju-gui-0.12.1.tgz', path)
341
def test_no_releases(self):
342
# A ValueError is raised if no releases are found.
343
with self.mock_releases_dir():
344
with self.assert_error():
345
get_release_file_path()
347
def test_no_releases_with_files(self):
348
# A ValueError is raised if no releases are found.
349
# Extraneous files are ignored while looking for releases.
350
self.add('jujugui-1.2.3.tgz') # Wrong prefix.
351
self.add('juju-gui-1.2.tgz') # Missing patch version number.
352
self.add('juju-gui-1.2.3.bz2') # Wrong file extension.
353
self.add('juju-gui-1.2.3.4.tgz') # Wrong version.
354
self.add('juju-gui-1.2.3.build.42.tgz') # Missing "+" separator.
355
self.add('juju-gui-1.2.3+built.42.tgz') # Typo.
356
self.add('juju-gui-1.2.3+build.42.47.tgz') # Invalid bzr revno.
357
self.add('juju-gui-1.2.3+build.42.bz2') # Wrong file extension again.
358
with self.mock_releases_dir():
359
with self.assert_error():
360
print get_release_file_path()
362
def test_stable_version(self):
363
# A specific stable version is correctly retrieved.
364
self.add('juju-gui-1.2.3.tgz')
365
self.add('juju-gui-2.0.1+build.42.tgz')
366
self.add('juju-gui-2.0.1.tgz')
367
self.add('juju-gui-3.2.1.tgz')
368
with self.mock_releases_dir():
369
path = get_release_file_path('2.0.1')
370
self.assert_path('juju-gui-2.0.1.tgz', path)
372
def test_development_version(self):
373
# A specific development version is correctly retrieved.
374
self.add('juju-gui-1.2.3+build.4247.tgz')
375
self.add('juju-gui-2.42.47+build.4247.tgz')
376
self.add('juju-gui-2.42.47.tgz')
377
self.add('juju-gui-3.42.47+build.4247.tgz')
378
with self.mock_releases_dir():
379
path = get_release_file_path('2.42.47+build.4247')
380
self.assert_path('juju-gui-2.42.47+build.4247.tgz', path)
382
def test_version_not_found(self):
383
# None is returned if the requested version is not found.
384
self.add('juju-gui-1.2.3.tgz')
385
self.add('juju-GUI-1.42.47.tgz') # This is not a valid release.
386
with self.mock_releases_dir():
387
path = get_release_file_path('1.42.47')
388
self.assertIsNone(path)
187
391
class TestLegacyJuju(unittest.TestCase):
309
513
def test_latest_stable_release(self):
310
514
# Ensure the correct URL is returned for the latest stable release.
311
url = get_release_file_url(self.project, 'stable', None)
515
url, name = get_launchpad_release(self.project, 'stable', None)
312
516
self.assertEqual('http://example.com/0.1.1.tgz', url)
517
self.assertEqual('0.1.1.tgz', name)
314
519
def test_latest_trunk_release(self):
315
520
# Ensure the correct URL is returned for the latest trunk release.
316
url = get_release_file_url(self.project, 'trunk', None)
521
url, name = get_launchpad_release(self.project, 'trunk', None)
317
522
self.assertEqual('http://example.com/0.1.1+build.1.tgz', url)
523
self.assertEqual('0.1.1+build.1.tgz', name)
319
525
def test_specific_stable_release(self):
320
526
# Ensure the correct URL is returned for a specific version of the
321
527
# stable release.
322
url = get_release_file_url(self.project, 'stable', '0.1.0')
528
url, name = get_launchpad_release(self.project, 'stable', '0.1.0')
323
529
self.assertEqual('http://example.com/0.1.0.tgz', url)
530
self.assertEqual('0.1.0.tgz', name)
325
532
def test_specific_trunk_release(self):
326
533
# Ensure the correct URL is returned for a specific version of the
328
url = get_release_file_url(self.project, 'trunk', '0.1.0+build.1')
535
url, name = get_launchpad_release(
536
self.project, 'trunk', '0.1.0+build.1')
329
537
self.assertEqual('http://example.com/0.1.0+build.1.tgz', url)
538
self.assertEqual('0.1.0+build.1.tgz', name)
331
540
def test_series_not_found(self):
332
541
# A ValueError is raised if the series cannot be found.
333
542
with self.assertRaises(ValueError) as cm:
334
get_release_file_url(self.project, 'unstable', None)
543
get_launchpad_release(self.project, 'unstable', None)
335
544
self.assertIn('series not found', str(cm.exception))
337
546
def test_no_releases(self):
338
547
# A ValueError is raised if the series does not contain releases.
339
548
project = AttrDict(series=[AttrDict(name='stable', releases=[])])
340
549
with self.assertRaises(ValueError) as cm:
341
get_release_file_url(project, 'stable', None)
550
get_launchpad_release(project, 'stable', None)
342
551
self.assertIn('series does not contain releases', str(cm.exception))
344
553
def test_release_not_found(self):
345
554
# A ValueError is raised if the release cannot be found.
346
555
with self.assertRaises(ValueError) as cm:
347
get_release_file_url(self.project, 'stable', '2.0')
556
get_launchpad_release(self.project, 'stable', '2.0')
348
557
self.assertIn('release not found', str(cm.exception))
350
559
def test_file_not_found(self):