diff --git a/couchpotato/core/settings.py b/couchpotato/core/settings.py index e99c543..978b37c 100644 --- a/couchpotato/core/settings.py +++ b/couchpotato/core/settings.py @@ -326,33 +326,43 @@ class Settings(object): option = kwargs.get('name') value = kwargs.get('value') - from couchpotato.environment import Env - soft_chroot = Env.get('softchroot') + if not self.isOptionWritable(section, option): + self.log.warning('Option "%s.%s" isn\'t writable', (section, option)) + return { + 'success' : False, + } + else: + + from couchpotato.environment import Env + soft_chroot = Env.get('softchroot') - if self.getType(section, option) == 'directory': - value = soft_chroot.chroot2abs(value) + if self.getType(section, option) == 'directory': + value = soft_chroot.chroot2abs(value) - if self.getType(section, option) == 'directories': - import json - value = json.loads(value) - if not (value and isinstance(value, list)): - value = [] - value = map(soft_chroot.chroot2abs, value) - value = self.directories_delimiter.join(value) + if self.getType(section, option) == 'directories': + import json + value = json.loads(value) + if not (value and isinstance(value, list)): + value = [] + value = map(soft_chroot.chroot2abs, value) + value = self.directories_delimiter.join(value) - # See if a value handler is attached, use that as value - new_value = fireEvent('setting.save.%s.%s' % (section, option), value, single = True) + # See if a value handler is attached, use that as value + new_value = fireEvent('setting.save.%s.%s' % (section, option), value, single = True) - self.set(section, option, (new_value if new_value else value).encode('unicode_escape')) - self.save() + self.set(section, option, (new_value if new_value else value).encode('unicode_escape')) + self.save() - # After save (for re-interval etc) - fireEvent('setting.save.%s.%s.after' % (section, option), single = True) - fireEvent('setting.save.%s.*.after' % section, single = True) + # After save (for re-interval etc) + fireEvent('setting.save.%s.%s.after' % (section, option), single = True) + fireEvent('setting.save.%s.*.after' % section, single = True) - return { - 'success': True - } + return { + 'success': True + } + + # unreachable code: + return None def isSectionReadable(self, section): meta = 'section_hidden' + self.optionMetaSuffix() diff --git a/couchpotato/core/settings_test.py b/couchpotato/core/settings_test.py new file mode 100644 index 0000000..034ddd3 --- /dev/null +++ b/couchpotato/core/settings_test.py @@ -0,0 +1,96 @@ +import mock +from mock import patch, Mock +import unittest +from unittest import TestCase + +from couchpotato.core.settings import Settings + +class DoNotUseMe: + """ Do not use this class, it is just for storing Mock ' s of Settings-class + + Usage: + Select appropriate Mocks and copy-paste them to your test-method + """ + def __do_not_call(self): + # s = Settings + s = Mock() + + # methods: + s.isOptionWritable = Mock(return_value=True) + s.set = Mock(return_value=None) + s.save = Mock() + + # props: + s.log = Mock() + +class SettingsSaveWritableNonWritable(TestCase): + def setUp(self): + self.s = Settings() + + def test_save_writable(self): + s = self.s + + # set up Settings-mocks : + # lets assume, that option is writable: + mock_isOptionWritable = s.isOptionWritable = Mock(return_value=True) + mock_set = s.set = Mock(return_value=None) + mock_p_save = s.save = Mock() + + section = 'core' + option = 'option_non_exist_be_sure' + value = "1000" + params = { 'section' : section, 'name' : option, 'value' : value } + + # call method: + env_mock = Mock() + + # HERE is an example of mocking LOCAL 'import' + with patch.dict('sys.modules', {'couchpotato.environment.Env': env_mock}): + result = s.saveView(**params) + + self.assertIsInstance(s, Settings) + self.assertIsInstance(result, dict) + self.assertTrue(result['success']) + + # ----------------------------------------- + # check mock + # ----------------------------------------- + mock_isOptionWritable.assert_called_with(section, option) + + # check, that Settings tried to save my value: + mock_set.assert_called_with(section, option, value) + + + def test_save_non_writable(self): + s = self.s + + # set up Settings-mocks : + # lets assume, that option is not writable: + mock_is_w = s.isOptionWritable = Mock(return_value=False) + mock_set = s.set = Mock(return_value=None) + mock_p_save = s.save = Mock() + mock_log_s = s.log = Mock() + + section = 'core' + option = 'option_non_exist_be_sure' + value = "1000" + params = { 'section' : section, 'name' : option, 'value' : value } + + # call method: + env_mock = Mock() + + # HERE is an example of mocking LOCAL 'import' + with patch.dict('sys.modules', {'couchpotato.environment.Env': env_mock}): + result = s.saveView(**params) + + + self.assertIsInstance(s, Settings) + self.assertIsInstance(result, dict) + self.assertFalse(result['success']) + + # ----------------------------------------- + # check mock + # ----------------------------------------- + # lets check, that 'set'-method was not called: + self.assertFalse(mock_set.called, 'Method `set` was called') + mock_is_w.assert_called_with(section, option) diff --git a/couchpotato/core/softchroot_test.py b/couchpotato/core/softchroot_test.py index d5618cb..5dfeaf0 100644 --- a/couchpotato/core/softchroot_test.py +++ b/couchpotato/core/softchroot_test.py @@ -3,7 +3,6 @@ import os import logging import unittest from unittest import TestCase -#from mock import MagicMock from couchpotato.core.softchroot import SoftChroot