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:
Fuxino
2026-10-14 08:05:30 +02:00
parent 4b28cb7edf
commit 41a45dad41
3 changed files with 249 additions and 0 deletions
+229
View File
@@ -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)