Warning
This document is for an in-development version of Galaxy. You can alternatively view this page in the latest release if it exists or view the top of the latest release's documentation.
Source code for galaxy_test.driver.integration_setup
"""
Test classes that should be shared between test scenarios.
"""
import os
import shutil
from time import (
monotonic,
sleep,
)
from typing import ClassVar
from unittest.mock import (
MagicMock,
patch,
)
from galaxy.app import UniverseApplication
from galaxy.model import StoredWorkflow
from galaxy.model.item_attrs import add_item_annotation
from galaxy.workflow import (
curated,
iwc_manifest,
)
from galaxy_test.driver.driver_util import GalaxyTestDriver
REQUIRED_ROLE_EXPRESSION = "user@bx.psu.edu"
GROUP_A = "fs_test_group"
GROUP_B = "group name with spaces"
REQUIRED_GROUP_EXPRESSION = f"{GROUP_A} or '{GROUP_B}'"
[docs]
def get_posix_file_source_config(
root_dir: str, roles: str, groups: str, include_test_data_dir: bool, prefer_links: bool = False
) -> str:
rval = f"""
- type: posix
id: posix_test
label: Posix
doc: Files from local path
root: {root_dir}
writable: true
requires_roles: {roles}
requires_groups: {groups}
"""
if prefer_links:
rval += f"""
- type: posix
id: linking_source
label: Posix
doc: Files from local path to links
root: {root_dir}
writable: true
prefer_links: true
"""
if include_test_data_dir:
rval += """
- type: posix
id: testdatafiles
label: Galaxy Stock Test Data
doc: Galaxy's test-data directory.
root: test-data
writable: false
"""
return rval
[docs]
def create_file_source_config_file_on(
temp_dir: str,
root_dir: str,
include_test_data_dir: bool,
required_role_expression: str,
required_group_expression: str,
prefer_links: bool = False,
):
file_contents = get_posix_file_source_config(
root_dir, required_role_expression, required_group_expression, include_test_data_dir, prefer_links=prefer_links
)
file_path = os.path.join(temp_dir, "file_sources_conf_posix.yml")
with open(file_path, "w") as f:
f.write(file_contents)
return file_path
[docs]
class PosixFileSourceSetup:
_test_driver: GalaxyTestDriver
root_dir: str
include_test_data_dir: ClassVar[bool] = False
# Require role for access but do not require groups by default on every test to simplify them
required_role_expression: str = REQUIRED_ROLE_EXPRESSION
required_group_expression: str = ""
prefer_links: bool = False
[docs]
@classmethod
def handle_galaxy_config_kwds(cls, config):
temp_dir = os.path.realpath(cls._test_driver.mkdtemp())
cls.root_dir = os.path.join(temp_dir, "root")
file_sources_config_file = create_file_source_config_file_on(
temp_dir,
cls.root_dir,
cls.include_test_data_dir,
cls.required_role_expression,
cls.required_group_expression,
prefer_links=cls.prefer_links,
)
config["file_sources_config_file"] = file_sources_config_file
# Disable all stock plugins
config["ftp_upload_dir"] = None
config["library_import_dir"] = None
config["user_library_import_dir"] = None
def _write_file_fixtures(self):
root = self.root_dir
if os.path.exists(root):
shutil.rmtree(root)
os.mkdir(root)
with open(os.path.join(root, "a"), "w") as f:
f.write("a\n")
subdir1 = os.path.join(root, "subdir1")
os.mkdir(subdir1)
with open(os.path.join(subdir1, "b"), "w") as f:
f.write("b\n")
return root
[docs]
class CuratedWorkflowsNetworkGuard:
"""Keeps curated workflow tests offline.
The feature's one outbound request is the IWC manifest download. Both it and
the refresh that would trigger it are replaced with functions that raise, and
every test asserts the download was never attempted.
"""
download_mock: MagicMock
refresh_mock: MagicMock
[docs]
def setUp(self):
super().setUp() # type: ignore[misc]
curated.clear_caches()
self._download_patch = patch.object(
iwc_manifest,
"fetch_manifest",
side_effect=AssertionError("curated workflow tests must never contact iwc.galaxyproject.org"),
)
self.download_mock = self._download_patch.start()
self._refresh_patch = patch.object(
curated,
"refresh_projection",
side_effect=RuntimeError("curated catalog refresh is disabled in tests"),
)
self.refresh_mock = self._refresh_patch.start()
[docs]
def tearDown(self):
try:
# Drain any background refresh thread before the patches come off --
# a thread that outlived the test would otherwise reach the real
# fetcher. The network assertion runs last, once every thread the
# test could have started is known to have finished.
self._wait_for_refresh_to_settle()
self._refresh_patch.stop()
self._download_patch.stop()
self._assert_no_network_access()
finally:
super().tearDown() # type: ignore[misc]
def _assert_no_network_access(self) -> None:
self.download_mock.assert_not_called()
@staticmethod
def _wait_for_refresh_to_settle(timeout: float = 30.0) -> None:
# Reads private module state on purpose: the flag flips to False only
# after the background thread has finished with ``refresh_projection``,
# which is precisely the condition that makes unpatching safe.
deadline = monotonic() + timeout
while curated._refresh_in_flight and monotonic() < deadline:
sleep(0.05)
[docs]
def store_raw_annotation(app: UniverseApplication, workflow_id: str, annotation: str) -> None:
"""Annotate a stored workflow straight in the database.
Bypasses the API write path on purpose, so a test can store exactly the
text it means to - including text a write would sanitize.
"""
sa_session = app.model.session
stored_workflow = sa_session.get(StoredWorkflow, app.security.decode_id(workflow_id))
assert stored_workflow is not None
add_item_annotation(sa_session, stored_workflow.user, stored_workflow, annotation)
sa_session.commit()