170 lines
4.8 KiB
Python
170 lines
4.8 KiB
Python
import argparse
|
|
import subprocess
|
|
from unittest import mock
|
|
|
|
from locutus.init import run_init
|
|
|
|
|
|
def make_args(profile_path):
|
|
args = argparse.Namespace()
|
|
args.profile = profile_path
|
|
return args
|
|
|
|
|
|
@mock.patch('builtins.input')
|
|
@mock.patch('getpass.getpass')
|
|
@mock.patch('subprocess.run')
|
|
def test_run_init_success_no_passphrase(
|
|
mock_subproc, mock_getpass, mock_input, tmp_path
|
|
):
|
|
# Simulate user enters repo path and empty passphrase
|
|
mock_input.return_value = '/test/repo'
|
|
mock_getpass.return_value = ''
|
|
mock_subproc.return_value = mock.Mock(returncode=0)
|
|
|
|
rc_path = tmp_path / 'test.rc'
|
|
args = make_args(str(rc_path))
|
|
result = run_init(args)
|
|
|
|
assert result == 0
|
|
with open(rc_path) as f:
|
|
content = f.read()
|
|
assert 'export BORG_REPO="/test/repo"' in content
|
|
assert 'BORG_PASSPHRASE' not in content
|
|
|
|
mock_subproc.assert_called_once()
|
|
call_args = mock_subproc.call_args[0][0]
|
|
assert call_args == ['borg', 'init', '--encryption=none', '/test/repo']
|
|
|
|
|
|
@mock.patch('builtins.input')
|
|
@mock.patch('getpass.getpass')
|
|
@mock.patch('subprocess.run')
|
|
def test_run_init_success_with_passphrase(
|
|
mock_subproc, mock_getpass, mock_input, tmp_path
|
|
):
|
|
mock_input.return_value = '/secure/repo'
|
|
# First call is passphrase, second is confirm, both match
|
|
mock_getpass.side_effect = ['hunter2', 'hunter2']
|
|
mock_subproc.return_value = mock.Mock(returncode=0)
|
|
|
|
rc_path = tmp_path / 'secure.rc'
|
|
args = make_args(str(rc_path))
|
|
result = run_init(args)
|
|
|
|
assert result == 0
|
|
with open(rc_path) as f:
|
|
content = f.read()
|
|
assert 'export BORG_REPO="/secure/repo"' in content
|
|
assert 'export BORG_PASSPHRASE="hunter2"' in content
|
|
|
|
mock_subproc.assert_called_once()
|
|
call_args = mock_subproc.call_args[0][0]
|
|
assert call_args == ['borg', 'init', '--encryption=repokey', '/secure/repo']
|
|
|
|
|
|
@mock.patch('builtins.input')
|
|
def test_run_init_empty_repo_aborts(mock_input, tmp_path):
|
|
mock_input.return_value = ''
|
|
args = make_args(str(tmp_path / 'fail.rc'))
|
|
result = run_init(args)
|
|
assert result == 1
|
|
|
|
|
|
@mock.patch('builtins.input')
|
|
@mock.patch('getpass.getpass')
|
|
def test_run_init_passphrase_mismatch(mock_getpass, mock_input, tmp_path):
|
|
mock_input.return_value = '/fail/repo'
|
|
mock_getpass.side_effect = [
|
|
'pass1',
|
|
'pass2',
|
|
'',
|
|
] # Third call: user gives up
|
|
args = make_args(str(tmp_path / 'fail2.rc'))
|
|
with mock.patch('subprocess.run'):
|
|
result = run_init(args)
|
|
assert result == 0 # Exits with no passphrase (encryption=none)
|
|
with open(args.profile) as f:
|
|
content = f.read()
|
|
assert 'BORG_PASSPHRASE' not in content
|
|
|
|
|
|
@mock.patch('builtins.input')
|
|
@mock.patch('getpass.getpass')
|
|
@mock.patch('subprocess.run', side_effect=Exception('init failed'))
|
|
def test_run_init_subprocess_failure(
|
|
mock_subproc, mock_getpass, mock_input, tmp_path
|
|
):
|
|
mock_input.return_value = '/failinit/repo'
|
|
mock_getpass.return_value = ''
|
|
args = make_args(str(tmp_path / 'failinit.rc'))
|
|
result = run_init(args)
|
|
assert result == 1
|
|
|
|
|
|
@mock.patch('builtins.input')
|
|
@mock.patch('getpass.getpass')
|
|
@mock.patch('os.makedirs', side_effect=Exception('cannot create dir'))
|
|
def test_run_init_makedirs_exception(
|
|
mock_makedirs, mock_getpass, mock_input, tmp_path
|
|
):
|
|
mock_input.return_value = '/faildir/repo'
|
|
mock_getpass.return_value = ''
|
|
args = make_args(str(tmp_path / 'faildir.rc'))
|
|
with mock.patch('subprocess.run'):
|
|
result = run_init(args)
|
|
assert result == 1
|
|
|
|
|
|
@mock.patch('builtins.input')
|
|
@mock.patch('getpass.getpass')
|
|
@mock.patch('os.makedirs')
|
|
@mock.patch('builtins.open', side_effect=Exception('cannot open file'))
|
|
def test_run_init_open_exception(
|
|
mock_open, mock_makedirs, mock_getpass, mock_input, tmp_path
|
|
):
|
|
mock_input.return_value = '/failfile/repo'
|
|
mock_getpass.return_value = ''
|
|
args = make_args(str(tmp_path / 'failfile.rc'))
|
|
with mock.patch('subprocess.run'):
|
|
result = run_init(args)
|
|
assert result == 1
|
|
|
|
|
|
@mock.patch('builtins.input')
|
|
@mock.patch('getpass.getpass')
|
|
@mock.patch('os.makedirs')
|
|
@mock.patch('builtins.open')
|
|
@mock.patch('subprocess.run', side_effect=Exception('unexpected'))
|
|
def test_run_init_unexpected_exception(
|
|
mock_subproc, mock_open, mock_makedirs, mock_getpass, mock_input, tmp_path
|
|
):
|
|
mock_input.return_value = '/unexpected/repo'
|
|
mock_getpass.return_value = ''
|
|
args = make_args(str(tmp_path / 'unexpected.rc'))
|
|
result = run_init(args)
|
|
assert result == 1
|
|
|
|
|
|
@mock.patch('builtins.input', side_effect=KeyboardInterrupt)
|
|
def test_run_init_keyboard_interrupt(mock_input, tmp_path):
|
|
args = make_args(str(tmp_path / 'kbint.rc'))
|
|
result = run_init(args)
|
|
assert result == 1
|
|
|
|
|
|
@mock.patch('builtins.input')
|
|
@mock.patch('getpass.getpass')
|
|
@mock.patch(
|
|
'subprocess.run',
|
|
side_effect=subprocess.CalledProcessError(1, ['borg', 'init']),
|
|
)
|
|
def test_run_init_calledprocesserror(
|
|
mock_subproc, mock_getpass, mock_input, tmp_path
|
|
):
|
|
mock_input.return_value = '/failsubproc/repo'
|
|
mock_getpass.return_value = ''
|
|
args = make_args(str(tmp_path / 'failsubproc.rc'))
|
|
result = run_init(args)
|
|
assert result == 1
|