209 lines
6.5 KiB
Python
209 lines
6.5 KiB
Python
import argparse
|
|
from unittest import mock
|
|
import pytest
|
|
import subprocess
|
|
|
|
from locutus.mount import run_mount
|
|
|
|
|
|
def make_args(config, profile, mountpoint, archive=None):
|
|
args = argparse.Namespace()
|
|
args.config = config
|
|
args.profile = profile
|
|
args.mountpoint = mountpoint
|
|
if archive is not None:
|
|
args.archive = archive
|
|
return args
|
|
|
|
|
|
@mock.patch('locutus.mount.LocutusConfig')
|
|
@mock.patch('os.path.isdir')
|
|
@mock.patch('subprocess.run')
|
|
def test_mount_all_archives_success(mock_run, mock_isdir, mock_config, capsys):
|
|
mock_config.return_value.get_repo.return_value = '/repo'
|
|
mock_config.return_value.env = {}
|
|
mock_isdir.return_value = True
|
|
mock_run.return_value.returncode = 0
|
|
|
|
args = make_args('dummy.toml', 'dummy.rc', '/mnt/test')
|
|
rc = run_mount(args)
|
|
assert rc == 0
|
|
out = capsys.readouterr().out
|
|
assert 'Archives mounted on /mnt/test' in out
|
|
cmd_args = mock_run.call_args[0][0]
|
|
assert cmd_args == ['borg', 'mount', '/repo', '/mnt/test']
|
|
|
|
|
|
@mock.patch('locutus.mount.LocutusConfig')
|
|
@mock.patch('os.path.isdir')
|
|
@mock.patch('subprocess.run')
|
|
def test_mount_by_name_success(mock_run, mock_isdir, mock_config, capsys):
|
|
mock_config.return_value.get_repo.return_value = '/repo'
|
|
mock_config.return_value.env = {}
|
|
mock_isdir.return_value = True
|
|
mock_run.return_value.returncode = 0
|
|
|
|
args = make_args(
|
|
'dummy.toml', 'dummy.rc', '/mnt/test', archive='archive-xyz'
|
|
)
|
|
rc = run_mount(args)
|
|
assert rc == 0
|
|
out = capsys.readouterr().out
|
|
assert "Archive 'archive-xyz' mounted on /mnt/test" in out
|
|
cmd_args = mock_run.call_args[0][0]
|
|
assert cmd_args == ['borg', 'mount', '/repo::archive-xyz', '/mnt/test']
|
|
|
|
|
|
@mock.patch('locutus.mount.LocutusConfig')
|
|
@mock.patch('os.path.isdir')
|
|
@mock.patch('subprocess.run')
|
|
def test_mount_by_index_success(mock_run, mock_isdir, mock_config, capsys):
|
|
mock_config.return_value.get_repo.return_value = '/repo'
|
|
mock_config.return_value.env = {}
|
|
mock_isdir.return_value = True
|
|
mock_run.return_value.returncode = 0
|
|
archives = ['archA', 'archB', 'archC'] # Oldest to newest
|
|
mock_run.side_effect = [
|
|
# First run: borg list
|
|
mock.Mock(stdout='\n'.join(archives) + '\n', returncode=0),
|
|
# Second run: borg mount
|
|
mock.Mock(returncode=0),
|
|
]
|
|
|
|
args = make_args('dummy.toml', 'dummy.rc', '/mnt/test', archive='1')
|
|
rc = run_mount(args)
|
|
assert rc == 0
|
|
out = capsys.readouterr().out
|
|
# Index 1 in reversed list is archB (so index 0=newest=archC, 1=archB, 2=archA)
|
|
assert "Archive 'archB' mounted on /mnt/test" in out
|
|
cmd_args = mock_run.call_args_list[1][0][0]
|
|
assert cmd_args == ['borg', 'mount', '/repo::archB', '/mnt/test']
|
|
|
|
|
|
@mock.patch('locutus.mount.LocutusConfig')
|
|
def test_mount_no_repo(mock_config, tmp_path, capsys):
|
|
mock_config.return_value.get_repo.return_value = None
|
|
mock_config.return_value.env = {}
|
|
args = make_args('dummy.toml', 'dummy.rc', str(tmp_path), archive='0')
|
|
rc = run_mount(args)
|
|
assert rc == 1
|
|
err = capsys.readouterr().err
|
|
assert 'No BORG_REPO configured' in err
|
|
|
|
|
|
@mock.patch('locutus.mount.LocutusConfig')
|
|
@mock.patch('os.path.isdir', return_value=False)
|
|
def test_mount_invalid_mountpoint(mock_isdir, mock_config, tmp_path, capsys):
|
|
mock_config.return_value.get_repo.return_value = '/repo'
|
|
mock_config.return_value.env = {}
|
|
args = make_args('dummy.toml', 'dummy.rc', '/not/a/dir', archive='0')
|
|
rc = run_mount(args)
|
|
assert rc == 1
|
|
err = capsys.readouterr().err
|
|
assert 'does not exist or is not a directory' in err
|
|
|
|
|
|
@mock.patch('locutus.mount.LocutusConfig')
|
|
@mock.patch('os.path.isdir', return_value=True)
|
|
@mock.patch('subprocess.run', side_effect=Exception('fail'))
|
|
def test_mount_unexpected_exception(
|
|
mock_run, mock_isdir, mock_config, tmp_path, capsys
|
|
):
|
|
mock_config.return_value.get_repo.return_value = '/repo'
|
|
mock_config.return_value.env = {}
|
|
args = make_args('dummy.toml', 'dummy.rc', str(tmp_path), archive='0')
|
|
rc = run_mount(args)
|
|
assert rc == 1
|
|
err = capsys.readouterr().err
|
|
assert 'borg mount failed' in err
|
|
|
|
|
|
@mock.patch('locutus.mount.LocutusConfig')
|
|
@mock.patch('os.path.isdir', return_value=True)
|
|
@mock.patch('subprocess.run')
|
|
def test_mount_by_index_out_of_range(
|
|
mock_run, mock_isdir, mock_config, tmp_path, capsys
|
|
):
|
|
mock_config.return_value.get_repo.return_value = '/repo'
|
|
mock_config.return_value.env = {}
|
|
archives = ['a', 'b']
|
|
mock_run.side_effect = [
|
|
mock.Mock(stdout='\n'.join(archives) + '\n', returncode=0)
|
|
]
|
|
args = make_args(
|
|
'dummy.toml', 'dummy.rc', str(tmp_path), archive='5'
|
|
) # Out of range
|
|
rc = run_mount(args)
|
|
assert rc == 1
|
|
err = capsys.readouterr().err
|
|
assert 'Archive index out of range' in err
|
|
|
|
|
|
@mock.patch('locutus.mount.LocutusConfig')
|
|
@mock.patch('os.path.isdir', return_value=True)
|
|
@mock.patch('subprocess.run')
|
|
def test_mount_by_index_no_archives(
|
|
mock_run, mock_isdir, mock_config, tmp_path, capsys
|
|
):
|
|
mock_config.return_value.get_repo.return_value = '/repo'
|
|
mock_config.return_value.env = {}
|
|
mock_run.return_value.stdout = '' # No archives
|
|
mock_run.return_value.returncode = 0
|
|
args = make_args('dummy.toml', 'dummy.rc', str(tmp_path), archive='0')
|
|
rc = run_mount(args)
|
|
assert rc == 1
|
|
err = capsys.readouterr().err
|
|
assert 'No archives found' in err
|
|
|
|
|
|
def test_mount_usage_message(capsys):
|
|
args = argparse.Namespace()
|
|
rc = run_mount(args)
|
|
assert rc == 1
|
|
err = capsys.readouterr().err
|
|
assert 'Usage:' in err
|
|
|
|
|
|
@mock.patch('locutus.mount.LocutusConfig')
|
|
@mock.patch('os.path.isdir', return_value=True)
|
|
@mock.patch('subprocess.run')
|
|
def test_mount_calledprocesserror_on_mount(
|
|
mock_run, mock_isdir, mock_config, tmp_path, capsys
|
|
):
|
|
mock_config.return_value.get_repo.return_value = '/repo'
|
|
mock_config.return_value.env = {}
|
|
archives = ['a', 'b', 'c']
|
|
mock_run.side_effect = [
|
|
mock.Mock(
|
|
stdout='\n'.join(archives) + '\n', returncode=0
|
|
), # listing works
|
|
subprocess.CalledProcessError(1, ['borg', 'mount']), # mount fails
|
|
]
|
|
args = make_args('dummy.toml', 'dummy.rc', str(tmp_path), archive='0')
|
|
rc = run_mount(args)
|
|
assert rc == 1
|
|
err = capsys.readouterr().err
|
|
assert 'borg mount failed:' in err
|
|
|
|
|
|
@mock.patch('locutus.mount.LocutusConfig')
|
|
@mock.patch('os.path.isdir', return_value=True)
|
|
@mock.patch('subprocess.run')
|
|
def test_mount_exception_on_mount(
|
|
mock_run, mock_isdir, mock_config, tmp_path, capsys
|
|
):
|
|
mock_config.return_value.get_repo.return_value = '/repo'
|
|
mock_config.return_value.env = {}
|
|
archives = ['a', 'b', 'c']
|
|
mock_run.side_effect = [
|
|
mock.Mock(
|
|
stdout='\n'.join(archives) + '\n', returncode=0
|
|
), # listing works
|
|
Exception(), # mount fails
|
|
]
|
|
args = make_args('dummy.toml', 'dummy.rc', str(tmp_path), archive='0')
|
|
rc = run_mount(args)
|
|
assert rc == 1
|
|
err = capsys.readouterr().err
|
|
assert 'borg mount failed (unexpected)' in err
|