コード例 #1
0
ファイル: test_torrent.py プロジェクト: mhadam/clutchless
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
コード例 #2
0
ファイル: test_torrent.py プロジェクト: mhadam/clutchless
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
コード例 #3
0
ファイル: test_find.py プロジェクト: mhadam/clutchless
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")
コード例 #4
0
ファイル: test_rename.py プロジェクト: mhadam/clutchless
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}}
コード例 #5
0
ファイル: test_find.py プロジェクト: mhadam/clutchless
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}
コード例 #6
0
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")
コード例 #7
0
ファイル: test_add.py プロジェクト: mhadam/clutchless
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")}
コード例 #8
0
ファイル: test_add.py プロジェクト: mhadam/clutchless
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")
コード例 #9
0
ファイル: test_add.py プロジェクト: mhadam/clutchless
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"
コード例 #10
0
ファイル: test_add.py プロジェクト: mhadam/clutchless
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"
コード例 #11
0
ファイル: test_add.py プロジェクト: mhadam/clutchless
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")
コード例 #12
0
ファイル: test_add.py プロジェクト: mhadam/clutchless
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"}
コード例 #13
0
ファイル: test_torrent.py プロジェクト: mhadam/clutchless
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")
コード例 #14
0
ファイル: metainfo.py プロジェクト: mhadam/clutchless
 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)
コード例 #15
0
ファイル: test_torrent.py プロジェクト: mhadam/clutchless
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
コード例 #16
0
ファイル: test_rename.py プロジェクト: mhadam/clutchless
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")
コード例 #17
0
ファイル: test_dedupe.py プロジェクト: mhadam/clutchless
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")
コード例 #18
0
ファイル: test_rename.py プロジェクト: mhadam/clutchless
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}
コード例 #19
0
ファイル: test_add.py プロジェクト: mhadam/clutchless
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"
コード例 #20
0
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}
コード例 #21
0
ファイル: test_rename.py プロジェクト: mhadam/clutchless
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 == {}
コード例 #22
0
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"))
コード例 #23
0
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
コード例 #24
0
ファイル: test_dedupe.py プロジェクト: mhadam/clutchless
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
コード例 #25
0
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}
コード例 #26
0
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"
コード例 #27
0
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")
コード例 #28
0
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
コード例 #29
0
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")]
コード例 #30
0
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")