async def test_get_path_in_mountpoint(base_mountpoint, alice_user_fs, event_bus): # Populate a bit the fs first... wid = await alice_user_fs.workspace_create(EntryName("mounted_wksp")) wid2 = await alice_user_fs.workspace_create(EntryName("not_mounted_wksp")) workspace1 = alice_user_fs.get_workspace(wid) workspace2 = alice_user_fs.get_workspace(wid2) await workspace1.touch("/bar.txt") await workspace2.touch("/foo.txt") # Now we can start fuse async with mountpoint_manager_factory( alice_user_fs, event_bus, base_mountpoint) as mountpoint_manager: await mountpoint_manager.mount_workspace(wid) bar_path = mountpoint_manager.get_path_in_mountpoint( wid, FsPath("/bar.txt")) assert isinstance(bar_path, PurePath) # Windows uses drives, not base_mountpoint if sys.platform != "win32": expected = base_mountpoint / "mounted_wksp" / "bar.txt" assert str(bar_path) == str(expected.absolute()) assert await trio.Path(bar_path).exists() with pytest.raises(MountpointNotMounted): mountpoint_manager.get_path_in_mountpoint(wid2, FsPath("/foo.txt"))
def test_created_field_modified_by_remote(gen_date, alice, with_local_changes): d1, d2, d3, d4 = [gen_date() for _ in range(4)] w1 = WorkspaceEntry.new(name=EntryName("w1"), timestamp=d2) base = UserManifest( author=alice.device_id, timestamp=d2, id=alice.user_manifest_id, version=1, created=d1, updated=d2, last_processed_message=0, workspaces=(w1,), ) local = LocalUserManifest.from_remote(base) if with_local_changes: w2 = WorkspaceEntry.new(name=EntryName("w1"), timestamp=d3) local = local.evolve( need_sync=True, updated=d3, last_processed_message=1, workspaces=(w1, w2) ) target = base.evolve(created=d4, version=2) expected_merged = local.evolve(base=target) merged = merge_local_user_manifests(local, target) # Remote always control the value of the create field assert merged == expected_merged
async def test_workspace_button_files(qtbot, workspace_fs, core_config, alice_user_info): switch_language(core_config, "en") roles = {alice_user_info.user_id: (WorkspaceRole.OWNER, alice_user_info)} w = WorkspaceButton.create( workspace_name=EntryName("Workspace"), workspace_fs=workspace_fs, users_roles=roles, is_mounted=True, files=[ EntryName("File1.txt"), EntryName("File2.txt"), EntryName("Dir1") ], ) qtbot.add_widget(w) w.show() assert w.widget_empty.isVisible() is False assert w.widget_files.isVisible() is True assert w.label_owner.isVisible() is True assert w.label_shared.isVisible() is False assert w.name == EntryName("Workspace") assert w.file1_name.text() == "File1.txt" assert w.file2_name.text() == "File2.txt" assert w.file3_name.text() == "Dir1" assert w.file4_name.text() == ""
async def test_rename_workspace(initial_user_manifest_state, alice_user_fs, alice): with freeze_time("2000-01-02"): wid = await alice_user_fs.workspace_create(EntryName("w1")) with freeze_time("2000-01-03"): await alice_user_fs.workspace_rename(wid, EntryName("w2")) um = alice_user_fs.get_user_manifest() expected_base_um = initial_user_manifest_state.get_user_manifest_v1_for_backend( alice) expected_um = LocalUserManifest( base=expected_base_um, need_sync=True, updated=datetime(2000, 1, 3), last_processed_message=expected_base_um.last_processed_message, workspaces=(WorkspaceEntry( name=EntryName("w2"), id=wid, key=KEY, encryption_revision=1, encrypted_on=datetime(2000, 1, 2), role_cached_on=datetime(2000, 1, 2), role=WorkspaceRole.OWNER, ), ), speculative=False, ) um = _update_user_manifest_key(um) assert um == expected_um
async def test_root_entry_info(alice_workspace_t2, alice_workspace_t4): stat2 = await alice_workspace_t2.transactions.entry_info(FsPath("/")) assert stat2 == { "type": "folder", "id": alice_workspace_t4.transactions.workspace_id, "base_version": 1, "is_placeholder": False, "need_sync": False, "created": datetime(1999, 12, 31), "updated": datetime(1999, 12, 31), "children": [EntryName("foo")], "confinement_point": None, } stat4 = await alice_workspace_t4.transactions.entry_info(FsPath("/")) assert stat4 == { "type": "folder", "id": alice_workspace_t4.transactions.workspace_id, "base_version": 2, "is_placeholder": False, "need_sync": False, "created": datetime(1999, 12, 31), "updated": datetime(2000, 1, 4), "children": [EntryName("files"), EntryName("foo")], "confinement_point": None, }
async def test_sync(running_backend, alice2_user_fs, alice2): with freeze_time("2000-01-02"): wid = await alice2_user_fs.workspace_create(EntryName("w1")) with freeze_time("2000-01-03"): await alice2_user_fs.sync() um = alice2_user_fs.get_user_manifest() expected_base_um = UserManifest( author=alice2.device_id, timestamp=datetime(2000, 1, 3), id=alice2.user_manifest_id, version=2, created=datetime(2000, 1, 1), updated=datetime(2000, 1, 2), last_processed_message=0, workspaces=(WorkspaceEntry( name=EntryName("w1"), id=wid, key=KEY, encryption_revision=1, encrypted_on=datetime(2000, 1, 2), role_cached_on=datetime(2000, 1, 2), role=WorkspaceRole.OWNER, ), ), ) expected_um = LocalUserManifest.from_remote(expected_base_um) um = _update_user_manifest_key(um) assert um == expected_um
def test_merge_local_user_manifest_changes_placeholder(gen_date, alice, speculative_placeholder): d1, d2, d3, d4 = [gen_date() for _ in range(4)] w1 = WorkspaceEntry.new(name=EntryName("w1"), timestamp=d2) w2 = WorkspaceEntry.new(name=EntryName("w2"), timestamp=d2) w3 = WorkspaceEntry.new(name=EntryName("w3"), timestamp=d2) diverged = LocalUserManifest.new_placeholder( alice.device_id, id=alice.user_manifest_id, timestamp=d4, speculative=speculative_placeholder, ).evolve(last_processed_message=30, workspaces=(w1, w3)) target = UserManifest( author=alice.device_id, timestamp=d2, id=alice.user_manifest_id, version=3, created=d1, updated=d3, last_processed_message=20, workspaces=(w1, w2), ) expected_merged = LocalUserManifest( base=target, updated=d4, last_processed_message=30, workspaces=(w1, w2, w3), need_sync=True, speculative=False, ) merged = merge_local_user_manifests(diverged, target) assert merged == expected_merged
async def test_workspace_button_owned_by(qtbot, workspace_fs, core_config, bob, alice_user_info, bob_user_info): switch_language(core_config, "en") roles = { bob.user_id: (WorkspaceRole.OWNER, bob_user_info), alice_user_info.user_id: (WorkspaceRole.READER, alice_user_info), } w = WorkspaceButton.create( workspace_name=EntryName("Workspace"), workspace_fs=workspace_fs, users_roles=roles, is_mounted=True, files=[], ) qtbot.add_widget(w) w.show() assert w.widget_empty.isVisible() is True assert w.widget_files.isVisible() is False assert w.label_owner.isVisible() is False assert w.label_shared.isVisible() is True assert w.name == EntryName("Workspace") assert w.label_title.text().startswith("Workspace") assert w.label_title.toolTip() == "Workspace (owned by Boby McBobFace)" assert w.label_role.text() == _("TEXT_WORKSPACE_ROLE_READER")
async def test_manifest_no_access(running_backend, alice_user_fs, bob_user_fs): wid = await create_shared_workspace(EntryName("w"), alice_user_fs, bob_user_fs) alice_w = alice_user_fs.get_workspace(wid) bob_w = bob_user_fs.get_workspace(wid) await alice_w.touch("/foo.txt") await alice_w.sync() await bob_w.sync() # Load workspace manifest info = await bob_w.path_info("/") assert list(info["children"]) == [EntryName("foo.txt")] # Remove access to bob await alice_user_fs.workspace_share(wid, bob_user_fs.device.user_id, None) # No read access with pytest.raises(FSWorkspaceNoAccess) as exc: await bob_w.path_info("/foo.txt") assert str(exc.value) == "Cannot load manifest: no read access" # Touch a file locally await bob_w.touch("/bar.txt") bar_id = await bob_w.path_id("/bar.txt") # No write access with pytest.raises(FSWorkspaceNoAccess) as exc: await bob_w.sync_by_id(bar_id) assert str(exc.value) == "Cannot upload manifest: no write access"
async def test_show_inconsistent_dir( aqtbot, autoclose_dialog, files_widget_testbed, running_backend ): tb = files_widget_testbed # Create a new workspace and make user GUI knows about it await tb.logged_gui.test_switch_to_workspaces_widget() await create_inconsistent_workspace(user_fs=tb.user_fs, name=EntryName("wksp2")) # Now wait for GUI to take it into account def _workspace_available(): assert tb.logged_gui.test_get_workspaces_widget().layout_workspaces.count() == 2 await aqtbot.wait_until(_workspace_available) # Jump into the workspace, should be fine await tb.logged_gui.test_switch_to_files_widget(workspace_name=EntryName("wksp2")) await tb.check_files_view( workspace_name=EntryName("wksp2"), path="/", expected_entries=["rep/"] ) # Now go into the folder containing the `newfail.txt` inconsistent file await tb.cd("rep") await tb.check_files_view( workspace_name=EntryName("wksp2"), path="/rep", expected_entries=["foo.txt", "newfail.txt!"] )
async def test_share_workspace_then_rename_it(running_backend, alice_user_fs, bob_user_fs, alice, bob): # Share a workspace between Alice and Bob with freeze_time("2000-01-02"): wid = await alice_user_fs.workspace_create(EntryName("w")) await alice_user_fs.workspace_share(wid, bob.user_id, WorkspaceRole.MANAGER) with freeze_time("2000-01-03"): await bob_user_fs.process_last_messages() # Now Bob and alice both rename the workpsace for there own taste await bob_user_fs.workspace_rename(wid, EntryName("from_alice")) await alice_user_fs.workspace_rename(wid, EntryName("to_bob")) await bob_user_fs.sync() await alice_user_fs.sync() # This should have not changed the workspace in any way bw = bob_user_fs.get_workspace(wid) aw = alice_user_fs.get_workspace(wid) await bw.touch("/ping_bob.txt") await aw.mkdir("/ping_alice") await bw.sync() await aw.sync() await bw.sync() aw_stat = await aw.path_info("/") bw_stat = await bw.path_info("/") assert aw_stat == bw_stat assert aw_stat["id"] == wid
async def test_concurrent_devices_agree_on_workspace_manifest( running_backend, user_fs_factory, data_base_dir, initialize_local_user_manifest, alice, alice2): await initialize_local_user_manifest(data_base_dir, alice, initial_user_manifest="v1") await initialize_local_user_manifest(data_base_dir, alice2, initial_user_manifest="v1") async with user_fs_factory(alice) as alice_user_fs: async with user_fs_factory(alice2) as alice2_user_fs: with freeze_time("2000-01-01"): wksp_id = await alice_user_fs.workspace_create( EntryName("wksp")) # Sync user manifest (containing the workspace entry), but # not the corresponding workspace manifest ! with freeze_time("2000-01-02"): await alice_user_fs.sync() # Retrieve the user manifest but not the workpace manifest, Alice2 hence has a speculative workspace manifest with freeze_time("2000-01-03"): await alice2_user_fs.sync() # Now workspace diverge between devices alice_wksp = alice_user_fs.get_workspace(wksp_id) alice2_wksp = alice2_user_fs.get_workspace(wksp_id) with freeze_time("2000-01-04"): await alice_wksp.mkdir("/from_alice") with freeze_time("2000-01-05"): await alice2_wksp.mkdir("/from_alice2") # Sync user_fs2 first to ensure created_on field is # kept even if further syncs have an earlier value with freeze_time("2000-01-06"): await alice2_wksp.sync() with freeze_time("2000-01-07"): await alice_wksp.sync() with freeze_time("2000-01-08"): await alice2_wksp.sync() # Now, both user fs should have the same view on workspace expected_alice_wksp_stat = { "id": wksp_id, "base_version": 3, "created": datetime(2000, 1, 1), "updated": datetime(2000, 1, 7), "is_placeholder": False, "need_sync": False, "type": "folder", "children": [EntryName("from_alice"), EntryName("from_alice2")], "confinement_point": None, } alice_wksp_stat = await alice_wksp.path_info("/") alice2_wksp_stat = await alice2_wksp.path_info("/") assert alice_wksp_stat == expected_alice_wksp_stat assert alice2_wksp_stat == expected_alice_wksp_stat
def test_entry_name_normalization(): nfc_str = normalize("NFC", "àáâäæãåāçćčèéêëēėęîïíīįìłñńôöòóœøōõßśšûüùúūÿžźż") nfd_str = normalize("NFD", nfc_str) assert nfc_str != nfd_str assert EntryName(nfd_str).str == nfc_str assert EntryName(nfc_str).str == nfc_str assert EntryName(nfc_str + nfd_str).str == nfc_str + nfc_str
def _new_workspace_listed(): assert w_w.layout_workspaces.count() == 2 wk_button1 = w_w.layout_workspaces.itemAt(0).widget() wk_button2 = w_w.layout_workspaces.itemAt(1).widget() assert isinstance(wk_button1, WorkspaceButton) assert isinstance(wk_button2, WorkspaceButton) assert wk_button1.name in [EntryName("Workspace1"), EntryName("Workspace2")] assert wk_button2.name in [EntryName("Workspace1"), EntryName("Workspace2")] assert not w_w.filter_remove_button.isVisible()
async def _bootstrap(user_fs, mountpoint_manager): nonlocal x_path, y_path xid = await user_fs.workspace_create(EntryName("x")) xworkspace = user_fs.get_workspace(xid) await xworkspace.touch("/foo.txt") yid = await user_fs.workspace_create(EntryName("y")) x_path = await mountpoint_manager.mount_workspace(xid) y_path = await mountpoint_manager.mount_workspace(yid) print(x_path, y_path)
async def test_autosync_placeholder_workspace_manifest( frozen_clock, running_backend, backend_data_binder, event_bus_factory, core_config, coolorg, alice, alice2, ): # Workspace created before user manifest placeholder sync await backend_data_binder.bind_organization( coolorg, alice, initial_user_manifest="not_synced") # Don't use `core_factory` fixture given it whole point is to waits for # monitors to be idle before returning the core async with logged_core_factory( core_config, alice, event_bus=event_bus_factory()) as alice_core: with alice_core.event_bus.listen() as spy: w1id = await alice_core.user_fs.workspace_create(EntryName("w1")) # Wait for the sync monitor to sync the new workspace await frozen_clock.sleep_with_autojump(60) await spy.wait_multiple_with_timeout( [ (CoreEvent.FS_ENTRY_SYNCED, { "id": alice.user_manifest_id }), (CoreEvent.FS_ENTRY_SYNCED, { "workspace_id": w1id, "id": w1id }), ], in_order=False, ) # Workspace created on a synced user manifest await backend_data_binder.bind_device(alice2) async with logged_core_factory( core_config, alice2, event_bus=event_bus_factory()) as alice2_core: # Workspace created before user manifest placeholder sync with alice2_core.event_bus.listen() as spy: w2id = await alice2_core.user_fs.workspace_create(EntryName("w2")) await frozen_clock.sleep_with_autojump(60) await spy.wait_multiple_with_timeout( [ (CoreEvent.FS_ENTRY_SYNCED, { "id": alice2.user_manifest_id }), (CoreEvent.FS_ENTRY_SYNCED, { "workspace_id": w2id, "id": w2id }), ], in_order=False, )
async def test_unshare_ok(running_backend, alice_user_fs, bob_user_fs, alice, bob): # Share a workspace... with freeze_time("2000-01-02"): wid = await alice_user_fs.workspace_create(EntryName("w1")) await alice_user_fs.workspace_share(wid, bob.user_id, WorkspaceRole.OWNER) await bob_user_fs.process_last_messages() # ...and unshare it await bob_user_fs.workspace_share(wid, alice.user_id, None) with alice_user_fs.event_bus.listen() as spy: with freeze_time("2000-01-03"): await alice_user_fs.process_last_messages() new_events = [] for event in spy.events: if event.event == CoreEvent.SHARING_UPDATED: event.kwargs["new_entry"] = event.kwargs["new_entry"].evolve( key=KEY) event.kwargs["previous_entry"] = event.kwargs[ "previous_entry"].evolve(key=KEY) new_events.append(event) spy.events = new_events spy.assert_event_occured( CoreEvent.SHARING_UPDATED, { "new_entry": WorkspaceEntry( name=EntryName("w1"), id=wid, key=KEY, encryption_revision=1, encrypted_on=datetime(2000, 1, 2), role_cached_on=datetime(2000, 1, 3), role=None, ), "previous_entry": WorkspaceEntry( name=EntryName("w1"), id=wid, key=KEY, encryption_revision=1, encrypted_on=datetime(2000, 1, 2), role_cached_on=datetime(2000, 1, 2), role=WorkspaceRole.OWNER, ), }, ) aum = alice_user_fs.get_user_manifest() aw = aum.workspaces[0] assert not aw.role
async def test_sharing_event_on_sync_if_same_role(running_backend, alice_user_fs, alice2_user_fs, bob_user_fs, alice, bob): # Share a workspace, alice2 knows about it with freeze_time("2000-01-02"): wid = await create_shared_workspace(EntryName("w"), bob_user_fs, alice_user_fs, alice2_user_fs) expected_entry_v1 = WorkspaceEntry( name=EntryName("w"), id=wid, key=KEY, encryption_revision=1, encrypted_on=datetime(2000, 1, 2), role_cached_on=datetime(2000, 1, 2), role=WorkspaceRole.MANAGER, ) # Then change alice's role... await bob_user_fs.workspace_share(wid, alice.user_id, WorkspaceRole.OWNER) with freeze_time("2000-01-03"): await alice_user_fs.process_last_messages() await alice_user_fs.sync() # ...and give back alice the same role await bob_user_fs.workspace_share(wid, alice.user_id, WorkspaceRole.MANAGER) with freeze_time("2000-01-04"): await alice_user_fs.process_last_messages() expected_entry_v3 = expected_entry_v1.evolve( role_cached_on=datetime(2000, 1, 4)) await alice_user_fs.sync() # A single sharing event should be triggered with alice2_user_fs.event_bus.listen() as spy: await alice2_user_fs.sync() new_events = [] for event in spy.events: if event.event == CoreEvent.SHARING_UPDATED: event.kwargs["new_entry"] = event.kwargs["new_entry"].evolve( key=KEY) event.kwargs["previous_entry"] = event.kwargs[ "previous_entry"].evolve(key=KEY) new_events.append(event) spy.events = new_events spy.assert_event_occured( CoreEvent.SHARING_UPDATED, { "new_entry": expected_entry_v3, "previous_entry": expected_entry_v1 }, )
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_create_workspace_same_name(alice_user_fs): with freeze_time("2000-01-02"): w1id = await alice_user_fs.workspace_create(EntryName("w")) with freeze_time("2000-01-03"): w2id = await alice_user_fs.workspace_create(EntryName("w")) um = alice_user_fs.get_user_manifest() assert um.updated == datetime(2000, 1, 3) assert len(um.workspaces) == 2 assert sorted((x.id, x.name) for x in um.workspaces) == sorted([(w1id, EntryName("w")), (w2id, EntryName("w"))])
def test_entry_name(): from parsec.api.data.entry import _RsEntryName, EntryName, _PyEntryName assert EntryName is _RsEntryName py_en = _PyEntryName("entry_name") rs_en = EntryName("entry_name") assert str(py_en) == str(rs_en) assert repr(py_en) == repr(rs_en) assert hash(py_en) == hash(rs_en) assert EntryName("a") == EntryName("a") assert EntryName("a") != EntryName("b")
def _workspace_filtered(): assert w_w.isVisible() assert w_w.layout_workspaces.count() == 2 wk_button_1 = w_w.layout_workspaces.itemAt(0).widget() wk_button_2 = w_w.layout_workspaces.itemAt(1).widget() assert isinstance(wk_button_1, WorkspaceButton) assert isinstance(wk_button_2, WorkspaceButton) assert wk_button_1.name in [EntryName("Workspace1"), EntryName("Workspace2")] assert wk_button_2.name in [EntryName("Workspace1"), EntryName("Workspace2")] assert w_w.filter_remove_button.isVisible() assert w_w.filter_label.text() == "Common workspaces with {}".format( alice.short_user_display )
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 winify_entry_name(name: EntryName) -> str: name = name.str prefix, *suffixes = name.split(".", 1) if prefix in _WIN32_RES_NAMES: full_suffix = f".{'.'.join(suffixes)}" if suffixes else "" name = f"{prefix[:-1]}~{ord(prefix[-1]):02x}{full_suffix}" else: for reserved in _WIN32_RES_CHARS: name = name.replace(reserved, f"~{ord(reserved):02x}") if name[-1] in (".", " "): name = f"{name[:-1]}~{ord(name[-1]):02x}" return name
async def test_file_history(aqtbot, running_backend, logged_gui, autoclose_dialog, catch_file_history_widget): core = logged_gui.test_get_core() wid = await core.user_fs.workspace_create(EntryName("wksp1")) wfs = core.user_fs.get_workspace(wid) w_w = logged_gui.test_get_workspaces_widget() def _workspace_available(): assert w_w.layout_workspaces.count() == 1 await aqtbot.wait_until(_workspace_available) f_w = await logged_gui.test_switch_to_files_widget(EntryName("wksp1")) # Add an entry to the workspace await wfs.touch("/file.txt") await wfs.sync() await wfs.write_bytes("/file.txt", data=b"v2") await wfs.sync() def _entry_available(): assert f_w.table_files.rowCount() == 2 await aqtbot.wait_until(_entry_available) # First select the entry... f_w.table_files.setRangeSelected( QtWidgets.QTableWidgetSelectionRange(1, 0, 1, 0), True) def _entry_selected(): assert f_w.table_files.selectedItems() await aqtbot.wait_until(_entry_selected) # ...then ask for history f_w.table_files.show_history_clicked.emit() hf_w = await catch_file_history_widget() def _history_displayed(): assert hf_w.isVisible() assert hf_w.layout_history.count() == 1 hb2_w = hf_w.layout_history.itemAt(0).widget() assert hb2_w.label_user.text() == "Boby McBobFace" assert hb2_w.label_version.text() == "2" await aqtbot.wait_until(_history_displayed)
async def test_outsider_profil_limit( aqtbot, running_backend, adam, core_config, gui_factory, alice_user_fs ): wid = await alice_user_fs.workspace_create(EntryName("workspace1")) await alice_user_fs.workspace_share(wid, adam.user_id, WorkspaceRole.READER) await alice_user_fs.process_last_messages() await alice_user_fs.sync() gui = await gui_factory() await gui.test_switch_to_logged_in(adam) w_w = await gui.test_switch_to_workspaces_widget() def _workspace_button_shown(): layout_workspace = w_w.layout_workspaces.itemAt(0) assert layout_workspace is not None workspace_button = layout_workspace.widget() assert not isinstance(workspace_button, QtWidgets.QLabel) await aqtbot.wait_until(_workspace_button_shown) layout_workspace = w_w.layout_workspaces.itemAt(0) workspace_button = layout_workspace.widget() assert workspace_button.button_share.isVisible() is False assert w_w.button_add_workspace.isVisible() is False c_w = gui.test_get_central_widget() assert c_w.menu.button_users.isVisible() is False
async def testbed(running_backend, alice_user_fs, alice, bob): with freeze_time("2000-01-01"): wid = await alice_user_fs.workspace_create(EntryName("w1")) workspace = alice_user_fs.get_workspace(wid) await workspace.sync() local_manifest = await workspace.local_storage.get_manifest(wid) with freeze_time("2000-01-03"): await alice_user_fs.workspace_share(wid, bob.user_id, WorkspaceRole.MANAGER) class TestBed: def __init__(self): self._next_version = 2 self.defaults = { "local_manifest": local_manifest, "blob": None, "signed_author": alice.device_id, "backend_author": alice.device_id, "signed_timestamp": datetime(2000, 1, 2), "backend_timestamp": datetime(2000, 1, 2), "author_signkey": alice.signing_key, "key": workspace.get_workspace_entry().key, } async def run(self, exc_msg, **kwargs): options = {**self.defaults, **kwargs} if options["blob"] is None: to_sync_um = options["local_manifest"].to_remote( author=options["signed_author"], timestamp=options["signed_timestamp"]) options["blob"] = to_sync_um.dump_sign_and_encrypt( author_signkey=options["author_signkey"], key=options["key"]) await running_backend.backend.vlob.update( organization_id=alice.organization_id, author=options["backend_author"], encryption_revision=1, vlob_id=VlobID(wid.uuid), version=self._next_version, timestamp=options["backend_timestamp"], blob=options["blob"], ) self._next_version += 1 # This should trigger FSError with pytest.raises(FSError) as exc: await workspace.sync() assert str(exc.value) == exc_msg # Also test timestamped workspace # Note: oxidation doesn't implement WorkspaceStorageTimestamped if not IS_OXIDIZED: with pytest.raises(FSError) as exc: await workspace.to_timestamped(options["backend_timestamp"] ) assert str(exc.value) == exc_msg return TestBed()
async def check_files_view(self, path, expected_entries, workspace_name=EntryName("wksp1")): expected_table_files = [] # Parent dir line brings to workspaces list if we looking into workspace's root if path == "/": expected_table_files.append(("Workspaces list", FileType.ParentWorkspace)) else: expected_table_files.append(("Parent folder", FileType.ParentFolder)) # Table contains one line per files + the "parent dir" line for name in expected_entries: if name.endswith("/"): expected_table_files.append((name[:-1], FileType.Folder)) elif name.endswith("!"): expected_table_files.append((name[:-1], FileType.Inconsistency)) else: expected_table_files.append((name, FileType.File)) def _view_ok(): # Check title (top part of the GUI) assert c_w.navigation_bar_widget.workspace_name == workspace_name assert str(c_w.navigation_bar_widget.get_current_path()) == path # Now check actual files view assert f_w.workspace_fs.get_workspace_name() == workspace_name assert f_w.table_files.rowCount() == len(expected_table_files) for i, (name, type) in enumerate(expected_table_files): assert f_w.table_files.item(i, 1).text() == name for j in range(5): assert f_w.table_files.item(i, j).data(TYPE_DATA_INDEX) == type await aqtbot.wait_until(_view_ok)
async def test_realm_notif_maintenance(running_backend, alice_backend_conn, alice2_user_fs): wid = await alice2_user_fs.workspace_create(EntryName("foo")) await alice2_user_fs.sync() with alice_backend_conn.event_bus.listen() as spy: # Start maintenance job = await alice2_user_fs.workspace_start_reencryption(wid) await spy.wait_multiple_with_timeout( [ ( CoreEvent.BACKEND_REALM_MAINTENANCE_STARTED, {"realm_id": wid, "encryption_revision": 2}, ), # Receive the message containing the new key and encryption revision CoreEvent.BACKEND_MESSAGE_RECEIVED, ] ) with alice_backend_conn.event_bus.listen() as spy: # Finish maintenance total, done = await job.do_one_batch() assert total == done await spy.wait_with_timeout( CoreEvent.BACKEND_REALM_MAINTENANCE_FINISHED, {"realm_id": wid, "encryption_revision": 2}, )
async def test_block_no_access(running_backend, alice_user_fs, bob_user_fs): wid = await create_shared_workspace(EntryName("w"), alice_user_fs, bob_user_fs) alice_w = alice_user_fs.get_workspace(wid) bob_w = bob_user_fs.get_workspace(wid) await alice_w.touch("/foo.txt") await alice_w.write_bytes("/foo.txt", b"foo data") await alice_w.sync() await bob_w.sync() # Ensure file manifest is in cache (but not blocks) await bob_w.path_info("/foo.txt") # Remove access to bob await alice_user_fs.workspace_share(wid, bob_user_fs.device.user_id, None) # Try to access blocks with pytest.raises(FSWorkspaceNoAccess) as exc: await bob_w.read_bytes("/foo.txt") assert str(exc.value) == "Cannot load block: no read access" await bob_w.touch("/bar.txt") bar_id = await bob_w.path_id("/bar.txt") await bob_w.write_bytes("/bar.txt", b"bar data") with pytest.raises(FSWorkspaceNoAccess) as exc: await bob_w.sync_by_id(bar_id) assert str(exc.value) == "Cannot upload block: no write access"