Exemple #1
0
def test_invite_device_confirmation():
    from parsec.api.data.invite import (
        _RsInviteDeviceConfirmation,
        InviteDeviceConfirmation,
        _PyInviteDeviceConfirmation,
    )

    assert InviteDeviceConfirmation is _RsInviteDeviceConfirmation

    di = DeviceID("a@b")
    dl = DeviceLabel("label")
    hh = HumanHandle("*****@*****.**",
                     "Hubert Farnsworth")
    profile = UserProfile.STANDARD
    pk = PrivateKey.generate()
    umi = EntryID.new()
    umk = SecretKey.generate()
    sk = SigningKey.generate()
    vk = sk.verify_key
    sek = SecretKey.generate()

    py_idc = _PyInviteDeviceConfirmation(
        device_id=di,
        device_label=dl,
        human_handle=hh,
        profile=profile,
        private_key=pk,
        user_manifest_id=umi,
        user_manifest_key=umk,
        root_verify_key=vk,
    )
    rs_idc = InviteDeviceConfirmation(
        device_id=di,
        device_label=dl,
        human_handle=hh,
        profile=profile,
        private_key=pk,
        user_manifest_id=umi,
        user_manifest_key=umk,
        root_verify_key=vk,
    )

    assert rs_idc.device_label.str == py_idc.device_label.str
    assert str(rs_idc.human_handle) == str(py_idc.human_handle)
    assert rs_idc.device_id.str == py_idc.device_id.str
    assert rs_idc.profile == py_idc.profile
    assert rs_idc.user_manifest_id.hex == py_idc.user_manifest_id.hex

    rs_encrypted = rs_idc.dump_and_encrypt(key=sek)
    py_encrypted = py_idc.dump_and_encrypt(key=sek)

    # Decrypt Rust-encrypted with Rust
    rs_idc2 = InviteDeviceConfirmation.decrypt_and_load(rs_encrypted, sek)
    assert rs_idc.device_label.str == rs_idc2.device_label.str
    assert str(rs_idc.human_handle) == str(rs_idc2.human_handle)
    assert rs_idc.device_id.str == rs_idc2.device_id.str
    assert rs_idc.profile == rs_idc2.profile
    assert rs_idc.user_manifest_id.hex == rs_idc2.user_manifest_id.hex

    # Decrypt Python-encrypted with Python
    rs_idc3 = InviteDeviceConfirmation.decrypt_and_load(py_encrypted, sek)
    assert rs_idc.device_label.str == rs_idc3.device_label.str
    assert str(rs_idc.human_handle) == str(rs_idc3.human_handle)
    assert rs_idc.device_id.str == rs_idc3.device_id.str
    assert rs_idc.profile == rs_idc3.profile
    assert rs_idc.user_manifest_id.hex == rs_idc3.user_manifest_id.hex

    # Decrypt Rust-encrypted with Python
    py_idc2 = _PyInviteDeviceConfirmation.decrypt_and_load(rs_encrypted, sek)
    assert rs_idc.device_label.str == py_idc2.device_label.str
    assert str(rs_idc.human_handle) == str(py_idc2.human_handle)
    assert rs_idc.device_id.str == py_idc2.device_id.str
    assert rs_idc.profile == py_idc2.profile
    assert rs_idc.user_manifest_id.hex == rs_idc2.user_manifest_id.hex

    # With human_handle and device_label as None
    py_idc = _PyInviteDeviceConfirmation(
        device_id=di,
        device_label=None,
        human_handle=None,
        profile=profile,
        private_key=pk,
        user_manifest_id=umi,
        user_manifest_key=umk,
        root_verify_key=vk,
    )
    rs_idc = InviteDeviceConfirmation(
        device_id=di,
        device_label=None,
        human_handle=None,
        profile=profile,
        private_key=pk,
        user_manifest_id=umi,
        user_manifest_key=umk,
        root_verify_key=vk,
    )

    assert py_idc.device_label is None
    assert rs_idc.device_label is None
    assert py_idc.human_handle is None
    assert rs_idc.human_handle is None
Exemple #2
0
async def test_rename_unknown_workspace(alice_user_fs):
    dummy_id = EntryID.new()
    with pytest.raises(FSWorkspaceNotFoundError):
        await alice_user_fs.workspace_rename(dummy_id, EntryName("whatever"))
def test_file_table_sort(qtbot, core_config):
    switch_language(core_config, "en")

    w = FileTable(parent=None)
    qtbot.add_widget(w)
    w.add_parent_workspace()
    w.add_folder(EntryName("Dir1"), EntryID.new(), True, False)
    w.add_file(
        EntryName("File1.txt"),
        EntryID.new(),
        100,
        pendulum.datetime(2000, 1, 15),
        pendulum.datetime(2000, 1, 20),
        True,
        False,
    )
    w.add_file(
        EntryName("AnotherFile.txt"),
        EntryID.new(),
        80,
        pendulum.datetime(2000, 1, 10),
        pendulum.datetime(2000, 1, 25),
        True,
        False,
    )
    assert w.rowCount() == 4
    assert w.item(0, 1).text() == "Workspaces list"
    assert w.item(1, 1).text() == "Dir1"
    assert w.item(2, 1).text() == "File1.txt"
    assert w.item(3, 1).text() == "AnotherFile.txt"

    # Name
    w.sortByColumn(1, QtCore.Qt.AscendingOrder)
    assert w.item(0, 1).text() == "Workspaces list"
    assert w.item(1, 1).text() == "AnotherFile.txt"
    assert w.item(2, 1).text() == "Dir1"
    assert w.item(3, 1).text() == "File1.txt"

    w.sortByColumn(1, QtCore.Qt.DescendingOrder)
    assert w.item(0, 1).text() == "Workspaces list"
    assert w.item(1, 1).text() == "File1.txt"
    assert w.item(2, 1).text() == "Dir1"
    assert w.item(3, 1).text() == "AnotherFile.txt"

    # Created
    w.sortByColumn(2, QtCore.Qt.AscendingOrder)
    assert w.item(0, 1).text() == "Workspaces list"
    assert w.item(1, 1).text() == "Dir1"
    assert w.item(2, 1).text() == "AnotherFile.txt"
    assert w.item(3, 1).text() == "File1.txt"

    w.sortByColumn(2, QtCore.Qt.DescendingOrder)
    assert w.item(0, 1).text() == "Workspaces list"
    assert w.item(1, 1).text() == "File1.txt"
    assert w.item(2, 1).text() == "AnotherFile.txt"
    assert w.item(3, 1).text() == "Dir1"

    # Updated
    w.sortByColumn(3, QtCore.Qt.AscendingOrder)
    assert w.item(0, 1).text() == "Workspaces list"
    assert w.item(1, 1).text() == "Dir1"
    assert w.item(2, 1).text() == "File1.txt"
    assert w.item(3, 1).text() == "AnotherFile.txt"

    w.sortByColumn(3, QtCore.Qt.DescendingOrder)
    assert w.item(0, 1).text() == "Workspaces list"
    assert w.item(1, 1).text() == "AnotherFile.txt"
    assert w.item(2, 1).text() == "File1.txt"
    assert w.item(3, 1).text() == "Dir1"

    # Size
    w.sortByColumn(4, QtCore.Qt.AscendingOrder)
    assert w.item(0, 1).text() == "Workspaces list"
    assert w.item(1, 1).text() == "Dir1"
    assert w.item(2, 1).text() == "AnotherFile.txt"
    assert w.item(3, 1).text() == "File1.txt"

    w.sortByColumn(4, QtCore.Qt.DescendingOrder)
    assert w.item(0, 1).text() == "Workspaces list"
    assert w.item(1, 1).text() == "File1.txt"
    assert w.item(2, 1).text() == "AnotherFile.txt"
    assert w.item(3, 1).text() == "Dir1"
Exemple #4
0
def test_workspace_manifest():
    from parsec.api.data.manifest import (
        _RsWorkspaceManifest,
        WorkspaceManifest,
        _PyWorkspaceManifest,
    )

    assert WorkspaceManifest is _RsWorkspaceManifest

    def _assert_workspace_manifest_eq(py, rs):
        assert isinstance(py, _PyWorkspaceManifest)
        assert isinstance(rs, _RsWorkspaceManifest)

        assert py.author == rs.author
        assert py.id == rs.id
        assert py.version == rs.version
        assert py.timestamp == rs.timestamp
        assert py.created == rs.created
        assert py.updated == rs.updated
        assert py.children == rs.children

    kwargs = {
        "author": DeviceID("user@device"),
        "id": EntryID.new(),
        "version": 42,
        "timestamp": pendulum.now(),
        "created": pendulum.now(),
        "updated": pendulum.now(),
        "children": {
            EntryName("file1.txt"): EntryID.new()
        },
    }
    py_wm = _PyWorkspaceManifest(**kwargs)
    rs_wm = WorkspaceManifest(**kwargs)
    _assert_workspace_manifest_eq(py_wm, rs_wm)

    kwargs = {
        "author": DeviceID("a@b"),
        "id": EntryID.new(),
        "version": 1337,
        "timestamp": pendulum.now(),
        "created": pendulum.now(),
        "updated": pendulum.now(),
        "children": {
            EntryName("file2.mp4"): EntryID.new()
        },
    }

    py_wm = py_wm.evolve(**kwargs)
    rs_wm = rs_wm.evolve(**kwargs)
    _assert_workspace_manifest_eq(py_wm, rs_wm)

    signing_key = SigningKey(b"a" * 32)
    secret_key = SecretKey.generate()

    py_signed_and_encrypted = py_wm.dump_sign_and_encrypt(
        signing_key, secret_key)
    rs_signed_and_encrypted = rs_wm.dump_sign_and_encrypt(
        signing_key, secret_key)

    wm1 = WorkspaceManifest.decrypt_verify_and_load(py_signed_and_encrypted,
                                                    secret_key,
                                                    signing_key.verify_key,
                                                    py_wm.author,
                                                    py_wm.timestamp)
    wm2 = _PyWorkspaceManifest.decrypt_verify_and_load(rs_signed_and_encrypted,
                                                       secret_key,
                                                       signing_key.verify_key,
                                                       py_wm.author,
                                                       py_wm.timestamp)
    assert isinstance(wm1, WorkspaceManifest)
    assert isinstance(wm2, WorkspaceManifest)
    assert wm1 == wm2
Exemple #5
0
def test_user_manifest():
    from parsec.api.data.manifest import (
        _RsUserManifest,
        UserManifest,
        _PyUserManifest,
        WorkspaceEntry,
    )

    assert UserManifest is _RsUserManifest

    def _assert_user_manifest_eq(py, rs):
        assert isinstance(py, _PyUserManifest)
        assert isinstance(rs, _RsUserManifest)

        assert py.author == rs.author
        assert py.version == rs.version
        assert py.id == rs.id
        assert py.timestamp == rs.timestamp
        assert py.created == rs.created
        assert py.updated == rs.updated
        assert py.last_processed_message == rs.last_processed_message
        assert py.workspaces == rs.workspaces

    kwargs = {
        "author":
        DeviceID("user@device"),
        "id":
        EntryID.new(),
        "version":
        42,
        "timestamp":
        pendulum.now(),
        "created":
        pendulum.now(),
        "updated":
        pendulum.now(),
        "last_processed_message":
        4,
        "workspaces": [
            WorkspaceEntry(
                name=EntryName("name"),
                id=EntryID.new(),
                key=SecretKey.generate(),
                encryption_revision=1,
                encrypted_on=pendulum.now(),
                role_cached_on=pendulum.now(),
                role=RealmRole.OWNER,
            )
        ],
    }

    py_um = _PyUserManifest(**kwargs)
    rs_um = UserManifest(**kwargs)
    _assert_user_manifest_eq(py_um, rs_um)

    kwargs = {
        "author":
        DeviceID("a@b"),
        "id":
        EntryID.new(),
        "version":
        1337,
        "timestamp":
        pendulum.now(),
        "created":
        pendulum.now(),
        "updated":
        pendulum.now(),
        "last_processed_message":
        7,
        "workspaces": [
            WorkspaceEntry(
                name=EntryName("name"),
                id=EntryID.new(),
                key=SecretKey.generate(),
                encryption_revision=1,
                encrypted_on=pendulum.now(),
                role_cached_on=pendulum.now(),
                role=RealmRole.OWNER,
            ),
            WorkspaceEntry(
                name=EntryName("other_name"),
                id=EntryID.new(),
                key=SecretKey.generate(),
                encryption_revision=2,
                encrypted_on=pendulum.now(),
                role_cached_on=pendulum.now(),
                role=RealmRole.CONTRIBUTOR,
            ),
        ],
    }

    py_wm = py_um.evolve(**kwargs)
    rs_wm = rs_um.evolve(**kwargs)
    _assert_user_manifest_eq(py_wm, rs_wm)
Exemple #6
0
def test_file_manifest():
    from parsec.api.data.manifest import _RsFileManifest, FileManifest, _PyFileManifest, BlockAccess

    assert FileManifest is _RsFileManifest

    def _assert_file_manifest_eq(py, rs):
        assert isinstance(py, _PyFileManifest)
        assert isinstance(rs, _RsFileManifest)

        assert py.author == rs.author
        assert py.id == rs.id
        assert py.parent == rs.parent
        assert py.version == rs.version
        assert py.size == rs.size
        assert py.blocksize == rs.blocksize
        assert py.timestamp == rs.timestamp
        assert py.created == rs.created
        assert py.updated == rs.updated
        assert len(py.blocks) == len(rs.blocks)
        assert all(
            isinstance(b2, BlockAccess) and b1.id == b2.id
            and b1.offset == b2.offset and b1.size == b2.size
            for (b1, b2) in zip(py.blocks, rs.blocks))

    kwargs = {
        "author":
        DeviceID("user@device"),
        "id":
        EntryID.new(),
        "parent":
        EntryID.new(),
        "version":
        42,
        "size":
        1337,
        "blocksize":
        64,
        "timestamp":
        pendulum.now(),
        "created":
        pendulum.now(),
        "updated":
        pendulum.now(),
        "blocks": (BlockAccess(
            id=BlockID.new(),
            key=SecretKey.generate(),
            offset=0,
            size=1024,
            digest=HashDigest.from_data(b"a"),
        ), ),
    }

    py_fm = _PyFileManifest(**kwargs)
    rs_fm = FileManifest(**kwargs)
    _assert_file_manifest_eq(py_fm, rs_fm)

    kwargs = {
        "author":
        DeviceID("a@b"),
        "id":
        EntryID.new(),
        "parent":
        EntryID.new(),
        "version":
        1337,
        "timestamp":
        pendulum.now(),
        "created":
        pendulum.now(),
        "updated":
        pendulum.now(),
        "blocks": (BlockAccess(
            id=BlockID.new(),
            key=SecretKey.generate(),
            offset=64,
            size=2048,
            digest=HashDigest.from_data(b"b"),
        ), ),
    }

    py_fm = py_fm.evolve(**kwargs)
    rs_fm = rs_fm.evolve(**kwargs)
    _assert_file_manifest_eq(py_fm, rs_fm)
Exemple #7
0
async def test_update_file(alice_workspace, monkeypatch):
    block_mock1 = mock.Mock()
    block_mock1.digest = b"block1"
    block_mock2 = mock.Mock()
    block_mock2.digest = b"block2"

    manifest_mock = mock.Mock(spec=FolderManifest)
    manifest_mock.blocks = [block_mock1, block_mock2]

    load_manifest_mock = AsyncMock(spec=mock.Mock,
                                   side_effect=lambda x: manifest_mock)
    alice_workspace.remote_loader.load_manifest = load_manifest_mock

    write_bytes_mock = AsyncMock(spec=mock.Mock)
    alice_workspace.write_bytes = write_bytes_mock

    sync_by_id_mock = AsyncMock(spec=mock.Mock)
    alice_workspace.sync_by_id = sync_by_id_mock
    monkeypatch.setattr(HashDigest, "from_data",
                        mock.Mock(side_effect=lambda x: x))

    with mock.patch(
            "parsec.core.cli.rsync._chunks_from_path",
            AsyncMock(spec=mock.Mock, side_effect=[[b"block1", b"block2"]]),
    ):
        entry_id = EntryID.new()
        await rsync._update_file(alice_workspace, entry_id,
                                 FsPath("/src_file"),
                                 FsPath("/path_in_workspace"))
        rsync._chunks_from_path.assert_called_once_with(FsPath("/src_file"))
        load_manifest_mock.assert_called_once_with(entry_id)
        write_bytes_mock.assert_not_called()
        sync_by_id_mock.assert_called_once_with(entry_id,
                                                remote_changed=False,
                                                recursive=False)

    load_manifest_mock.reset_mock()
    sync_by_id_mock.reset_mock()

    with mock.patch(
            "parsec.core.cli.rsync._chunks_from_path",
            AsyncMock(spec=mock.Mock, side_effect=[[b"block1", b"block3"]]),
    ):

        await rsync._update_file(alice_workspace, entry_id,
                                 FsPath("/src_file"),
                                 FsPath("/path_in_workspace"))
        rsync._chunks_from_path.assert_called_once_with(FsPath("/src_file"))
        load_manifest_mock.assert_called_once_with(entry_id)
        write_bytes_mock.assert_called_once_with(FsPath("/path_in_workspace"),
                                                 b"block3", len("block1"))
        sync_by_id_mock.assert_called_once_with(entry_id,
                                                remote_changed=False,
                                                recursive=False)

    load_manifest_mock.reset_mock()
    sync_by_id_mock.reset_mock()
    write_bytes_mock.reset_mock()

    with mock.patch(
            "parsec.core.cli.rsync._chunks_from_path",
            AsyncMock(spec=mock.Mock, side_effect=[[b"block3", b"block4"]]),
    ):
        await rsync._update_file(alice_workspace, entry_id,
                                 FsPath("/src_file"),
                                 FsPath("/path_in_workspace"))
        rsync._chunks_from_path.assert_called_once_with(FsPath("/src_file"))
        alice_workspace.remote_loader.load_manifest.assert_called_once_with(
            entry_id)
        write_bytes_mock.assert_has_calls([
            mock.call(FsPath("/path_in_workspace"), b"block3", 0),
            mock.call(FsPath("/path_in_workspace"), b"block4", len("block3")),
        ])
        sync_by_id_mock.assert_called_once_with(entry_id,
                                                remote_changed=False,
                                                recursive=False)
def test_local_user_manifest():
    from parsec.core.types.manifest import (
        _RsLocalUserManifest,
        LocalUserManifest,
        _PyLocalUserManifest,
    )

    assert LocalUserManifest is _RsLocalUserManifest

    def _assert_local_user_manifest_eq(py,
                                       rs,
                                       exclude_base=False,
                                       exclude_id=False):
        assert isinstance(py, _PyLocalUserManifest)
        assert isinstance(rs, _RsLocalUserManifest)

        if not exclude_base:
            assert py.base == rs.base
        if not exclude_id:
            assert py.id == rs.id
        assert py.need_sync == rs.need_sync
        assert py.updated == rs.updated
        assert py.last_processed_message == rs.last_processed_message
        assert len(py.workspaces) == len(rs.workspaces)
        assert isinstance(rs.workspaces, type(py.workspaces))
        assert py.workspaces == rs.workspaces
        assert py.speculative == rs.speculative

    def _assert_user_manifest_eq(py, rs):
        assert py.author == rs.author
        assert py.version == rs.version
        assert py.timestamp == rs.timestamp
        assert py.created == rs.created
        assert py.updated == rs.updated
        assert py.last_processed_message == rs.last_processed_message
        assert py.workspaces == rs.workspaces
        assert isinstance(rs.workspaces, type(py.workspaces))

    kwargs = {
        "base":
        UserManifest(
            author=DeviceID("user@device"),
            id=EntryID.new(),
            version=42,
            timestamp=pendulum.now(),
            created=pendulum.now(),
            updated=pendulum.now(),
            last_processed_message=0,
            workspaces=(WorkspaceEntry.new(EntryName("user"),
                                           pendulum.now()), ),
        ),
        "need_sync":
        True,
        "updated":
        pendulum.now(),
        "last_processed_message":
        0,
        "workspaces": (),
        "speculative":
        True,
    }

    py_lum = _PyLocalUserManifest(**kwargs)
    rs_lum = LocalUserManifest(**kwargs)
    _assert_local_user_manifest_eq(py_lum, rs_lum)

    kwargs = {
        "base":
        kwargs["base"].evolve(
            **{
                "author":
                DeviceID("a@b"),
                "id":
                EntryID.new(),
                "version":
                1337,
                "timestamp":
                pendulum.now(),
                "created":
                pendulum.now(),
                "updated":
                pendulum.now(),
                "last_processed_message":
                1,
                "workspaces": (
                    WorkspaceEntry.new(EntryName("user"), pendulum.now()), ),
            }),
        "need_sync":
        False,
        "updated":
        pendulum.now(),
        "last_processed_message":
        1,
        "workspaces": (WorkspaceEntry.new(EntryName("wk"), pendulum.now()), ),
        "speculative":
        False,
    }

    py_lum = py_lum.evolve(**kwargs)
    rs_lum = rs_lum.evolve(**kwargs)
    _assert_local_user_manifest_eq(py_lum, rs_lum)

    sk = SecretKey.generate()

    py_enc = py_lum.dump_and_encrypt(sk)
    rs_enc = py_lum.dump_and_encrypt(sk)

    # Decrypt rust encrypted with Python and vice versa
    lum1 = _PyLocalUserManifest.decrypt_and_load(rs_enc, sk)
    lum2 = LocalUserManifest.decrypt_and_load(py_enc, sk)
    assert isinstance(lum1, LocalUserManifest)
    assert isinstance(lum2, LocalUserManifest)
    assert lum1 == lum2

    assert py_lum.to_stats() == rs_lum.to_stats()
    assert py_lum.asdict() == rs_lum.asdict()

    ts = pendulum.now()
    ei = EntryID.new()
    di = DeviceID("a@b")

    # With optional parameters
    py_lum = _PyLocalUserManifest.new_placeholder(author=di,
                                                  id=ei,
                                                  timestamp=ts,
                                                  speculative=True)
    rs_lum = LocalUserManifest.new_placeholder(author=di,
                                               id=ei,
                                               timestamp=ts,
                                               speculative=True)
    _assert_local_user_manifest_eq(py_lum,
                                   rs_lum,
                                   exclude_base=True,
                                   exclude_id=True)

    # Without optional parameters
    py_lum = _PyLocalUserManifest.new_placeholder(author=di, timestamp=ts)
    rs_lum = LocalUserManifest.new_placeholder(author=di, timestamp=ts)
    _assert_local_user_manifest_eq(py_lum,
                                   rs_lum,
                                   exclude_base=True,
                                   exclude_id=True)

    py_rum = py_lum.to_remote(author=di, timestamp=ts)
    rs_rum = rs_lum.to_remote(author=di, timestamp=ts)
    _assert_user_manifest_eq(py_rum, rs_rum)

    assert py_lum.match_remote(py_rum) == rs_lum.match_remote(rs_rum)

    py_lum2 = _PyLocalUserManifest.from_remote(py_rum)
    rs_lum2 = LocalUserManifest.from_remote(rs_rum)
    _assert_local_user_manifest_eq(py_lum2,
                                   rs_lum2,
                                   exclude_base=True,
                                   exclude_id=True)

    _PyLocalUserManifest.from_remote_with_local_context(
        remote=py_rum,
        prevent_sync_pattern=r".+",
        local_manifest=py_lum2,
        timestamp=ts)
    LocalUserManifest.from_remote_with_local_context(
        remote=rs_rum,
        prevent_sync_pattern=r".+",
        local_manifest=rs_lum2,
        timestamp=ts)
def test_local_workspace_manifest():
    from parsec.core.types.manifest import (
        _RsLocalWorkspaceManifest,
        LocalWorkspaceManifest,
        _PyLocalWorkspaceManifest,
    )

    assert LocalWorkspaceManifest is _RsLocalWorkspaceManifest

    def _assert_local_workspace_manifest_eq(py,
                                            rs,
                                            exclude_base=False,
                                            exclude_id=False):
        assert isinstance(py, _PyLocalWorkspaceManifest)
        assert isinstance(rs, _RsLocalWorkspaceManifest)

        if not exclude_base:
            assert py.base == rs.base
        if not exclude_id:
            assert py.id == rs.id
        assert py.need_sync == rs.need_sync
        assert py.updated == rs.updated
        assert py.speculative == rs.speculative
        assert len(py.children) == len(rs.children)
        assert isinstance(rs.children, type(
            py.children)), "Rust type is {}, should be {}".format(
                type(rs.children), type(py.children))
        assert all(
            isinstance(name1, EntryName) and isinstance(id1, EntryID)
            and name1 == name2 and id1 == id2
            for ((name1, id1), (name2, id2)) in zip(
                sorted(py.children.items()), sorted(rs.children.items())))
        assert len(py.local_confinement_points) == len(
            rs.local_confinement_points)
        assert py.local_confinement_points == rs.local_confinement_points
        assert len(py.remote_confinement_points) == len(
            rs.remote_confinement_points)
        assert py.remote_confinement_points == rs.remote_confinement_points

    def _assert_workspace_manifest_eq(py, rs):
        assert py.author == rs.author
        assert py.version == rs.version
        assert py.timestamp == rs.timestamp
        assert py.created == rs.created
        assert py.updated == rs.updated
        assert py.children == rs.children

    kwargs = {
        "base":
        WorkspaceManifest(
            author=DeviceID("user@device"),
            id=EntryID.new(),
            version=42,
            timestamp=pendulum.now(),
            created=pendulum.now(),
            updated=pendulum.now(),
            children={EntryName("file1.txt"): EntryID.new()},
        ),
        "need_sync":
        True,
        "updated":
        pendulum.now(),
        "children": {
            EntryName("wksp2"): EntryID.new()
        },
        "local_confinement_points":
        frozenset({EntryID.new()}),
        "remote_confinement_points":
        frozenset({EntryID.new()}),
        "speculative":
        True,
    }

    py_lwm = _PyLocalWorkspaceManifest(**kwargs)
    rs_lwm = LocalWorkspaceManifest(**kwargs)
    _assert_local_workspace_manifest_eq(py_lwm, rs_lwm)

    kwargs = {
        "base":
        kwargs["base"].evolve(
            **{
                "author": DeviceID("a@b"),
                "id": EntryID.new(),
                "version": 1337,
                "timestamp": pendulum.now(),
                "created": pendulum.now(),
                "updated": pendulum.now(),
                "children": {
                    EntryName("file2.mp4"): EntryID.new()
                },
            }),
        "need_sync":
        False,
        "updated":
        pendulum.now(),
        "children": {
            EntryName("wksp1"): EntryID.new()
        },
        "local_confinement_points":
        frozenset({EntryID.new()}),
        "remote_confinement_points":
        frozenset({EntryID.new()}),
        "speculative":
        False,
    }

    py_lwm = py_lwm.evolve(**kwargs)
    rs_lwm = rs_lwm.evolve(**kwargs)
    _assert_local_workspace_manifest_eq(py_lwm, rs_lwm)

    sk = SecretKey.generate()

    py_enc = py_lwm.dump_and_encrypt(sk)
    rs_enc = py_lwm.dump_and_encrypt(sk)

    # Decrypt rust encrypted with Python and vice versa
    lwm1 = _PyLocalWorkspaceManifest.decrypt_and_load(rs_enc, sk)
    lwm2 = LocalWorkspaceManifest.decrypt_and_load(py_enc, sk)
    assert isinstance(lwm1, LocalWorkspaceManifest)
    assert isinstance(lwm2, LocalWorkspaceManifest)
    assert lwm1 == lwm2

    assert py_lwm.to_stats() == rs_lwm.to_stats()
    assert py_lwm.asdict() == rs_lwm.asdict()

    ts = pendulum.now()
    ei = EntryID.new()
    di = DeviceID("a@b")

    # With optional parameters
    py_lwm = _PyLocalWorkspaceManifest.new_placeholder(author=di,
                                                       id=ei,
                                                       timestamp=ts,
                                                       speculative=True)
    rs_lwm = LocalWorkspaceManifest.new_placeholder(author=di,
                                                    id=ei,
                                                    timestamp=ts,
                                                    speculative=True)
    _assert_local_workspace_manifest_eq(py_lwm,
                                        rs_lwm,
                                        exclude_base=True,
                                        exclude_id=True)

    # Without optional parameters
    py_lwm = _PyLocalWorkspaceManifest.new_placeholder(author=di, timestamp=ts)
    rs_lwm = LocalWorkspaceManifest.new_placeholder(author=di, timestamp=ts)
    _assert_local_workspace_manifest_eq(py_lwm,
                                        rs_lwm,
                                        exclude_base=True,
                                        exclude_id=True)

    py_rwm = py_lwm.to_remote(author=di, timestamp=ts)
    rs_rwm = rs_lwm.to_remote(author=di, timestamp=ts)
    _assert_workspace_manifest_eq(py_rwm, rs_rwm)

    children = {EntryName("wksp1"): EntryID.new()}
    py_lwm = py_lwm.evolve_and_mark_updated(timestamp=ts, children=children)
    rs_lwm = rs_lwm.evolve_and_mark_updated(timestamp=ts, children=children)

    _assert_local_workspace_manifest_eq(py_lwm,
                                        rs_lwm,
                                        exclude_base=True,
                                        exclude_id=True)

    with pytest.raises(TypeError) as excinfo:
        py_lwm.evolve_and_mark_updated(timestamp=ts, need_sync=True)
    assert str(excinfo.value) == "Unexpected keyword argument `need_sync`"
    with pytest.raises(TypeError) as excinfo:
        rs_lwm.evolve_and_mark_updated(timestamp=ts, need_sync=True)
    assert str(excinfo.value) == "Unexpected keyword argument `need_sync`"

    py_lwm2 = _PyLocalWorkspaceManifest.from_remote(py_rwm, r".+")
    rs_lwm2 = LocalWorkspaceManifest.from_remote(rs_rwm, r".+")
    _assert_local_workspace_manifest_eq(py_lwm2,
                                        rs_lwm2,
                                        exclude_base=True,
                                        exclude_id=True)

    py_lwm2 = _PyLocalWorkspaceManifest.from_remote_with_local_context(
        remote=py_rwm,
        prevent_sync_pattern=r".+",
        local_manifest=py_lwm2,
        timestamp=ts)
    rs_lwm2 = LocalWorkspaceManifest.from_remote_with_local_context(
        remote=rs_rwm,
        prevent_sync_pattern=r".+",
        local_manifest=rs_lwm2,
        timestamp=ts)

    assert py_lwm.match_remote(py_rwm) == rs_lwm.match_remote(rs_rwm)
def test_local_file_manifest():
    from parsec.api.data.manifest import BlockAccess
    from parsec.core.types.manifest import (
        _RsLocalFileManifest,
        LocalFileManifest,
        _PyLocalFileManifest,
        Chunk,
    )

    assert LocalFileManifest is _RsLocalFileManifest

    def _assert_local_file_manifest_eq(py,
                                       rs,
                                       exclude_base=False,
                                       exclude_id=False):
        assert isinstance(py, _PyLocalFileManifest)
        assert isinstance(rs, _RsLocalFileManifest)
        if not exclude_base:
            assert py.base == rs.base
        assert py.need_sync == rs.need_sync
        assert py.updated == rs.updated
        assert py.size == rs.size
        assert py.blocksize == rs.blocksize
        assert len(py.blocks) == len(rs.blocks)
        assert isinstance(rs.blocks, type(py.blocks))
        if len(py.blocks):
            assert isinstance(rs.blocks[0], type(py.blocks[0]))
        if not exclude_id:
            assert py.id == rs.id
        assert py.created == rs.created
        assert py.base_version == rs.base_version
        assert py.is_placeholder == rs.is_placeholder
        assert py.is_reshaped() == rs.is_reshaped()
        for (b1, b2) in zip(sorted(py.blocks), sorted(rs.blocks)):
            assert len(b1) == len(b2)
            assert all(
                isinstance(c2, Chunk) and c1.id == c2.id and c1.start == c2.
                start and c1.stop == c2.stop and c1.raw_offset == c2.raw_offset
                and c1.raw_size == c2.raw_size and c1.access == c2.access
                for (c1, c2) in zip(b1, b2))

    def _assert_file_manifest_eq(py, rs):
        assert py.author == rs.author
        assert py.parent == rs.parent
        assert py.version == rs.version
        assert py.size == rs.size
        assert py.blocksize == rs.blocksize
        assert py.timestamp == rs.timestamp
        assert py.created == rs.created
        assert py.updated == rs.updated
        assert len(py.blocks) == len(rs.blocks)
        assert isinstance(rs.blocks, type(py.blocks))
        assert all(
            isinstance(b2, BlockAccess) and b1.id == b2.id
            and b1.offset == b2.offset and b1.size == b2.size
            for (b1, b2) in zip(sorted(py.blocks), sorted(rs.blocks)))

    kwargs = {
        "base":
        FileManifest(
            author=DeviceID("user@device"),
            id=EntryID.new(),
            parent=EntryID.new(),
            version=42,
            size=1337,
            blocksize=85,
            timestamp=pendulum.now(),
            created=pendulum.now(),
            updated=pendulum.now(),
            blocks=(BlockAccess(
                id=BlockID.new(),
                key=SecretKey.generate(),
                offset=0,
                size=1024,
                digest=HashDigest.from_data(b"a"),
            ), ),
        ),
        "need_sync":
        True,
        "updated":
        pendulum.now(),
        "size":
        42,
        "blocksize":
        64,
        "blocks": ((
            Chunk(
                id=ChunkID.new(),
                start=0,
                stop=250,
                raw_offset=0,
                raw_size=512,
                access=BlockAccess(
                    id=BlockID.new(),
                    key=SecretKey.generate(),
                    offset=0,
                    size=512,
                    digest=HashDigest.from_data(b"aa"),
                ),
            ),
            Chunk(id=ChunkID.new(),
                  start=0,
                  stop=250,
                  raw_offset=250,
                  raw_size=250,
                  access=None),
        ), ),
    }

    py_lfm = _PyLocalFileManifest(**kwargs)
    rs_lfm = LocalFileManifest(**kwargs)
    _assert_local_file_manifest_eq(py_lfm, rs_lfm)

    kwargs = {
        "base":
        kwargs["base"].evolve(
            **{
                "author":
                DeviceID("a@b"),
                "id":
                EntryID.new(),
                "parent":
                EntryID.new(),
                "version":
                1337,
                "size":
                4096,
                "blocksize":
                512,
                "timestamp":
                pendulum.now(),
                "created":
                pendulum.now(),
                "updated":
                pendulum.now(),
                "blocks": (BlockAccess(
                    id=BlockID.new(),
                    key=SecretKey.generate(),
                    offset=64,
                    size=2048,
                    digest=HashDigest.from_data(b"b"),
                ), ),
            }),
        "need_sync":
        False,
        "updated":
        pendulum.now(),
        "size":
        2048,
        "blocksize":
        1024,
        "blocks": ((
            Chunk(
                id=ChunkID.new(),
                start=0,
                stop=1024,
                raw_offset=0,
                raw_size=1024,
                access=BlockAccess(
                    id=BlockID.new(),
                    key=SecretKey.generate(),
                    offset=0,
                    size=1024,
                    digest=HashDigest.from_data(b"bb"),
                ),
            ),
            Chunk(
                id=ChunkID.new(),
                start=1024,
                stop=2048,
                raw_offset=1024,
                raw_size=1024,
                access=None,
            ),
        ), ),
    }

    py_lfm = py_lfm.evolve(**kwargs)
    rs_lfm = rs_lfm.evolve(**kwargs)
    _assert_local_file_manifest_eq(py_lfm, rs_lfm)

    sk = SecretKey.generate()

    py_enc = py_lfm.dump_and_encrypt(sk)
    rs_enc = rs_lfm.dump_and_encrypt(sk)

    # Decrypt rust encrypted with Python and vice versa
    lfm1 = _PyLocalFileManifest.decrypt_and_load(rs_enc, sk)
    lfm2 = LocalFileManifest.decrypt_and_load(py_enc, sk)
    assert isinstance(lfm1, LocalFileManifest)
    assert isinstance(lfm2, LocalFileManifest)
    assert lfm1 == lfm2

    py_lfm = py_lfm.evolve(**{"size": 1337})
    rs_lfm = rs_lfm.evolve(**{"size": 1337})
    _assert_local_file_manifest_eq(py_lfm, rs_lfm)

    with pytest.raises(AssertionError):
        py_lfm.assert_integrity()
    with pytest.raises(AssertionError):
        rs_lfm.assert_integrity()

    assert py_lfm.to_stats() == rs_lfm.to_stats()
    assert py_lfm.parent == rs_lfm.parent
    assert py_lfm.get_chunks(0) == rs_lfm.get_chunks(0)
    assert py_lfm.get_chunks(1000) == rs_lfm.get_chunks(1000)
    assert py_lfm.asdict() == rs_lfm.asdict()

    di = DeviceID("a@b")
    ts = pendulum.now()

    kwargs = {
        "size":
        1024,
        "blocksize":
        1024,
        "blocks": ((Chunk(
            id=ChunkID.new(),
            start=0,
            stop=1024,
            raw_offset=0,
            raw_size=1024,
            access=BlockAccess(
                id=BlockID.new(),
                key=SecretKey.generate(),
                offset=0,
                size=1024,
                digest=HashDigest.from_data(b"bb"),
            ),
        ), ), ),
    }

    py_lfm = py_lfm.evolve(**kwargs)
    rs_lfm = rs_lfm.evolve(**kwargs)
    _assert_local_file_manifest_eq(py_lfm, rs_lfm)

    py_rfm = py_lfm.to_remote(author=di, timestamp=ts)
    rs_rfm = rs_lfm.to_remote(author=di, timestamp=ts)
    _assert_file_manifest_eq(py_rfm, rs_rfm)

    py_lfm2 = _PyLocalFileManifest.from_remote(py_rfm)
    rs_lfm2 = LocalFileManifest.from_remote(rs_rfm)
    _assert_local_file_manifest_eq(py_lfm2,
                                   rs_lfm2,
                                   exclude_base=True,
                                   exclude_id=True)

    py_lfm2 = _PyLocalFileManifest.from_remote_with_local_context(
        remote=py_rfm,
        prevent_sync_pattern=r".+",
        local_manifest=py_lfm2,
        timestamp=ts)
    rs_lfm2 = LocalFileManifest.from_remote_with_local_context(
        remote=rs_rfm,
        prevent_sync_pattern=r".+",
        local_manifest=rs_lfm2,
        timestamp=ts)

    assert py_lfm.match_remote(py_rfm) == rs_lfm.match_remote(rs_rfm)

    py_lfm = py_lfm.evolve_and_mark_updated(timestamp=ts, size=4096)
    rs_lfm = rs_lfm.evolve_and_mark_updated(timestamp=ts, size=4096)

    _assert_local_file_manifest_eq(py_lfm,
                                   rs_lfm,
                                   exclude_base=True,
                                   exclude_id=True)

    with pytest.raises(TypeError) as excinfo:
        py_lfm.evolve_and_mark_updated(timestamp=ts, need_sync=True)
    assert str(excinfo.value) == "Unexpected keyword argument `need_sync`"
    with pytest.raises(TypeError) as excinfo:
        rs_lfm.evolve_and_mark_updated(timestamp=ts, need_sync=True)
    assert str(excinfo.value) == "Unexpected keyword argument `need_sync`"

    ei = EntryID.new()

    # Without blocksize
    py_lfm = _PyLocalFileManifest.new_placeholder(author=di,
                                                  parent=ei,
                                                  timestamp=ts)
    rs_lfm = LocalFileManifest.new_placeholder(author=di,
                                               parent=ei,
                                               timestamp=ts)
    _assert_local_file_manifest_eq(py_lfm,
                                   rs_lfm,
                                   exclude_base=True,
                                   exclude_id=True)
    # With blocksize
    py_lfm = _PyLocalFileManifest.new_placeholder(author=di,
                                                  parent=ei,
                                                  timestamp=ts,
                                                  blocksize=1024)
    rs_lfm = LocalFileManifest.new_placeholder(author=di,
                                               parent=ei,
                                               timestamp=ts,
                                               blocksize=1024)
    _assert_local_file_manifest_eq(py_lfm,
                                   rs_lfm,
                                   exclude_base=True,
                                   exclude_id=True)