def test_add_linking_success(mocker: MockerFixture): api = mocker.Mock(spec=TransmissionApi) api.add_torrent_with_files.return_value = CommandResult(success=True) api.add_torrent.return_value = CommandResult(success=True) add_service = AddService(api) fs = MockFilesystem({"test_path"}) locator: TorrentDataLocator = DefaultTorrentDataLocator(fs) find_service = FindService(locator) path = Path("/", "test_path") file = MetainfoFile( { "name": "meaningless", "info_hash": "meaningless and necessary", "info": { "length": 5 }, }, path, ) command = LinkingAddCommand(add_service, fs, {TorrentData(file, Path("/some/place"))}) output: LinkingAddOutput = command.run() assert not fs.exists(path) assert output.linked_torrents == {file: Path("/some/place")}
def test_mock_fs_nested_children(): files = {"file1", "file2.torrent", "file3"} fs = MockFilesystem({"upper": {"testing": files}}) result = set(fs.children(Path("/upper/testing"))) assert result == {Path("/", "upper", "testing", file) for file in files}
def test_add_linking_unknown(mocker: MockerFixture): api = mocker.Mock(spec=TransmissionApi) api.add_torrent_with_files.return_value = CommandResult(error="unknown", success=False) api.add_torrent.return_value = CommandResult(error="unknown", success=False) add_service = AddService(api) fs = MockFilesystem({"test_path"}) path = Path("/", "test_path") file = MetainfoFile( { "name": "meaningless", "info_hash": "meaningless and necessary", "info": { "length": 5 }, }, path, ) command = LinkingAddCommand(add_service, fs, {TorrentData(file, Path("/some/place"))}) output: LinkingAddOutput = command.run() assert fs.exists(path) assert output.failed_torrents == {file: "unknown"}
def test_mock_fs_mixed_abstraction(): files = ["file1", "file2", "file3"] fs = MockFilesystem({"folder": files}) assert set(fs.children(Path("/folder"))) == { Path("/folder", path) for path in files }
def test_mock_fs_nested_complex(): fs = MockFilesystem( { "upper": {"testing": {"file1", "file2.torrent", "file3", "file4.torrent"}}, "multi": ["file7", {"another": ["file5", {"level3": {"file8"}}]}], } ) assert fs.exists(Path("/multi/another/level3"))
def test_prune_folder_dry_run(mocker: MockerFixture): fs = MockFilesystem({"some_path"}) files = { MetainfoFile({ "info_hash": "aaa", "name": "some_name" }, Path("/some_path")) } service: PruneService = mocker.Mock(spec=PruneService) service.get_torrent_hashes.return_value = {"aaa", "bbb"} command = PruneFolderCommand(service, fs, files) command.dry_run() assert fs.exists(Path("/some_path"))
def test_linking_add_run_display_failed(mocker: MockerFixture, capsys): api = mocker.Mock(spec=TransmissionApi) api.add_torrent_with_files.return_value = CommandResult(error="unknown", success=False) api.add_torrent.return_value = CommandResult(error="unknown", success=False) add_service = AddService(api) fs = MockFilesystem({"test_path"}) locator: TorrentDataLocator = DefaultTorrentDataLocator(fs) find_service = FindService(locator) path = Path("/", "test_path") file = MetainfoFile( { "name": "meaningless", "info_hash": "meaningless and necessary", "info": { "length": 5 }, }, path, ) command = LinkingAddCommand(add_service, fs, {TorrentData(file, Path("/some/place"))}) output: LinkingAddOutput = command.run() output.display() result = capsys.readouterr().out assert result == "\n".join(["1 failed:", "meaningless because: unknown" ]) + "\n"
def test_linking_add_run_display_duplicated(mocker: MockerFixture, capsys): api = mocker.Mock(spec=TransmissionApi) api.add_torrent_with_files.return_value = CommandResult(success=False, error="duplicate") api.add_torrent.return_value = CommandResult(success=False, error="duplicate") add_service = AddService(api) fs = MockFilesystem({"test_path"}) path = Path("/", "test_path") file = MetainfoFile( { "name": "meaningless", "info_hash": "meaningless and necessary", "info": { "length": 5 }, }, path, ) command = LinkingAddCommand(add_service, fs, {TorrentData(file, Path("/some/place"))}) output: LinkingAddOutput = command.run() output.display() result = capsys.readouterr().out assert result == "\n".join(["There are 1 duplicates:", "meaningless" ]) + "\n"
def test_aggregate_with_timeout_cancel(): fs = MockFilesystem({ "some_path": { "child1": {"file2.torrent", "file3"} }, "another_path": {"file.torrent"}, }) locator = SingleDirectoryFileLocator(fs) forever_locator = SingleDirectoryFileLocator(InfinitelyDeepFilesystem()) locators = [locator, forever_locator] async def _callback(): paths = set() async for result in collect_from_aggregate(fs, locators): paths.add(result) return paths async def _timed(): task = asyncio.create_task(_callback()) await asyncio.sleep(0.001) task.cancel() await asyncio.sleep(0.001) return await task with pytest.raises(asyncio.CancelledError): _ = asyncio.run(_timed())
def test_find_missing(): metainfo_path = Path("/", "metainfo.torrent") properties = { "info_hash": "meaningless and necessary", "name": "test_name", "info": { "files": [ { "path": ["file1"], "length": 0 }, { "path": ["file2"], "length": 0 }, ] }, } metainfo_file = MetainfoFile(properties, metainfo_path) fs = MockFilesystem({"data"}) locator = DefaultTorrentDataLocator(fs) find_service = FindService(locator) command = FindCommand(find_service, {metainfo_file}) output = command.run() assert output.found == set() assert output.missing == {metainfo_file}
def test_linking_add_dry_run_display(mocker: MockerFixture, capsys): api = mocker.Mock(spec=TransmissionApi) api.add_torrent_with_files.return_value = CommandResult(success=True) api.add_torrent.return_value = CommandResult(success=True) add_service = AddService(api) fs = MockFilesystem({"test_path"}) path = Path("/", "test_path") file = MetainfoFile( { "name": "meaningless", "info_hash": "meaningless and necessary", "info": { "length": 5 }, }, path, ) torrent_data = {TorrentData(file, Path("/some/place")), TorrentData(file)} command = LinkingAddCommand(add_service, fs, torrent_data) output: LinkingAddOutput = command.dry_run() output.dry_run_display() result = capsys.readouterr().out assert (result == "\n".join([ "Would add 1 torrents with data:", "meaningless at /some/place", "Would add 1 torrents without data:", "meaningless", ]) + "\n")
def test_find_run_output(capsys): metainfo_path = Path("/", "metainfo.torrent") properties = { "info_hash": "meaningless and necessary", "name": "test_name", "info": { "files": [ { "path": ["file1"], "length": 0 }, { "path": ["file2"], "length": 0 }, ] }, } metainfo_file = MetainfoFile(properties, metainfo_path) missing_properties = { "info_hash": "meaningless and necessary", "name": "another_name", "info": { "files": [ { "path": ["file3"], "length": 0 }, { "path": ["file4"], "length": 0 }, ] }, } missing_metainfo_file = MetainfoFile(missing_properties, Path("/missing.torrent")) fs = MockFilesystem({"data": {"test_name": {"file1", "file2"}}}) locator = DefaultTorrentDataLocator(fs) find_service = FindService(locator) command = FindCommand(find_service, {metainfo_file, missing_metainfo_file}) output = command.run() output.display() result = capsys.readouterr().out assert (result == "\n".join([ "Starting search - press Ctrl+C to cancel", "1/2 test_name found at /data", "Found 1 torrents:", "\x1b[32m✓ test_name at /data", "Did not find 1 torrents:", "\x1b[31m✗ another_name", ]) + "\n")
def test_rename_command_dry_run_output_empty(capsys): files = [] fs = MockFilesystem({}) command = RenameCommand(fs, files) output = command.dry_run() output.dry_run_display() result = capsys.readouterr().out assert result == "No files found to rename.\n"
async def test_single_directory_filesystem_locate_file_complex_nested(name, expected): fs = MockFilesystem( { "upper": {"testing": {"file1", "file2.torrent", "file3", "file4.torrent"}}, "multi": ["file7", {"another": ["file5", {"level3": {"file8"}}]}], } ) locator = SingleDirectoryFileLocator(fs) result = await locator.locate_file(name) assert result == Path(expected)
async def test_collect_task_cancel(): fs = MockFilesystem({ "some_path": { "child1": {"file2.torrent", "file3"} }, "another_path": {"file.torrent"}, }) paths = {Path("/")} task = asyncio.create_task(_collect(fs, paths)) task.cancel() with pytest.raises(asyncio.CancelledError): await task
async def test_locate_torrents(): fs = MockFilesystem( {"upper": {"testing": {"file1", "file2.torrent", "file3", "file4.torrent"}}} ) locator = SingleDirectoryFileLocator(fs, Path("/upper/testing")) results = set() async for path in locator.collect(".torrent"): results.add(path) assert results == { Path("/upper/testing/file2.torrent"), Path("/upper/testing/file4.torrent"), }
async def test_multiple_directory_filesystem_locate_file(): location = Path("/multi/another/level3") fs = MockFilesystem( { "upper": {"testing": {"file1", "file2.torrent", "file3", "file4.torrent"}}, "multi": ["file7", {"another": ["file5", {"level3": {"file8"}}]}], } ) directories = [Path("/upper"), Path("/multi/another")] locator = MultipleDirectoryFileLocator(directories, fs) result = await locator.locate_file("file8") assert result == location
def test_query_failure_output(mocker: MockerFixture, capsys): archive_path = Path("/", "test_path") fs = MockFilesystem({}) client = mocker.Mock(spec=TransmissionApi) client.get_torrent_files_by_id.return_value = QueryResult( error="some_error", success=False ) client.get_torrent_name_by_id.return_value = QueryResult({1: "test_name"}) command = ArchiveCommand(archive_path, fs, client) output: ArchiveOutput = command.run() output.display() result = capsys.readouterr().out assert result == "Query failed: get_torrent_files_by_id\n"
def test_prune_folder_run_empty_output(mocker: MockerFixture, capsys): fs = MockFilesystem({"some_path"}) files = { MetainfoFile({ "info_hash": "ccc", "name": "some_name" }, Path("/some_path")) } service: PruneService = mocker.Mock(spec=PruneService) service.get_torrent_hashes.return_value = {"aaa", "bbb"} command = PruneFolderCommand(service, fs, files) output = command.run() output.display() result = capsys.readouterr().out assert result == "No metainfo files were removed.\n"
def test_collect_metainfo_paths(): raw_paths = { "/some_path", "/another_path/file.torrent", } fs = MockFilesystem({ "some_path": { "child1": {"file2.torrent", "file3"} }, "another_path": {"file.torrent"}, }) results = collect_metainfo_paths(fs, raw_paths) assert set(results) == { Path("/another_path/file.torrent"), Path("/some_path/child1/file2.torrent"), }
async def test_metainfo_find(): torrent_files = {Path("torrent_file1"), Path("folder/torrent_file2")} info_files = mock_info_files(torrent_files) fs = MockFilesystem( {"data": {"torrent_name": ["torrent_file1", {"folder": "torrent_file2"}]}} ) metainfo_file = MetainfoFile( {"name": "torrent_name", "info": {"files": info_files}} ) data_locator: TorrentDataLocator = CustomTorrentDataLocator( SingleDirectoryFileLocator(fs), DefaultTorrentDataReader(fs) ) result = await data_locator.find(metainfo_file) assert result.location
def test_rename_command_dry_run_output(capsys): original = MetainfoFile( { "info_hash": "arbitrary", "name": "some_name" }, path=Path("/some/some_name.arbitrary.torrent"), ) dupe = MetainfoFile({ "info_hash": "arbitrary", "name": "some_name" }, path=Path("/some/file1")) second_dupe = MetainfoFile({ "info_hash": "arbitrary", "name": "some_name" }, path=Path("/some/file2")) unique = MetainfoFile({ "info_hash": "unique", "name": "unique" }, path=Path("/some/file3")) files = [original, dupe, second_dupe, unique] fs = MockFilesystem( {"some": ["some_name.arbitrary.torrent", "file1", "file2", "file3"]}) command = RenameCommand(fs, files) output = command.dry_run() output.dry_run_display() result = capsys.readouterr().out assert (result == "\n".join([ "Found 1 clashing renames:", "‣ some_name has dupes:", "⁃ /some/file1 (selected)", "⁃ /some/file2", "Found 1 metainfo files to rename:", "/some/file3 to unique.unique.torrent", "Found 1 metainfo files with new names that already exist:", "/some/file1 with new name some_name.arbitrary.torrent", ]) + "\n")
def test_prune_folder_dry_run_output(mocker: MockerFixture, capsys): fs = MockFilesystem({"some_path"}) files = { MetainfoFile({ "info_hash": "aaa", "name": "some_name" }, Path("/some_path")) } service: PruneService = mocker.Mock(spec=PruneService) service.get_torrent_hashes.return_value = {"aaa", "bbb"} command = PruneFolderCommand(service, fs, files) output = command.dry_run() output.dry_run_display() result = capsys.readouterr().out assert (result == "\n".join([ "The following metainfo files would be removed:", "some_name at /some_path", ]) + "\n")
def test_output_dry_run(capsys): files = [ MetainfoFile({ "name": "some_name", "info_hash": "aaa" }, Path("/some/path")), MetainfoFile({ "name": "some_name", "info_hash": "aaa" }, Path("/some/path2")), ] fs = MockFilesystem({"some": ["path", "path2"]}) command = DedupeCommand(fs, files) output = command.dry_run() output.dry_run_display() result = capsys.readouterr().out assert (result == "\n".join( ["Would delete 1 duplicate files:", "‣ some_name:", "⁃ /some/path2"]) + "\n")
def test_collect_metainfo_files_with_timeout(mocker: MockerFixture): fs = MockFilesystem({ "some_path": { "child1": {"file2.torrent", "file3"} }, "another_path": {"file.torrent"}, }) paths = {Path("/")} reader = mocker.Mock(spec=MetainfoIO) def from_path(path: Path): return MetainfoFile({"info_hash": str(path)}) reader.from_path.side_effect = from_path result = collect_metainfo_files(reader, fs, paths) assert set(result) == { from_path(Path("/some_path/child1/file2.torrent")), from_path(Path("/another_path/file.torrent")), }
def test_run_display_copy_failure(mocker: MockerFixture, capsys): archive_path = Path("/", "test_path") fs = MockFilesystem({"file_1", "test_path"}) client = mocker.Mock(spec=TransmissionApi) client.get_torrent_files_by_id.return_value = QueryResult({1: Path("/", "file_1")}) client.get_torrent_name_by_id.return_value = QueryResult({1: "test_name"}) command = ArchiveCommand(archive_path, fs, client) output: ArchiveOutput = command.run() output.display() result = capsys.readouterr().out assert ( result == "\n".join( [ "Failed to move 1 metainfo files:", "\x1b[31m✗ failed to move /file_1 because:destination is a file", ] ) + "\n" )
def test_run_display_already_exists(mocker: MockerFixture, capsys): archive_path = Path("/", "test_path") fs = MockFilesystem(["file_1", {"test_path": ["file_1"]}]) client = mocker.Mock(spec=TransmissionApi) client.get_torrent_files_by_id.return_value = QueryResult({1: Path("/", "file_1")}) client.get_torrent_name_by_id.return_value = QueryResult({1: "test_name"}) command = ArchiveCommand(archive_path, fs, client) output: ArchiveOutput = command.run() output.display() result = capsys.readouterr().out assert ( result == "\n".join( [ "Found 1 duplicate metainfo files:", "test_name", "No metainfo files moved", ] ) + "\n" )
async def test_multiple_directory_filesystem(): fs = MockFilesystem( { "upper": {"testing": {"file1", "file2.torrent", "file3", "file4.torrent"}}, "multi": [ "file7", {"another": ["file5", {"level3": {"file8", "file10.torrent"}}]}, ], } ) directories = [Path("/upper"), Path("/multi")] locator = MultipleDirectoryFileLocator(directories, fs) results = set() async for path in locator.collect(".torrent"): results.add(path) assert results == { Path("/multi/another/level3/file10.torrent"), Path("/upper/testing/file2.torrent"), Path("/upper/testing/file4.torrent"), }
def test_rename_command_dry_run(): original = MetainfoFile( { "info_hash": "arbitrary", "name": "some_name" }, path=Path("/some/some_name.arbitrary.torrent"), ) dupe = MetainfoFile({ "info_hash": "arbitrary", "name": "some_name" }, path=Path("/some/file1")) second_dupe = MetainfoFile({ "info_hash": "arbitrary", "name": "some_name" }, path=Path("/some/file2")) files = [original, dupe, second_dupe] fs = MockFilesystem( {"some": ["some_name.arbitrary.torrent", "file1", "file2"]}) command = RenameCommand(fs, files) output = command.dry_run() assert len(output.others_by_selected) == 1 selected, others = next(iter(output.others_by_selected.items())) assert selected in {dupe, second_dupe} other_dupe = next(iter(others)) assert other_dupe in {dupe, second_dupe} assert select != other_dupe assert output.new_name_by_existing_file == { selected: "some_name.arbitrary.torrent", } assert output.new_name_by_actionable_file == {}
def test_dry_run_display_copied(mocker: MockerFixture, capsys): archive_path = Path("/", "test_path") fs = MockFilesystem({}) client = mocker.Mock(spec=TransmissionApi) client.get_errors_by_id.return_value = QueryResult( {1: (1, "some tracker error"), 2: (3, "some local error")} ) client.get_torrent_files_by_id.return_value = QueryResult( {1: Path("/some/path"), 2: Path("/some/path2"), 3: Path("/some/path3")} ) client.get_torrent_name_by_id.return_value = QueryResult( {1: "some_name", 2: "another_name", 3: "third_name"} ) command = ErrorArchiveCommand(archive_path, fs, client) output = command.dry_run() output.dry_run_display() result = capsys.readouterr().out assert ( result == "\n".join( [ "Found 1 torrent local errors:", 'another_name with error "some local error"', "Found 1 torrent tracker errors:", 'some_name with error "some tracker error"', "Will move 1 metainfo files to /test_path/tracker_error", "another_name", "Will move 1 metainfo files to /test_path/local_error", "some_name", "Will move 1 metainfo files to /test_path:", "/some/path3", ] ) + "\n" )