Configure pytest in pyproject.toml to import the repo copy, add a conftest that keeps tests out of the journal and desktop notifications, and test reading the config file and expanding paths.
230 lines
5.8 KiB
Python
230 lines
5.8 KiB
Python
"""Tests for reading the configuration file and expanding paths"""
|
|
|
|
import configparser
|
|
import os
|
|
import pwd
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from simple_backup import simple_backup as sb
|
|
|
|
USER = pwd.getpwuid(os.getuid()).pw_name
|
|
HOME = pwd.getpwuid(os.getuid()).pw_dir
|
|
|
|
|
|
def write_config(path: Path, text: str) -> str:
|
|
config_file = path / 'simple_backup.conf'
|
|
config_file.write_text(text)
|
|
|
|
return str(config_file)
|
|
|
|
|
|
def test_repo_copy_imported() -> None:
|
|
repo = Path(__file__).resolve().parents[1]
|
|
|
|
assert Path(sb.__file__).resolve().is_relative_to(repo)
|
|
|
|
|
|
@pytest.mark.parametrize('path, expected', [
|
|
('~', HOME),
|
|
('~/docs', f'{HOME}/docs'),
|
|
('/abs/~/path', '/abs/~/path'),
|
|
('relative', 'relative'),
|
|
])
|
|
def test_expand_home(path: str, expected: str) -> None:
|
|
assert sb._expand_home(path, USER) == expected
|
|
|
|
|
|
def test_expand_inputs_glob(tmp_path: Path) -> None:
|
|
for name in ['a.txt', 'b.txt', 'c.log']:
|
|
(tmp_path / name).touch()
|
|
|
|
result = sb._expand_inputs([f'{tmp_path}/*.txt', ''], USER)
|
|
|
|
assert sorted(result) == [f'{tmp_path}/a.txt', f'{tmp_path}/b.txt']
|
|
|
|
|
|
def test_expand_inputs_no_match(tmp_path: Path, caplog: pytest.LogCaptureFixture) -> None:
|
|
assert sb._expand_inputs([f'{tmp_path}/missing'], USER) == []
|
|
assert 'No file or directory matching input' in caplog.text
|
|
|
|
|
|
def test_expand_inputs_tilde_without_user(caplog: pytest.LogCaptureFixture) -> None:
|
|
sb._expand_inputs(['~/nothing-here'], None)
|
|
|
|
assert 'Cannot expand' in caplog.text
|
|
|
|
|
|
def test_missing_config(tmp_path: Path, caplog: pytest.LogCaptureFixture) -> None:
|
|
config_args = sb._read_config(str(tmp_path / 'missing.conf'), USER)
|
|
|
|
assert config_args['inputs'] is None
|
|
assert config_args['output'] is None
|
|
assert config_args['keep'] == -1
|
|
assert 'does not exist' in caplog.text
|
|
|
|
|
|
def test_missing_config_without_user(tmp_path: Path, caplog: pytest.LogCaptureFixture) -> None:
|
|
sb._read_config(str(tmp_path / 'missing.conf'), None)
|
|
|
|
assert 'User not specified' in caplog.text
|
|
|
|
|
|
def test_read_config(tmp_path: Path) -> None:
|
|
(tmp_path / 'one').mkdir()
|
|
(tmp_path / 'two').mkdir()
|
|
|
|
config_file = write_config(tmp_path, f"""
|
|
[backup]
|
|
inputs={tmp_path}/one, {tmp_path}/two, {tmp_path}/one
|
|
backup_dir=~/backups
|
|
exclude=*.bak, , *.tmp
|
|
keep=3
|
|
""")
|
|
|
|
config_args = sb._read_config(config_file, USER)
|
|
|
|
assert sorted(config_args['inputs']) == [f'{tmp_path}/one', f'{tmp_path}/two']
|
|
# Expanded later, as it may be a path on the server
|
|
assert config_args['output'] == '~/backups'
|
|
assert config_args['exclude'] == ['*.bak', '*.tmp']
|
|
assert config_args['keep'] == 3
|
|
assert config_args['ssh_host'] is None
|
|
assert config_args['ssh_user'] is None
|
|
assert config_args['ssh_keyfile'] is None
|
|
assert config_args['remote_sudo'] is False
|
|
assert config_args['numeric_ids'] is False
|
|
|
|
|
|
def test_optional_keys_missing(tmp_path: Path) -> None:
|
|
config_file = write_config(tmp_path, f"""
|
|
[backup]
|
|
inputs={tmp_path}
|
|
backup_dir=/backups
|
|
""")
|
|
|
|
config_args = sb._read_config(config_file, USER)
|
|
|
|
assert config_args['exclude'] == []
|
|
assert config_args['keep'] == -1
|
|
|
|
|
|
def test_old_default_section(tmp_path: Path) -> None:
|
|
config_file = write_config(tmp_path, f"""
|
|
[default]
|
|
inputs={tmp_path}
|
|
backup_dir=/backups
|
|
""")
|
|
|
|
config_args = sb._read_config(config_file, USER)
|
|
|
|
assert config_args['inputs'] == [str(tmp_path)]
|
|
assert config_args['output'] == '/backups'
|
|
|
|
|
|
def test_percent_in_values(tmp_path: Path) -> None:
|
|
config_file = write_config(tmp_path, f"""
|
|
[backup]
|
|
inputs={tmp_path}
|
|
backup_dir=/backups/100%
|
|
exclude=%temp%
|
|
""")
|
|
|
|
config_args = sb._read_config(config_file, USER)
|
|
|
|
assert config_args['output'] == '/backups/100%'
|
|
assert config_args['exclude'] == ['%temp%']
|
|
|
|
|
|
def test_server_section(tmp_path: Path) -> None:
|
|
config_file = write_config(tmp_path, f"""
|
|
[backup]
|
|
inputs={tmp_path}
|
|
backup_dir=backups
|
|
|
|
[server]
|
|
ssh_host=server
|
|
ssh_user=user
|
|
ssh_keyfile=/keys/id_ed25519
|
|
remote_sudo=yes
|
|
numeric_ids=true
|
|
""")
|
|
|
|
config_args = sb._read_config(config_file, USER)
|
|
|
|
assert config_args['ssh_host'] == 'server'
|
|
assert config_args['ssh_user'] == 'user'
|
|
assert config_args['ssh_keyfile'] == '/keys/id_ed25519'
|
|
assert config_args['remote_sudo'] is True
|
|
assert config_args['numeric_ids'] is True
|
|
|
|
|
|
def test_empty_server_values(tmp_path: Path) -> None:
|
|
config_file = write_config(tmp_path, f"""
|
|
[backup]
|
|
inputs={tmp_path}
|
|
backup_dir=/backups
|
|
|
|
[server]
|
|
ssh_host=
|
|
ssh_user=
|
|
ssh_keyfile=
|
|
remote_sudo=
|
|
numeric_ids=
|
|
""")
|
|
|
|
config_args = sb._read_config(config_file, USER)
|
|
|
|
for key in ['ssh_host', 'ssh_user', 'ssh_keyfile']:
|
|
assert config_args[key] is None
|
|
|
|
assert config_args['remote_sudo'] is False
|
|
assert config_args['numeric_ids'] is False
|
|
|
|
|
|
def test_only_ssh_host(tmp_path: Path) -> None:
|
|
config_file = write_config(tmp_path, f"""
|
|
[backup]
|
|
inputs={tmp_path}
|
|
backup_dir=/backups
|
|
|
|
[server]
|
|
ssh_host=server
|
|
""")
|
|
|
|
config_args = sb._read_config(config_file, USER)
|
|
|
|
# Kept, so that the missing user is reported instead of silently backing up locally
|
|
assert config_args['ssh_host'] == 'server'
|
|
assert config_args['ssh_user'] is None
|
|
|
|
|
|
@pytest.mark.parametrize('line', ['keep=abc', 'remote_sudo=maybe'])
|
|
def test_bad_value(tmp_path: Path, line: str) -> None:
|
|
section = '[server]' if line.startswith('remote_sudo') else ''
|
|
|
|
config_file = write_config(tmp_path, f"""
|
|
[backup]
|
|
inputs={tmp_path}
|
|
backup_dir=/backups
|
|
{section}
|
|
{line}
|
|
""")
|
|
|
|
with pytest.raises(ValueError):
|
|
sb._read_config(config_file, USER)
|
|
|
|
|
|
@pytest.mark.parametrize('text', [
|
|
'[backup]\nbackup_dir=/backups\n',
|
|
'[backup]\ninputs=/tmp\n',
|
|
'[other]\ninputs=/tmp\n',
|
|
'inputs=/tmp\n',
|
|
])
|
|
def test_bad_config(tmp_path: Path, text: str) -> None:
|
|
config_file = write_config(tmp_path, text)
|
|
|
|
with pytest.raises(configparser.Error):
|
|
sb._read_config(config_file, USER)
|