Skip to content

WIP: Maintenance/cleanup webserver test fixtures #1731

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 1 addition & 7 deletions services/web/server/tests/unit/isolated/test_activity.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
# pylint:disable=redefined-outer-name

import importlib
from asyncio import Future
from pathlib import Path

import pytest
Expand All @@ -12,19 +11,14 @@
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
from simcore_service_webserver.security import setup_security
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(
Expand Down
6 changes: 0 additions & 6 deletions services/web/server/tests/unit/with_dbs/_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
293 changes: 171 additions & 122 deletions services/web/server/tests/unit/with_dbs/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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):
"""
Expand Down Expand Up @@ -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")
Expand All @@ -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")
Expand All @@ -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:
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Loading