Beispiel #1
0
def is_version_compatible(
    version_id: str, version: Version, server: str, has_browser_login: bool, /
) -> bool:
    """
    Check Drive <-> server version compatibility.

    Try first the min_all and max_all keys that contain all server versions.
    Fallback on min and max keys that contain only one server version:
    the oldest supported.
    """
    if not (has_browser_login or version_lt(version_id, "4")):
        return False

    # Remove HF and SNAPSHOT
    base_server = server.split("-")[0]

    ver_min = (
        version.get("min_all", {}).get(base_server) or version.get("min", "")
    ).upper()
    if not ver_min or version_lt(server, ver_min):
        return False

    ver_max = (
        version.get("max_all", {}).get(base_server) or version.get("max", "")
    ).upper()

    return not (ver_max and version_lt(ver_max, server))
    def test_synchronize_remote_deletion(self):
        """Test that deleting remote documents is impacted client side

        Use cases:
          - Remotely delete a regular folder
              => Folder should be locally deleted
          - Remotely restore folder from the trash
              => Folder should be locally re-created
          - Remotely delete a synchronization root
              => Synchronization root should be locally deleted
          - Remotely restore synchronization root from the trash
              => Synchronization root should be locally re-created

        See TestIntegrationSecurityUpdates.test_synchronize_denying_read_access
        as the same uses cases are tested
        """
        # Bind the server and root workspace
        self.engine_1.start()
        # Get local and remote clients
        local = self.local_1
        remote = self.remote_document_client_1
        remote_admin = self.root_remote

        # Create documents in the remote root workspace
        # then synchronize
        folder_id = remote.make_folder("/", "Test folder")
        file_id = remote.make_file("/Test folder", "joe.txt", content=b"Some content")

        self.wait_sync(wait_for_async=True)
        assert local.exists("/Test folder")
        assert local.exists("/Test folder/joe.txt")

        # Delete remote folder then synchronize
        remote.delete("/Test folder")
        self.wait_sync(wait_for_async=True)
        assert not local.exists("/Test folder")

        # Restore folder from trash then synchronize
        remote.undelete(folder_id)
        if version_lt(remote.client.server_version, "10.2"):
            remote.undelete(file_id)
        self.wait_sync(wait_for_async=True)
        assert local.exists("/Test folder")
        assert local.exists("/Test folder/joe.txt")

        # Delete sync root then synchronize
        remote_admin.delete(self.workspace)
        self.wait_sync(wait_for_async=True)
        assert not local.exists("/")

        # Restore sync root from trash then synchronize
        remote_admin.undelete(self.workspace)
        if version_lt(remote.client.server_version, "10.2"):
            remote_admin.undelete(folder_id)
            remote_admin.undelete(file_id)
        self.wait_sync(wait_for_async=True)
        assert local.exists("/")
        assert local.exists("/Test folder")
        assert local.exists("/Test folder/joe.txt")
Beispiel #3
0
def test_comments_with_params(server):
    """Test GET parameters that allow to retrieve partial list of comments."""
    if version_lt(server.client.server_version, "10.3"):
        pytest.skip("Nuxeo 10.3 minimum")

    doc = Document(name=WORKSPACE_NAME,
                   type="File",
                   properties={"dc:title": "bar.txt"})
    doc = server.documents.create(doc, parent_path=WORKSPACE_ROOT)
    try:
        # Create a bunch of comments for that document
        for idx in range(8):
            doc.comment(f"This is my comment n° {idx}")

        # Get maximum comments with default values
        comments = doc.comments()
        assert len(comments) == 8

        # Page 1
        comments = doc.comments(pageSize=5, currentPageIndex=0)
        assert len(comments) == 5

        # Page 2
        comments = doc.comments(pageSize=5, currentPageIndex=1)
        assert len(comments) == 3

        # Page 3
        comments = doc.comments(pageSize=5, currentPageIndex=2)
        assert len(comments) == 0
    finally:
        doc.delete()
Beispiel #4
0
def test_document_comment(server):
    """Test the Document.comment() method, it is a simple helper."""
    if version_lt(server.client.server_version, "10.3"):
        pytest.skip("Nuxeo 10.3 minimum")

    doc = Document(name=WORKSPACE_NAME,
                   type="File",
                   properties={"dc:title": "bar.txt"})
    doc = server.documents.create(doc, parent_path=WORKSPACE_ROOT)
    try:
        # At first, the document has no comment
        assert not doc.comments()

        # Create a comment for that document
        doc.comment("This is my super comment")

        # There is now 1 comment
        comments = doc.comments()
        assert len(comments) == 1
        assert comments[0].text == "This is my super comment"

        # Delete the comment
        server.comments.delete(comments[0].uid)
    finally:
        doc.delete()
Beispiel #5
0
    def _get_update_status(self) -> None:
        """ Retrieve available versions and find a possible candidate. """

        try:
            # Fetch all available versions
            self._fetch_versions()
        except UpdateError:
            status, version = UPDATE_STATUS_UNAVAILABLE_SITE, None
        else:
            # Special case to test the auto-updater without the need for an account
            if os.getenv("FORCE_USE_LATEST_VERSION", "0") == "1":
                version = max(self.versions)
                if version_lt(self.manager.version, version):
                    log.info(
                        f"FORCE_USE_LATEST_VERSION is set, upgrading to {version!r}"
                    )
                    self._set_status(UPDATE_STATUS_UPDATE_AVAILABLE,
                                     version=version)
                else:
                    log.info(
                        f"FORCE_USE_LATEST_VERSION is set, but {version!r} not newer than the current version"
                    )
                return

            login_type = Login.NONE
            for engine in self.manager.engines.copy().values():
                url = engine.server_url
                login_type |= self.manager.get_server_login_type(url,
                                                                 _raise=False)

            channel = self.manager.get_update_channel()
            log.info(
                f"Getting update status for version {self.manager.version!r}"
                f" (channel={channel}, desired client_version={Options.client_version!r})"
                f" on server {self.server_ver}")
            status, version = get_update_status(
                self.manager.version,
                self.versions,
                channel,
                self.server_ver,
                login_type,
            )
            log.debug(f"Guessed status {status!r} and version {version!r}.")

        # Check the digest is available for that version on that OS
        if version:
            info = self.versions.get(version, {})
            checksums = info.get("checksum", {})
            checksum = checksums.get(self.ext, "").lower()
            if not checksum:
                log.warning(
                    f"There is no downloadable file for the version {version!r} on that OS."
                )
                return

        if status and version and self.enable and self.can_update:
            self._set_status(status, version=version)
        elif status:
            self.status = status
            self.version = ""
Beispiel #6
0
def test_add_permission(server):
    if version_lt(server.client.server_version, "10.10"):
        pytest.skip("Nuxeo 10.10 minimum")

    with patch.object(nuxeo.constants, "CHECK_PARAMS",
                      new=True), Doc(server) as doc:
        # NXPY-84: here we should not fail with KeyError: 'list' in check_params()
        doc.add_permission({
            "permission": "ReadWrite",
            "users": ["Administrator"]
        })
Beispiel #7
0
def validate_client_version(value: str) -> str:
    """The minimum version which implements the Centralized channel is 4.2.0,
    downgrades below this version are not allowed.
    """
    from nuxeo.utils import version_lt

    if not version_lt(value, "4.2.0"):
        return value
    raise ValueError(
        f"Downgrade to version {value!r} is not possible. It must be >= '4.2.0'."
    )
Beispiel #8
0
def test_reply(server):
    if version_lt(server.client.server_version, "10.3"):
        pytest.skip("Nuxeo 10.3 minimum")

    doc = server.documents.create(document, parent_path=WORKSPACE_ROOT)
    try:
        # Create a comment for that document
        comment = server.comments.create(doc.uid, "This is my comment")
        assert not comment.has_replies()

        # Add a 1st reply to that comment
        reply1 = comment.reply("This is my reply comment")
        assert isinstance(reply1, Comment)
        assert comment.has_replies()

        # Check the comment has 1 reply (refetch it to ensure data is correct)
        replies = server.comments.get(doc.uid, uid=comment.uid)
        assert isinstance(replies, Comment)
        assert replies.numberOfReplies == 1
        assert replies.numberOfReplies == comment.numberOfReplies
        assert replies.lastReplyDate == reply1.creationDate

        # Add a 2nd reply to that comment
        reply2 = comment.reply("This is another reply, yeah! ᕦ(ò_óˇ)ᕤ")
        assert isinstance(reply2, Comment)
        assert comment.numberOfReplies == 2
        assert not reply2.has_replies()

        # And a reply to that 2nd reply
        last_reply = reply2.reply(
            "And a reply of the 2nd reply with \N{SNOWMAN}, boom!")
        assert isinstance(last_reply, Comment)
        assert reply2.has_replies()

        # Check the comment has 2 direct replies
        replies = server.comments.get(doc.uid, uid=comment.uid)
        assert replies.numberOfReplies == 2
        assert replies.lastReplyDate == reply2.creationDate

        # Check the 2nd reply has 1 reply
        replies = server.comments.get(doc.uid, uid=reply2.uid)
        assert replies.numberOfReplies == 1
        assert replies.lastReplyDate == last_reply.creationDate

        # Test partial list
        assert len(comment.replies()) == 2
        assert len(comment.replies(pageSize=1, currentPageIndex=0)) == 1
        assert len(comment.replies(pageSize=1, currentPageIndex=1)) == 1
        assert len(comment.replies(pageSize=1, currentPageIndex=2)) == 0
    finally:
        doc.delete()
Beispiel #9
0
def test_locking(server):
    with Doc(server) as doc:
        assert not doc.fetch_lock_status()
        assert not doc.is_locked()

        doc.lock()
        status = doc.fetch_lock_status()
        assert status["lockOwner"] == "Administrator"
        assert "lockCreated" in status
        assert doc.is_locked()

        # Double locking with the same user should work if the server has NXP-24359
        if not version_lt(server.client.server_version, "11.1"):
            doc.lock()

        doc.unlock()
        assert not doc.is_locked()
Beispiel #10
0
 def force_downgrade(self) -> None:
     try:
         # Fetch all available versions
         self._fetch_versions()
     except UpdateError:
         self._set_status(UPDATE_STATUS_UNAVAILABLE_SITE)
     else:
         versions = {
             version: info
             for version, info in self.versions.items()
             if info.get("type", "").lower() in (
                 self.manager.get_update_channel(),
                 "release") and version_lt(version, "4")
         }
         if versions:
             version = max(versions.keys())
             self._set_status(UPDATE_STATUS_INCOMPATIBLE_SERVER,
                              version=version)
     self.serverIncompatible.emit()
Beispiel #11
0
def test_crud(server):
    if version_lt(server.client.server_version, "10.3"):
        pytest.skip("Nuxeo 10.3 minimum")

    doc = server.documents.create(document, parent_path=WORKSPACE_ROOT)
    try:
        # At first, the document has no comment
        assert not doc.comments()

        # Create a comment for that document
        comment = server.comments.create(doc.uid, "This is my comment")
        assert isinstance(comment, Comment)

        # Check we can retrieve the comment with its ID
        assert server.comments.get(doc.uid, uid=comment.uid)

        # There is now 1 comment
        comments = doc.comments()
        assert len(comments) == 1
        assert isinstance(comments[0], Comment)
        assert comments[0].text == "This is my comment"

        # Update that comment
        comment.text = "Comment modified"
        comment.save()

        # Check the text has changed
        comments = doc.comments()
        assert isinstance(comments[0], Comment)
        assert comments[0].text == "Comment modified"
        assert comments[0].modificationDate is not None

        # Delete the comment
        comment.delete()

        # Check there si no comments for the document
        assert not doc.comments()
    finally:
        doc.delete()
Beispiel #12
0
def test_additionnal_params(server):
    if version_lt(server.client.server_version, "10.2"):
        pytest.skip("Nuxeo 10.2 minimum (NXP-21078)")

    func = partial(server.directories.get, "nature")

    # The number of returned entries is configured by the querySizeLimit parameters on the server (50 by default)
    # https://github.com/nuxeo/nuxeo/blob/82d0328/nuxeo-distribution/nuxeo-nxr-server/src/main/resources/templates/common/config/default-directories-bundle.xml#L23
    total = len(func().entries)

    # Get only 10 entries
    assert len(func(pageSize=10).entries) == 10

    # Get all entries
    assert len(func(pageSize=total).entries) == total

    # Get the last page of entries
    page_number, count = divmod(total, 10)
    assert len(func(pageSize=10,
                    currentPageIndex=page_number).entries) == count

    # Set an invalid/unknown parameter does not raise
    assert len(func(pageSizesssssssss=10).entries) == total
Beispiel #13
0
    def test_many_changes(self):
        """
        Objective: The objective is to make a lot of remote changes (including a folder
        modified) and wait for nuxeo-drive to successfully sync even if network error
        happens.

        1. Configure drive and wait for sync
        2. Create 3 folders folder1, folder2 and shared
        3. Create files inside the 3 folders: folder1/file1.txt, folder2/file2.txt,
            shared/readme1.txt, shared/readme2.txt
        4. Wait for 3 folders, 4 files to sync to local PC
        5. Check the 3 folders and 4 files are synced to local PC
        6. Trigger simulation of network error for GetChildren API using the mock
           (2 successive failures)
        7. Do the following changes in DM side in same order:
            I.   Create 'folder1/sample1.txt'
            II.  Delete 'shared' folder, and immediately restore 'shared' folder
            IV.  Restore 'shared/readme1.txt'
            V.   Create 'shared/readme3.txt'
            VI.  Create 'folder2/sample2.txt'
        8. Wait for remote changes to sync for unaffected folders folder1 and folder2
        9. Check that folder1/sample1.txt, folder2/sample2.txt are synced to local PC
        10. Sleep for two remote scan attempts (to compensate for two network failures)
        11. Check if two files 'shared/readme1.txt' and 'shared/readme3.txt' are synced
        to local PC.
        """
        local = self.local_1
        remote = self.remote_document_client_1
        network_error = 2

        self.engine_1.start()
        self.wait_sync(wait_for_async=True)

        # create some folders on the server
        folder1 = remote.make_folder(self.workspace, "folder1")
        folder2 = remote.make_folder(self.workspace, "folder2")
        shared = remote.make_folder(self.workspace, "shared")

        remote.make_file(folder1,
                         "file1.txt",
                         content=b"This is a sample file1")
        remote.make_file(folder2,
                         "file2.txt",
                         content=b"This is a sample file2")
        readme1 = remote.make_file(shared,
                                   "readme1.txt",
                                   content=b"This is a readme file")
        remote.make_file(shared,
                         "readme2.txt",
                         content=b"This is a readme file")

        self.wait_sync(wait_for_async=True)

        assert local.exists("/folder1")
        assert local.exists("/folder2")
        assert local.exists("/shared")
        assert local.exists("/folder1/file1.txt")
        assert local.exists("/folder2/file2.txt")
        assert local.exists("/shared/readme1.txt")
        assert local.exists("/shared/readme2.txt")

        def get_children_info(self, *args, **kwargs):
            nonlocal network_error
            if network_error > 0:
                network_error -= 1
                # Simulate a network error during the call to NuxeoDrive.GetChildren
                raise ConnectionError(
                    "Network error simulated for NuxeoDrive.GetChildren")
            return Remote.get_fs_children(self.engine_1.remote, *args,
                                          **kwargs)

        def mock_method_factory(original):
            def wrapped_method(data):
                data["canScrollDescendants"] = True
                return original(data)

            return wrapped_method

        with patch.object(remote, "get_children_info",
                          new=get_children_info), patch.object(
                              RemoteFileInfo,
                              "from_dict",
                              wraps=mock_method_factory(
                                  RemoteFileInfo.from_dict),
                          ):
            # Simulate network error for GetChildren API twice
            # This is to ensure Drive will eventually recover even after multiple
            # failures of GetChildren API.
            remote.make_file(folder1,
                             "sample1.txt",
                             content=b"This is a another sample file1")
            self.remote_2.register_as_root(shared)

            # Delete folder 'shared'
            remote.delete(shared)
            self.wait_sync(wait_for_async=True)

            # Restore folder 'shared' from trash
            remote.undelete(shared)
            if version_lt(remote.client.server_version, "10.2"):
                remote.undelete(readme1)
            self.wait_sync(wait_for_async=True)

            remote.make_file(shared,
                             "readme3.txt",
                             content=b"This is a another shared file")
            remote.make_file(folder2,
                             "sample2.txt",
                             content=b"This is a another sample file2")

            self.wait_sync(wait_for_async=True)
            assert local.exists("/folder2/sample2.txt")
            assert local.exists("/folder1/sample1.txt")

            # Although sync failed for one folder, GetChangeSummary will return
            # zero event in successive calls.  We need to wait two remote scans,
            # so sleep for TEST_DEFAULT_DELAY * 2
            sleep(TEST_DEFAULT_DELAY * 2)
            assert local.exists("/shared/readme1.txt")
            assert local.exists("/shared/readme3.txt")
 def _get_trash_condition(self) -> str:
     if version_lt(self.client.server_version, "10.2"):
         return "AND ecm:currentLifeCycleState != 'deleted'"
     return "AND ecm:isTrashed = 0"
Beispiel #15
0
def test_version_lt(x, y):
    assert version_lt(x, y)