async def test_mountpoint_remote_error_event(aqtbot, running_backend, logged_gui, snackbar_catcher): c_w = logged_gui.test_get_central_widget() async with aqtbot.wait_signal(c_w.new_notification): c_w.event_bus.send( CoreEvent.MOUNTPOINT_REMOTE_ERROR, exc=FSWorkspaceNoReadAccess("Cannot get workspace roles: no read access"), path=FsPath("/bar"), mountpoint=Path("/foo"), operation="open", workspace_id=EntryID.new(), timestamp=None, ) assert "Read access to " in snackbar_catcher.snackbars[0][1] snackbar_catcher.reset() async with aqtbot.wait_signal(c_w.new_notification): c_w.event_bus.send( CoreEvent.MOUNTPOINT_UNHANDLED_ERROR, exc=RuntimeError("D'Oh !"), path=FsPath("/bar"), mountpoint=Path("/foo"), operation="unlink", workspace_id=EntryID.new(), ) assert "Error with the mountpoint " in snackbar_catcher.snackbars[0][1] snackbar_catcher.reset()
async def test_upsert_file(alice_workspace): _update_file_mock = AsyncMock(spec=mock.Mock()) _create_path_mock = AsyncMock(spec=mock.Mock()) path = FsPath("/test") workspace_path = FsPath("/path_in_workspace") entry_id = EntryID.new() with mock.patch("parsec.core.cli.rsync._create_path", _create_path_mock): with mock.patch("parsec.core.cli.rsync._update_file", _update_file_mock): await rsync._upsert_file(entry_id, alice_workspace, path, workspace_path) _update_file_mock.assert_called_once_with(alice_workspace, entry_id, path, workspace_path) _create_path_mock.assert_not_called() _update_file_mock.reset_mock() entry_id = None with mock.patch("parsec.core.cli.rsync._create_path", _create_path_mock): with mock.patch("parsec.core.cli.rsync._update_file", _update_file_mock): await rsync._upsert_file(None, alice_workspace, path, workspace_path) _update_file_mock.assert_not_called() _create_path_mock.assert_called_once_with(alice_workspace, False, path, workspace_path)
async def test_get_or_create_directory(alice_workspace): manifest1 = mock.Mock(spec=FolderManifest) manifest2 = mock.Mock(spec=FolderManifest) load_manifest_mock = AsyncMock(spec=mock.Mock(), side_effect=lambda x: manifest1) alice_workspace.remote_loader.load_manifest = load_manifest_mock _create_path_mock = AsyncMock(spec=mock.Mock(), side_effect=lambda *x: manifest2) with mock.patch("parsec.core.cli.rsync._create_path", _create_path_mock): entry_id = EntryID.new() res = await rsync._get_or_create_directory( entry_id, alice_workspace, FsPath("/test_directory"), FsPath("/path_in_workspace")) load_manifest_mock.assert_called_once_with(entry_id) _create_path_mock.assert_not_called() assert res is manifest1 load_manifest_mock.reset_mock() with mock.patch("parsec.core.cli.rsync._create_path", _create_path_mock): res = await rsync._get_or_create_directory( None, alice_workspace, FsPath("/test_directory"), FsPath("/path_in_workspace")) load_manifest_mock.assert_not_called() _create_path_mock.assert_called_once_with(alice_workspace, True, FsPath("/test_directory"), FsPath("/path_in_workspace")) assert res is manifest2
def new_placeholder( cls, author: DeviceID, parent: EntryID, timestamp: DateTime, blocksize: int = DEFAULT_BLOCK_SIZE, ) -> "LocalFileManifest": id = EntryID.new() blocks = () return cls( base=RemoteFileManifest( author=author, timestamp=timestamp, id=id, parent=parent, version=0, created=timestamp, updated=timestamp, blocksize=blocksize, size=0, blocks=blocks, ), need_sync=True, updated=timestamp, blocksize=blocksize, size=0, blocks=blocks, )
def new_placeholder( cls, author: DeviceID, parent: EntryID, id: Optional[EntryID] = None, now: DateTime = None, blocksize=DEFAULT_BLOCK_SIZE, ) -> "LocalFileManifest": now = now or pendulum_now() blocks = () return cls( base=RemoteFileManifest( author=author, timestamp=now, id=id or EntryID.new(), parent=parent, version=0, created=now, updated=now, blocksize=blocksize, size=0, blocks=blocks, ), need_sync=True, updated=now, blocksize=blocksize, size=0, blocks=blocks, )
async def test_mount_unknown_workspace(base_mountpoint, alice_user_fs, event_bus): async with mountpoint_manager_factory( alice_user_fs, event_bus, base_mountpoint) as mountpoint_manager: wid = EntryID.new() with pytest.raises(MountpointConfigurationError) as exc: await mountpoint_manager.mount_workspace(wid) assert exc.value.args == (f"Workspace `{wid}` doesn't exist", )
def test_workspace_entry(): from parsec.api.data.manifest import _RsWorkspaceEntry, WorkspaceEntry, _PyWorkspaceEntry from parsec.api.data import EntryName assert WorkspaceEntry is _RsWorkspaceEntry def _assert_workspace_entry_eq(py, rs): assert isinstance(py, _PyWorkspaceEntry) assert isinstance(rs, _RsWorkspaceEntry) assert py.is_revoked() == rs.is_revoked() assert py.name == rs.name assert py.id == rs.id assert py.key == rs.key assert py.encryption_revision == rs.encryption_revision assert py.encrypted_on == rs.encrypted_on assert py.role_cached_on == rs.role_cached_on assert py.role == rs.role kwargs = { "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_we = _PyWorkspaceEntry(**kwargs) rs_we = WorkspaceEntry(**kwargs) _assert_workspace_entry_eq(py_we, rs_we) kwargs = { "name": EntryName("new_name"), "id": EntryID.new(), "key": SecretKey.generate(), "encryption_revision": 42, "encrypted_on": pendulum.now(), "role_cached_on": pendulum.now(), "role": None, } py_we = py_we.evolve(**kwargs) rs_we = rs_we.evolve(**kwargs) _assert_workspace_entry_eq(py_we, rs_we)
async def rename_workspace(self, workspace, new_name): wid, workspace = workspace src = f"/{workspace}" dst = f"/{new_name}" expected_status = self.oracle_fs.rename_workspace(src, dst) if expected_status == "ok": await self.user_fs.workspace_rename(wid, EntryName(new_name)) else: with pytest.raises(FSWorkspaceNotFoundError): await self.user_fs.workspace_rename( EntryID.new(), EntryName(new_name)) return wid, new_name
async def test_sync_directory(alice_workspace): _get_or_create_directory_mock = AsyncMock( spec=mock.Mock(), side_effect=lambda *x: "folder_manifest_mock") _sync_directory_content_mock = AsyncMock(spec=mock.Mock()) _clear_directory_mock = AsyncMock(spec=mock.Mock()) entry_id = EntryID.new() path = FsPath("/test") workspace_path = FsPath("/path_in_workspace") with mock.patch("parsec.core.cli.rsync._get_or_create_directory", _get_or_create_directory_mock): with mock.patch("parsec.core.cli.rsync._sync_directory_content", _sync_directory_content_mock): with mock.patch("parsec.core.cli.rsync._clear_directory", _clear_directory_mock): await rsync._sync_directory(entry_id, alice_workspace, path, workspace_path) _get_or_create_directory_mock.assert_called_once_with( entry_id, alice_workspace, path, workspace_path) _sync_directory_content_mock.assert_called_once_with( workspace_path, path, alice_workspace, "folder_manifest_mock") _clear_directory_mock.assert_called_once_with( workspace_path, path, alice_workspace, "folder_manifest_mock") _get_or_create_directory_mock.reset_mock() _sync_directory_content_mock.reset_mock() _clear_directory_mock.reset_mock() with mock.patch("parsec.core.cli.rsync._get_or_create_directory", _get_or_create_directory_mock): with mock.patch("parsec.core.cli.rsync._sync_directory_content", _sync_directory_content_mock): with mock.patch("parsec.core.cli.rsync._clear_directory", _clear_directory_mock): await rsync._sync_directory(None, alice_workspace, path, workspace_path) _get_or_create_directory_mock.assert_called_once_with( None, alice_workspace, path, workspace_path) _sync_directory_content_mock.assert_called_once_with( workspace_path, path, alice_workspace, "folder_manifest_mock") _clear_directory_mock.assert_not_called()
def test_folder_manifest(): from parsec.api.data.manifest import _RsFolderManifest, FolderManifest, _PyFolderManifest assert FolderManifest is _RsFolderManifest def _assert_folder_manifest_eq(py, rs): assert isinstance(py, _PyFolderManifest) assert isinstance(rs, _RsFolderManifest) assert py.author == rs.author assert py.parent == rs.parent 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(), "parent": EntryID.new(), "version": 42, "timestamp": pendulum.now(), "created": pendulum.now(), "updated": pendulum.now(), "children": { EntryName("file1.txt"): EntryID.new() }, } py_wm = _PyFolderManifest(**kwargs) rs_wm = FolderManifest(**kwargs) _assert_folder_manifest_eq(py_wm, rs_wm) kwargs = { "author": DeviceID("a@b"), "id": EntryID.new(), "parent": 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_folder_manifest_eq(py_wm, rs_wm)
def new_placeholder(cls, author: DeviceID, parent: EntryID, timestamp: DateTime) -> "LocalFolderManifest": id = EntryID.new() children = FrozenDict() return cls( base=RemoteFolderManifest( author=author, timestamp=timestamp, id=id, parent=parent, version=0, created=timestamp, updated=timestamp, children=children, ), need_sync=True, updated=timestamp, children=children, local_confinement_points=frozenset(), remote_confinement_points=frozenset(), )
def new_placeholder(cls, author: DeviceID, id: EntryID = None, now: DateTime = None) -> "LocalUserManifest": workspaces = () now = now or pendulum_now() return cls( base=RemoteUserManifest( author=author, timestamp=now, id=id or EntryID.new(), version=0, created=now, updated=now, last_processed_message=0, workspaces=workspaces, ), need_sync=True, updated=now, last_processed_message=0, workspaces=workspaces, )
def new_placeholder(cls, author: DeviceID, id: EntryID = None, now: DateTime = None) -> "LocalWorkspaceManifest": now = now or pendulum_now() children = FrozenDict() return cls( base=RemoteWorkspaceManifest( author=author, timestamp=now, id=id or EntryID.new(), version=0, created=now, updated=now, children=children, ), need_sync=True, updated=now, children=children, local_confinement_points=frozenset(), remote_confinement_points=frozenset(), )
def new_placeholder(cls, author: DeviceID, timestamp: DateTime, id: EntryID = None, speculative: bool = False) -> "LocalUserManifest": return cls( base=RemoteUserManifest( author=author, timestamp=timestamp, id=id or EntryID.new(), version=0, created=timestamp, updated=timestamp, last_processed_message=0, workspaces=(), ), need_sync=True, updated=timestamp, last_processed_message=0, workspaces=(), speculative=speculative, )
def new_placeholder(cls, author: DeviceID, timestamp: DateTime, id: EntryID = None, speculative: bool = False) -> "LocalWorkspaceManifest": children = FrozenDict() return cls( base=RemoteWorkspaceManifest( author=author, timestamp=timestamp, id=id or EntryID.new(), version=0, created=timestamp, updated=timestamp, children=children, ), need_sync=True, updated=timestamp, children=children, local_confinement_points=frozenset(), remote_confinement_points=frozenset(), speculative=speculative, )
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)
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)
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
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"
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_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)
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"))
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_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)
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