diff --git a/tests/test_cli.py b/tests/test_cli.py index 371233f..72c0c19 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -20,7 +20,6 @@ def test_load_config(tmpdir, monkeypatch): f.write(dedent(''' [general] status_path = {status} - foo = 1 [pair bob] a = bob_a @@ -46,7 +45,7 @@ def test_load_config(tmpdir, monkeypatch): errors = [] monkeypatch.setattr('vdirsyncer.cli.cli_logger.error', errors.append) general, pairs, storages = cli.load_config(fname, pair_options=('bam',)) - assert general == {'foo': 1, 'status_path': status_path} + assert general == {'status_path': status_path} assert pairs == {'bob': ('bob_a', 'bob_b', {'bam': True}, {'foo': 'bar'})} assert storages == { 'bob_a': {'type': 'filesystem', 'path': contacts_path, 'fileext': @@ -188,7 +187,31 @@ def test_missing_general_section(tmpdir): env={'VDIRSYNCER_CONFIG': str(config_file)} ) assert result.exception - assert 'critical: unable to find general section' in result.output.lower() + assert result.output.startswith('critical:') + assert 'unable to find general section' in result.output.lower() + + +def test_wrong_general_section(tmpdir): + config_file = tmpdir.join('config') + config_file.write(dedent(''' + [general] + wrong = yes + ''')) + + runner = CliRunner() + result = runner.invoke( + cli.app, ['sync'], + env={'VDIRSYNCER_CONFIG': str(config_file)} + ) + + assert result.exception + lines = result.output.splitlines() + assert lines[:-1] == [ + 'critical: general section doesn\'t take the parameters: wrong', + 'critical: general section is missing the parameters: status_path' + ] + assert lines[-1].startswith('critical:') + assert lines[-1].endswith('Invalid general section.') def test_verbosity(tmpdir): diff --git a/vdirsyncer/cli.py b/vdirsyncer/cli.py index 278b76b..e606849 100644 --- a/vdirsyncer/cli.py +++ b/vdirsyncer/cli.py @@ -31,6 +31,9 @@ cli_logger = log.get(__name__) PROJECT_HOME = 'https://github.com/untitaker/vdirsyncer' DOCS_HOME = 'https://vdirsyncer.readthedocs.org/en/latest' +GENERAL_ALL = set(['processes', 'status_path']) +GENERAL_REQUIRED = set(['status_path']) + class CliError(RuntimeError): pass @@ -42,6 +45,28 @@ def get_status_name(pair, collection): return pair + '/' + collection +def validate_general_section(general_config): + if general_config is None: + raise CliError( + 'Unable to find general section. You should copy the example ' + 'config from the repository and edit it.\n{}'.format(PROJECT_HOME) + ) + + invalid = set(general_config) - GENERAL_ALL + missing = GENERAL_REQUIRED - set(general_config) + + if invalid: + cli_logger.critical(u'general section doesn\'t take the parameters: {}' + .format(u', '.join(invalid))) + + if missing: + cli_logger.critical(u'general section is missing the parameters: {}' + .format(u', '.join(missing))) + + if invalid or missing: + raise CliError('Invalid general section.') + + def load_config(fname, pair_options=('collections', 'conflict_resolution')): c = RawConfigParser() with open(fname) as f: @@ -80,12 +105,7 @@ def load_config(fname, pair_options=('collections', 'conflict_resolution')): else: handlers.get(section_type, bad_section)(name, get_options(section)) - if general is None: - raise CliError( - 'Unable to find general section. You should copy the example ' - 'config from the repository and edit it.\n{}'.format(PROJECT_HOME) - ) - + validate_general_section(general) return general, pairs, storages @@ -273,7 +293,7 @@ def _create_app(): try: ctx.obj['config'] = load_config(fname) except Exception as e: - raise CliError('Error during reading config{}:\n{}' + raise CliError('Error during reading config{}: {}' .format(fname, e)) @app.command()