Add pytest setup and configuration tests
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.
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
"""Shared fixtures for the simple_backup tests"""
|
||||
|
||||
import pytest
|
||||
|
||||
from simple_backup import simple_backup as sb
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def no_side_effects(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
"""Keep tests out of the systemd journal and the desktop notifications"""
|
||||
|
||||
if hasattr(sb, 'j_handler'):
|
||||
sb.logger.removeHandler(sb.j_handler)
|
||||
|
||||
monkeypatch.setattr(sb, '_notify', lambda text: None)
|
||||
@@ -0,0 +1,229 @@
|
||||
"""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)
|
||||
Reference in New Issue
Block a user