diff --git a/services/web/server/tests/unit/isolated/test_activity.py b/services/web/server/tests/unit/isolated/test_activity.py index cb3a3c5f6d1..5dbe95960f2 100644 --- a/services/web/server/tests/unit/isolated/test_activity.py +++ b/services/web/server/tests/unit/isolated/test_activity.py @@ -3,7 +3,6 @@ # pylint:disable=redefined-outer-name import importlib -from asyncio import Future from pathlib import Path import pytest @@ -12,6 +11,7 @@ from aiohttp.client_exceptions import ClientConnectionError from pytest_simcore.helpers.utils_assert import assert_status +from pytest_simcore.helpers.utils_mock import future_with_result from servicelib.application import create_safe_application from simcore_service_webserver.activity import handlers, setup_activity from simcore_service_webserver.rest import setup_rest @@ -19,12 +19,6 @@ from simcore_service_webserver.session import setup_session -def future_with_result(result): - f = Future() - f.set_result(result) - return f - - @pytest.fixture def mocked_login_required(mocker): mock = mocker.patch( diff --git a/services/web/server/tests/unit/with_dbs/_helpers.py b/services/web/server/tests/unit/with_dbs/_helpers.py index ed826046a94..5da6a29e1e3 100644 --- a/services/web/server/tests/unit/with_dbs/_helpers.py +++ b/services/web/server/tests/unit/with_dbs/_helpers.py @@ -63,9 +63,3 @@ def standard_role_response() -> Tuple[str, List[Tuple[UserRole, ExpectedResponse ), ], ) - - -def future_with_result(result) -> Future: - f = Future() - f.set_result(result) - return f diff --git a/services/web/server/tests/unit/with_dbs/conftest.py b/services/web/server/tests/unit/with_dbs/conftest.py index e334d31a82e..0a140c61cd8 100644 --- a/services/web/server/tests/unit/with_dbs/conftest.py +++ b/services/web/server/tests/unit/with_dbs/conftest.py @@ -31,8 +31,9 @@ import simcore_service_webserver.utils from pytest_simcore.helpers.utils_assert import assert_status from pytest_simcore.helpers.utils_login import NewUser +from pytest_simcore.helpers.utils_mock import future_with_result + from servicelib.aiopg_utils import DSN -from servicelib.rest_responses import unwrap_envelope from simcore_service_webserver.application import create_application from simcore_service_webserver.application_config import app_schema as app_schema from simcore_service_webserver.groups_api import ( @@ -46,6 +47,9 @@ current_dir = Path(sys.argv[0] if __name__ == "__main__" else __file__).resolve().parent +# WEB SERVER FIXTURES ------------------------------------------------ + + @pytest.fixture(scope="session") def default_app_cfg(osparc_simcore_root_dir, fake_static_dir): # NOTE: ONLY used at the session scopes @@ -101,47 +105,6 @@ def docker_compose_file(default_app_cfg): os.environ = old -@pytest.fixture(scope="session") -def postgres_dsn(docker_services, docker_ip, default_app_cfg: Dict) -> Dict: - cfg = deepcopy(default_app_cfg["db"]["postgres"]) - cfg["host"] = docker_ip - cfg["port"] = docker_services.port_for("postgres", 5432) - return cfg - - -@pytest.fixture(scope="session") -def postgres_service(docker_services, postgres_dsn): - url = DSN.format(**postgres_dsn) - - # Wait until service is responsive. - docker_services.wait_until_responsive( - check=lambda: is_postgres_responsive(url), timeout=30.0, pause=0.1, - ) - - return url - - -@pytest.fixture -def postgres_db( - app_cfg: Dict, postgres_dsn: Dict, postgres_service: str -) -> sa.engine.Engine: - url = postgres_service - - # Configures db and initializes tables - pg_cli.discover.callback(**postgres_dsn) - pg_cli.upgrade.callback("head") - # Uses syncrounous engine for that - engine = sa.create_engine(url, isolation_level="AUTOCOMMIT") - - yield engine - - pg_cli.downgrade.callback("base") - pg_cli.clean.callback() - - orm.metadata.drop_all(engine) - engine.dispose() - - @pytest.fixture def web_server(loop, aiohttp_server, app_cfg, monkeypatch, postgres_db): # original APP @@ -160,6 +123,20 @@ def client(loop, aiohttp_client, web_server, mock_orphaned_services): return client +# SUBSYSTEM MOCKS FIXTURES ------------------------------------------------ +# +# Add mocks for web-server subsystems here +# + +@pytest.fixture +def computational_system_mock(mocker): + mock_fun = mocker.patch( + "simcore_service_webserver.projects.projects_handlers.update_pipeline_db", + return_value=Future(), + ) + mock_fun.return_value.set_result("") + return mock_fun + @pytest.fixture async def storage_subsystem_mock(loop, mocker): """ @@ -187,27 +164,135 @@ async def _mock_copy_data_from_project(*args): return mock, mock1 -# helpers --------------- +@pytest.fixture +def mocked_director_subsystem(mocker): + mock_director_api = { + "get_running_interactive_services": mocker.patch( + "simcore_service_webserver.director.director_api.get_running_interactive_services", + return_value=future_with_result(""), + ), + "start_service": mocker.patch( + "simcore_service_webserver.director.director_api.start_service", + return_value=future_with_result(""), + ), + "stop_service": mocker.patch( + "simcore_service_webserver.director.director_api.stop_service", + return_value=future_with_result(""), + ), + } + return mock_director_api -def path_mail(monkeypatch): - async def send_mail(*args): - print("=== EMAIL TO: {}\n=== SUBJECT: {}\n=== BODY:\n{}".format(*args)) +@pytest.fixture +async def mocked_director_api(loop, mocker): + mocks = {} + mocked_running_services = mocker.patch( + "simcore_service_webserver.director.director_api.get_running_interactive_services", + return_value=Future(), + ) + mocked_running_services.return_value.set_result("") + mocks["get_running_interactive_services"] = mocked_running_services + mocked_stop_service = mocker.patch( + "simcore_service_webserver.director.director_api.stop_service", + return_value=Future(), + ) + mocked_stop_service.return_value.set_result("") + mocks["stop_service"] = mocked_stop_service - monkeypatch.setattr( - simcore_service_webserver.login.utils, "compose_mail", send_mail + yield mocks + + +@pytest.fixture +async def mocked_dynamic_service(loop, client, mocked_director_api): + services = [] + + async def create(user_id, project_id) -> Dict: + SERVICE_UUID = str(uuid4()) + SERVICE_KEY = "simcore/services/dynamic/3d-viewer" + SERVICE_VERSION = "1.4.2" + url = client.app.router["create_node"].url_for(project_id=project_id) + create_node_data = { + "service_key": SERVICE_KEY, + "service_version": SERVICE_VERSION, + "service_uuid": SERVICE_UUID, + } + + running_service_dict = { + "published_port": "23423", + "service_uuid": SERVICE_UUID, + "service_key": SERVICE_KEY, + "service_version": SERVICE_VERSION, + "service_host": "some_service_host", + "service_port": "some_service_port", + "service_state": "some_service_state", + } + + services.append(running_service_dict) + # reset the future or an invalidStateError will appear as set_result sets the future to done + mocked_director_api["get_running_interactive_services"].return_value = Future() + mocked_director_api["get_running_interactive_services"].return_value.set_result( + services + ) + return running_service_dict + + return create + + +@pytest.fixture +def asyncpg_storage_system_mock(mocker): + mocked_method = mocker.patch( + "simcore_service_webserver.login.storage.AsyncpgStorage.delete_user", + return_value=Future(), ) + mocked_method.return_value.set_result("") + return mocked_method -def is_postgres_responsive(url): - """Check if something responds to ``url`` """ - try: - engine = sa.create_engine(url) - conn = engine.connect() - conn.close() - except sa.exc.OperationalError: - return False - return True +# POSTGRES CORE SERVICE --------------------------------------------------- + + +@pytest.fixture(scope="session") +def postgres_dsn(docker_services, docker_ip, default_app_cfg: Dict) -> Dict: + cfg = deepcopy(default_app_cfg["db"]["postgres"]) + cfg["host"] = docker_ip + cfg["port"] = docker_services.port_for("postgres", 5432) + return cfg + + +@pytest.fixture(scope="session") +def postgres_service(docker_services, postgres_dsn): + url = DSN.format(**postgres_dsn) + + # Wait until service is responsive. + docker_services.wait_until_responsive( + check=lambda: is_postgres_responsive(url), timeout=30.0, pause=0.1, + ) + + return url + + +@pytest.fixture +def postgres_db( + app_cfg: Dict, postgres_dsn: Dict, postgres_service: str +) -> sa.engine.Engine: + url = postgres_service + + # Configures db and initializes tables + pg_cli.discover.callback(**postgres_dsn) + pg_cli.upgrade.callback("head") + # Uses syncrounous engine for that + engine = sa.create_engine(url, isolation_level="AUTOCOMMIT") + + yield engine + + pg_cli.downgrade.callback("base") + pg_cli.clean.callback() + + orm.metadata.drop_all(engine) + engine.dispose() + + +# REDIS CORE SERVICE ------------------------------------------------------ @pytest.fixture(scope="session") @@ -223,11 +308,6 @@ def redis_service(docker_services, docker_ip): return url -def is_redis_responsive(host: str, port: int) -> bool: - r = redis.Redis(host=host, port=port) - return r.ping() == True - - @pytest.fixture async def redis_client(loop, redis_service): client = await aioredis.create_redis_pool(str(redis_service), encoding="utf-8") @@ -238,6 +318,14 @@ async def redis_client(loop, redis_service): await client.wait_closed() +def is_redis_responsive(host: str, port: int) -> bool: + r = redis.Redis(host=host, port=port) + return r.ping() == True + + +# SOCKETS FIXTURES -------------------------------------------------------- + + @pytest.fixture() def socketio_url(client) -> Callable: def create_url(client_override: Optional = None) -> str: @@ -308,59 +396,7 @@ def create() -> str(): return create -@pytest.fixture -async def mocked_director_api(loop, mocker): - mocks = {} - mocked_running_services = mocker.patch( - "simcore_service_webserver.director.director_api.get_running_interactive_services", - return_value=Future(), - ) - mocked_running_services.return_value.set_result("") - mocks["get_running_interactive_services"] = mocked_running_services - mocked_stop_service = mocker.patch( - "simcore_service_webserver.director.director_api.stop_service", - return_value=Future(), - ) - mocked_stop_service.return_value.set_result("") - mocks["stop_service"] = mocked_stop_service - - yield mocks - - -@pytest.fixture -async def mocked_dynamic_service(loop, client, mocked_director_api): - services = [] - - async def create(user_id, project_id) -> Dict: - SERVICE_UUID = str(uuid4()) - SERVICE_KEY = "simcore/services/dynamic/3d-viewer" - SERVICE_VERSION = "1.4.2" - url = client.app.router["create_node"].url_for(project_id=project_id) - create_node_data = { - "service_key": SERVICE_KEY, - "service_version": SERVICE_VERSION, - "service_uuid": SERVICE_UUID, - } - - running_service_dict = { - "published_port": "23423", - "service_uuid": SERVICE_UUID, - "service_key": SERVICE_KEY, - "service_version": SERVICE_VERSION, - "service_host": "some_service_host", - "service_port": "some_service_port", - "service_state": "some_service_state", - } - - services.append(running_service_dict) - # reset the future or an invalidStateError will appear as set_result sets the future to done - mocked_director_api["get_running_interactive_services"].return_value = Future() - mocked_director_api["get_running_interactive_services"].return_value.set_result( - services - ) - return running_service_dict - - return create +# USER DATA FIXTURES ------------------------------------------------------- @pytest.fixture @@ -417,11 +453,24 @@ async def all_group(client, logged_user) -> Dict[str, str]: return all_group -@pytest.fixture -def asyncpg_storage_system_mock(mocker): - mocked_method = mocker.patch( - "simcore_service_webserver.login.storage.AsyncpgStorage.delete_user", - return_value=Future(), +# GENERIC HELPER FUNCTIONS ---------------------------------------------------- + + +def path_mail(monkeypatch): + async def send_mail(*args): + print("=== EMAIL TO: {}\n=== SUBJECT: {}\n=== BODY:\n{}".format(*args)) + + monkeypatch.setattr( + simcore_service_webserver.login.utils, "compose_mail", send_mail ) - mocked_method.return_value.set_result("") - return mocked_method + + +def is_postgres_responsive(url): + """Check if something responds to ``url`` """ + try: + engine = sa.create_engine(url) + conn = engine.connect() + conn.close() + except sa.exc.OperationalError: + return False + return True diff --git a/services/web/server/tests/unit/with_dbs/slow/test_projects.py b/services/web/server/tests/unit/with_dbs/slow/test_projects.py index da6a9c8428b..04f689f12cd 100644 --- a/services/web/server/tests/unit/with_dbs/slow/test_projects.py +++ b/services/web/server/tests/unit/with_dbs/slow/test_projects.py @@ -15,16 +15,12 @@ import socketio from aiohttp import web from mock import call -from socketio.exceptions import ConnectionError +from socketio.exceptions import ConnectionError as SocketConnectionError -from _helpers import ( - ExpectedResponse, - HTTPLocked, - future_with_result, - standard_role_response, -) +from _helpers import ExpectedResponse, HTTPLocked, standard_role_response from pytest_simcore.helpers.utils_assert import assert_status -from pytest_simcore.helpers.utils_login import LoggedUser, create_user, log_client_in +from pytest_simcore.helpers.utils_login import LoggedUser, log_client_in +from pytest_simcore.helpers.utils_mock import future_with_result from pytest_simcore.helpers.utils_projects import NewProject, delete_all_projects from servicelib.application import create_safe_application from simcore_service_webserver.db import setup_db @@ -54,25 +50,6 @@ API_PREFIX = "/" + API_VERSION -@pytest.fixture -def mocked_director_subsystem(mocker): - mock_director_api = { - "get_running_interactive_services": mocker.patch( - "simcore_service_webserver.director.director_api.get_running_interactive_services", - return_value=future_with_result(""), - ), - "start_service": mocker.patch( - "simcore_service_webserver.director.director_api.start_service", - return_value=future_with_result(""), - ), - "stop_service": mocker.patch( - "simcore_service_webserver.director.director_api.stop_service", - return_value=future_with_result(""), - ), - } - return mock_director_api - - DEFAULT_GARBAGE_COLLECTOR_INTERVAL_SECONDS: int = 3 DEFAULT_GARBAGE_COLLECTOR_DELETION_TIMEOUT_SECONDS: int = 3 @@ -188,16 +165,6 @@ async def template_project( print("<----- removed template project", template_project["name"]) -@pytest.fixture -def computational_system_mock(mocker): - mock_fun = mocker.patch( - "simcore_service_webserver.projects.projects_handlers.update_pipeline_db", - return_value=Future(), - ) - mock_fun.return_value.set_result("") - return mock_fun - - @pytest.fixture def fake_services(): def create_fakes(number_services: int) -> List[Dict]: @@ -937,7 +904,7 @@ async def test_get_active_project( try: sio = await socketio_client(client_id1) assert sio.sid - except ConnectionError: + except SocketConnectionError: if expected == web.HTTPOk: pytest.fail("socket io connection should not fail") @@ -970,7 +937,7 @@ async def test_get_active_project( try: sio = await socketio_client(client_id2) assert sio.sid - except ConnectionError: + except SocketConnectionError: if expected == web.HTTPOk: pytest.fail("socket io connection should not fail") # get active projects -> empty @@ -1014,7 +981,7 @@ async def test_delete_multiple_opened_project_forbidden( client_session_id1 = client_session_id() try: sio1 = await socketio_client(client_session_id1) - except ConnectionError: + except SocketConnectionError: if user_role != UserRole.ANONYMOUS: pytest.fail("socket io connection should not fail") url = client.app.router["open_project"].url_for(project_id=user_project["uuid"]) @@ -1024,7 +991,7 @@ async def test_delete_multiple_opened_project_forbidden( client_session_id2 = client_session_id() try: sio2 = await socketio_client(client_session_id2) - except ConnectionError: + except SocketConnectionError: if user_role != UserRole.ANONYMOUS: pytest.fail("socket io connection should not fail") await _delete_project(client, user_project, expected_forbidden) @@ -1215,7 +1182,7 @@ async def _connect_websocket( for event, handler in events.items(): sio.on(event, handler=handler) return sio - except ConnectionError: + except SocketConnectionError: if check_connection: pytest.fail("socket io connection should not fail")