def test_metainfo_unequal(): first_file = MetainfoFile({"info_hash": "not_equal"}) second_file = MetainfoFile({"info_hash": "test"}) assert first_file != 5 assert first_file != second_file assert first_file is not second_file
def test_metainfo_equal(): first_file = MetainfoFile({"info_hash": "test"}) second_file = MetainfoFile({"info_hash": "test"}) assert first_file == second_file assert first_file == first_file assert first_file is not second_file
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_select(): original = MetainfoFile({"info_hash": "arbitrary"}, path=Path("/some/file1")) dupe = MetainfoFile({"info_hash": "arbitrary"}, path=Path("/some/file2")) second_dupe = MetainfoFile({"info_hash": "arbitrary"}, path=Path("/some/file3")) dupes = {Path("/some/new_name"): {original, dupe, second_dupe}} result: Mapping[MetainfoFile, Set[MetainfoFile]] = select(dupes) assert result == {original: {dupe, second_dupe}}
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_link_dry_run_failure_output(mocker: MockerFixture, capsys): metainfo_file = MetainfoFile({ "info_hash": "meaningless", "name": "some_name" }) location = Path() link_service = mocker.Mock(spec=LinkService) link_service.get_incomplete_id_by_metainfo_file.return_value = { metainfo_file: 1 } link_service.change_location.side_effect = RuntimeError("something") find_service = mocker.Mock(spec=FindService) torrent_data = TorrentData(metainfo_file, location) missing_torrent_data = TorrentData(metainfo_file) find_service.find.return_value = {torrent_data, missing_torrent_data} command = LinkCommand(link_service, find_service) output = command.dry_run() output.dry_run_display() result = capsys.readouterr().out assert (result == "\n".join([ "Found the following torrents:", "some_name at .", "Couldn't find data for the following torrents:", "some_name", ]) + "\n")
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_add_run_display(mocker: MockerFixture, capsys): api = mocker.Mock(spec=TransmissionApi) api.add_torrent.return_value = CommandResult() service = AddService(api) fs = mocker.Mock(spec=Filesystem) metainfo_paths = {Path("/", "test_path", str(n)) for n in range(2)} metainfo_files = { MetainfoFile({ "info_hash": path, "name": "some_name" }, path) for path in metainfo_paths } command = AddCommand(service, fs, metainfo_files) output: AddOutput = command.run() output.display() result = capsys.readouterr().out assert (result == "\n".join([ "2 torrents were added:", "some_name", "some_name", "2 torrents were deleted:", "some_name at /test_path/0", "some_name at /test_path/1", ]) + "\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_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_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_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_metainfo_invalid(): properties = {"info": {}} file = MetainfoFile(properties) with pytest.raises(ValueError) as e: _ = file.is_single_file assert str(e.value).startswith("must contain either length key")
def from_path(self, path: Path) -> MetainfoFile: external_torrent = ExternalTorrent.from_file(str(path)) properties = { prop: getattr(external_torrent, prop) for prop in MetainfoFile.PROPERTIES } properties["info"] = external_torrent._struct.get("info") or dict() return MetainfoFile(properties, path)
def test_metainfo(): files = [TorrentFile(Path("/"), 1)] info = {"files": [{"path": "/", "length": 1}]} data = {"name": "test_name", "info_hash": "test_hash", "info": info} file = MetainfoFile(data) assert file.name == "test_name" assert file.info == info assert file.info_hash == "test_hash" assert file.files == files
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_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_get_clashing_renames(): unique = MetainfoFile(properties={ "name": "file1", "info_hash": "a" * 16 }, path=Path("/file1")) first_clashing = MetainfoFile(properties={ "name": "file2", "info_hash": "b" * 16 }, path=Path("/file2")) second_clashing = MetainfoFile(properties={ "name": "file2", "info_hash": "b" * 16 }, path=Path("/file3")) files = [unique, first_clashing, second_clashing] clashing, not_clashing = get_clashing_renames(files) assert clashing == {"b" * 16: {first_clashing, second_clashing}} assert not_clashing == {unique}
def test_add_run_unknown(mocker: MockerFixture): api = mocker.Mock(spec=TransmissionApi) api.add_torrent.return_value = CommandResult(error="unknown", success=False) service = AddService(api) fs = mocker.Mock(spec=Filesystem) metainfo_path = Path("/", "test_path") metainfo_file = MetainfoFile({"info_hash": "arbitrary"}, metainfo_path) command = AddCommand(service, fs, {metainfo_file}) output: AddOutput = command.run() fs.remove.assert_not_called() assert output.failed_torrents[metainfo_file] == "unknown"
def test_link_list_run(mocker: MockerFixture): metainfo_file = MetainfoFile({ "info_hash": "meaningless", "name": "some_name" }) link_service = mocker.Mock(spec=LinkService) link_service.get_incomplete_id_by_metainfo_file.return_value = { metainfo_file: 1 } command = ListLinkCommand(link_service) output = command.run() assert output.files == {metainfo_file}
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_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_default_torrent_reader_verify_multiple_file(mocker: MockerFixture): fs = mocker.Mock(spec=Filesystem) fs.exists.side_effect = lambda path: path == Path("/root/torrent_name/file1") path = Path("/root") metainfo_file = MetainfoFile( {"name": "torrent_name", "info": {"files": [{"path": ["file1"], "length": 5}]}} ) reader: TorrentDataReader = DefaultTorrentDataReader(fs) result = reader.verify(path, metainfo_file) fs.exists.assert_called_once_with(Path("/root/torrent_name/file1")) assert result
def test_dry_run(): files = [ MetainfoFile({ "name": "some_name", "info_hash": "aaa" }, Path("/some/path")), MetainfoFile({ "name": "some_name", "info_hash": "aaa" }, Path("/some/path2")), ] fs = MockFilesystem({}) command = DedupeCommand(fs, files) output = command.dry_run() # can't guarantee how files are selected hence the messy assertions assert len(output.deleted_files_by_hash) == 1 deleted_file = output.deleted_files_by_hash["aaa"].pop() assert deleted_file in files assert len(output.remaining_files) == 1 remaining_file = output.remaining_files.pop() assert remaining_file in files assert remaining_file != deleted_file
def test_link_no_matching_data(mocker: MockerFixture): metainfo_file = MetainfoFile({"info_hash": "meaningless"}) link_service = mocker.Mock(spec=LinkService) link_service.get_incomplete_id_by_metainfo_file.return_value = { metainfo_file: 1 } find_service = mocker.Mock(spec=FindService) find_service.find.return_value = {TorrentData(metainfo_file)} command = LinkCommand(link_service, find_service) output = command.run() assert output.success == [] assert output.no_matching_data == {metainfo_file}
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_link_list_output(mocker: MockerFixture, capsys): metainfo_file = MetainfoFile({ "info_hash": "meaningless", "name": "some_name" }) link_service = mocker.Mock(spec=LinkService) link_service.get_incomplete_id_by_metainfo_file.return_value = { metainfo_file: 1 } command = ListLinkCommand(link_service) output = command.run() output.display() result = capsys.readouterr().out assert (result == "\n".join( ["Found following missing data torrents:", "some_name"]) + "\n")
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_link_failure(mocker: MockerFixture): metainfo_file = MetainfoFile({"info_hash": "meaningless"}) location = Path() link_service = mocker.Mock(spec=LinkService) link_service.get_incomplete_id_by_metainfo_file.return_value = { metainfo_file: 1 } link_service.change_location.side_effect = RuntimeError("something") find_service = mocker.Mock(spec=FindService) torrent_data = TorrentData(metainfo_file, location) find_service.find.return_value = {torrent_data} command = LinkCommand(link_service, find_service) output = command.run() assert output.success == [] assert output.no_matching_data == set() assert output.fail == [LinkFailure(torrent_data, "something")]
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")