borgmatic/tests/integration/config/test_validate.py

284 lines
7.7 KiB
Python
Raw Normal View History

2017-07-05 03:32:37 +02:00
import io
import string
import sys
import pytest
2019-05-13 23:39:10 +02:00
from flexmock import flexmock
2017-07-05 03:32:37 +02:00
from borgmatic.config import validate as module
2017-07-05 03:32:37 +02:00
2023-04-01 18:40:32 +02:00
def test_schema_filename_returns_plausible_path():
schema_path = module.schema_filename()
2017-07-05 03:32:37 +02:00
assert schema_path.endswith('/schema.yaml')
2017-10-26 07:32:06 +02:00
def mock_config_and_schema(config_yaml, schema_yaml=None):
2017-07-05 03:32:37 +02:00
'''
2017-10-26 07:32:06 +02:00
Set up mocks for the given config config YAML string and the schema YAML string, or the default
schema if no schema is provided. The idea is that that the code under test consumes these mocks
when parsing the configuration.
2017-07-05 03:32:37 +02:00
'''
config_stream = io.StringIO(config_yaml)
config_stream.name = 'config.yaml'
2017-10-26 07:32:06 +02:00
if schema_yaml is None:
schema_stream = open(module.schema_filename())
else:
schema_stream = io.StringIO(schema_yaml)
schema_stream.name = 'schema.yaml'
2017-10-26 07:32:06 +02:00
builtins = flexmock(sys.modules['builtins'])
flexmock(module.os).should_receive('getcwd').and_return('/tmp')
flexmock(module.os.path).should_receive('isabs').and_return(False)
flexmock(module.os.path).should_receive('exists').and_return(True)
builtins.should_receive('open').with_args('/tmp/config.yaml').and_return(config_stream)
builtins.should_receive('open').with_args('/tmp/schema.yaml').and_return(schema_stream)
2017-07-05 03:32:37 +02:00
def test_parse_configuration_transforms_file_into_mapping():
mock_config_and_schema(
'''
location:
source_directories:
- /home
- /etc
repositories:
- path: hostname.borg
2017-07-05 03:32:37 +02:00
retention:
keep_minutely: 60
keep_hourly: 24
2017-07-05 03:32:37 +02:00
keep_daily: 7
consistency:
checks:
- name: repository
- name: archives
2017-07-05 03:32:37 +02:00
'''
)
config, logs = module.parse_configuration('/tmp/config.yaml', '/tmp/schema.yaml')
2017-07-05 03:32:37 +02:00
assert config == {
'location': {
'source_directories': ['/home', '/etc'],
'repositories': [{'path': 'hostname.borg'}],
},
'retention': {'keep_daily': 7, 'keep_hourly': 24, 'keep_minutely': 60},
'consistency': {'checks': [{'name': 'repository'}, {'name': 'archives'}]},
2017-07-05 03:32:37 +02:00
}
assert logs == []
2017-07-05 03:32:37 +02:00
def test_parse_configuration_passes_through_quoted_punctuation():
escaped_punctuation = string.punctuation.replace('\\', r'\\').replace('"', r'\"')
mock_config_and_schema(
f'''
2017-07-05 03:32:37 +02:00
location:
source_directories:
- "/home/{escaped_punctuation}"
2017-07-05 03:32:37 +02:00
repositories:
- path: test.borg
'''
2017-07-05 03:32:37 +02:00
)
config, logs = module.parse_configuration('/tmp/config.yaml', '/tmp/schema.yaml')
2017-07-05 03:32:37 +02:00
assert config == {
2017-07-05 03:32:37 +02:00
'location': {
'source_directories': [f'/home/{string.punctuation}'],
'repositories': [{'path': 'test.borg'}],
}
2017-07-05 03:32:37 +02:00
}
assert logs == []
2017-07-05 03:32:37 +02:00
2017-10-26 07:32:06 +02:00
def test_parse_configuration_with_schema_lacking_examples_does_not_raise():
mock_config_and_schema(
'''
location:
source_directories:
- /home
repositories:
- path: hostname.borg
2017-10-26 07:32:06 +02:00
''',
'''
map:
location:
required: true
map:
source_directories:
required: true
seq:
- type: scalar
repositories:
required: true
seq:
- type: scalar
''',
2017-10-26 07:32:06 +02:00
)
module.parse_configuration('/tmp/config.yaml', '/tmp/schema.yaml')
2017-10-26 07:32:06 +02:00
def test_parse_configuration_inlines_include():
mock_config_and_schema(
'''
location:
source_directories:
- /home
repositories:
- path: hostname.borg
retention:
!include include.yaml
'''
)
builtins = flexmock(sys.modules['builtins'])
include_file = io.StringIO(
'''
keep_daily: 7
keep_hourly: 24
'''
)
include_file.name = 'include.yaml'
builtins.should_receive('open').with_args('/tmp/include.yaml').and_return(include_file)
config, logs = module.parse_configuration('/tmp/config.yaml', '/tmp/schema.yaml')
assert config == {
'location': {'source_directories': ['/home'], 'repositories': [{'path': 'hostname.borg'}]},
'retention': {'keep_daily': 7, 'keep_hourly': 24},
}
assert logs == []
def test_parse_configuration_merges_include():
mock_config_and_schema(
'''
location:
source_directories:
- /home
repositories:
- path: hostname.borg
retention:
keep_daily: 1
<<: !include include.yaml
'''
)
builtins = flexmock(sys.modules['builtins'])
include_file = io.StringIO(
'''
keep_daily: 7
keep_hourly: 24
'''
)
include_file.name = 'include.yaml'
builtins.should_receive('open').with_args('/tmp/include.yaml').and_return(include_file)
config, logs = module.parse_configuration('/tmp/config.yaml', '/tmp/schema.yaml')
assert config == {
'location': {'source_directories': ['/home'], 'repositories': [{'path': 'hostname.borg'}]},
'retention': {'keep_daily': 1, 'keep_hourly': 24},
}
assert logs == []
2017-07-05 03:32:37 +02:00
def test_parse_configuration_raises_for_missing_config_file():
with pytest.raises(FileNotFoundError):
module.parse_configuration('/tmp/config.yaml', '/tmp/schema.yaml')
2017-07-05 03:32:37 +02:00
def test_parse_configuration_raises_for_missing_schema_file():
mock_config_and_schema('')
builtins = flexmock(sys.modules['builtins'])
builtins.should_receive('open').with_args('/tmp/schema.yaml').and_raise(FileNotFoundError)
2017-07-05 03:32:37 +02:00
with pytest.raises(FileNotFoundError):
module.parse_configuration('/tmp/config.yaml', '/tmp/schema.yaml')
2017-07-05 03:32:37 +02:00
def test_parse_configuration_raises_for_syntax_error():
mock_config_and_schema('foo:\nbar')
2017-07-05 03:32:37 +02:00
with pytest.raises(ValueError):
module.parse_configuration('/tmp/config.yaml', '/tmp/schema.yaml')
2017-07-05 03:32:37 +02:00
def test_parse_configuration_raises_for_validation_error():
mock_config_and_schema(
'''
location:
source_directories: yes
repositories:
- path: hostname.borg
2017-07-05 03:32:37 +02:00
'''
)
with pytest.raises(module.Validation_error):
module.parse_configuration('/tmp/config.yaml', '/tmp/schema.yaml')
def test_parse_configuration_applies_overrides():
mock_config_and_schema(
'''
location:
source_directories:
- /home
repositories:
- path: hostname.borg
local_path: borg1
'''
)
config, logs = module.parse_configuration(
'/tmp/config.yaml', '/tmp/schema.yaml', overrides=['location.local_path=borg2']
)
assert config == {
'location': {
'source_directories': ['/home'],
'repositories': [{'path': 'hostname.borg'}],
'local_path': 'borg2',
}
}
assert logs == []
def test_parse_configuration_applies_normalization():
mock_config_and_schema(
'''
location:
source_directories:
- /home
repositories:
- path: hostname.borg
exclude_if_present: .nobackup
'''
)
config, logs = module.parse_configuration('/tmp/config.yaml', '/tmp/schema.yaml')
assert config == {
'location': {
'source_directories': ['/home'],
'repositories': [{'path': 'hostname.borg'}],
'exclude_if_present': ['.nobackup'],
}
}
assert logs