~benji/charms/precise/juju-gui/make-etags-ignore-inode

65.2.1 by Nicola Larosa
Added the COPYING file, and headers to source files.
1
# This file is part of the Juju GUI, which lets users view and manage Juju
2
# environments within a graphical interface (https://launchpad.net/juju-gui).
3
# Copyright (C) 2012-2013 Canonical Ltd.
4
#
5
# This program is free software: you can redistribute it and/or modify it under
6
# the terms of the GNU Affero General Public License version 3, as published by
7
# the Free Software Foundation.
8
#
9
# This program is distributed in the hope that it will be useful, but WITHOUT
10
# ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
11
# SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
# Affero General Public License for more details.
13
#
14
# You should have received a copy of the GNU Affero General Public License
15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
80.1.1 by Nicola Larosa
Merge from refactor-gui-startup.
17
"""Backend tests."""
18
19
113.1.1 by Francesco Banconi
Checkpoint.
20
from contextlib import (
21
    contextmanager,
22
    nested,
23
)
100.2.5 by Francesco Banconi
Tests passing.
24
import os
49.1.1 by Nicola Larosa
Add tests, remove dead code, rename some other code, general reformatting.
25
import shutil
26
import tempfile
41.1.1 by Benjamin Saller
checkpoint charm work
27
import unittest
28
98.1.1 by Francesco Banconi
Checkpoint.
29
import mock
49.1.5 by Nicola Larosa
Implemented test_install and test_start.
30
31
import backend
49.1.1 by Nicola Larosa
Add tests, remove dead code, rename some other code, general reformatting.
32
import utils
33
34
113.1.1 by Francesco Banconi
Checkpoint.
35
EXPECTED_PYTHON_LEGACY_DEBS = ('apache2', 'curl', 'haproxy', 'openssl')
126.1.2 by Francesco Banconi
Fix backend tests.
36
EXPECTED_GO_LEGACY_DEBS = ('apache2', 'curl', 'haproxy', 'openssl')
113.1.1 by Francesco Banconi
Checkpoint.
37
EXPECTED_PYTHON_BUILTIN_DEBS = (
38
    'curl', 'openssl', 'python-bzrlib', 'python-pip')
126.1.2 by Francesco Banconi
Fix backend tests.
39
EXPECTED_GO_BUILTIN_DEBS = ('curl', 'openssl', 'python-bzrlib', 'python-pip')
113.1.1 by Francesco Banconi
Checkpoint.
40
80.1.1 by Nicola Larosa
Merge from refactor-gui-startup.
41
49.1.5 by Nicola Larosa
Implemented test_install and test_start.
42
class TestBackendProperties(unittest.TestCase):
80.1.1 by Nicola Larosa
Merge from refactor-gui-startup.
43
    """Ensure the correct mixins and property values are collected."""
45.1.2 by Nicola Larosa
Reformat the code to appease the Sublime Text editor, add docstrings.
44
113.1.1 by Francesco Banconi
Checkpoint.
45
    def assert_mixins(self, expected, backend):
46
        """Ensure the given backend includes the expected mixins."""
47
        obtained = tuple(mixin.__class__.__name__ for mixin in backend.mixins)
48
        self.assertEqual(tuple(expected), obtained)
49
50
    def assert_dependencies(self, expected_debs, expected_repository, backend):
51
        """Ensure the given backend includes the expected dependencies."""
52
        obtained_debs, obtained_repository = backend.get_dependencies()
53
        self.assertEqual(set(expected_debs), obtained_debs)
54
        self.assertEqual(expected_repository, obtained_repository)
98.1.1 by Francesco Banconi
Checkpoint.
55
56
    def check_sandbox_mode(self):
57
        """The backend includes the correct mixins when sandbox mode is active.
58
        """
113.1.1 by Francesco Banconi
Checkpoint.
59
        expected_mixins = (
60
            'SetUpMixin', 'SandboxMixin', 'GuiMixin', 'HaproxyApacheMixin')
61
        config = {
62
            'builtin-server': False,
63
            'repository-location': 'ppa:my/location',
64
            'sandbox': True,
65
        }
66
        test_backend = backend.Backend(config=config)
67
        self.assert_mixins(expected_mixins, test_backend)
68
        self.assert_dependencies(
69
            EXPECTED_PYTHON_LEGACY_DEBS, 'ppa:my/location', test_backend)
98.1.1 by Francesco Banconi
Checkpoint.
70
71
    def test_go_sandbox_backend(self):
147.1.1 by Rick Harding
Remove all the pyjuju
72
        self.check_sandbox_mode()
49.1.1 by Nicola Larosa
Add tests, remove dead code, rename some other code, general reformatting.
73
74
    def test_go_backend(self):
113.1.1 by Francesco Banconi
Checkpoint.
75
        expected_mixins = (
76
            'SetUpMixin', 'GoMixin', 'GuiMixin', 'HaproxyApacheMixin')
77
        config = {
78
            'builtin-server': False,
79
            'repository-location': 'ppa:my/location',
80
            'sandbox': False,
81
        }
147.1.1 by Rick Harding
Remove all the pyjuju
82
        test_backend = backend.Backend(config=config)
83
        self.assert_mixins(expected_mixins, test_backend)
84
        self.assert_dependencies(
85
            EXPECTED_GO_LEGACY_DEBS, 'ppa:my/location', test_backend)
79.1.5 by Nicola Larosa
Move not urgent changes to other branch to make the diff smaller.
86
113.1.1 by Francesco Banconi
Checkpoint.
87
    def test_go_builtin_server(self):
88
        config = {
89
            'builtin-server': True,
90
            'repository-location': 'ppa:my/location',
91
            'sandbox': False,
92
        }
100.2.5 by Francesco Banconi
Tests passing.
93
        expected_mixins = (
94
            'SetUpMixin', 'GoMixin', 'GuiMixin', 'BuiltinServerMixin')
147.1.1 by Rick Harding
Remove all the pyjuju
95
        test_backend = backend.Backend(config)
96
        self.assert_mixins(expected_mixins, test_backend)
97
        self.assert_dependencies(
98
            EXPECTED_GO_BUILTIN_DEBS, None, test_backend)
113.1.1 by Francesco Banconi
Checkpoint.
99
100
    def test_sandbox_builtin_server(self):
101
        config = {
102
            'builtin-server': True,
103
            'repository-location': 'ppa:my/location',
104
            'sandbox': True,
105
        }
106
        expected_mixins = (
107
            'SetUpMixin', 'SandboxMixin', 'GuiMixin', 'BuiltinServerMixin')
147.1.1 by Rick Harding
Remove all the pyjuju
108
        test_backend = backend.Backend(config)
109
        self.assert_mixins(expected_mixins, test_backend)
110
        self.assert_dependencies(
111
            EXPECTED_PYTHON_BUILTIN_DEBS, None, test_backend)
98.1.5 by Francesco Banconi
Add builtin/staging tests.
112
79.1.5 by Nicola Larosa
Move not urgent changes to other branch to make the diff smaller.
113
49.1.5 by Nicola Larosa
Implemented test_install and test_start.
114
class TestBackendCommands(unittest.TestCase):
115
49.1.8 by Nicola Larosa
Polish tests, remove the overrideable mechanism, use prefixed imports everywhere.
116
    def setUp(self):
113.1.1 by Francesco Banconi
Checkpoint.
117
        # Set up directories.
100.2.5 by Francesco Banconi
Tests passing.
118
        self.playground = tempfile.mkdtemp()
113.1.1 by Francesco Banconi
Checkpoint.
119
        self.addCleanup(shutil.rmtree, self.playground)
120
        self.base_dir = os.path.join(self.playground, 'juju-gui')
121
        self.command_log_file = os.path.join(self.playground, 'logs')
122
        self.ssl_cert_path = os.path.join(self.playground, 'ssl-cert-path')
123
        # Set up default values.
124
        self.juju_gui_source = 'stable'
125
        self.repository_location = 'ppa:my/location'
126
        self.parse_source_return_value = ('stable', None)
127
128
    def make_config(self, options=None):
129
        """Create and return a backend configuration dict."""
130
        config = {
131
            'builtin-server': True,
132
            'builtin-server-logging': 'info',
125.1.6 by Brad Crittenden
Fixed tests to account for failing slash in charmworld-url
133
            'charmworld-url': 'http://charmworld.example.com/',
113.1.1 by Francesco Banconi
Checkpoint.
134
            'command-log-file': self.command_log_file,
135
            'ga-key': 'my-key',
136
            'juju-gui-debug': False,
137
            'juju-gui-console-enabled': False,
138
            'juju-gui-source': self.juju_gui_source,
139
            'login-help': 'login-help',
140
            'read-only': False,
141
            'repository-location': self.repository_location,
142
            'sandbox': False,
143
            'secure': True,
144
            'serve-tests': False,
145
            'show-get-juju-button': False,
146
            'ssl-cert-path': self.ssl_cert_path,
147
        }
148
        if options is not None:
149
            config.update(options)
150
        return config
151
152
    @contextmanager
153
    def mock_all(self):
154
        """Mock all the extrenal functions used by the backend framework."""
155
        mock_parse_source = mock.Mock(
156
            return_value=self.parse_source_return_value)
157
        mocks = {
158
            'base_dir': mock.patch('backend.utils.BASE_DIR', self.base_dir),
159
            'compute_build_dir': mock.patch('backend.utils.compute_build_dir'),
160
            'fetch_gui_from_branch': mock.patch(
161
                'backend.utils.fetch_gui_from_branch'),
162
            'fetch_gui_release': mock.patch('backend.utils.fetch_gui_release'),
163
            'install_builtin_server': mock.patch(
164
                'backend.utils.install_builtin_server'),
165
            'install_missing_packages': mock.patch(
166
                'backend.utils.install_missing_packages'),
167
            'log': mock.patch('backend.log'),
168
            'open_port': mock.patch('backend.open_port'),
169
            'parse_source': mock.patch(
170
                'backend.utils.parse_source', mock_parse_source),
171
            'save_or_create_certificates': mock.patch(
172
                'backend.utils.save_or_create_certificates'),
173
            'setup_gui': mock.patch('backend.utils.setup_gui'),
174
            'start_builtin_server': mock.patch(
175
                'backend.utils.start_builtin_server'),
176
            'start_haproxy_apache': mock.patch(
177
                'backend.utils.start_haproxy_apache'),
178
            'stop_builtin_server': mock.patch(
179
                'backend.utils.stop_builtin_server'),
180
            'stop_haproxy_apache': mock.patch(
181
                'backend.utils.stop_haproxy_apache'),
182
            'write_gui_config': mock.patch('backend.utils.write_gui_config'),
183
        }
184
        # Note: nested is deprecated for good reasons which do not apply here.
185
        # Used here to easily nest a dynamically generated list of context
186
        # managers.
187
        with nested(*mocks.values()) as context_managers:
188
            object_dict = dict(zip(mocks.keys(), context_managers))
189
            yield type('Mocks', (object,), object_dict)
49.1.8 by Nicola Larosa
Polish tests, remove the overrideable mechanism, use prefixed imports everywhere.
190
113.1.3 by Francesco Banconi
Checkpoint.
191
    def assert_write_gui_config_called(self, mocks, config):
192
        """Ensure the mocked write_gui_config has been properly called."""
193
        mocks.write_gui_config.assert_called_once_with(
194
            config['juju-gui-console-enabled'], config['login-help'],
147.1.1 by Rick Harding
Remove all the pyjuju
195
            config['read-only'], config['charmworld-url'],
113.1.3 by Francesco Banconi
Checkpoint.
196
            mocks.compute_build_dir(), secure=config['secure'],
197
            sandbox=config['sandbox'], ga_key=config['ga-key'],
198
            show_get_juju_button=config['show-get-juju-button'], password=None)
199
100.2.6 by Francesco Banconi
Fixed backend test.
200
    def test_base_dir_created(self):
113.1.1 by Francesco Banconi
Checkpoint.
201
        # The base Juju GUI directory is correctly created.
202
        config = self.make_config()
203
        test_backend = backend.Backend(config=config)
204
        with self.mock_all():
205
            test_backend.install()
206
        self.assertTrue(os.path.isdir(self.base_dir))
100.2.6 by Francesco Banconi
Fixed backend test.
207
208
    def test_base_dir_removed(self):
113.1.1 by Francesco Banconi
Checkpoint.
209
        # The base Juju GUI directory is correctly removed.
210
        config = self.make_config()
211
        test_backend = backend.Backend(config=config)
212
        with self.mock_all():
213
            test_backend.install()
214
            test_backend.destroy()
100.2.6 by Francesco Banconi
Fixed backend test.
215
        self.assertFalse(os.path.exists(utils.BASE_DIR), utils.BASE_DIR)
216
113.1.1 by Francesco Banconi
Checkpoint.
217
    def test_install_go_legacy_stable(self):
218
        # Install a juju-core backend with legacy server and stable release.
219
        config = self.make_config({'builtin-server': False})
147.1.1 by Rick Harding
Remove all the pyjuju
220
        test_backend = backend.Backend(config=config)
221
        with self.mock_all() as mocks:
222
            test_backend.install()
113.1.1 by Francesco Banconi
Checkpoint.
223
        mocks.install_missing_packages.assert_called_once_with(
224
            set(EXPECTED_GO_LEGACY_DEBS), repository=self.repository_location)
225
        mocks.parse_source.assert_called_once_with(self.juju_gui_source)
226
        mocks.fetch_gui_release.assert_called_once_with(
227
            *self.parse_source_return_value)
228
        self.assertFalse(mocks.fetch_gui_from_branch.called)
229
        mocks.setup_gui.assert_called_once_with(mocks.fetch_gui_release())
230
        self.assertFalse(mocks.install_builtin_server.called)
231
232
    def test_install_go_builtin_stable(self):
233
        # Install a juju-core backend with builtin server and stable release.
234
        config = self.make_config({'builtin-server': True})
147.1.1 by Rick Harding
Remove all the pyjuju
235
        test_backend = backend.Backend(config=config)
236
        with self.mock_all() as mocks:
237
            test_backend.install()
113.1.1 by Francesco Banconi
Checkpoint.
238
        mocks.install_missing_packages.assert_called_once_with(
239
            set(EXPECTED_GO_BUILTIN_DEBS), repository=None)
240
        mocks.parse_source.assert_called_once_with(self.juju_gui_source)
241
        mocks.fetch_gui_release.assert_called_once_with(
242
            *self.parse_source_return_value)
243
        self.assertFalse(mocks.fetch_gui_from_branch.called)
244
        mocks.setup_gui.assert_called_once_with(mocks.fetch_gui_release())
245
        mocks.install_builtin_server.assert_called_once_with()
246
247
    def test_install_go_builtin_branch(self):
248
        # Install a juju-core backend with builtin server and branch release.
249
        self.parse_source_return_value = ('branch', ('lp:juju-gui', 42))
250
        expected_calls = [
251
            mock.call(set(EXPECTED_GO_BUILTIN_DEBS), repository=None),
252
            mock.call(
253
                utils.DEB_BUILD_DEPENDENCIES,
254
                repository=self.repository_location,
255
            ),
256
        ]
257
        config = self.make_config({'builtin-server': True})
147.1.1 by Rick Harding
Remove all the pyjuju
258
        test_backend = backend.Backend(config=config)
259
        with self.mock_all() as mocks:
260
            test_backend.install()
113.1.1 by Francesco Banconi
Checkpoint.
261
        mocks.install_missing_packages.assert_has_calls(expected_calls)
262
        mocks.parse_source.assert_called_once_with(self.juju_gui_source)
263
        mocks.fetch_gui_from_branch.assert_called_once_with(
264
            'lp:juju-gui', 42, self.command_log_file)
265
        self.assertFalse(mocks.fetch_gui_release.called)
266
        mocks.setup_gui.assert_called_once_with(mocks.fetch_gui_from_branch())
267
        mocks.install_builtin_server.assert_called_once_with()
268
269
    def test_start_go_legacy(self):
270
        # Start a juju-core backend with legacy server.
271
        config = self.make_config({'builtin-server': False})
147.1.1 by Rick Harding
Remove all the pyjuju
272
        test_backend = backend.Backend(config=config)
273
        with self.mock_all() as mocks:
274
            test_backend.start()
113.1.1 by Francesco Banconi
Checkpoint.
275
        mocks.compute_build_dir.assert_called_with(
276
            config['juju-gui-debug'], config['serve-tests'])
113.1.3 by Francesco Banconi
Checkpoint.
277
        self.assert_write_gui_config_called(mocks, config)
113.1.1 by Francesco Banconi
Checkpoint.
278
        mocks.open_port.assert_has_calls([mock.call(80), mock.call(443)])
279
        mocks.start_haproxy_apache.assert_called_once_with(
280
            mocks.compute_build_dir(), config['serve-tests'],
281
            self.ssl_cert_path, config['secure'])
282
        self.assertFalse(mocks.start_builtin_server.called)
283
284
    def test_start_go_builtin(self):
285
        # Start a juju-core backend with builtin server.
286
        config = self.make_config({'builtin-server': True})
147.1.1 by Rick Harding
Remove all the pyjuju
287
        test_backend = backend.Backend(config=config)
288
        with self.mock_all() as mocks:
289
            test_backend.start()
113.1.1 by Francesco Banconi
Checkpoint.
290
        mocks.compute_build_dir.assert_called_with(
291
            config['juju-gui-debug'], config['serve-tests'])
113.1.3 by Francesco Banconi
Checkpoint.
292
        self.assert_write_gui_config_called(mocks, config)
113.1.1 by Francesco Banconi
Checkpoint.
293
        mocks.open_port.assert_has_calls([mock.call(80), mock.call(443)])
294
        mocks.start_builtin_server.assert_called_once_with(
295
            mocks.compute_build_dir(), self.ssl_cert_path,
296
            config['serve-tests'], config['sandbox'],
125.1.1 by Brad Crittenden
Pass 'charmworld-url' to guiserver, update deployment count on successful deployment of a bundle.
297
            config['builtin-server-logging'], not config['secure'],
298
            config['charmworld-url'])
113.1.1 by Francesco Banconi
Checkpoint.
299
        self.assertFalse(mocks.start_haproxy_apache.called)
300
301
    def test_stop_go_legacy(self):
302
        # Stop a juju-core backend with legacy server.
303
        config = self.make_config({'builtin-server': False})
147.1.1 by Rick Harding
Remove all the pyjuju
304
        test_backend = backend.Backend(config=config)
305
        with self.mock_all() as mocks:
306
            test_backend.stop()
113.1.1 by Francesco Banconi
Checkpoint.
307
        mocks.stop_haproxy_apache.assert_called_once_with()
308
        self.assertFalse(mocks.stop_builtin_server.called)
309
310
    def test_stop_go_builtin(self):
311
        # Stop a juju-core backend with builtin server.
312
        config = self.make_config({'builtin-server': True})
147.1.1 by Rick Harding
Remove all the pyjuju
313
        test_backend = backend.Backend(config=config)
314
        with self.mock_all() as mocks:
315
            test_backend.stop()
113.1.1 by Francesco Banconi
Checkpoint.
316
        mocks.stop_builtin_server.assert_called_once_with()
317
        self.assertFalse(mocks.stop_haproxy_apache.called)
49.1.6 by Nicola Larosa
Implement test_stop, improve test_start, streamline all.
318
49.1.5 by Nicola Larosa
Implemented test_install and test_start.
319
320
class TestBackendUtils(unittest.TestCase):
49.1.1 by Nicola Larosa
Add tests, remove dead code, rename some other code, general reformatting.
321
322
    def test_same_config(self):
49.1.5 by Nicola Larosa
Implemented test_install and test_start.
323
        test_backend = backend.Backend(
80.1.11 by Nicola Larosa
More fixes from reviews.
324
            config={
147.1.1 by Rick Harding
Remove all the pyjuju
325
                'sandbox': False, 'builtin-server': False},
80.1.11 by Nicola Larosa
More fixes from reviews.
326
            prev_config={
147.1.1 by Rick Harding
Remove all the pyjuju
327
                'sandbox': False, 'builtin-server': False},
49.1.1 by Nicola Larosa
Add tests, remove dead code, rename some other code, general reformatting.
328
        )
49.1.5 by Nicola Larosa
Implemented test_install and test_start.
329
        self.assertFalse(test_backend.different('sandbox'))
147.1.1 by Rick Harding
Remove all the pyjuju
330
        self.assertFalse(test_backend.different('builtin-server'))
49.1.1 by Nicola Larosa
Add tests, remove dead code, rename some other code, general reformatting.
331
332
    def test_different_config(self):
49.1.5 by Nicola Larosa
Implemented test_install and test_start.
333
        test_backend = backend.Backend(
80.1.11 by Nicola Larosa
More fixes from reviews.
334
            config={
147.1.1 by Rick Harding
Remove all the pyjuju
335
                'sandbox': False, 'builtin-server': False},
80.1.11 by Nicola Larosa
More fixes from reviews.
336
            prev_config={
147.1.1 by Rick Harding
Remove all the pyjuju
337
                'sandbox': True, 'builtin-server': False},
49.1.1 by Nicola Larosa
Add tests, remove dead code, rename some other code, general reformatting.
338
        )
49.1.5 by Nicola Larosa
Implemented test_install and test_start.
339
        self.assertTrue(test_backend.different('sandbox'))
147.1.1 by Rick Harding
Remove all the pyjuju
340
        self.assertFalse(test_backend.different('builtin-server'))
100.2.6 by Francesco Banconi
Fixed backend test.
341
342
113.1.1 by Francesco Banconi
Checkpoint.
343
class TestCallMethods(unittest.TestCase):
100.2.6 by Francesco Banconi
Fixed backend test.
344
345
    def setUp(self):
346
        self.called = []
113.1.1 by Francesco Banconi
Checkpoint.
347
        self.objects = [self.make_object('Obj1'), self.make_object('Obj2')]
348
349
    def make_object(self, name, has_method=True):
350
        """Create and return an test object with the given name."""
351
        def method(obj, *args):
352
            self.called.append([obj.__class__.__name__, args])
353
        object_dict = {'method': method} if has_method else {}
354
        return type(name, (object,), object_dict)()
355
356
    def test_call(self):
357
        # The methods are correctly called.
358
        backend.call_methods(self.objects, 'method', 'arg1', 'arg2')
359
        expected = [['Obj1', ('arg1', 'arg2')], ['Obj2', ('arg1', 'arg2')]]
360
        self.assertEqual(expected, self.called)
361
362
    def test_no_method(self):
363
        # An object without the method is ignored.
364
        self.objects.append(self.make_object('Obj3', has_method=False))
365
        backend.call_methods(self.objects, 'method')
366
        expected = [['Obj1', ()], ['Obj2', ()]]
367
        self.assertEqual(expected, self.called)