Skip to content

Commit

Permalink
NXDRIVE-2860: Code Coverage - added all rumming test cases from old f…
Browse files Browse the repository at this point in the history
…unctional - 10/01 --1
  • Loading branch information
gitofanindya committed Jan 10, 2024
1 parent 65d81ef commit 326201d
Show file tree
Hide file tree
Showing 22 changed files with 5,029 additions and 0 deletions.
161 changes: 161 additions & 0 deletions tests/functional/test_bulk_remote_changes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
"""
Technical Background: GetChildren API can throw error
due to network issues or server load.
GetChildren API is also called when processing remote events.
Issue: When processing remote event, a error in GetChildren API
(for a folder) call results in drive failing to process the
remaining remote events in the queue.
Fix: Handle the error in GetChildren API gracefully and re-queue
same folder again for another remote scan
Testing: This issue can be testing by simulating network of the API
using a mock framework:
1. Emulate the GetChildren API error by mocking the
Remote.get_fs_children method
2. The mocked method will raise an exception on demand
to simulate the server side / network errors
Note: searching for the following regular expression in log file
will filter the manual test case:
STEP:|VERIFY:|Error:
"""

from logging import getLogger
from time import sleep
from unittest.mock import patch

from nuxeo.utils import version_lt
from requests import ConnectionError

from nxdrive.client.remote_client import Remote
from nxdrive.objects import RemoteFileInfo

from .conftest import TEST_DEFAULT_DELAY, TwoUsersTest

log = getLogger(__name__)


class TestBulkRemoteChanges(TwoUsersTest):
"""
Test Bulk Remote Changes when network error happen in get_children_info()
will simulate network error when required. test_many_changes method will
make server side changes, simulate error for GetChildren API and still
verify if all remote changes are successfully synced.
"""

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")
69 changes: 69 additions & 0 deletions tests/functional/test_collection.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
"""
from contextlib import suppress
import pytest
from .conftest import OneUserTest
class TestCollection(OneUserTest):
@pytest.fixture(autouse=True)
def teardown(self):
yield
with suppress(Exception):
# Happened when the test fails at setup_method()
self.remote_document_client_1.delete(
self.collection["uid"], use_trash=False
)
def test_collection_synchronization(self):
remote = self.remote_1
# Remove synchronization root
remote.unregister_as_root(self.workspace)
# Create a document "Fiiile" in a folder "Test"
folder = self.remote_document_client_1.make_folder("/", "Test")
# Attach a file "abcde.txt" to the document
doc = self.remote_document_client_1.make_file_with_blob(
folder, "abcde.txt", b"abcde"
)
# Create a collection and add the document to it
self.collection = remote.execute(
command="Collection.Create",
name="CollectionA",
description="Test collection",
)
remote.execute(
command="Document.AddToCollection",
collection=self.collection["uid"],
input_obj=f"doc:{doc}",
)
# Register the collection as the synchronization root
remote.register_as_root(self.collection["uid"])
# Sync locally
self.engine_1.start()
self.wait_sync(wait_for_async=True)
# Get a client on the newly synchronized collection
local = self.get_local_client(self.local_nxdrive_folder_1 / "CollectionA")
# Check the attached file is here
assert local.exists("/abcde.txt")
# Attach a file "fghij.txt" to the document
# This should effectively replace the previous file
# since we did not specify another xpath than the main blob.
self.remote_document_client_1.attach_blob(doc, b"fghij", "fghij.txt")
# Sync locally
self.wait_sync(wait_for_async=True)
# Check the new attached file is here, and the previous isn't
assert local.exists("/fghij.txt")
assert not local.exists("/abcde.txt")
"""
Loading

0 comments on commit 326201d

Please sign in to comment.