1import os
2import sys
3import tempfile
4import unittest
5
6from six import b as b_
7from webtest import TestApp
8
9import pecan
10from pecan.tests import PecanTestCase
11
12
13__here__ = os.path.dirname(__file__)
14
15
16class TestConf(PecanTestCase):
17
18    def test_update_config_fail_identifier(self):
19        """Fail when naming does not pass correctness"""
20        from pecan import configuration
21        bad_dict = {'bad name': 'value'}
22        self.assertRaises(ValueError, configuration.Config, bad_dict)
23
24    def test_update_set_config(self):
25        """Update an empty configuration with the default values"""
26        from pecan import configuration
27
28        conf = configuration.initconf()
29        conf.update(configuration.conf_from_file(os.path.join(
30            __here__,
31            'config_fixtures/config.py'
32        )))
33
34        self.assertEqual(conf.app.root, None)
35        self.assertEqual(conf.app.template_path, 'myproject/templates')
36        self.assertEqual(conf.app.static_root, 'public')
37
38        self.assertEqual(conf.server.host, '1.1.1.1')
39        self.assertEqual(conf.server.port, '8081')
40
41    def test_update_set_default_config(self):
42        """Update an empty configuration with the default values"""
43        from pecan import configuration
44
45        conf = configuration.initconf()
46        conf.update(configuration.conf_from_file(os.path.join(
47            __here__,
48            'config_fixtures/empty.py'
49        )))
50
51        self.assertEqual(conf.app.root, None)
52        self.assertEqual(conf.app.template_path, '')
53        self.assertEqual(conf.app.static_root, 'public')
54
55        self.assertEqual(conf.server.host, '0.0.0.0')
56        self.assertEqual(conf.server.port, '8080')
57
58    def test_update_force_dict(self):
59        """Update an empty configuration with the default values"""
60        from pecan import configuration
61        conf = configuration.initconf()
62        conf.update(configuration.conf_from_file(os.path.join(
63            __here__,
64            'config_fixtures/forcedict.py'
65        )))
66
67        self.assertEqual(conf.app.root, None)
68        self.assertEqual(conf.app.template_path, '')
69        self.assertEqual(conf.app.static_root, 'public')
70
71        self.assertEqual(conf.server.host, '0.0.0.0')
72        self.assertEqual(conf.server.port, '8080')
73
74        self.assertTrue(isinstance(conf.beaker, dict))
75        self.assertEqual(conf.beaker['session.key'], 'key')
76        self.assertEqual(conf.beaker['session.type'], 'cookie')
77        self.assertEqual(
78            conf.beaker['session.validate_key'],
79            '1a971a7df182df3e1dec0af7c6913ec7'
80        )
81        self.assertEqual(conf.beaker.get('__force_dict__'), None)
82
83    def test_update_config_with_dict(self):
84        from pecan import configuration
85        conf = configuration.initconf()
86        d = {'attr': True}
87        conf['attr'] = d
88        self.assertTrue(conf.attr.attr)
89
90    def test_config_repr(self):
91        from pecan import configuration
92        conf = configuration.Config({'a': 1})
93        self.assertEqual(repr(conf), "Config({'a': 1})")
94
95    def test_config_from_dict(self):
96        from pecan import configuration
97        conf = configuration.conf_from_dict({})
98        conf['path'] = '%(confdir)s'
99        self.assertTrue(os.path.samefile(conf['path'], os.getcwd()))
100
101    def test_config_from_file(self):
102        from pecan import configuration
103        path = os.path.join(
104            os.path.dirname(__file__), 'config_fixtures', 'config.py'
105        )
106        configuration.conf_from_file(path)
107
108    def test_config_illegal_ids(self):
109        from pecan import configuration
110        conf = configuration.Config({})
111        conf.update(configuration.conf_from_file(os.path.join(
112            __here__,
113            'config_fixtures/bad/module_and_underscore.py'
114        )))
115        self.assertEqual([], list(conf))
116
117    def test_config_missing_file(self):
118        from pecan import configuration
119        path = ('doesnotexist.py',)
120        configuration.Config({})
121        self.assertRaises(
122            RuntimeError,
123            configuration.conf_from_file,
124            os.path.join(__here__, 'config_fixtures', *path)
125        )
126
127    def test_config_missing_file_on_path(self):
128        from pecan import configuration
129        path = ('bad', 'bad', 'doesnotexist.py',)
130        configuration.Config({})
131
132        self.assertRaises(
133            RuntimeError,
134            configuration.conf_from_file,
135            os.path.join(__here__, 'config_fixtures', *path)
136        )
137
138    def test_config_with_syntax_error(self):
139        from pecan import configuration
140        with tempfile.NamedTemporaryFile('wb') as f:
141            f.write(b_('\n'.join(['if false', 'var = 3'])))
142            f.flush()
143            configuration.Config({})
144
145            self.assertRaises(
146                SyntaxError,
147                configuration.conf_from_file,
148                f.name
149            )
150
151    def test_config_with_non_package_relative_import(self):
152        from pecan import configuration
153        with tempfile.NamedTemporaryFile('wb', suffix='.py') as f:
154            f.write(b_('\n'.join(['from . import variables'])))
155            f.flush()
156            configuration.Config({})
157
158            try:
159                configuration.conf_from_file(f.name)
160            except (ValueError, SystemError, ImportError) as e:
161                assert 'relative import' in str(e)
162            else:
163                raise AssertionError(
164                    "A relative import-related error should have been raised"
165                )
166
167    def test_config_with_bad_import(self):
168        from pecan import configuration
169        path = ('bad', 'importerror.py')
170        configuration.Config({})
171
172        self.assertRaises(
173            ImportError,
174            configuration.conf_from_file,
175            os.path.join(
176                __here__,
177                'config_fixtures',
178                *path
179            )
180        )
181
182    def test_config_dir(self):
183        from pecan import configuration
184        conf = configuration.Config({})
185        self.assertEqual([], dir(conf))
186        conf = configuration.Config({'a': 1})
187        self.assertEqual(['a'], dir(conf))
188
189    def test_config_bad_key(self):
190        from pecan import configuration
191        conf = configuration.Config({'a': 1})
192        assert conf.a == 1
193        self.assertRaises(AttributeError, getattr, conf, 'b')
194
195    def test_config_get_valid_key(self):
196        from pecan import configuration
197        conf = configuration.Config({'a': 1})
198        assert conf.get('a') == 1
199
200    def test_config_get_invalid_key(self):
201        from pecan import configuration
202        conf = configuration.Config({'a': 1})
203        assert conf.get('b') is None
204
205    def test_config_get_invalid_key_return_default(self):
206        from pecan import configuration
207        conf = configuration.Config({'a': 1})
208        assert conf.get('b', True) is True
209
210    def test_config_to_dict(self):
211        from pecan import configuration
212        conf = configuration.initconf()
213
214        assert isinstance(conf, configuration.Config)
215
216        to_dict = conf.to_dict()
217
218        assert isinstance(to_dict, dict)
219        assert to_dict['server']['host'] == '0.0.0.0'
220        assert to_dict['server']['port'] == '8080'
221        assert to_dict['app']['modules'] == []
222        assert to_dict['app']['root'] is None
223        assert to_dict['app']['static_root'] == 'public'
224        assert to_dict['app']['template_path'] == ''
225
226    def test_config_to_dict_nested(self):
227        from pecan import configuration
228        """have more than one level nesting and convert to dict"""
229        conf = configuration.initconf()
230        nested = {'one': {'two': 2}}
231        conf['nested'] = nested
232
233        to_dict = conf.to_dict()
234
235        assert isinstance(to_dict, dict)
236        assert to_dict['server']['host'] == '0.0.0.0'
237        assert to_dict['server']['port'] == '8080'
238        assert to_dict['app']['modules'] == []
239        assert to_dict['app']['root'] is None
240        assert to_dict['app']['static_root'] == 'public'
241        assert to_dict['app']['template_path'] == ''
242        assert to_dict['nested']['one']['two'] == 2
243
244    def test_config_to_dict_prefixed(self):
245        from pecan import configuration
246        """Add a prefix for keys"""
247        conf = configuration.initconf()
248
249        assert isinstance(conf, configuration.Config)
250
251        to_dict = conf.to_dict('prefix_')
252
253        assert isinstance(to_dict, dict)
254        assert to_dict['prefix_server']['prefix_host'] == '0.0.0.0'
255        assert to_dict['prefix_server']['prefix_port'] == '8080'
256        assert to_dict['prefix_app']['prefix_modules'] == []
257        assert to_dict['prefix_app']['prefix_root'] is None
258        assert to_dict['prefix_app']['prefix_static_root'] == 'public'
259        assert to_dict['prefix_app']['prefix_template_path'] == ''
260
261
262class TestGlobalConfig(PecanTestCase):
263
264    def tearDown(self):
265        from pecan import configuration
266        configuration.set_config(
267            dict(configuration.initconf()),
268            overwrite=True
269        )
270
271    def test_paint_from_dict(self):
272        from pecan import configuration
273        configuration.set_config({'foo': 'bar'})
274        assert dict(configuration._runtime_conf) != {'foo': 'bar'}
275        self.assertEqual(configuration._runtime_conf.foo, 'bar')
276
277    def test_overwrite_from_dict(self):
278        from pecan import configuration
279        configuration.set_config({'foo': 'bar'}, overwrite=True)
280        assert dict(configuration._runtime_conf) == {'foo': 'bar'}
281
282    def test_paint_from_file(self):
283        from pecan import configuration
284        configuration.set_config(os.path.join(
285            __here__,
286            'config_fixtures/foobar.py'
287        ))
288        assert dict(configuration._runtime_conf) != {'foo': 'bar'}
289        assert configuration._runtime_conf.foo == 'bar'
290
291    def test_overwrite_from_file(self):
292        from pecan import configuration
293        configuration.set_config(
294            os.path.join(
295                __here__,
296                'config_fixtures/foobar.py',
297            ),
298            overwrite=True
299        )
300        assert dict(configuration._runtime_conf) == {'foo': 'bar'}
301
302    def test_set_config_none_type(self):
303        from pecan import configuration
304        self.assertRaises(RuntimeError, configuration.set_config, None)
305
306    def test_set_config_to_dir(self):
307        from pecan import configuration
308        self.assertRaises(RuntimeError, configuration.set_config, '/')
309
310
311class TestConfFromEnv(PecanTestCase):
312    #
313    # Note that there is a good chance of pollution if ``tearDown`` does not
314    # reset the configuration like this class does. If implementing new classes
315    # for configuration this tearDown **needs to be implemented**
316    #
317
318    def setUp(self):
319        super(TestConfFromEnv, self).setUp()
320        self.addCleanup(self._remove_config_key)
321
322        from pecan import configuration
323        self.get_conf_path_from_env = configuration.get_conf_path_from_env
324
325    def _remove_config_key(self):
326        os.environ.pop('PECAN_CONFIG', None)
327
328    def test_invalid_path(self):
329        os.environ['PECAN_CONFIG'] = '/'
330        msg = "PECAN_CONFIG was set to an invalid path: /"
331        self.assertRaisesRegexp(
332            RuntimeError,
333            msg,
334            self.get_conf_path_from_env
335        )
336
337    def test_is_not_set(self):
338        msg = "PECAN_CONFIG is not set and " \
339              "no config file was passed as an argument."
340        self.assertRaisesRegexp(
341            RuntimeError,
342            msg,
343            self.get_conf_path_from_env
344        )
345
346    def test_return_valid_path(self):
347        __here__ = os.path.abspath(__file__)
348        os.environ['PECAN_CONFIG'] = __here__
349        assert self.get_conf_path_from_env() == __here__
350
351
352class TestConfigCleanup(unittest.TestCase):
353
354    def setUp(self):
355        class RootController(object):
356            @pecan.expose()
357            def index(self):
358                return 'Hello, World!'
359        self.app = TestApp(pecan.Pecan(RootController()))
360
361    def tearDown(self):
362        pecan.configuration.set_config(pecan.configuration.DEFAULT,
363                                       overwrite=True)
364
365    def test_conf_default(self):
366        assert pecan.conf.server.to_dict() == {
367            'port': '8080', 'host': '0.0.0.0'
368        }
369
370    def test_conf_changed(self):
371        pecan.conf.server = pecan.configuration.Config({'port': '80'})
372        assert pecan.conf.server.to_dict() == {'port': '80'}
373