Skip to content

Commit c58b0ff

Browse files
committed
Fixes on deprecated mocks
1 parent c4360b8 commit c58b0ff

File tree

2 files changed

+105
-82
lines changed

2 files changed

+105
-82
lines changed

services/web/server/tests/integration/test_exporter.py

+10-43
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
1+
import asyncio
2+
13
# pylint:disable=redefined-outer-name,unused-argument,too-many-arguments
24
import cgi
5+
import itertools
36
import json
47
import logging
8+
import operator
59
import sys
6-
import asyncio
710
import tempfile
11+
from collections import deque
812
from contextlib import contextmanager
913
from copy import deepcopy
10-
import operator
11-
import itertools
1214
from pathlib import Path
13-
from typing import Any, Dict, List, Set, Callable, Tuple
14-
from collections import deque
15+
from typing import Any, Callable, Dict, List, Set, Tuple
1516

1617
import aiofiles
1718
import aiohttp
@@ -27,6 +28,8 @@
2728
from simcore_service_webserver.director import setup_director
2829
from simcore_service_webserver.director_v2 import setup_director_v2
2930
from simcore_service_webserver.exporter import setup_exporter
31+
from simcore_service_webserver.exporter.async_hashing import Algorithm, checksum
32+
from simcore_service_webserver.exporter.file_downloader import ParallelDownloader
3033
from simcore_service_webserver.login import setup_login
3134
from simcore_service_webserver.projects import setup_projects
3235
from simcore_service_webserver.resource_manager import setup_resource_manager
@@ -35,11 +38,9 @@
3538
from simcore_service_webserver.security_roles import UserRole
3639
from simcore_service_webserver.session import setup_session
3740
from simcore_service_webserver.socketio import setup_socketio
38-
from simcore_service_webserver.users import setup_users
39-
from simcore_service_webserver.storage_handlers import get_file_download_url
4041
from simcore_service_webserver.storage import setup_storage
41-
from simcore_service_webserver.exporter.file_downloader import ParallelDownloader
42-
from simcore_service_webserver.exporter.async_hashing import Algorithm, checksum
42+
from simcore_service_webserver.storage_handlers import get_file_download_url
43+
from simcore_service_webserver.users import setup_users
4344
from yarl import URL
4445

4546
log = logging.getLogger(__name__)
@@ -177,39 +178,6 @@ def get_exported_projects() -> List[Path]:
177178
return [x for x in exporter_dir.glob("*.osparc")]
178179

179180

180-
@pytest.fixture
181-
async def monkey_patch_asyncio_subporcess(mocker):
182-
# TODO: The below bug is not allowing me to fully test,
183-
# mocking and waiting for an update
184-
# https://bugs.python.org/issue35621
185-
# this issue was patched in 3.8, no need
186-
if sys.version_info.major == 3 and sys.version_info.minor >= 8:
187-
raise RuntimeError(
188-
"Issue no longer present in this version of python, "
189-
"please remote this mock on python >= 3.8"
190-
)
191-
192-
import subprocess
193-
194-
async def create_subprocess_exec(*command, **extra_params):
195-
class MockResponse:
196-
def __init__(self, command, **kwargs):
197-
self.proc = subprocess.Popen(command, **extra_params)
198-
199-
async def communicate(self):
200-
return self.proc.communicate()
201-
202-
@property
203-
def returncode(self):
204-
return self.proc.returncode
205-
206-
mock_response = MockResponse(command, **extra_params)
207-
208-
return mock_response
209-
210-
mocker.patch("asyncio.create_subprocess_exec", side_effect=create_subprocess_exec)
211-
212-
213181
@pytest.fixture(scope="session")
214182
def push_services_to_registry(
215183
docker_registry: str, node_meta_schema: Dict
@@ -417,7 +385,6 @@ async def test_import_export_import_duplicate(
417385
db_engine,
418386
redis_client,
419387
export_version,
420-
monkey_patch_asyncio_subporcess,
421388
simcore_services,
422389
monkey_patch_aiohttp_request_url,
423390
):

services/web/server/tests/unit/isolated/test_exporter_archiving.py

+95-39
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,16 @@
1-
# pylint:disable=unused-argument
2-
# pylint:disable=redefined-outer-name
3-
# pylint:disable=no-name-in-module
1+
# pylint:disable=redefined-outer-name,unused-argument
42

53
import asyncio
64
import hashlib
75
import os
86
import random
97
import secrets
108
import string
11-
import sys
9+
import tempfile
1210
import uuid
1311
from concurrent.futures import ProcessPoolExecutor
1412
from pathlib import Path
15-
from typing import Dict, Iterator, List, Set
13+
from typing import Dict, Iterator, List, Set, Tuple
1614

1715
import pytest
1816
from simcore_service_webserver.exporter.archiving import (
@@ -25,51 +23,35 @@
2523

2624

2725
@pytest.fixture
28-
async def monkey_patch_asyncio_subporcess(loop, mocker):
29-
# TODO: The below bug is not allowing me to fully test,
30-
# mocking and waiting for an update
31-
# https://bugs.python.org/issue35621
32-
# this issue was patched in 3.8, no need
33-
if sys.version_info.major == 3 and sys.version_info.minor >= 8:
34-
raise RuntimeError(
35-
"Issue no longer present in this version of python, "
36-
"please remote this mock on python >= 3.8"
37-
)
38-
39-
import subprocess
40-
41-
async def create_subprocess_exec(*command, **extra_params):
42-
class MockResponse:
43-
def __init__(self, command, **kwargs):
44-
self.proc = subprocess.Popen(command, **extra_params)
45-
46-
async def communicate(self):
47-
return self.proc.communicate()
48-
49-
@property
50-
def returncode(self):
51-
return self.proc.returncode
52-
53-
mock_response = MockResponse(command, **extra_params)
26+
def temp_dir(tmpdir) -> Path:
27+
# cast to Path object
28+
return Path(tmpdir)
5429

55-
return mock_response
5630

57-
mocker.patch("asyncio.create_subprocess_exec", side_effect=create_subprocess_exec)
31+
@pytest.fixture
32+
def temp_dir2() -> Iterator[Path]:
33+
with tempfile.TemporaryDirectory() as temp_dir:
34+
temp_dir_path = Path(temp_dir)
35+
extract_dir_path = temp_dir_path / "extract_dir"
36+
extract_dir_path.mkdir(parents=True, exist_ok=True)
37+
yield extract_dir_path
5838

5939

6040
@pytest.fixture
61-
def temp_dir(tmpdir) -> Path:
62-
# Casts https://docs.pytest.org/en/stable/tmpdir.html#the-tmpdir-fixture to Path
63-
return Path(tmpdir)
41+
def temp_file() -> Iterator[Path]:
42+
file_path = Path("/") / f"tmp/{next(tempfile._get_candidate_names())}"
43+
file_path.write_text("test_data")
44+
yield file_path
45+
file_path.unlink()
6446

6547

6648
@pytest.fixture
67-
def project_uuid() -> str:
49+
def project_uuid():
6850
return str(uuid.uuid4())
6951

7052

7153
@pytest.fixture
72-
def dir_with_random_content() -> Path:
54+
def dir_with_random_content() -> Iterator[Path]:
7355
def random_string(length: int) -> str:
7456
return "".join(secrets.choice(string.ascii_letters) for i in range(length))
7557

@@ -101,7 +83,7 @@ def make_subdirectories_with_content(
10183
)
10284

10385
def get_dirs_and_subdris_in_path(path_to_scan: Path) -> Iterator[Path]:
104-
return [path for path in path_to_scan.rglob("*") if path.is_dir()]
86+
return (path for path in path_to_scan.rglob("*") if path.is_dir())
10587

10688
with tempfile.TemporaryDirectory() as temp_dir:
10789
temp_dir_path = Path(temp_dir)
@@ -154,6 +136,80 @@ def temp_dir_to_compress_with_too_many_targets(temp_dir, project_uuid) -> Path:
154136
return nested_dir
155137

156138

139+
def strip_directory_from_path(input_path: Path, to_strip: Path) -> Path:
140+
_to_strip = f"{str(to_strip)}/"
141+
return Path(str(input_path).replace(_to_strip, ""))
142+
143+
144+
def get_all_files_in_dir(dir_path: Path) -> Set[Path]:
145+
return {
146+
strip_directory_from_path(x, dir_path)
147+
for x in dir_path.rglob("*")
148+
if x.is_file()
149+
}
150+
151+
152+
def _compute_hash(file_path: Path) -> Tuple[Path, str]:
153+
with open(file_path, "rb") as file_to_hash:
154+
file_hash = hashlib.md5()
155+
chunk = file_to_hash.read(8192)
156+
while chunk:
157+
file_hash.update(chunk)
158+
chunk = file_to_hash.read(8192)
159+
160+
return file_path, file_hash.hexdigest()
161+
162+
163+
async def compute_hashes(file_paths: List[Path]) -> Dict[Path, str]:
164+
"""given a list of files computes hashes for the files on a process pool"""
165+
166+
loop = asyncio.get_event_loop()
167+
168+
with ProcessPoolExecutor() as prcess_pool_executor:
169+
tasks = [
170+
loop.run_in_executor(prcess_pool_executor, _compute_hash, file_path)
171+
for file_path in file_paths
172+
]
173+
return {k: v for k, v in await asyncio.gather(*tasks)}
174+
175+
176+
def full_file_path_from_dir_and_subdirs(dir_path: Path) -> List[Path]:
177+
return [x for x in dir_path.rglob("*") if x.is_file()]
178+
179+
180+
async def assert_same_directory_content(
181+
dir_to_compress: Path, output_dir: Path
182+
) -> None:
183+
input_set = get_all_files_in_dir(dir_to_compress)
184+
output_set = get_all_files_in_dir(output_dir)
185+
assert (
186+
input_set == output_set
187+
), f"There following files are missing {input_set - output_set}"
188+
189+
# computing the hashes for dir_to_compress and map in a dict
190+
# with the name starting from the root of the directory and md5sum
191+
dir_to_compress_hashes = {
192+
strip_directory_from_path(k, dir_to_compress): v
193+
for k, v in (
194+
await compute_hashes(full_file_path_from_dir_and_subdirs(dir_to_compress))
195+
).items()
196+
}
197+
198+
# computing the hashes for output_dir and map in a dict
199+
# with the name starting from the root of the directory and md5sum
200+
output_dir_hashes = {
201+
strip_directory_from_path(k, output_dir): v
202+
for k, v in (
203+
await compute_hashes(full_file_path_from_dir_and_subdirs(output_dir))
204+
).items()
205+
}
206+
207+
# finally check if hashes are mapped 1 to 1 in order to verify
208+
# that the compress/decompress worked correctly
209+
for key in dir_to_compress_hashes:
210+
assert dir_to_compress_hashes[key] == output_dir_hashes[key]
211+
212+
157213
def test_validate_osparc_file_name_ok():
158214
algorithm, digest_sum = validate_osparc_import_name(
159215
"v1#SHA256=80e69a0973e15f4a9c3c180d00a39ee0b0dfafe43356f867983e1180e9b5a892.osparc"

0 commit comments

Comments
 (0)