def test_edit_creation_date(create_torrent, tmp_path, assert_torrents_equal): outfile = str(tmp_path / 'out.torrent') with create_torrent() as infile: orig = torf.Torrent.read(infile) run(['-i', infile, '--date', '3000-05-30 15:03:01', '-o', outfile]) new = torf.Torrent.read(outfile) assert_torrents_equal(orig, new, creation_date=datetime(3000, 5, 30, 15, 3, 1))
def test_metainfo_with_verbosity_level_two(capsys, nonstandard_torrent): run(['-i', nonstandard_torrent, '--metainfo', '--verbose', '--verbose']) cap = capsys.readouterr() assert cap.err == '' assert json.loads(cap.out) == { 'created by': f'torf {torf.__version__}', 'announce': 'https://foo.example.org', 'foo': 'bar', 'info': { 'baz': [1, 2, 3], 'private': 1, 'files': [{ 'length': 3, 'path': ['dir', 'file3'], 'sneaky': 'pete' }, { 'length': 3, 'path': ['file1'] }, { 'length': 3, 'path': ['file2'] }], 'name': 'content', 'piece length': 16384, 'pieces': 'YscFPSkTuTXkBSgIyyaqj/HVRXU=' } }
def test_replace_webseeds(create_torrent, tmp_path, assert_torrents_equal): outfile = str(tmp_path / 'out.torrent') with create_torrent(webseeds=['http://webseed1', 'http://webseed2']) as infile: orig = torf.Torrent.read(infile) run(['-i', infile, '--nowebseed', '--webseed', 'http://webseed10', '--webseed', 'http://webseed20', '-o', outfile]) new = torf.Torrent.read(outfile) assert_torrents_equal(orig, new, webseeds=['http://webseed10', 'http://webseed20'])
def test_json_with_magnet_uri(capsys, regex): magnet = ( 'magnet:?xt=urn:btih:e167b1fbb42ea72f051f4f50432703308efb8fd1&dn=My+Torrent&xl=142631' '&tr=https%3A%2F%2Flocalhost%3A123%2Fannounce&&tr=https%3A%2F%2Flocalhost%3A456%2Fannounce' ) run(['-i', magnet, '--json']) cap = capsys.readouterr() assert cap.err == '' assert json.loads(cap.out) == { "Error": [ 'https://localhost:123/file?info_hash=%E1g%B1%FB%B4.%A7/%05%1FOPC%27%030%8E%FB%8F%D1: Connection refused', 'https://localhost:456/file?info_hash=%E1g%B1%FB%B4.%A7/%05%1FOPC%27%030%8E%FB%8F%D1: Connection refused' ], 'Name': 'My Torrent', 'Size': 142631, 'Trackers': ['https://localhost:123/announce', 'https://localhost:456/announce'], 'Piece Size': 16384, 'Piece Count': 9, 'File Count': 1, 'Files': ['My Torrent'] }
def test_metainfo_with_verbosity_level_zero(capsys, nonstandard_torrent): run(['-i', nonstandard_torrent, '--metainfo']) cap = capsys.readouterr() assert cap.err == '' assert json.loads(cap.out) == { 'created by': f'torf {torf.__version__}', 'announce': 'https://foo.example.org', 'info': { 'name': 'content', 'piece length': 16384, 'private': 1, 'files': [{ 'length': 3, 'path': ['dir', 'file3'] }, { 'length': 3, 'path': ['file1'] }, { 'length': 3, 'path': ['file2'] }] } }
def test_content_path_doesnt_exist(capsys, human_readable, hr_enabled): with human_readable(hr_enabled): with patch('sys.exit') as mock_exit: run(['/path/doesnt/exist']) mock_exit.assert_called_once_with(err.Code.READ) cap = capsys.readouterr() assert cap.err == f'{_vars.__appname__}: /path/doesnt/exist: No such file or directory\n'
def test_add_private(create_torrent, tmp_path, assert_torrents_equal): outfile = str(tmp_path / 'out.torrent') with create_torrent(private=False) as infile: orig = torf.Torrent.read(infile) run(['-i', infile, '--private', '-o', outfile]) new = torf.Torrent.read(outfile) assert_torrents_equal(orig, new, private=True)
def test_edit_comment(create_torrent, tmp_path, assert_torrents_equal): outfile = str(tmp_path / 'out.torrent') with create_torrent(comment='A comment') as infile: orig = torf.Torrent.read(infile) run(['-i', infile, '--comment', 'A different comment', '-o', outfile]) new = torf.Torrent.read(outfile) assert_torrents_equal(orig, new, comment='A different comment')
def test_remove_creator(create_torrent, tmp_path, assert_torrents_equal): outfile = str(tmp_path / 'out.torrent') with create_torrent(created_by='The creator') as infile: orig = torf.Torrent.read(infile) run(['-i', infile, '--nocreator', '-o', outfile]) new = torf.Torrent.read(outfile) assert_torrents_equal(orig, new, created_by=None)
def test_file_tree_and_file_count(capsys, create_torrent, human_readable, tmp_path, clear_ansi, regex): root = tmp_path / 'root' (root / 'subdir1' / 'subdir1.0' / 'subdir1.0.0').mkdir(parents=True) (root / 'subdir2').mkdir(parents=True) (root / 'subdir1' / 'file1').write_text('data') (root / 'subdir1' / 'subdir1.0' / 'file2').write_text('data') (root / 'subdir1' / 'subdir1.0' / 'subdir1.0.0' / 'file3').write_text('data') (root / 'subdir2' / 'file4').write_text('data') with create_torrent(path=str(root)) as torrent_file: with human_readable(True): run(['-i', torrent_file]) cap = capsys.readouterr() assert clear_ansi(cap.out) == regex(r'^\s*File Count 4$', flags=re.MULTILINE) assert clear_ansi(cap.out) == regex(r'^(\s*) Files root\n' r'\1 ├─subdir1\n' r'\1 │ ├─file1 \[4 B\]\n' r'\1 │ └─subdir1.0\n' r'\1 │ ├─file2 \[4 B\]\n' r'\1 │ └─subdir1.0.0\n' r'\1 │ └─file3 \[4 B\]\n' r'\1 └─subdir2\n' r'\1 └─file4 \[4 B\]$', flags=re.MULTILINE) assert cap.err == '' with human_readable(False): run(['-i', torrent_file]) cap = capsys.readouterr() assert cap.out == regex(r'^File Count\t4$', flags=re.MULTILINE) exp_files = '\t'.join(('root/subdir1/file1', 'root/subdir1/subdir1.0/file2', 'root/subdir1/subdir1.0/subdir1.0.0/file3', 'root/subdir2/file4')) assert cap.out == regex(rf'^Files\t{exp_files}$', flags=re.MULTILINE) assert cap.err == ''
def test_no_changes(create_torrent, tmp_path, assert_torrents_equal): outfile = str(tmp_path / 'out.torrent') with create_torrent() as infile: orig = torf.Torrent.read(infile) run(['-i', infile, '-o', outfile]) new = torf.Torrent.read(outfile) assert_torrents_equal(orig, new)
def test_nonexisting_input(capsys): nonexisting_path = '/no/such/file' with patch('sys.exit') as mock_exit: run(['-i', nonexisting_path, '-o', 'out.torrent']) mock_exit.assert_called_once_with(err.Code.READ) cap = capsys.readouterr() assert cap.err == f'{_vars.__appname__}: {nonexisting_path}: No such file or directory\n'
def test_illegal_configfile_arguments(capsys, cfgfile, mock_content, mock_create_mode): for arg in ('config', 'profile'): cfgfile.write_text( textwrap.dedent(f''' [foo] {arg} = foo ''')) with patch('sys.exit') as mock_exit: run(['--config', str(cfgfile), str(mock_content)]) mock_exit.assert_called_once_with(err.Code.CONFIG) cap = capsys.readouterr() assert cap.err == f'{_vars.__appname__}: {cfgfile}: Not allowed in config file: {arg}\n' assert mock_create_mode.call_args is None for arg in ('noconfig', 'help', 'version'): cfgfile.write_text( textwrap.dedent(f''' [foo] {arg} ''')) with patch('sys.exit') as mock_exit: run(['--config', str(cfgfile), str(mock_content)]) mock_exit.assert_called_once_with(err.Code.CONFIG) cap = capsys.readouterr() assert cap.err == f'{_vars.__appname__}: {cfgfile}: Not allowed in config file: {arg}\n' assert mock_create_mode.call_args is None
def test_replace_trackers(create_torrent, tmp_path, assert_torrents_equal): outfile = str(tmp_path / 'out.torrent') with create_torrent(trackers=['http://tracker1', 'http://tracker2']) as infile: orig = torf.Torrent.read(infile) run(['-i', infile, '--notracker', '--tracker', 'http://tracker10', '--tracker', 'http://tracker20', '-o', outfile]) new = torf.Torrent.read(outfile) assert_torrents_equal(orig, new, trackers=[['http://tracker10'], ['http://tracker20']])
def test_remove_source(create_torrent, tmp_path, assert_torrents_equal): outfile = str(tmp_path / 'out.torrent') with create_torrent(source='the source') as infile: orig = torf.Torrent.read(infile) run(['-i', infile, '--nosource', '-o', outfile]) new = torf.Torrent.read(outfile) assert_torrents_equal(orig, new, source=None)
def test_metainfo_with_unreadable_torrent(capsys): with patch('sys.exit') as mock_exit: run(['-i', 'no/such/path.torrent', '--metainfo']) mock_exit.assert_called_once_with(err.Code.READ) cap = capsys.readouterr() assert cap.err == f'{_vars.__appname__}: no/such/path.torrent: No such file or directory\n' assert json.loads(cap.out) == {}
def test_unwritable_output(capsys, create_torrent): unwritable_path = '/out.torrent' with create_torrent() as infile: with patch('sys.exit') as mock_exit: run(['-i', infile, '-o', unwritable_path]) mock_exit.assert_called_once_with(err.Code.WRITE) cap = capsys.readouterr() assert cap.err == f'{_vars.__appname__}: {unwritable_path}: Permission denied\n'
def test_custom_configfile(tmp_path, mock_content, mock_create_mode): cfgfile = tmp_path / 'special_config' cfgfile.write_text(textwrap.dedent(''' comment = asdf ''')) run(['--config', str(cfgfile), str(mock_content)]) cfg = mock_create_mode.call_args[0][1] assert cfg['comment'] == 'asdf'
def test_json_contains_cli_errors(capsys): with patch('sys.exit') as mock_exit: run(['--foo', '--json']) mock_exit.assert_called_once_with(err.Code.CLI) cap = capsys.readouterr() assert cap.err == '' j = json.loads(cap.out) assert j['Error'] == ['Unrecognized arguments: --foo']
def test_add_xseed(create_torrent, tmp_path, assert_torrents_equal): outfile = str(tmp_path / 'out.torrent') with create_torrent(randomize_infohash=False) as infile: orig = torf.Torrent.read(infile) run(['-i', infile, '--xseed', '-o', outfile]) new = torf.Torrent.read(outfile) assert_torrents_equal(orig, new, randomize_infohash=True) assert orig.infohash != new.infohash
def test_reading_invalid_magnet(capsys): magnet = 'magnet:?xt=urn:btih:e167b1fbb42ea72f051f4f50432703308efb8fd1&xl=not_an_int' with patch('sys.exit') as mock_exit: run(['-i', magnet]) mock_exit.assert_called_once_with(err.Code.READ) cap = capsys.readouterr() assert cap.err == f'{_vars.__appname__}: not_an_int: Invalid exact length ("xl")\n' assert cap.out == ''
def test_version(capsys): with patch('sys.exit') as mock_exit: run(['--version']) mock_exit.assert_not_called() cap = capsys.readouterr() from torfcli._config import VERSION_TEXT assert cap.out == VERSION_TEXT + '\n' assert cap.err == ''
def test_adding_to_list_via_cli(cfgfile, mock_content, mock_create_mode): cfgfile.write_text(textwrap.dedent(''' tracker = https://foo tracker = https://bar ''')) run([str(mock_content), '--tracker', 'https://baz']) cfg = mock_create_mode.call_args[0][1] assert cfg['tracker'] == ['https://foo', 'https://bar', 'https://baz']
def test_reading_valid_torrent_data_from_stdin(capsys, monkeypatch, clear_ansi, create_torrent, regex): with create_torrent(name='Foo', comment='Bar.') as torrent_file: monkeypatch.setattr(sys, 'stdin', open(torrent_file, 'rb')) run(['-i', '-']) cap = capsys.readouterr() assert clear_ansi(cap.out) == regex(r'^Name\tFoo$', flags=re.MULTILINE) assert clear_ansi(cap.out) == regex(r'^Comment\tBar.$', flags=re.MULTILINE) assert cap.err == ''
def test_no_arguments(capsys): with patch('sys.exit') as mock_exit: run([]) mock_exit.assert_called_once_with(_errors.Code.CLI) cap = capsys.readouterr() assert cap.out == '' assert cap.err == (f'{_vars.__appname__}: Not sure what to do ' f'(see USAGE in `{_vars.__appname__} -h`)\n')
def test_metainfo_when_creating_torrent(capsys, mock_content): run([str(mock_content), '--metainfo', '-vv']) cap = capsys.readouterr() assert cap.err == '' j = json.loads(cap.out) assert 'info' in j assert 'name' in j['info'] assert 'pieces' in j['info']
def test_invalid_creation_date(capsys, create_torrent, tmp_path, assert_torrents_equal): outfile = str(tmp_path / 'out.torrent') with create_torrent() as infile: with patch('sys.exit') as mock_exit: run(['-i', infile, '--date', 'foo', '-o', outfile]) mock_exit.assert_called_once_with(err.Code.CLI) cap = capsys.readouterr() assert cap.err == f'{_vars.__appname__}: foo: Invalid date\n' assert not os.path.exists(outfile)
def test_invalid_webseed_url(capsys, create_torrent, tmp_path, assert_torrents_equal): outfile = str(tmp_path / 'out.torrent') with create_torrent(webseeds=['http://webseed1', 'http://webseed2']) as infile: with patch('sys.exit') as mock_exit: run(['-i', infile, '--webseed', 'not a url', '-o', outfile]) mock_exit.assert_called_once_with(err.Code.CLI) cap = capsys.readouterr() assert cap.err == f'{_vars.__appname__}: not a url: Invalid URL\n' assert not os.path.exists(outfile)
def test_add_private_and_remove_all_trackers(create_torrent, tmp_path, assert_torrents_equal, capsys): outfile = str(tmp_path / 'out.torrent') with create_torrent(private=False) as infile: orig = torf.Torrent.read(infile) run(['-i', infile, '--private', '--notracker', '-o', outfile]) cap = capsys.readouterr() assert cap.err == f'{_vars.__appname__}: WARNING: Torrent is private and has no trackers\n' new = torf.Torrent.read(outfile) assert_torrents_equal(orig, new, private=True, trackers=())
def test_json_contains_sigint(capsys, mock_create_mode, mock_content): mock_create_mode.side_effect = KeyboardInterrupt() with patch('sys.exit') as mock_exit: run([str(mock_content), '--json']) mock_exit.assert_called_once_with(err.Code.ABORTED) cap = capsys.readouterr() assert cap.err == '' j = json.loads(cap.out) assert j['Error'] == ['Aborted']