Skip to content
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

Integration test case for tool error reporting. #17953

Closed
wants to merge 1 commit into from
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
19 changes: 13 additions & 6 deletions lib/galaxy/util/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -644,12 +644,6 @@ def pretty_print_time_interval(time=False, precise=False, utc=False):
return "a few years ago"


def pretty_print_json(json_data, is_json_string=False):
if is_json_string:
json_data = json.loads(json_data)
return json.dumps(json_data, sort_keys=True, indent=4)


# characters that are valid
valid_chars = set(string.ascii_letters + string.digits + " -=_.()/+*^,:?!")

Expand Down Expand Up @@ -1621,6 +1615,19 @@ def send_mail(frm, to, subject, body, config, html=None, reply_to=None):
:type reply_to: str
:param reply_to: Reply-to address (Default None)
"""
if config.smtp_server.startswith("mock_emails_to_path://"):
path = config.smtp_server[len("mock_emails_to_path://") :]
email_dict = {
"from": frm,
"to": to,
"subject": subject,
"body": body,
"html": html,
"reply_to": reply_to,
}
email_json = json.to_json_string(email_dict)
with open(path, "w") as f:
f.write(email_json)
Dismissed Show dismissed Hide dismissed

to = listify(to)
if html:
Expand Down
30 changes: 30 additions & 0 deletions lib/galaxy_test/base/populators.py
Original file line number Diff line number Diff line change
Expand Up @@ -1048,6 +1048,36 @@ def new_error_dataset(self, history_id: str) -> str:
assert output_details["state"] == "error", output_details
return output_details["id"]

def report_job_error_raw(
self, job_id: str, dataset_id: str, message: str = "", email: Optional[str] = None
) -> Response:
url = f"jobs/{job_id}/error"
payload = dict(
dataset_id=dataset_id,
message=message,
)
if email is not None:
payload["email"] = email
report_response = self._post(url, data=payload, json=True)
return report_response

def report_job_error(
self, job_id: str, dataset_id: str, message: str = "", email: Optional[str] = None
) -> Response:
report_response = self.report_job_error_raw(job_id, dataset_id, message=message, email=email)
api_asserts.assert_status_code_is_ok(report_response)
return report_response.json()

def run_detect_errors(self, history_id: str, exit_code: int, stdout: str = "", stderr: str = "") -> dict:
inputs = {
"stdoutmsg": stdout,
"stderrmsg": stderr,
"exit_code": exit_code,
}
response = self.run_tool("detect_errors", inputs, history_id)
self.wait_for_history(history_id, assert_ok=False)
return response

def run_exit_code_from_file(self, history_id: str, hdca_id: str) -> dict:
exit_code_inputs = {
"input": {"batch": True, "values": [{"src": "hdca", "id": hdca_id}]},
Expand Down
114 changes: 114 additions & 0 deletions test/integration/test_error_report.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
"""Integration tests for user error reporting."""

import json
import os
import string

from galaxy_test.base.populators import DatasetPopulator
from galaxy_test.driver import integration_util

JSON_ERROR_REPORTS = """
- type: json
verbose: true
user_submission: true
directory: ${reports_directory}
"""

MOCK_EMAIL_ERROR_REPORTS = """
- type: email
verbose: true
user_submission: true
"""


class TestErrorReportIntegration(integration_util.IntegrationTestCase):
dataset_populator: DatasetPopulator
reports_directory: str
framework_tool_and_types = True

def setUp(self):
super().setUp()
self.dataset_populator = DatasetPopulator(self.galaxy_interactor)

@classmethod
def handle_galaxy_config_kwds(cls, config):
reports_directory = cls._test_driver.mkdtemp()
cls.reports_directory = reports_directory
template = string.Template(JSON_ERROR_REPORTS)
reports_yaml = template.safe_substitute({"reports_directory": reports_directory})
reports_conf = os.path.join(reports_directory, "error_report.yml")
with open(reports_conf, "w") as f:
f.write(reports_yaml)
config["error_report_file"] = reports_conf

def test_basic_tool_error(self):
with self.dataset_populator.test_history() as history_id:
response = self.dataset_populator.run_detect_errors(history_id, 6, "my stdout", "my stderr")
job_id = response["jobs"][0]["id"]
dataset_result = response["outputs"][0]
self.dataset_populator.report_job_error(job_id, dataset_result["id"])
assert len(os.listdir(self.reports_directory)) == 2
error_json = self.read_error_report(job_id)
error_dict = json.loads(error_json)
assert error_dict["exit_code"] == 6

def test_tool_error_custom_message_and_email(self):
with self.dataset_populator.test_history() as history_id:
response = self.dataset_populator.run_detect_errors(history_id, 6, "my stdout", "my stderr")
job_id = response["jobs"][0]["id"]
dataset_result = response["outputs"][0]
self.dataset_populator.report_job_error(
job_id, dataset_result["id"], "some new details", "[email protected]"
)
error_json = self.read_error_report(job_id)
error_dict = json.loads(error_json)
assert error_dict["exit_code"] == 6
assert error_dict["message"] == "some new details"
assert error_dict["email"] == "[email protected]"

def read_error_report(self, job_id: str):
app = self._app
job_id_decoded = app.security.decode_id(job_id)
with open(os.path.join(self.reports_directory, str(job_id_decoded))) as f:
return f.read()


class TestErrorEmailReportIntegration(integration_util.IntegrationTestCase):
dataset_populator: DatasetPopulator
reports_directory: str
framework_tool_and_types = True

def setUp(self):
super().setUp()
self.dataset_populator = DatasetPopulator(self.galaxy_interactor)

@classmethod
def handle_galaxy_config_kwds(cls, config):
reports_directory = cls._test_driver.mkdtemp()
cls.reports_directory = reports_directory
template = string.Template(MOCK_EMAIL_ERROR_REPORTS)
reports_yaml = template.safe_substitute({"reports_directory": reports_directory})
reports_conf = os.path.join(reports_directory, "error_report.yml")
with open(reports_conf, "w") as f:
f.write(reports_yaml)
config["error_report_file"] = reports_conf
config["smtp_server"] = f"mock_emails_to_path://{reports_directory}/email.json"
config["error_email_to"] = "[email protected]"

def test_tool_error_custom_message_and_email(self):
with self.dataset_populator.test_history() as history_id:
response = self.dataset_populator.run_detect_errors(history_id, 6, "my stdout", "my stderr")
job_id = response["jobs"][0]["id"]
dataset_result = response["outputs"][0]
self.dataset_populator.report_job_error(
job_id, dataset_result["id"], "some new details", "[email protected]"
)
error_json = self.read_most_recent_error_report()
error_dict = json.loads(error_json)
assert error_dict["to"] == "[email protected], [email protected]"
assert error_dict["subject"] == "Galaxy tool error report from [email protected] (detect_errors)"
assert "<h1>Galaxy Tool Error Report</h1>" in error_dict["html"]

def read_most_recent_error_report(self):
with open(os.path.join(self.reports_directory, "email.json")) as f:
return f.read()
Loading