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