diff --git a/package-lock.json b/package-lock.json index 7bf13a7d96..684f7dc42b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4237,9 +4237,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001523", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001523.tgz", - "integrity": "sha512-I5q5cisATTPZ1mc588Z//pj/Ox80ERYDfR71YnvY7raS/NOk8xXlZcB0sF7JdqaV//kOaa6aus7lRfpdnt1eBA==", + "version": "1.0.30001561", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001561.tgz", + "integrity": "sha512-NTt0DNoKe958Q0BE0j0c1V9jbUzhBxHIEJy7asmGrpE0yG63KTV7PLHPnK2E1O9RsQrQ081I3NLuXGS6zht3cw==", "dev": true, "funding": [ { diff --git a/src/biokbase/auth.py b/src/biokbase/auth.py index 12a229c0ed..3659b2c96c 100644 --- a/src/biokbase/auth.py +++ b/src/biokbase/auth.py @@ -54,26 +54,23 @@ def __init__(self, user_dict: dict): self.user_name = user_dict.get("user") -def validate_token(): +def validate_token() -> bool: """ Validates the currently set auth token. Returns True if valid, False otherwise. """ headers = {"Authorization": get_auth_token()} r = requests.get(token_api_url + endpt_token, headers=headers) - if r.status_code == 200: - return True - else: - return False + return r.status_code == 200 -def set_environ_token(token: str) -> None: +def set_environ_token(token: str | None) -> None: """ Sets a login token in the local environment variable. """ kbase_env.auth_token = token -def get_auth_token() -> Optional[str]: +def get_auth_token() -> str | None: """ Returns the current login token being used, or None if one isn't set. """ diff --git a/src/biokbase/narrative/contents/narrativeio.py b/src/biokbase/narrative/contents/narrativeio.py index 9c586af837..bd32e6d437 100644 --- a/src/biokbase/narrative/contents/narrativeio.py +++ b/src/biokbase/narrative/contents/narrativeio.py @@ -99,15 +99,14 @@ def narrative_exists(self, ref): except WorkspaceError as err: if err.http_code == 404: return False - else: - raise + raise def _validate_nar_type(self, t, ref): if not t.startswith(NARRATIVE_TYPE): err = "Expected a Narrative object" if ref is not None: - err += " with reference {}".format(ref) - err += ", got a {}".format(t) + err += f" with reference {ref}" + err += f", got a {t}" raise HTTPError(500, err) def read_narrative(self, ref, content=True, include_metadata=True): diff --git a/src/biokbase/narrative/jobs/specmanager.py b/src/biokbase/narrative/jobs/specmanager.py index 43b868e149..4fad76e2cd 100644 --- a/src/biokbase/narrative/jobs/specmanager.py +++ b/src/biokbase/narrative/jobs/specmanager.py @@ -140,7 +140,12 @@ def app_usage(self, app_id, tag="release"): return AppUsage(usage) - def check_app(self, app_id, tag="release", raise_exception=False): + def check_app( + self: "SpecManager", + app_id: str, + tag: str = "release", + raise_exception: bool = False, + ): """ Checks if a method (and release tag) is available for running and such. If raise_exception==True, and either the tag or app_id are invalid, a ValueError is raised. diff --git a/src/biokbase/narrative/tests/job_test_constants.py b/src/biokbase/narrative/tests/job_test_constants.py index 721f73ca1a..7fd5f1eb6c 100644 --- a/src/biokbase/narrative/tests/job_test_constants.py +++ b/src/biokbase/narrative/tests/job_test_constants.py @@ -1,14 +1,27 @@ import copy +from typing import Any from biokbase.narrative.jobs.job import TERMINAL_STATUSES from .util import ConfigTests config = ConfigTests() -TEST_JOBS = config.load_json_file(config.get("jobs", "ee2_job_test_data_file")) - - -def generate_error(job_id, err_type): +TEST_JOBS: dict[str, dict] = config.load_json_file( + config.get("jobs", "ee2_job_test_data_file") +) + + +def generate_error(job_id: str, err_type: str) -> str: + """Given a job id and an error type, generate the appropriate error string. + + :param job_id: job ID + :type job_id: str + :param err_type: error type + :type err_type: str + :raises KeyError: if the error type does not exist + :return: error string + :rtype: str + """ user_id = None status = None @@ -32,11 +45,25 @@ def generate_error(job_id, err_type): return error_strings[err_type] -def get_test_job(job_id): +def get_test_job(job_id: str) -> dict[str, Any]: + """Given a job ID, fetch the appropriate job. + + :param job_id: job ID + :type job_id: str + :return: job data + :rtype: dict[str, Any] + """ return copy.deepcopy(TEST_JOBS[job_id]) -def get_test_jobs(job_ids): +def get_test_jobs(job_ids: list[str]) -> dict[str, dict[str, Any]]: + """Given a list of job IDs, fetch the appropriate jobs. + + :param job_ids: list of job IDs + :type job_ids: list[str] + :return: dict of jobs keyed by job ID + :rtype: dict[str, dict[str, Any]] + """ return {job_id: get_test_job(job_id) for job_id in job_ids} @@ -93,16 +120,15 @@ def get_test_jobs(job_ids): BATCH_RETRY_ERROR, ] -BATCH_PARENT_CHILDREN = [BATCH_PARENT] + BATCH_CHILDREN +BATCH_PARENT_CHILDREN = [BATCH_PARENT, *BATCH_CHILDREN] -JOB_TERMINAL_STATE = { - job_id: TEST_JOBS[job_id]["status"] in TERMINAL_STATUSES - for job_id in TEST_JOBS.keys() +JOB_TERMINAL_STATE: dict[str, bool] = { + job_id: TEST_JOBS[job_id]["status"] in TERMINAL_STATUSES for job_id in TEST_JOBS } -TERMINAL_JOBS = [] -ACTIVE_JOBS = [] -REFRESH_STATE = {} +TERMINAL_JOBS: list[str] = [] +ACTIVE_JOBS: list[str] = [] +REFRESH_STATE: dict[str, bool] = {} for key, value in JOB_TERMINAL_STATE.items(): if value: TERMINAL_JOBS.append(key) diff --git a/src/biokbase/narrative/tests/narrative_mock/mockclients.py b/src/biokbase/narrative/tests/narrative_mock/mockclients.py index a9a97b2175..4b226afee9 100644 --- a/src/biokbase/narrative/tests/narrative_mock/mockclients.py +++ b/src/biokbase/narrative/tests/narrative_mock/mockclients.py @@ -81,7 +81,7 @@ def test_my_function(self): config = ConfigTests() _job_state_data = TEST_JOBS - def __init__(self, client_name=None, token=None): + def __init__(self, client_name=None, token=None) -> None: if token is not None: assert isinstance(token, str) self.client_name = client_name @@ -326,9 +326,7 @@ def log_gen(log_params, total_lines=MAX_LOG_LINES): lines = [] if skip < total_lines: for i in range(total_lines - skip): - lines.append( - {"is_error": 0, "line": "This is line {}".format(i + skip)} - ) + lines.append({"is_error": 0, "line": f"This is line {i + skip}"}) return {"last_line_number": max(total_lines, skip), "lines": lines} if job_id == JOB_COMPLETED: @@ -355,6 +353,7 @@ def log_gen(log_params, total_lines=MAX_LOG_LINES): def sync_call(self, call, params): if call == "NarrativeService.list_objects_with_sets": return self._mock_ns_list_objects_with_sets(params) + return None def _mock_ns_list_objects_with_sets(self, params): """ @@ -374,7 +373,7 @@ def _mock_ns_list_objects_with_sets(self, params): if params.get("workspaces"): ws_name = params["workspaces"][0] dp_id = 999 - dp_ref = "{}/{}".format(ws_id, dp_id) + dp_ref = f"{ws_id}/{dp_id}" data = { "data": [ @@ -511,10 +510,7 @@ def _mock_ns_list_objects_with_sets(self, params): data["data"] = list( filter( lambda x: any( - [ - x["object_info"][2].lower().startswith(t.lower()) - for t in types - ] + x["object_info"][2].lower().startswith(t.lower()) for t in types ), data["data"], ) @@ -535,7 +531,8 @@ def get_failing_mock_client(client_name, token=None): class FailingMockClient: - def __init__(self, token=None): + def __init__(self, token=None) -> None: + # nothing to do here pass def check_workspace_jobs(self, params): @@ -590,7 +587,7 @@ class assert_obj_method_called: ) """ - def __init__(self, target, method_name, call_status=True): + def __init__(self, target, method_name, call_status=True) -> None: self.target = target self.method_name = method_name self.call_status = call_status diff --git a/src/biokbase/narrative/tests/narrative_mock/mockcomm.py b/src/biokbase/narrative/tests/narrative_mock/mockcomm.py index 17457b02ea..235c59b44b 100644 --- a/src/biokbase/narrative/tests/narrative_mock/mockcomm.py +++ b/src/biokbase/narrative/tests/narrative_mock/mockcomm.py @@ -5,7 +5,7 @@ class MockComm: analyzed during the test. """ - def __init__(self, *args, **kwargs): + def __init__(self, *args, **kwargs) -> None: """Mock the init""" self.messages = [] diff --git a/src/biokbase/narrative/tests/test_app_util.py b/src/biokbase/narrative/tests/test_app_util.py index 46aa46821a..d618adbe71 100644 --- a/src/biokbase/narrative/tests/test_app_util.py +++ b/src/biokbase/narrative/tests/test_app_util.py @@ -3,13 +3,10 @@ """ import copy import os +import re from unittest import mock import pytest -import re -from biokbase.narrative.common.url_config import URLS -from biokbase.workspace.client import Workspace - from biokbase.narrative.app_util import ( app_param, check_tag, @@ -19,10 +16,11 @@ map_outputs_from_state, transform_param_value, ) - -from biokbase.narrative.tests.conftest import narrative_vcr as vcr +from biokbase.narrative.common.url_config import URLS from biokbase.narrative.tests import util +from biokbase.narrative.tests.conftest import narrative_vcr as vcr from biokbase.narrative.upa import is_upa +from biokbase.workspace.client import Workspace config = util.ConfigTests() user_name = config.get("users", "test_user") @@ -74,7 +72,7 @@ def set_ws_name(ws_name): ] -@pytest.mark.parametrize("result,path,expected", get_result_sub_path_cases) +@pytest.mark.parametrize(("result", "path", "expected"), get_result_sub_path_cases) def test_get_result_sub_path(result, path, expected): assert get_result_sub_path(result, path) == expected @@ -275,7 +273,7 @@ def test_map_outputs_from_state_bad_spec(workspace_name): ] -@pytest.mark.parametrize("field_type,spec_add,expect_add", app_param_cases) +@pytest.mark.parametrize(("field_type", "spec_add", "expect_add"), app_param_cases) def test_app_param(field_type, spec_add, expect_add): spec_param = copy.deepcopy(base_app_param) expected = copy.deepcopy(base_expect) @@ -307,7 +305,8 @@ def test_app_param(field_type, spec_add, expect_add): @pytest.mark.parametrize( - "transform_type,value,spec_param,expected", transform_param_value_simple_cases + ("transform_type", "value", "spec_param", "expected"), + transform_param_value_simple_cases, ) def test_transform_param_value_simple(transform_type, value, spec_param, expected): assert transform_param_value(transform_type, value, spec_param) == expected @@ -328,7 +327,7 @@ def test_transform_param_value_fail(): ] -@pytest.mark.parametrize("value,expected", textsubdata_cases) +@pytest.mark.parametrize(("value", "expected"), textsubdata_cases) def test_transform_param_value_textsubdata(value, expected): spec = {"type": "textsubdata"} assert transform_param_value(None, value, spec) == expected @@ -506,9 +505,6 @@ def get_workspace(_): class RefChainWorkspace: - def __init__(self): - pass - def get_object_info3(self, params): """ Makes quite a few assumptions about input, as it's used for a specific test. @@ -568,7 +564,7 @@ def get_ref_path_mock_ws(name="workspace"): ) @mock.patch("biokbase.narrative.app_util.clients.get", get_ref_path_mock_ws) def test_transform_param_value_upa_path(tf_type): - upa_path = f"69375/2/2;67729/2/2" + upa_path = "69375/2/2;67729/2/2" assert transform_param_value(tf_type, upa_path, None) == upa_path diff --git a/src/biokbase/narrative/tests/test_appeditor.py b/src/biokbase/narrative/tests/test_appeditor.py index 7643ec4628..2764271e1f 100644 --- a/src/biokbase/narrative/tests/test_appeditor.py +++ b/src/biokbase/narrative/tests/test_appeditor.py @@ -5,6 +5,7 @@ import re import unittest +import pytest from biokbase.narrative.appeditor import generate_app_cell from .util import ConfigTests @@ -22,24 +23,26 @@ def setUpClass(cls): def test_gen_app_cell_post_validation(self): js = generate_app_cell(validated_spec=self.specs_list[0]) - self.assertIsNotNone(js) + assert js is not None def test_gen_app_cell_pre_valid(self): js = generate_app_cell( spec_tuple=(json.dumps(self.spec_json), self.display_yaml) ) - self.assertIsNotNone(js) - self.assertIsNotNone(js.data) - self.assertIn( - "A description string, with "quoted" values, shouldn't fail.", - js.data, + assert js is not None + assert js.data is not None + assert ( + "A description string, with "quoted" values, shouldn't fail." + in js.data ) - self.assertIn("Test Simple Inputs with "quotes"", js.data) - self.assertIn("A simple test spec with a single 'input'.", js.data) + assert "Test Simple Inputs with "quotes"" in js.data + assert "A simple test spec with a single 'input'." in js.data def test_gen_app_cell_fail_validation(self): - with self.assertRaisesRegexp( + with pytest.raises( Exception, - re.escape("Can't find sub-node [categories] within path [/] in spec.json"), + match=re.escape( + "Can't find sub-node [categories] within path [/] in spec.json" + ), ): generate_app_cell(spec_tuple=("{}", self.display_yaml)) diff --git a/src/biokbase/narrative/tests/test_appmanager.py b/src/biokbase/narrative/tests/test_appmanager.py index ef86f10110..23c1f6b84e 100644 --- a/src/biokbase/narrative/tests/test_appmanager.py +++ b/src/biokbase/narrative/tests/test_appmanager.py @@ -10,10 +10,9 @@ from unittest import mock from unittest.mock import MagicMock -from IPython.display import HTML, Javascript - +import pytest from biokbase.auth import TokenInfo -import biokbase.narrative.app_util as app_util +from biokbase.narrative import app_util from biokbase.narrative.jobs.appmanager import BATCH_APP, AppManager from biokbase.narrative.jobs.job import Job from biokbase.narrative.jobs.jobcomm import MESSAGE_TYPE @@ -24,6 +23,7 @@ READS_OBJ_1, READS_OBJ_2, ) +from IPython.display import HTML, Javascript from .narrative_mock.mockclients import WSID_STANDARD, get_mock_client from .util import ConfigTests @@ -205,52 +205,49 @@ def run_app_expect_error( """ output = io.StringIO() sys.stdout = output - self.assertIsNone(run_func()) + assert run_func() is None sys.stdout = sys.__stdout__ # reset to normal output_str = output.getvalue() if print_error is not None and len(print_error): - self.assertIn( - f"Error while trying to start your app ({func_name})!", output_str - ) - self.assertIn(print_error, output_str) + assert f"Error while trying to start your app ({func_name})!" in output_str + assert print_error in output_str else: - self.assertEqual( - "", output_str - ) # if nothing gets written to a StringIO, getvalue returns an empty string + # if nothing gets written to a StringIO, getvalue returns an empty string + assert output_str == "" self._verify_comm_error(comm_mock, cell_id=cell_id) def test_reload(self): self.am.reload() info = self.am.app_usage(self.good_app_id, self.good_tag) - self.assertTrue(info) + assert info def test_app_usage(self): # good id and good tag usage = self.am.app_usage(self.good_app_id, self.good_tag) - self.assertTrue(usage) + assert usage # bad id - with self.assertRaises(ValueError): + with pytest.raises(ValueError): self.am.app_usage(self.bad_app_id) # bad tag - with self.assertRaises(ValueError): + with pytest.raises(ValueError): self.am.app_usage(self.good_app_id, self.bad_tag) def test_app_usage_html(self): usage = self.am.app_usage(self.good_app_id, self.good_tag) - self.assertTrue(usage._repr_html_()) + assert usage._repr_html_() def test_app_usage_str(self): usage = self.am.app_usage(self.good_app_id, self.good_tag) - self.assertTrue(str(usage)) + assert str(usage) def test_available_apps_good(self): apps = self.am.available_apps(self.good_tag) - self.assertIsInstance(apps, HTML) + assert isinstance(apps, HTML) def test_available_apps_bad(self): - with self.assertRaises(ValueError): + with pytest.raises(ValueError): self.am.available_apps(self.bad_tag) # Testing run_app @@ -275,8 +272,8 @@ def test_run_app__dry_run(self, auth, c): output = self.am.run_app( self.test_app_id, self.test_app_params, tag=self.test_tag, dry_run=True ) - self.assertEqual(expected, output) - self.assertEqual(mock_comm.call_count, 0) + assert expected == output + assert mock_comm.call_count == 0 @mock.patch(CLIENTS_AM, get_mock_client) @mock.patch(JOB_COMM_MOCK) @@ -289,11 +286,11 @@ def test_run_app__good_inputs(self, auth, c): new_job = self.am.run_app( self.test_app_id, self.test_app_params, tag=self.test_tag ) - self.assertIsInstance(new_job, Job) - self.assertEqual(self.jm.get_job(self.test_job_id), new_job) + assert isinstance(new_job, Job) + assert self.jm.get_job(self.test_job_id) == new_job self._verify_comm_success(c.return_value.send_comm_message, False) - self.assertEqual(False, self.jm._running_jobs[new_job.job_id]["refresh"]) + assert self.jm._running_jobs[new_job.job_id]["refresh"] is False @mock.patch(CLIENTS_AM, get_mock_client) @mock.patch(JOB_COMM_MOCK) @@ -304,13 +301,14 @@ def test_run_app__good_inputs(self, auth, c): def test_run_app__from_gui_cell(self, auth, c): cell_id = "12345" c.return_value.send_comm_message = MagicMock() - self.assertIsNone( + assert ( self.am.run_app( self.test_app_id, self.test_app_params, tag=self.test_tag, cell_id=cell_id, ) + is None ) self._verify_comm_success( c.return_value.send_comm_message, False, cell_id=cell_id @@ -371,7 +369,7 @@ def run_func(): ) def test_run_app__missing_inputs(self, auth, c): c.return_value.send_comm_message = MagicMock() - self.assertIsNotNone(self.am.run_app(self.good_app_id, None, tag=self.good_tag)) + assert self.am.run_app(self.good_app_id, None, tag=self.good_tag) is not None self._verify_comm_success(c.return_value.send_comm_message, False) @mock.patch(CLIENTS_AM, get_mock_client) @@ -465,7 +463,7 @@ def test_run_legacy_batch_app__dry_run_good_inputs(self, auth, c): "wsid": WSID_STANDARD, } - self.assertEqual(job_runner_inputs, expected) + assert job_runner_inputs == expected @mock.patch(CLIENTS_AM, get_mock_client) @mock.patch(JOB_COMM_MOCK) @@ -482,11 +480,11 @@ def test_run_legacy_batch_app__good_inputs(self, auth, c): version=self.test_app_version, tag=self.test_tag, ) - self.assertIsInstance(new_job, Job) - self.assertEqual(self.jm.get_job(self.test_job_id), new_job) + assert isinstance(new_job, Job) + assert self.jm.get_job(self.test_job_id) == new_job self._verify_comm_success(c.return_value.send_comm_message, False) - self.assertEqual(False, self.jm._running_jobs[new_job.job_id]["refresh"]) + assert self.jm._running_jobs[new_job.job_id]["refresh"] is False @mock.patch(CLIENTS_AM, get_mock_client) @mock.patch(JOB_COMM_MOCK) @@ -497,13 +495,14 @@ def test_run_legacy_batch_app__good_inputs(self, auth, c): def test_run_legacy_batch_app__gui_cell(self, auth, c): cell_id = "12345" c.return_value.send_comm_message = MagicMock() - self.assertIsNone( + assert ( self.am.run_legacy_batch_app( self.test_app_id, [self.test_app_params, self.test_app_params], tag=self.test_tag, cell_id=cell_id, ) + is None ) self._verify_comm_success( c.return_value.send_comm_message, False, cell_id=cell_id @@ -568,8 +567,9 @@ def run_func(): ) def test_run_legacy_batch_app__missing_inputs(self, auth, c): c.return_value.send_comm_message = MagicMock() - self.assertIsNotNone( + assert ( self.am.run_legacy_batch_app(self.good_app_id, None, tag=self.good_tag) + is not None ) self._verify_comm_success(c.return_value.send_comm_message, False) @@ -623,8 +623,8 @@ def test_run_local_app_ok(self, auth, c): {"param0": "fakegenome"}, tag="release", ) - self.assertIsInstance(result, Javascript) - self.assertIn("KBaseNarrativeOutputCell", result.data) + assert isinstance(result, Javascript) + assert "KBaseNarrativeOutputCell" in result.data @mock.patch(CLIENTS_AM, get_mock_client) @mock.patch(JOB_COMM_MOCK) @@ -733,15 +733,15 @@ def test_run_app_batch__dry_run(self, auth, c): expected_batch_run_keys = {"method", "service_ver", "params", "app_id", "meta"} # expect only the above keys in each batch run params (note the missing wsid key) for param_set in batch_run_params: - self.assertTrue(expected_batch_run_keys == set(param_set.keys())) - self.assertEqual(["wsid"], list(batch_params.keys())) + assert expected_batch_run_keys == set(param_set.keys()) + assert ["wsid"] == list(batch_params.keys()) # expect shared_params to have been merged into respective param_sets for exp, outp in zip( iter_bulk_run_good_inputs_param_sets(spec_mapped=True), batch_run_params ): got = outp["params"][0] - self.assertDictEqual({**got, **exp}, got) # assert exp_params <= got_params + assert {**got, **exp} == got def mod(param_set): for key, value in param_set.items(): @@ -766,10 +766,10 @@ def mod(param_set): ] exp_batch_params = {"wsid": WSID_STANDARD} - self.assertEqual(exp_batch_run_params, batch_run_params) - self.assertEqual(exp_batch_params, batch_params) + assert exp_batch_run_params == batch_run_params + assert exp_batch_params == batch_params - self.assertEqual(mock_comm.call_count, 0) + assert mock_comm.call_count == 0 @mock.patch(CLIENTS_AM, get_mock_client) @mock.patch(JOB_COMM_MOCK) @@ -782,23 +782,22 @@ def test_run_app_batch__good_inputs(self, auth, c): test_input = get_bulk_run_good_inputs() new_jobs = self.am.run_app_batch(test_input) - self.assertIsInstance(new_jobs, dict) - self.assertIn("parent_job", new_jobs) - self.assertIn("child_jobs", new_jobs) - self.assertTrue(new_jobs["parent_job"]) + assert isinstance(new_jobs, dict) + assert "parent_job" in new_jobs + assert "child_jobs" in new_jobs + assert new_jobs["parent_job"] parent_job = new_jobs["parent_job"] child_jobs = new_jobs["child_jobs"] - self.assertIsInstance(parent_job, Job) - self.assertIsInstance(child_jobs, list) - self.assertEqual(len(child_jobs), 3) - self.assertEqual( - [job.job_id for job in child_jobs], - [f"{self.test_job_id}_child_{i}" for i in range(len(child_jobs))], - ) + assert isinstance(parent_job, Job) + assert isinstance(child_jobs, list) + assert len(child_jobs) == 3 + assert [job.job_id for job in child_jobs] == [ + f"{self.test_job_id}_child_{i}" for i in range(len(child_jobs)) + ] self._verify_comm_success(c.return_value.send_comm_message, True, num_jobs=4) - for job in [parent_job] + child_jobs: - self.assertEqual(False, self.jm._running_jobs[job.job_id]["refresh"]) + for job in [parent_job, *child_jobs]: + assert self.jm._running_jobs[job.job_id]["refresh"] is False @mock.patch(CLIENTS_AM, get_mock_client) @mock.patch(JOB_COMM_MOCK) @@ -814,10 +813,11 @@ def test_run_app_batch__from_gui_cell(self, auth, c): # test with / w/o run_id # should return None, fire a couple of messages for run_id in run_ids: - self.assertIsNone( + assert ( self.am.run_app_batch( get_bulk_run_good_inputs(), cell_id=cell_id, run_id=run_id ) + is None ) self._verify_comm_success( @@ -969,25 +969,25 @@ def test_reconstitute_shared_params(self): # Merge shared_params into each params dict self.am._reconstitute_shared_params(app_info_el) - self.assertEqual(expected, app_info_el) + assert expected == app_info_el # No shared_params means no change self.am._reconstitute_shared_params(app_info_el) - self.assertEqual(expected, app_info_el) + assert expected == app_info_el @mock.patch(CLIENTS_AM_SM, get_mock_client) def test_app_description(self): desc = self.am.app_description(self.good_app_id, tag=self.good_tag) - self.assertIsInstance(desc, HTML) + assert isinstance(desc, HTML) @mock.patch(CLIENTS_AM_SM, get_mock_client) def test_app_description_bad_tag(self): - with self.assertRaises(ValueError): + with pytest.raises(ValueError): self.am.app_description(self.good_app_id, tag=self.bad_tag) @mock.patch(CLIENTS_AM_SM, get_mock_client) def test_app_description_bad_name(self): - with self.assertRaises(ValueError): + with pytest.raises(ValueError): self.am.app_description(self.bad_app_id) @mock.patch(CLIENTS_AM_SM, get_mock_client) @@ -1016,9 +1016,9 @@ def test_validate_params(self): (params, ws_inputs) = app_util.validate_parameters( app_id, tag, spec_params, inputs ) - self.assertDictEqual(params, inputs) - self.assertIn("12345/8/1", ws_inputs) - self.assertIn("12345/7/1", ws_inputs) + assert params == inputs + assert "12345/8/1" in ws_inputs + assert "12345/7/1" in ws_inputs @mock.patch(CLIENTS_AM_SM, get_mock_client) @mock.patch(CLIENTS_SM, get_mock_client) @@ -1073,13 +1073,13 @@ def test_input_mapping(self): "workspace": ws_name, } ] - self.assertDictEqual(expected[0], mapped_inputs[0]) + assert expected[0] == mapped_inputs[0] ref_path = ( ws_name + "/MyReadsSet; " + ws_name + "/rhodobacterium.art.q10.PE.reads" ) # ref_paths get mocked as 1/1/1;2/2/2;...N/N/N;18836/5/1 ret = app_util.transform_param_value("resolved-ref", ref_path, None) - self.assertEqual(ret, "1/1/1;18836/5/1") + assert ret == "1/1/1;18836/5/1" @mock.patch(CLIENTS_AM_SM, get_mock_client) def test_generate_input(self): @@ -1088,14 +1088,14 @@ def test_generate_input(self): num_symbols = 8 generator = {"symbols": num_symbols, "prefix": prefix, "suffix": suffix} rand_str = self.am._generate_input(generator) - self.assertTrue(rand_str.startswith(prefix)) - self.assertTrue(rand_str.endswith(suffix)) - self.assertEqual(len(rand_str), len(prefix) + len(suffix) + num_symbols) + assert rand_str.startswith(prefix) + assert rand_str.endswith(suffix) + assert len(rand_str) == len(prefix) + len(suffix) + num_symbols def test_generate_input_bad(self): - with self.assertRaises(ValueError): + with pytest.raises(ValueError): self.am._generate_input({"symbols": "foo"}) - with self.assertRaises(ValueError): + with pytest.raises(ValueError): self.am._generate_input({"symbols": -1}) # N.b. the following test contacts the workspace @@ -1169,10 +1169,10 @@ def test_transform_input_good(self): for test in test_data: spec = test.get("spec") ret = app_util.transform_param_value(test["type"], test["value"], spec) - self.assertEqual(ret, test["expected"]) + assert ret == test["expected"] def test_transform_input_bad(self): - with self.assertRaises(ValueError): + with pytest.raises(ValueError): app_util.transform_param_value("foo", "bar", None) def _transform_comm_messages(self, comm_mock): @@ -1205,10 +1205,10 @@ def _verify_comm_error(self, comm_mock, cell_id=None, run_id=None) -> None: expected_message["content"]["cell_id"] = cell_id for key in ["error_message", "error_stacktrace"]: - self.assertTrue(key in transformed_call_args_list[0]["content"]) + assert key in transformed_call_args_list[0]["content"] del transformed_call_args_list[0]["content"][key] - self.assertEqual(transformed_call_args_list, [expected_message]) + assert transformed_call_args_list == [expected_message] def _single_messages(self, cell_id=None, run_id=None): return [ @@ -1254,7 +1254,7 @@ def _verify_comm_success( else: expected = self._single_messages(cell_id, run_id) - self.assertEqual(transformed_call_args_list, expected) + assert transformed_call_args_list == expected if __name__ == "__main__": diff --git a/src/biokbase/narrative/tests/test_auth.py b/src/biokbase/narrative/tests/test_auth.py index 040c2d4614..3047c81bdd 100644 --- a/src/biokbase/narrative/tests/test_auth.py +++ b/src/biokbase/narrative/tests/test_auth.py @@ -2,8 +2,6 @@ import os import pytest -from requests import HTTPError - from biokbase.auth import ( TokenInfo, UserInfo, @@ -18,6 +16,7 @@ ) from biokbase.narrative.common.url_config import URLS from biokbase.narrative.common.util import kbase_env +from requests import HTTPError AUTH_URL = URLS.auth + "/api/V2/" @@ -46,7 +45,7 @@ } -@pytest.fixture +@pytest.fixture() def mock_auth_call(requests_mock): def run_mock_auth( verb: str, endpoint: str, token: str, return_data: dict, status_code=200 @@ -68,9 +67,11 @@ def run_mock_auth( return run_mock_auth -@pytest.fixture +@pytest.fixture() def mock_token_endpoint(mock_auth_call): - def token_mocker(token, verb, return_info={}, status_code=200): + def token_mocker(token, verb, return_info=None, status_code=200): + if return_info is None: + return_info = {} return mock_auth_call( verb, "token", token, return_info, status_code=status_code ) @@ -78,9 +79,11 @@ def token_mocker(token, verb, return_info={}, status_code=200): return token_mocker -@pytest.fixture +@pytest.fixture() def mock_display_names_call(mock_auth_call): - def names_mocker(token, user_ids, return_info={}, status_code=200): + def names_mocker(token, user_ids, return_info=None, status_code=200): + if return_info is None: + return_info = {} return mock_auth_call( "GET", f"users/?list={','.join(user_ids)}", @@ -92,7 +95,7 @@ def names_mocker(token, user_ids, return_info={}, status_code=200): return names_mocker -@pytest.fixture +@pytest.fixture() def mock_me_call(mock_auth_call): def me_mocker(token, return_info, status_code=200): return mock_auth_call("GET", "me", token, return_info, status_code=status_code) diff --git a/src/biokbase/narrative/tests/test_batch.py b/src/biokbase/narrative/tests/test_batch.py index 56c3499895..269f7facb5 100644 --- a/src/biokbase/narrative/tests/test_batch.py +++ b/src/biokbase/narrative/tests/test_batch.py @@ -5,6 +5,7 @@ from unittest import mock import biokbase.narrative.jobs.specmanager +import pytest from biokbase.narrative.jobs.batch import ( _generate_vals, _is_singleton, @@ -37,17 +38,17 @@ def test_list_objects(self): name=t.get("name"), fuzzy_name=t.get("fuzzy_name", True), ) - self.assertEqual(len(objs), t["count"]) + assert len(objs) == t["count"] for o in objs: if t.get("type"): - self.assertTrue(o["type"].startswith(t.get("type"))) - [self.assertIn(k, o) for k in req_keys] + assert o["type"].startswith(t.get("type")) + for k in req_keys: + assert k in o @mock.patch("biokbase.narrative.jobs.batch.clients.get", get_mock_client) def test_list_objects_bad_type(self): - with self.assertRaises(ValueError) as e: + with pytest.raises(ValueError, match="is not a valid type."): list_objects(obj_type="NotAType") - self.assertIn("is not a valid type.", str(e.exception)) @mock.patch( "biokbase.narrative.jobs.batch.specmanager.clients.get", get_mock_client @@ -81,21 +82,21 @@ def test_get_input_scaffold(self): "trailing_min_quality": None, "translate_to_phred33": None, } - self.assertEqual(scaffold_standard, scaffold) + assert scaffold_standard == scaffold @mock.patch("biokbase.narrative.jobs.specmanager.clients.get", get_mock_client) def test_get_input_scaffold_bad_id(self): - with self.assertRaises(ValueError) as e: + with pytest.raises( + ValueError, match='Unknown app id "foo" tagged as "release"' + ): get_input_scaffold("foo") - self.assertIn('Unknown app id "foo" tagged as "release"', str(e.exception)) @mock.patch("biokbase.narrative.jobs.specmanager.clients.get", get_mock_client) def test_get_input_scaffold_bad_tag(self): - with self.assertRaises(ValueError) as e: + with pytest.raises( + ValueError, match="Can't find tag bar - allowed tags are release, beta, dev" + ): get_input_scaffold("foo", tag="bar") - self.assertIn( - "Can't find tag bar - allowed tags are release, beta, dev", str(e.exception) - ) @mock.patch( "biokbase.narrative.jobs.batch.specmanager.clients.get", get_mock_client @@ -128,7 +129,7 @@ def test_get_input_scaffold_defaults(self): "trailing_min_quality": "3", "translate_to_phred33": "1", } - self.assertEqual(scaffold_standard, scaffold) + assert scaffold_standard == scaffold @mock.patch("biokbase.narrative.jobs.batch.StagingHelper", MockStagingHelper) def test_list_files(self): @@ -139,7 +140,7 @@ def test_list_files(self): ] for f in name_filters: files = list_files(name=f.get("name")) - self.assertEqual(len(files), f.get("count")) + assert len(files) == f.get("count") def test__generate_vals(self): good_inputs = [ @@ -170,28 +171,28 @@ def test__generate_vals(self): ret_val = _generate_vals(i["tuple"]) if "is_float" in i: ret_val = [round(x, 2) for x in ret_val] - self.assertEqual(ret_val, i["vals"]) + assert ret_val == i["vals"] unreachable_inputs = [(-1, -1, 5), (0, -1, 10), (-20, 5, -30), (10, -1, 20)] for t in unreachable_inputs: - with self.assertRaises(ValueError) as e: + with pytest.raises( + ValueError, + match="The maximum value of this tuple will never be reached based on the interval value", + ): _generate_vals(t) - self.assertIn( - "The maximum value of this tuple will never be reached based on the interval value", - str(e.exception), - ) - with self.assertRaises(ValueError) as e: + + with pytest.raises( + ValueError, match="The input tuple must be entirely numeric" + ): _generate_vals(("a", -1, 1)) - self.assertIn("The input tuple must be entirely numeric", str(e.exception)) - with self.assertRaises(ValueError) as e: + + with pytest.raises(ValueError, match="The interval value must not be 0"): _generate_vals((10, 0, 1)) - self.assertIn("The interval value must not be 0", str(e.exception)) wrong_size = [(1,), (1, 2), (1, 2, 3, 4), ()] for s in wrong_size: - with self.assertRaises(ValueError) as e: + with pytest.raises(ValueError, match="The input tuple must have 3 values"): _generate_vals(s) - self.assertIn("The input tuple must have 3 values", str(e.exception)) def test__is_singleton(self): scalar_param = { @@ -201,10 +202,10 @@ def test__is_singleton(self): "is_output": 0, "type": "int", } - self.assertTrue(_is_singleton(1, scalar_param)) - self.assertTrue(_is_singleton("foo", scalar_param)) - self.assertFalse(_is_singleton([1, 2, 3], scalar_param)) - self.assertFalse(_is_singleton([1], scalar_param)) + assert _is_singleton(1, scalar_param) + assert _is_singleton("foo", scalar_param) + assert _is_singleton([1, 2, 3], scalar_param) is False + assert _is_singleton([1], scalar_param) is False group_param = { "allow_multiple": 0, @@ -213,8 +214,8 @@ def test__is_singleton(self): "parameter_ids": ["first", "second"], "type": "group", } - self.assertTrue(_is_singleton({"first": 1, "second": 2}, group_param)) - self.assertFalse(_is_singleton([{"first": 1, "second": 2}], group_param)) + assert _is_singleton({"first": 1, "second": 2}, group_param) is True + assert _is_singleton([{"first": 1, "second": 2}], group_param) is False list_param = { "allow_multiple": 1, @@ -222,9 +223,9 @@ def test__is_singleton(self): "is_group": False, "type": "int", } - self.assertTrue(_is_singleton(["foo"], list_param)) - self.assertFalse(_is_singleton([["a", "b"], ["c", "d"]], list_param)) - self.assertFalse(_is_singleton([["a"]], list_param)) + assert _is_singleton(["foo"], list_param) is True + assert _is_singleton([["a", "b"], ["c", "d"]], list_param) is False + assert _is_singleton([["a"]], list_param) is False @mock.patch( "biokbase.narrative.jobs.batch.specmanager.clients.get", get_mock_client @@ -234,28 +235,23 @@ def test_generate_input_batch(self): biokbase.narrative.jobs.specmanager.SpecManager().reload() app_id = "kb_trimmomatic/run_trimmomatic" # fail with no inputs - with self.assertRaises(ValueError) as e: + with pytest.raises( + ValueError, + match="No inputs were given! If you just want to build an empty input set, try get_input_scaffold.", + ): generate_input_batch(app_id) - self.assertIn( - "No inputs were given! If you just want to build an empty input set, try get_input_scaffold.", - str(e.exception), - ) # fail with bad app id - with self.assertRaises(ValueError) as e: + with pytest.raises(ValueError, match="Unknown app id"): generate_input_batch("nope") - self.assertIn("Unknown app id", str(e.exception)) # fail with bad tag, and good app id - with self.assertRaises(ValueError) as e: + with pytest.raises(ValueError, match="Can't find tag foo"): generate_input_batch(app_id, tag="foo") - self.assertIn("Can't find tag foo", str(e.exception)) # fail with no output template and no default - with self.assertRaises(ValueError) as e: + with pytest.raises(ValueError, match="No output template provided"): generate_input_batch(app_id, input_reads_ref="abcde") - self.assertIn("No output template provided", str(e.exception)) # fail with incorrect input - with self.assertRaises(ValueError) as e: + with pytest.raises(ValueError, match="is not a parameter"): generate_input_batch(app_id, not_an_input="something") - self.assertIn("is not a parameter", str(e.exception)) # a simple test, should make 4, all with same input and output strings. inputs = { @@ -265,15 +261,15 @@ def test_generate_input_batch(self): "translate_to_phred33": [0, 1], } input_batch = generate_input_batch(app_id, **inputs) - self.assertEqual(len(input_batch), 4) + assert len(input_batch) == 4 for b in input_batch: - self.assertEqual(b["input_reads_ref"], inputs["input_reads_ref"]) - self.assertEqual(b["output_reads_name"], inputs["output_reads_name"]) - self.assertIn( - b["adapter_clip"][0]["palindrome_clip_threshold"], - inputs["palindrome_clip_threshold"], + assert b["input_reads_ref"] == inputs["input_reads_ref"] + assert b["output_reads_name"] == inputs["output_reads_name"] + assert ( + b["adapter_clip"][0]["palindrome_clip_threshold"] + in inputs["palindrome_clip_threshold"] ) - self.assertIn(b["translate_to_phred33"], inputs["translate_to_phred33"]) + assert b["translate_to_phred33"] in inputs["translate_to_phred33"] # more complex test, should make several, uses ranges. inputs = { @@ -282,9 +278,8 @@ def test_generate_input_batch(self): "min_length": (0, 10, 100), } input_batch = generate_input_batch(app_id, **inputs) - self.assertEqual( - len(input_batch), 22 - ) # product of [0,10,20,30,40,50,60,70,80,90,100] and [5,7] + assert len(input_batch) == 22 + # product of [0,10,20,30,40,50,60,70,80,90,100] and [5,7] len_ranges = {} for i in range(0, 101, 10): len_ranges[i] = 0 @@ -295,24 +290,24 @@ def test_generate_input_batch(self): for b in input_batch: palindrome_ranges[b["adapter_clip"][0]["palindrome_clip_threshold"]] += 1 - self.assertIn( - b["adapter_clip"][0]["palindrome_clip_threshold"], - inputs["palindrome_clip_threshold"], + assert ( + b["adapter_clip"][0]["palindrome_clip_threshold"] + in inputs["palindrome_clip_threshold"] ) len_ranges[b["min_length"]] += 1 - self.assertIn(b["min_length"], len_ranges) - self.assertIn(b["output_reads_name"], out_strs) + assert b["min_length"] in len_ranges + assert b["output_reads_name"] in out_strs out_strs[b["output_reads_name"]] += 1 # make sure each value is used the right number of times. - self.assertEqual(len(len_ranges.keys()), 11) + assert len(len_ranges.keys()) == 11 for v in len_ranges.values(): - self.assertEqual(v, 2) - self.assertEqual(len(palindrome_ranges.keys()), 2) + assert v == 2 + assert len(palindrome_ranges.keys()) == 2 for v in palindrome_ranges.values(): - self.assertEqual(v, 11) - self.assertEqual(len(out_strs.keys()), 22) + assert v == 11 + assert len(out_strs.keys()) == 22 for v in out_strs.values(): - self.assertEqual(v, 1) + assert v == 1 def test__prepare_output_vals(self): basic_params_dict = { @@ -333,35 +328,32 @@ def test__prepare_output_vals(self): out_vals = _prepare_output_vals(o, basic_params_dict, 3) o2 = o.copy() o2.update({"other_param": "default_output${run_number}"}) - self.assertEqual(o2, out_vals) + assert o2 == out_vals # check default output value out_vals = _prepare_output_vals( {"output": "foo", "other_param": None}, basic_params_dict, 3 ) - self.assertEqual( - out_vals["other_param"], - basic_params_dict["other_param"]["default"] + "${run_number}", + assert ( + out_vals["other_param"] + == basic_params_dict["other_param"]["default"] + "${run_number}" ) # some fails - with self.assertRaises(ValueError) as e: + with pytest.raises( + ValueError, + match="The output parameter output must have 5 values if it's a list", + ): _prepare_output_vals({"output": ["a", "b", "c"]}, basic_params_dict, 5) - self.assertIn( - "The output parameter output must have 5 values if it's a list", - str(e.exception), - ) - with self.assertRaises(ValueError) as e: + with pytest.raises( + ValueError, + match='No output template provided for parameter "output" and no default value found!', + ): _prepare_output_vals({"output": None}, basic_params_dict, 3) - self.assertIn( - 'No output template provided for parameter "output" and no default value found!', - str(e.exception), - ) - with self.assertRaises(ValueError) as e: + with pytest.raises( + ValueError, + match="Output template field not_a_param doesn't match a parameter id or 'run_number'", + ): _prepare_output_vals({"output": "foo_${not_a_param}"}, basic_params_dict, 3) - self.assertIn( - "Output template field not_a_param doesn't match a parameter id or 'run_number'", - str(e.exception), - ) diff --git a/src/biokbase/narrative/tests/test_clients.py b/src/biokbase/narrative/tests/test_clients.py index 6d995ffae5..15c25a00f4 100644 --- a/src/biokbase/narrative/tests/test_clients.py +++ b/src/biokbase/narrative/tests/test_clients.py @@ -1,15 +1,13 @@ import pytest - -from biokbase.narrative import clients from biokbase.catalog.Client import Catalog as Catalog_Client from biokbase.execution_engine2.execution_engine2Client import ( execution_engine2 as EE2_Client, ) +from biokbase.narrative import clients from biokbase.narrative_method_store.client import NarrativeMethodStore as NMS_Client from biokbase.service.Client import Client as Service_Client from biokbase.workspace.client import Workspace as WS_Client - name_to_type_tests = [ ("workspace", WS_Client), ("execution_engine2", EE2_Client), @@ -19,7 +17,7 @@ ] -@pytest.mark.parametrize("client_name,client_type", name_to_type_tests) +@pytest.mark.parametrize(("client_name", "client_type"), name_to_type_tests) def test_valid_clients(client_name, client_type): client = clients.get(client_name) assert isinstance(client, client_type) diff --git a/src/biokbase/narrative/tests/test_content_manager_util.py b/src/biokbase/narrative/tests/test_content_manager_util.py index 66a65b1fa1..d1a421eb1f 100644 --- a/src/biokbase/narrative/tests/test_content_manager_util.py +++ b/src/biokbase/narrative/tests/test_content_manager_util.py @@ -8,21 +8,21 @@ def test_base_model(self): name = "foo" path = "bar" model = base_model(name, path) - self.assertIn("name", model) - self.assertEqual(model["name"], name) - self.assertIn("path", model) - self.assertEqual(model["path"], path) - self.assertIn("last_modified", model) - self.assertEqual(model["last_modified"], "00-00-0000") - self.assertIn("created", model) - self.assertEqual(model["created"], "00-00-0000") - self.assertIn("content", model) - self.assertIsNone(model["content"]) - self.assertIn("format", model) - self.assertIsNone(model["format"]) - self.assertIn("mimetype", model) - self.assertIsNone(model["mimetype"]) - self.assertIn("writable", model) - self.assertFalse(model["writable"]) - self.assertIn("type", model) - self.assertIsNone(model["type"]) + assert "name" in model + assert model["name"] == name + assert "path" in model + assert model["path"] == path + assert "last_modified" in model + assert model["last_modified"] == "00-00-0000" + assert "created" in model + assert model["created"] == "00-00-0000" + assert "content" in model + assert model["content"] is None + assert "format" in model + assert model["format"] is None + assert "mimetype" in model + assert model["mimetype"] is None + assert "writable" in model + assert not model["writable"] + assert "type" in model + assert model["type"] is None diff --git a/src/biokbase/narrative/tests/test_exception_util.py b/src/biokbase/narrative/tests/test_exception_util.py index 36f09ca646..2eb595dd6f 100644 --- a/src/biokbase/narrative/tests/test_exception_util.py +++ b/src/biokbase/narrative/tests/test_exception_util.py @@ -1,10 +1,9 @@ import unittest import requests -from requests.exceptions import HTTPError - from biokbase.execution_engine2.baseclient import ServerError as EEServerError from biokbase.narrative.exception_util import transform_job_exception +from requests.exceptions import HTTPError ERROR_MSG = "some error message" @@ -19,11 +18,11 @@ def test_transform_ee2_err(self): name = "EEError" ee2_err = EEServerError(name, code, message) nar_err = transform_job_exception(ee2_err) - self.assertEqual(nar_err.code, code) - self.assertEqual(nar_err.message, message) - self.assertEqual(nar_err.name, name) - self.assertEqual(nar_err.source, "ee2") - self.assertIsNone(nar_err.error) + assert nar_err.code == code + assert nar_err.message == message + assert nar_err.name == name + assert nar_err.source == "ee2" + assert nar_err.error is None def test_transform_ee2_err__with_error(self): code = 1000 @@ -32,11 +31,11 @@ def test_transform_ee2_err__with_error(self): error = "Unable to perform some request" ee2_err = EEServerError(name, code, message) nar_err = transform_job_exception(ee2_err, error) - self.assertEqual(nar_err.code, code) - self.assertEqual(nar_err.message, message) - self.assertEqual(nar_err.name, name) - self.assertEqual(nar_err.source, "ee2") - self.assertEqual(nar_err.error, error) + assert nar_err.code == code + assert nar_err.message == message + assert nar_err.name == name + assert nar_err.source == "ee2" + assert nar_err.error == error def test_transform_http_err_unavailable(self): codes = [404, 502, 503] @@ -47,11 +46,11 @@ def test_transform_http_err_unavailable(self): res.status_code = c err = HTTPError(HTTP_ERROR_MSG, response=res) nar_err = transform_job_exception(err) - self.assertEqual(nar_err.code, c) - self.assertEqual(nar_err.message, message) - self.assertEqual(nar_err.name, name) - self.assertEqual(nar_err.source, "network") - self.assertIsNone(nar_err.error) + assert nar_err.code == c + assert nar_err.message == message + assert nar_err.name == name + assert nar_err.source == "network" + assert nar_err.error is None def test_transform_http_err_timeout(self): codes = [504, 598, 599] @@ -62,11 +61,11 @@ def test_transform_http_err_timeout(self): res.status_code = c err = HTTPError(HTTP_ERROR_MSG, response=res) nar_err = transform_job_exception(err) - self.assertEqual(nar_err.code, c) - self.assertEqual(nar_err.message, message) - self.assertEqual(nar_err.name, name) - self.assertEqual(nar_err.source, "network") - self.assertIsNone(nar_err.error) + assert nar_err.code == c + assert nar_err.message == message + assert nar_err.name == name + assert nar_err.source == "network" + assert nar_err.error is None def test_transform_http_err_internal(self): code = 500 @@ -76,11 +75,11 @@ def test_transform_http_err_internal(self): res.status_code = code err = HTTPError(HTTP_ERROR_MSG, response=res) nar_err = transform_job_exception(err) - self.assertEqual(nar_err.code, code) - self.assertEqual(nar_err.message, message) - self.assertEqual(nar_err.name, name) - self.assertEqual(nar_err.source, "network") - self.assertIsNone(nar_err.error) + assert nar_err.code == code + assert nar_err.message == message + assert nar_err.name == name + assert nar_err.source == "network" + assert nar_err.error is None def test_transform_http_err_unknown(self): code = 666 @@ -90,8 +89,8 @@ def test_transform_http_err_unknown(self): res.status_code = code err = HTTPError(HTTP_ERROR_MSG, response=res) nar_err = transform_job_exception(err) - self.assertEqual(nar_err.code, code) - self.assertEqual(nar_err.message, message) - self.assertEqual(nar_err.name, name) - self.assertEqual(nar_err.source, "network") - self.assertIsNone(nar_err.error) + assert nar_err.code == code + assert nar_err.message == message + assert nar_err.name == name + assert nar_err.source == "network" + assert nar_err.error is None diff --git a/src/biokbase/narrative/tests/test_job.py b/src/biokbase/narrative/tests/test_job.py index 2f33cf0002..3e636a27fb 100644 --- a/src/biokbase/narrative/tests/test_job.py +++ b/src/biokbase/narrative/tests/test_job.py @@ -1,11 +1,13 @@ import copy import itertools +import re import sys import unittest from contextlib import contextmanager from io import StringIO from unittest import mock +import pytest from biokbase.execution_engine2.baseclient import ServerError from biokbase.narrative.app_util import map_inputs_from_job, map_outputs_from_state from biokbase.narrative.jobs.job import ( @@ -29,8 +31,8 @@ JOB_COMPLETED, JOB_CREATED, JOB_RUNNING, - JOB_TERMINATED, JOB_TERMINAL_STATE, + JOB_TERMINATED, MAX_LOG_LINES, TERMINAL_JOBS, get_test_job, @@ -173,13 +175,13 @@ def setUpClass(cls): cls.NEW_CHILD_JOBS = ["cerulean", "magenta"] def check_jobs_equal(self, jobl, jobr): - self.assertEqual(jobl._acc_state, jobr._acc_state) + assert jobl._acc_state == jobr._acc_state with mock.patch(CLIENTS, get_mock_client): - self.assertEqual(jobl.refresh_state(), jobr.refresh_state()) + assert jobl.refresh_state() == jobr.refresh_state() for attr in JOB_ATTRS: - self.assertEqual(getattr(jobl, attr), getattr(jobr, attr)) + assert getattr(jobl, attr), getattr(jobr == attr) def check_job_attrs_custom(self, job, exp_attr=None): if not exp_attr: @@ -199,7 +201,7 @@ def check_job_attrs(self, job, job_id, exp_attrs=None, skip_state=False): if not exp_attrs and not skip_state: state = create_state_from_ee2(job_id) with mock.patch(CLIENTS, get_mock_client): - self.assertEqual(state, job.refresh_state()) + assert state == job.refresh_state() attrs = create_attrs_from_ee2(job_id) attrs.update(exp_attrs) @@ -208,17 +210,15 @@ def check_job_attrs(self, job, job_id, exp_attrs=None, skip_state=False): if name in ["child_jobs", "retry_ids"]: # job.child_jobs and job.retry_ids may query EE2 with mock.patch(CLIENTS, get_mock_client): - self.assertEqual(value, getattr(job, name)) + assert value == getattr(job, name) else: with assert_obj_method_called( MockClients, "check_job", call_status=False ): - self.assertEqual(value, getattr(job, name)) + assert value == getattr(job, name) def test_job_init__error_no_job_id(self): - with self.assertRaisesRegex( - ValueError, "Cannot create a job without a job ID!" - ): + with pytest.raises(ValueError, match="Cannot create a job without a job ID!"): Job({"params": {}, "app_id": "this/that"}) def test_job_init__from_job_id(self): @@ -277,7 +277,7 @@ def test_job_init__batch_family(self): self.check_job_attrs(job, job_id) batch_job = batch_jobs[BATCH_PARENT] - self.assertEqual(batch_job.job_id, batch_job.batch_id) + assert batch_job.job_id == batch_job.batch_id def test_job_from_state__custom(self): """ @@ -324,17 +324,17 @@ def test_set_job_attrs(self): job = create_job_from_ee2(JOB_COMPLETED) expected = create_state_from_ee2(JOB_COMPLETED) # job is completed so refresh_state will do nothing - self.assertEqual(job.refresh_state(), expected) + assert job.refresh_state() == expected for attr in ALL_ATTRS: - with self.assertRaisesRegex( + with pytest.raises( AttributeError, - "Job attributes must be updated using the `update_state` method", + match="Job attributes must be updated using the `update_state` method", ): setattr(job, attr, "BLAM!") # ensure nothing has changed - self.assertEqual(job.refresh_state(), expected) + assert job.refresh_state() == expected @mock.patch(CLIENTS, get_mock_client) def test_refresh_state__non_terminal(self): @@ -343,26 +343,25 @@ def test_refresh_state__non_terminal(self): """ # ee2_state is fully populated (includes job_input, no job_output) job = create_job_from_ee2(JOB_CREATED) - self.assertFalse(job.in_terminal_state()) + assert not job.in_terminal_state() state = job.refresh_state() - self.assertFalse(job.in_terminal_state()) - self.assertEqual(state["status"], "created") - + assert not job.in_terminal_state() + assert state["status"] == "created" expected_state = create_state_from_ee2(JOB_CREATED) - self.assertEqual(state, expected_state) + assert state == expected_state def test_refresh_state__terminal(self): """ test that a completed job emits its state without calling check_job """ job = create_job_from_ee2(JOB_COMPLETED) - self.assertTrue(job.in_terminal_state()) + assert job.in_terminal_state() expected = create_state_from_ee2(JOB_COMPLETED) with assert_obj_method_called(MockClients, "check_job", call_status=False): state = job.refresh_state() - self.assertEqual(state["status"], "completed") - self.assertEqual(state, expected) + assert state["status"] == "completed" + assert state == expected @mock.patch(CLIENTS, get_failing_mock_client) def test_refresh_state__raise_exception(self): @@ -370,8 +369,8 @@ def test_refresh_state__raise_exception(self): test that the correct exception is thrown if check_jobs cannot be called """ job = create_job_from_ee2(JOB_CREATED) - self.assertFalse(job.in_terminal_state()) - with self.assertRaisesRegex(ServerError, "check_jobs failed"): + assert not job.in_terminal_state() + with pytest.raises(ServerError, match="check_jobs failed"): job.refresh_state() # TODO: improve this test @@ -384,7 +383,7 @@ def test_job_update__no_state(self): assert job.cached_state() == job_copy.cached_state() # should fail with error 'state must be a dict' - with self.assertRaisesRegex(TypeError, "state must be a dict"): + with pytest.raises(TypeError, match="state must be a dict"): job.update_state(None) assert job.cached_state() == job_copy.cached_state() @@ -402,10 +401,10 @@ def test_job_update__invalid_job_id(self): """ job = create_job_from_ee2(JOB_RUNNING) expected = create_state_from_ee2(JOB_RUNNING) - self.assertEqual(job.refresh_state(), expected) + assert job.refresh_state() == expected # try to update it with the job state from a different job - with self.assertRaisesRegex(ValueError, "Job ID mismatch in update_state"): + with pytest.raises(ValueError, match="Job ID mismatch in update_state"): job.update_state(get_test_job(JOB_COMPLETED)) @mock.patch(CLIENTS, get_mock_client) @@ -426,37 +425,37 @@ def test_job_info(self): ) with capture_stdout() as (out, err): job.info() - self.assertIn(info_str, out.getvalue().strip()) + assert info_str in out.getvalue().strip() def test_repr(self): job = create_job_from_ee2(JOB_COMPLETED) job_str = job.__repr__() - self.assertRegex("KBase Narrative Job - " + job.job_id, job_str) + assert re.search(job_str, "KBase Narrative Job - " + job.job_id) @mock.patch(CLIENTS, get_mock_client) def test_repr_js(self): job = create_job_from_ee2(JOB_COMPLETED) js_out = job._repr_javascript_() - self.assertIsInstance(js_out, str) + assert isinstance(js_out, str) # spot check to make sure the core pieces are present. needs the # element.html part, job_id, and widget - self.assertIn("element.html", js_out) - self.assertIn(job.job_id, js_out) - self.assertIn("kbaseNarrativeJobStatus", js_out) + assert "element.html" in js_out + assert job.job_id in js_out + assert "kbaseNarrativeJobStatus" in js_out @mock.patch("biokbase.narrative.widgetmanager.WidgetManager.show_output_widget") @mock.patch(CLIENTS, get_mock_client) def test_show_output_widget(self, mock_method): mock_method.return_value = True job = Job(get_test_job(JOB_COMPLETED)) - self.assertTrue(job.show_output_widget()) + assert job.show_output_widget() mock_method.assert_called_once() @mock.patch(CLIENTS, get_mock_client) def test_show_output_widget__incomplete_state(self): job = Job(get_test_job(JOB_CREATED)) - self.assertRegex( - job.show_output_widget(), "Job is incomplete! It has status 'created'" + assert re.search( + "Job is incomplete! It has status 'created'", job.show_output_widget() ) @mock.patch(CLIENTS, get_mock_client) @@ -468,34 +467,34 @@ def test_log(self): job = create_job_from_ee2(JOB_COMPLETED) logs = job.log() # we know there's MAX_LOG_LINES lines total, so roll with it that way. - self.assertEqual(logs[0], total_lines) - self.assertEqual(len(logs[1]), total_lines) + assert logs[0] == total_lines + assert len(logs[1]) == total_lines for i in range(len(logs[1])): line = logs[1][i] - self.assertIn("is_error", line) - self.assertIn("line", line) - self.assertIn(str(i), line["line"]) + assert "is_error" in line + assert "line" in line + assert str(i) in line["line"] # grab the last half offset = int(MAX_LOG_LINES / 2) logs = job.log(first_line=offset) - self.assertEqual(logs[0], total_lines) - self.assertEqual(len(logs[1]), offset) + assert logs[0] == total_lines + assert len(logs[1]) == offset for i in range(total_lines - offset): - self.assertIn(str(i + offset), logs[1][i]["line"]) + assert str(i + offset) in logs[1][i]["line"] # grab a bite from the middle num_fetch = int(MAX_LOG_LINES / 5) logs = job.log(first_line=offset, num_lines=num_fetch) - self.assertEqual(logs[0], total_lines) - self.assertEqual(len(logs[1]), num_fetch) + assert logs[0] == total_lines + assert len(logs[1]) == num_fetch for i in range(num_fetch): - self.assertIn(str(i + offset), logs[1][i]["line"]) + assert str(i + offset) in logs[1][i]["line"] # should normalize negative numbers properly logs = job.log(first_line=-5) - self.assertEqual(logs[0], total_lines) - self.assertEqual(len(logs[1]), total_lines) + assert logs[0] == total_lines + assert len(logs[1]) == total_lines logs = job.log(num_lines=-5) - self.assertEqual(logs[0], total_lines) - self.assertEqual(len(logs[1]), 0) + assert logs[0] == total_lines + assert len(logs[1]) == 0 @mock.patch(CLIENTS, get_mock_client) def test_parameters(self): @@ -504,14 +503,14 @@ def test_parameters(self): """ job_state = get_test_job(JOB_COMPLETED) job_params = job_state.get("job_input", {}).get("params") - self.assertIsNotNone(job_params) + assert job_params is not None job = Job(job_state) - self.assertIsNotNone(job.params) + assert job.params is not None with assert_obj_method_called(MockClients, "check_job", call_status=False): params = job.parameters() - self.assertIsNotNone(params) - self.assertEqual(params, job_params) + assert params is not None + assert params == job_params @mock.patch(CLIENTS, get_mock_client) def test_parameters__param_fetch_ok(self): @@ -521,16 +520,16 @@ def test_parameters__param_fetch_ok(self): """ job_state = get_test_job(JOB_CREATED) job_params = job_state.get("job_input", {}).get("params") - self.assertIsNotNone(job_params) + assert job_params is not None # delete the job params from the input del job_state["job_input"]["params"] job = Job(job_state) - self.assertEqual(job.params, JOB_ATTR_DEFAULTS["params"]) + assert job.params == JOB_ATTR_DEFAULTS["params"] with assert_obj_method_called(MockClients, "check_job", call_status=True): params = job.parameters() - self.assertEqual(params, job_params) + assert params == job_params @mock.patch(CLIENTS, get_failing_mock_client) def test_parameters__param_fetch_fail(self): @@ -540,9 +539,9 @@ def test_parameters__param_fetch_fail(self): job_state = get_test_job(JOB_TERMINATED) del job_state["job_input"]["params"] job = Job(job_state) - self.assertEqual(job.params, JOB_ATTR_DEFAULTS["params"]) + assert job.params == JOB_ATTR_DEFAULTS["params"] - with self.assertRaisesRegex(Exception, "Unable to fetch parameters for job"): + with pytest.raises(Exception, match="Unable to fetch parameters for job"): job.parameters() @mock.patch(CLIENTS, get_mock_client) @@ -552,8 +551,7 @@ def test_parent_children__ok(self): create_state_from_ee2(BATCH_PARENT), children=child_jobs, ) - - self.assertFalse(parent_job.in_terminal_state()) + assert not parent_job.in_terminal_state() # Make all child jobs completed with mock.patch.object( @@ -563,34 +561,34 @@ def test_parent_children__ok(self): ): for child_job in child_jobs: child_job.refresh_state(force_refresh=True) - - self.assertTrue(parent_job.in_terminal_state()) + assert parent_job.in_terminal_state() def test_parent_children__fail(self): parent_state = create_state_from_ee2(BATCH_PARENT) child_states = [create_state_from_ee2(job_id) for job_id in BATCH_CHILDREN] - with self.assertRaisesRegex( - ValueError, "Must supply children when setting children of batch job parent" + with pytest.raises( + ValueError, + match="Must supply children when setting children of batch job parent", ): Job(parent_state) child_jobs = [Job(child_state) for child_state in child_states] - with self.assertRaisesRegex(ValueError, CHILD_ID_MISMATCH): + with pytest.raises(ValueError, match=CHILD_ID_MISMATCH): Job( parent_state, children=child_jobs[1:], ) - with self.assertRaisesRegex(ValueError, CHILD_ID_MISMATCH): + with pytest.raises(ValueError, match=CHILD_ID_MISMATCH): Job( parent_state, children=child_jobs * 2, ) - with self.assertRaisesRegex(ValueError, CHILD_ID_MISMATCH): + with pytest.raises(ValueError, match=CHILD_ID_MISMATCH): Job( parent_state, - children=child_jobs + [create_job_from_ee2(JOB_COMPLETED)], + children=[*child_jobs, create_job_from_ee2(JOB_COMPLETED)], ) def test_get_viewer_params__active(self): @@ -600,7 +598,7 @@ def test_get_viewer_params__active(self): job = create_job_from_ee2(job_id) state = create_state_from_ee2(job_id) out = job.get_viewer_params(state) - self.assertIsNone(out) + assert out is None @mock.patch(CLIENTS, get_mock_client) def test_get_viewer_params__finished(self): @@ -609,7 +607,7 @@ def test_get_viewer_params__finished(self): state = create_state_from_ee2(job_id) exp = get_widget_info(job_id) got = job.get_viewer_params(state) - self.assertEqual(exp, got) + assert exp == got def test_get_viewer_params__batch_parent(self): """ @@ -621,7 +619,7 @@ def test_get_viewer_params__batch_parent(self): job = create_job_from_ee2(BATCH_PARENT, children=batch_children) out = job.get_viewer_params(state) - self.assertIsNone(out) + assert out is None @mock.patch(CLIENTS, get_mock_client) def test_query_job_states_single_job(self): @@ -645,14 +643,14 @@ def test_query_job_states(self): exp = create_state_from_ee2( job_id, exclude_fields=JOB_INIT_EXCLUDED_JOB_STATE_FIELDS ) - self.assertEqual(exp, got) + assert exp == got states = Job.query_ee2_states(ALL_JOBS, init=False) for job_id, got in states.items(): exp = create_state_from_ee2( job_id, exclude_fields=EXCLUDED_JOB_STATE_FIELDS ) - self.assertEqual(exp, got) + assert exp == got def test_refresh_attrs__non_batch_active(self): """ @@ -663,7 +661,7 @@ def test_refresh_attrs__non_batch_active(self): self.check_job_attrs(job, job_id) def mock_check_job(self_, params): - self.assertEqual(params["job_id"], job_id) + assert params["job_id"] == job_id return {"retry_ids": self.NEW_RETRY_IDS} with mock.patch.object(MockClients, "check_job", mock_check_job): @@ -678,7 +676,7 @@ def test_refresh_attrs__non_batch_terminal(self): self.check_job_attrs(job, job_id) def mock_check_job(self_, params): - self.assertEqual(params["job_id"], job_id) + assert params["job_id"] == job_id return {"retry_ids": self.NEW_RETRY_IDS} with mock.patch.object(MockClients, "check_job", mock_check_job): @@ -704,7 +702,7 @@ def test_refresh_attrs__batch(self): self.check_job_attrs(job, job_id) def mock_check_job(self_, params): - self.assertEqual(params["job_id"], job_id) + assert params["job_id"] == job_id return {"child_jobs": self.NEW_CHILD_JOBS} with mock.patch.object(MockClients, "check_job", mock_check_job): @@ -714,24 +712,22 @@ def test_in_terminal_state(self): all_jobs = get_all_jobs() for job_id, job in all_jobs.items(): - self.assertEqual(JOB_TERMINAL_STATE[job_id], job.in_terminal_state()) + assert JOB_TERMINAL_STATE[job_id] == job.in_terminal_state() @mock.patch(CLIENTS, get_mock_client) def test_in_terminal_state__batch(self): batch_fam = get_batch_family_jobs(return_list=True) batch_job, child_jobs = batch_fam[0], batch_fam[1:] - - self.assertFalse(batch_job.in_terminal_state()) + assert not batch_job.in_terminal_state() def mock_check_job(self_, params): - self.assertTrue(params["job_id"] in BATCH_CHILDREN) + assert params["job_id"] in BATCH_CHILDREN return {"status": COMPLETED_STATUS} with mock.patch.object(MockClients, "check_job", mock_check_job): for job in child_jobs: job.refresh_state(force_refresh=True) - - self.assertTrue(batch_job.in_terminal_state()) + assert batch_job.in_terminal_state() def test_in_cells(self): all_jobs = get_all_jobs() @@ -739,20 +735,21 @@ def test_in_cells(self): # Iterate through all combinations of cell IDs for combo_len in range(len(cell_ids) + 1): for combo in itertools.combinations(cell_ids, combo_len): - combo = list(combo) + combo_list = list(combo) # Get jobs expected to be associated with the cell IDs exp_job_ids = [ job_id for cell_id, job_ids in JOBS_BY_CELL_ID.items() for job_id in job_ids - if cell_id in combo + if cell_id in combo_list ] for job_id, job in all_jobs.items(): - self.assertEqual(job_id in exp_job_ids, job.in_cells(combo)) + expected = job_id in exp_job_ids + assert job.in_cells(combo_list) == expected def test_in_cells__none(self): job = create_job_from_ee2(JOB_COMPLETED) - with self.assertRaisesRegex(ValueError, "cell_ids cannot be None"): + with pytest.raises(ValueError, match="cell_ids cannot be None"): job.in_cells(None) def test_in_cells__batch__same_cell(self): @@ -762,9 +759,9 @@ def test_in_cells__batch__same_cell(self): for job in child_jobs: job._acc_state["job_input"]["narrative_cell_info"]["cell_id"] = "hello" - self.assertTrue(batch_job.in_cells(["hi", "hello"])) + assert batch_job.in_cells(["hi", "hello"]) - self.assertFalse(batch_job.in_cells(["goodbye", "hasta manana"])) + assert not batch_job.in_cells(["goodbye", "hasta manana"]) def test_in_cells__batch__diff_cells(self): batch_fam = get_batch_family_jobs(return_list=True) @@ -775,17 +772,17 @@ def test_in_cells__batch__diff_cells(self): job._acc_state["job_input"]["narrative_cell_info"]["cell_id"] = cell_id for cell_id in children_cell_ids: - self.assertTrue(batch_job.in_cells([cell_id])) - self.assertTrue(batch_job.in_cells(["A", cell_id, "B"])) - self.assertTrue(batch_job.in_cells([cell_id, "B", "A"])) - self.assertTrue(batch_job.in_cells(["B", "A", cell_id])) + assert batch_job.in_cells([cell_id]) + assert batch_job.in_cells(["A", cell_id, "B"]) + assert batch_job.in_cells([cell_id, "B", "A"]) + assert batch_job.in_cells(["B", "A", cell_id]) - self.assertFalse(batch_job.in_cells(["goodbye", "hasta manana"])) + assert not batch_job.in_cells(["goodbye", "hasta manana"]) def test_app_name(self): for job in get_all_jobs().values(): if job.batch_job: - self.assertEqual("batch", job.app_name) + assert job.app_name == "batch" else: test_spec = get_test_spec(job.tag, job.app_id) - self.assertEqual(test_spec["info"]["name"], job.app_name) + assert test_spec["info"]["name"] == job.app_name diff --git a/src/biokbase/narrative/tests/test_job_util.py b/src/biokbase/narrative/tests/test_job_util.py index 656d86f160..554957fc36 100644 --- a/src/biokbase/narrative/tests/test_job_util.py +++ b/src/biokbase/narrative/tests/test_job_util.py @@ -1,5 +1,6 @@ import unittest +import pytest from biokbase.narrative.jobs.util import load_job_constants @@ -14,7 +15,7 @@ def test_load_job_constants__no_file(self): "job_constants", "does_not_exist.json", ] - with self.assertRaises(FileNotFoundError): + with pytest.raises(FileNotFoundError): load_job_constants(file_path) def test_load_job_constants__missing_section(self): @@ -27,8 +28,9 @@ def test_load_job_constants__missing_section(self): "job_constants", "job_config-missing-datatype.json", ] - with self.assertRaisesRegex( - ValueError, "job_config.json is missing the 'message_types' config section" + with pytest.raises( + ValueError, + match="job_config.json is missing the 'message_types' config section", ): load_job_constants(file_path) @@ -42,9 +44,9 @@ def test_load_job_constants__missing_value(self): "job_constants", "job_config-missing-item.json", ] - with self.assertRaisesRegex( + with pytest.raises( ValueError, - "job_config.json is missing the following values for params: BATCH_ID, FIRST_LINE, JOB_ID, LATEST, NUM_LINES, TS", + match="job_config.json is missing the following values for params: BATCH_ID, FIRST_LINE, JOB_ID, LATEST, NUM_LINES, TS", ): load_job_constants(file_path) @@ -52,9 +54,9 @@ def test_load_job_constants__valid(self): # the live file! (params, message_types) = load_job_constants() for item in ["BATCH_ID", "JOB_ID"]: - self.assertIn(item, params) + assert item in params for item in ["STATUS", "RETRY", "INFO", "ERROR"]: - self.assertIn(item, message_types) + assert item in message_types if __name__ == "__main__": diff --git a/src/biokbase/narrative/tests/test_jobcomm.py b/src/biokbase/narrative/tests/test_jobcomm.py index ca49991b2e..757e41558c 100644 --- a/src/biokbase/narrative/tests/test_jobcomm.py +++ b/src/biokbase/narrative/tests/test_jobcomm.py @@ -5,6 +5,7 @@ import unittest from unittest import mock +import pytest from biokbase.narrative.exception_util import ( JobRequestException, NarrativeException, @@ -169,19 +170,16 @@ def check_error_message(self, req, err, extra_params=None): source = req msg = self.jc._comm.last_message - self.assertEqual( - { - "msg_type": ERROR, - "content": { - "request": request, - "source": source, - **extra_params, - "name": type(err).__name__, - "message": str(err), - }, + assert msg == { + "msg_type": ERROR, + "content": { + "request": request, + "source": source, + **extra_params, + "name": type(err).__name__, + "message": str(err), }, - msg, - ) + } def check_job_id_list__no_jobs(self, request_type): job_id_list = [None, ""] @@ -190,12 +188,12 @@ def check_job_id_list__no_jobs(self, request_type): err = JobRequestException(JOBS_MISSING_ERR, job_id_list) # using handler - with self.assertRaisesRegex(type(err), re.escape(str(err))): + with pytest.raises(type(err), match=re.escape(str(err))): self.jc._handle_comm_message(req_dict) self.check_error_message(req_dict, err) # run directly - with self.assertRaisesRegex(type(err), re.escape(str(err))): + with pytest.raises(type(err), match=re.escape(str(err))): self.jc._msg_map[request_type](req) def check_job_id_list__dne_jobs(self, request_type, response_type=None): @@ -208,17 +206,14 @@ def check_job_id_list__dne_jobs(self, request_type, response_type=None): req_dict = make_comm_msg(request_type, job_id_list, False) self.jc._handle_comm_message(req_dict) msg = self.jc._comm.last_message - self.assertEqual( - { - "msg_type": response_type if response_type else request_type, - "content": expected_output, - }, - msg, - ) + assert msg == { + "msg_type": response_type if response_type else request_type, + "content": expected_output, + } def check_id_error(self, req_dict, err): self.jc._comm.clear_message_cache() - with self.assertRaisesRegex(type(err), re.escape(str(err))): + with pytest.raises(type(err), match=re.escape(str(err))): self.jc._handle_comm_message(req_dict) self.check_error_message(req_dict, err) @@ -231,7 +226,7 @@ def check_job_id__no_job_test(self, request_type): self.check_id_error(req_dict, err) # run directly - with self.assertRaisesRegex(type(err), re.escape(str(err))): + with pytest.raises(type(err), match=re.escape(str(err))): self.jc._msg_map[request_type](req) def check_job_id__dne_test(self, request_type): @@ -261,77 +256,62 @@ def check_batch_id__not_batch_test(self, request_type): def test_send_comm_msg_ok(self): self.jc.send_comm_message("some_msg", {"foo": "bar"}) msg = self.jc._comm.last_message - self.assertEqual( - msg, - { - "msg_type": "some_msg", - "content": {"foo": "bar"}, - }, - ) + assert msg == { + "msg_type": "some_msg", + "content": {"foo": "bar"}, + } self.jc._comm.clear_message_cache() def test_send_error_msg__JobRequest(self): req = make_comm_msg("bar", "aeaeae", True) self.jc.send_error_message(req, {"extra": "field"}) msg = self.jc._comm.last_message - self.assertEqual( - { - "msg_type": ERROR, - "content": { - "source": "bar", - "extra": "field", - "request": req.rq_data, - }, + assert msg == { + "msg_type": ERROR, + "content": { + "source": "bar", + "extra": "field", + "request": req.rq_data, }, - msg, - ) + } def test_send_error_msg__dict(self): req_dict = make_comm_msg("bar", "aeaeae", False) self.jc.send_error_message(req_dict, {"extra": "field"}) msg = self.jc._comm.last_message - self.assertEqual( - { - "msg_type": ERROR, - "content": { - "source": "bar", - "extra": "field", - "request": req_dict["content"]["data"], - }, + assert msg == { + "msg_type": ERROR, + "content": { + "source": "bar", + "extra": "field", + "request": req_dict["content"]["data"], }, - msg, - ) + } def test_send_error_msg__None(self): self.jc.send_error_message(None, {"extra": "field"}) msg = self.jc._comm.last_message - self.assertEqual( - { - "msg_type": ERROR, - "content": { - "source": None, - "extra": "field", - "request": None, - }, + assert msg == { + "msg_type": ERROR, + "content": { + "source": None, + "extra": "field", + "request": None, }, - msg, - ) + } def test_send_error_msg__str(self): source = "test_jobcomm" self.jc.send_error_message(source, {"extra": "field"}) msg = self.jc._comm.last_message - self.assertEqual( - { - "msg_type": ERROR, - "content": { - "source": source, - "extra": "field", - "request": source, - }, + assert msg == { + "msg_type": ERROR, + "content": { + "source": source, + "extra": "field", + "request": source, }, - msg, - ) + } # --------------------- # Requests @@ -341,7 +321,7 @@ def test_req_no_inputs__succeed(self): req_dict = make_comm_msg(STATUS_ALL, None, False) self.jc._handle_comm_message(req_dict) msg = self.jc._comm.last_message - self.assertEqual(STATUS_ALL, msg["msg_type"]) + assert STATUS_ALL == msg["msg_type"] def test_req_no_inputs__fail(self): functions = [ @@ -357,7 +337,7 @@ def test_req_no_inputs__fail(self): for msg_type in functions: req_dict = make_comm_msg(msg_type, None, False) err = JobRequestException(ONE_INPUT_TYPE_ONLY_ERR) - with self.assertRaisesRegex(type(err), str(err)): + with pytest.raises(type(err), match=str(err)): self.jc._handle_comm_message(req_dict) self.check_error_message(req_dict, err) @@ -375,7 +355,7 @@ def test_req_multiple_inputs__fail(self): msg_type, {"job_id": "something", "batch_id": "another_thing"}, False ) err = JobRequestException(ONE_INPUT_TYPE_ONLY_ERR) - with self.assertRaisesRegex(type(err), str(err)): + with pytest.raises(type(err), match=str(err)): self.jc._handle_comm_message(req_dict) self.check_error_message(req_dict, err) @@ -384,28 +364,26 @@ def test_req_multiple_inputs__fail(self): # --------------------- @mock.patch(CLIENTS, get_mock_client) def test_start_stop_job_status_loop(self): - self.assertFalse(self.jc._running_lookup_loop) - self.assertIsNone(self.jc._lookup_timer) + assert self.jc._running_lookup_loop is False + assert self.jc._lookup_timer is None self.jc.start_job_status_loop() msg = self.jc._comm.last_message - self.assertEqual( - { - "msg_type": STATUS_ALL, - "content": { - job_id: ALL_RESPONSE_DATA[STATUS][job_id] - for job_id in REFRESH_STATE - if REFRESH_STATE[job_id] - }, + assert msg == { + "msg_type": STATUS_ALL, + "content": { + job_id: ALL_RESPONSE_DATA[STATUS][job_id] + for job_id in REFRESH_STATE + if REFRESH_STATE[job_id] }, - msg, - ) - self.assertTrue(self.jc._running_lookup_loop) - self.assertIsNotNone(self.jc._lookup_timer) + } + + assert self.jc._running_lookup_loop is True + assert self.jc._lookup_timer is not None self.jc.stop_job_status_loop() - self.assertFalse(self.jc._running_lookup_loop) - self.assertIsNone(self.jc._lookup_timer) + assert self.jc._running_lookup_loop is False + assert self.jc._lookup_timer is None @mock.patch(CLIENTS, get_mock_client) def test_start_job_status_loop__cell_ids(self): @@ -413,20 +391,18 @@ def test_start_job_status_loop__cell_ids(self): # Iterate through all combinations of cell IDs for combo_len in range(len(cell_ids) + 1): for combo in itertools.combinations(cell_ids, combo_len): - combo = list(combo) - self.jm._running_jobs = {} - self.assertFalse(self.jc._running_lookup_loop) - self.assertIsNone(self.jc._lookup_timer) + assert self.jc._running_lookup_loop is False + assert self.jc._lookup_timer is None - self.jc.start_job_status_loop(init_jobs=True, cell_list=combo) + self.jc.start_job_status_loop(init_jobs=True, cell_list=list(combo)) msg = self.jc._comm.last_message exp_job_ids = [ job_id for cell_id, job_ids in JOBS_BY_CELL_ID.items() for job_id in job_ids - if cell_id in combo and REFRESH_STATE[job_id] + if cell_id in list(combo) and REFRESH_STATE[job_id] ] exp_msg = { "msg_type": "job_status_all", @@ -435,36 +411,33 @@ def test_start_job_status_loop__cell_ids(self): for job_id in exp_job_ids }, } - self.assertEqual(exp_msg, msg) + assert exp_msg == msg if exp_job_ids: - self.assertTrue(self.jc._running_lookup_loop) - self.assertTrue(self.jc._lookup_timer) + assert self.jc._running_lookup_loop + assert self.jc._lookup_timer self.jc.stop_job_status_loop() - self.assertFalse(self.jc._running_lookup_loop) - self.assertIsNone(self.jc._lookup_timer) + assert self.jc._running_lookup_loop is False + assert self.jc._lookup_timer is None @mock.patch(CLIENTS, get_failing_mock_client) def test_start_job_status_loop__initialise_jobs_error(self): # check_workspace_jobs throws an EEServerError self.jc.start_job_status_loop(init_jobs=True) - self.assertEqual( - self.jc._comm.last_message, - { - "msg_type": ERROR, - "content": { - "code": -32000, - "error": "Unable to get initial jobs list", - "message": "check_workspace_jobs failed", - "name": "JSONRPCError", - "request": "jc.start_job_status_loop", - "source": "ee2", - }, + assert self.jc._comm.last_message == { + "msg_type": ERROR, + "content": { + "code": -32000, + "error": "Unable to get initial jobs list", + "message": "check_workspace_jobs failed", + "name": "JSONRPCError", + "request": "jc.start_job_status_loop", + "source": "ee2", }, - ) - self.assertFalse(self.jc._running_lookup_loop) + } + assert self.jc._running_lookup_loop is False @mock.patch(CLIENTS, get_mock_client) def test_start_job_status_loop__no_jobs_stop_loop(self): @@ -472,16 +445,14 @@ def test_start_job_status_loop__no_jobs_stop_loop(self): self.jm._running_jobs = {} self.jm._jobs_by_cell_id = {} self.jm = JobManager() - self.assertEqual(self.jm._running_jobs, {}) + assert self.jm._running_jobs == {} # this will trigger a call to get_all_job_states # a message containing all jobs (i.e. {}) will be sent out # when it returns 0 jobs, the JobComm will run stop_job_status_loop self.jc.start_job_status_loop() - self.assertFalse(self.jc._running_lookup_loop) - self.assertIsNone(self.jc._lookup_timer) - self.assertEqual( - self.jc._comm.last_message, {"msg_type": STATUS_ALL, "content": {}} - ) + assert self.jc._running_lookup_loop is False + assert self.jc._lookup_timer is None + assert self.jc._comm.last_message == {"msg_type": STATUS_ALL, "content": {}} # --------------------- # Lookup all job states @@ -521,21 +492,18 @@ def check_job_output_states( output_states = self.jc._handle_comm_message(req_dict) msg = self.jc._comm.last_message - self.assertEqual( - { - "msg_type": response_type, - "content": output_states, - }, - msg, - ) + assert msg == { + "msg_type": response_type, + "content": output_states, + } for job_id, state in output_states.items(): - self.assertEqual(ALL_RESPONSE_DATA[STATUS][job_id], state) + assert ALL_RESPONSE_DATA[STATUS][job_id] == state if job_id in ok_states: validate_job_state(state) else: # every valid job ID should be in either error_states or ok_states - self.assertIn(job_id, error_states) + assert job_id in error_states @mock.patch(CLIENTS, get_mock_client) def test_get_all_job_states__ok(self): @@ -553,8 +521,8 @@ def test_get_job_state__1_ok(self): ) def test_get_job_state__no_job(self): - with self.assertRaisesRegex( - JobRequestException, re.escape(f"{JOBS_MISSING_ERR}: {[None]}") + with pytest.raises( + JobRequestException, match=re.escape(f"{JOBS_MISSING_ERR}: {[None]}") ): self.jc.get_job_state(None) @@ -636,13 +604,10 @@ def mock_check_jobs(params): self.jc._handle_comm_message(req_dict) msg = self.jc._comm.last_message - self.assertEqual( - { - "msg_type": STATUS, - "content": expected, - }, - msg, - ) + assert msg == { + "msg_type": STATUS, + "content": expected, + } # ----------------------- # get cell job states @@ -651,7 +616,7 @@ def test_get_job_states_by_cell_id__cell_id_list_none(self): cell_id_list = None req_dict = make_comm_msg(CELL_JOB_STATUS, {CELL_ID_LIST: cell_id_list}, False) err = JobRequestException(CELLS_NOT_PROVIDED_ERR) - with self.assertRaisesRegex(type(err), re.escape(str(err))): + with pytest.raises(type(err), match=re.escape(str(err))): self.jc._handle_comm_message(req_dict) self.check_error_message(req_dict, err) @@ -659,7 +624,7 @@ def test_get_job_states_by_cell_id__empty_cell_id_list(self): cell_id_list = [] req_dict = make_comm_msg(CELL_JOB_STATUS, {CELL_ID_LIST: cell_id_list}, False) err = JobRequestException(CELLS_NOT_PROVIDED_ERR) - with self.assertRaisesRegex(type(err), re.escape(str(err))): + with pytest.raises(type(err), match=re.escape(str(err))): self.jc._handle_comm_message(req_dict) self.check_error_message(req_dict, err) @@ -669,28 +634,22 @@ def test_get_job_states_by_cell_id__invalid_cell_id_list(self): req_dict = make_comm_msg(CELL_JOB_STATUS, {CELL_ID_LIST: cell_id_list}, False) self.jc._handle_comm_message(req_dict) msg = self.jc._comm.last_message - self.assertEqual( - { - "msg_type": CELL_JOB_STATUS, - "content": NO_JOBS_MAPPING, - }, - msg, - ) + assert msg == { + "msg_type": CELL_JOB_STATUS, + "content": NO_JOBS_MAPPING, + } @mock.patch(CLIENTS, get_mock_client) def test_get_job_states_by_cell_id__invalid_cell_id_list_req(self): cell_id_list = ["a", "b", "c"] req_dict = make_comm_msg(CELL_JOB_STATUS, {CELL_ID_LIST: cell_id_list}, False) result = self.jc._handle_comm_message(req_dict) - self.assertEqual(result, NO_JOBS_MAPPING) + assert result == NO_JOBS_MAPPING msg = self.jc._comm.last_message - self.assertEqual( - { - "msg_type": CELL_JOB_STATUS, - "content": NO_JOBS_MAPPING, - }, - msg, - ) + assert msg == { + "msg_type": CELL_JOB_STATUS, + "content": NO_JOBS_MAPPING, + } @mock.patch(CLIENTS, get_mock_client) def test_get_job_states_by_cell_id__all_results(self): @@ -703,14 +662,12 @@ def test_get_job_states_by_cell_id__all_results(self): req_dict = make_comm_msg(CELL_JOB_STATUS, {CELL_ID_LIST: cell_id_list}, False) self.jc._handle_comm_message(req_dict) msg = self.jc._comm.last_message - self.assertEqual(set(msg.keys()), set(["msg_type", "content"])) - self.assertEqual(msg["msg_type"], CELL_JOB_STATUS) - self.assertEqual(msg["content"]["jobs"], expected_states) - self.assertEqual(set(cell_id_list), set(msg["content"]["mapping"].keys())) - for key in msg["content"]["mapping"].keys(): - self.assertEqual( - set(TEST_CELL_IDs[key]), set(msg["content"]["mapping"][key]) - ) + assert set(msg.keys()), set(["msg_type" == "content"]) + assert msg["msg_type"] == CELL_JOB_STATUS + assert msg["content"]["jobs"] == expected_states + assert set(cell_id_list) == set(msg["content"]["mapping"].keys()) + for key in msg["content"]["mapping"]: + assert set(TEST_CELL_IDs[key]) == set(msg["content"]["mapping"][key]) # ----------------------- # Lookup job info @@ -722,13 +679,10 @@ def check_job_info_results(self, job_args, job_id_list): self.jc._handle_comm_message(req_dict) msg = self.jc._comm.last_message expected = {job_id: ALL_RESPONSE_DATA[INFO][job_id] for job_id in job_id_list} - self.assertEqual( - { - "msg_type": INFO, - "content": expected, - }, - msg, - ) + assert msg == { + "msg_type": INFO, + "content": expected, + } @mock.patch(CLIENTS, get_mock_client) def test_get_job_info__job_id__ok(self): @@ -787,7 +741,7 @@ def test_cancel_jobs__job_id__invalid(self): for job_id in job_id_list: req_dict = make_comm_msg(CANCEL, {JOB_ID: job_id}, False) err = JobRequestException(JOBS_MISSING_ERR, [job_id]) - with self.assertRaisesRegex(type(err), re.escape(str(err))): + with pytest.raises(type(err), match=re.escape(str(err))): self.jc._handle_comm_message(req_dict) self.check_error_message(req_dict, err) @@ -811,13 +765,13 @@ def test_cancel_jobs__job_id_list__no_jobs(self): job_id_list = None req_dict = make_comm_msg(CANCEL, {JOB_ID_LIST: job_id_list}, False) err = JobRequestException(JOBS_MISSING_ERR, job_id_list) - with self.assertRaisesRegex(type(err), str(err)): + with pytest.raises(type(err), match=str(err)): self.jc._handle_comm_message(req_dict) job_id_list = [None, ""] req_dict = make_comm_msg(CANCEL, job_id_list, False) err = JobRequestException(JOBS_MISSING_ERR, job_id_list) - with self.assertRaisesRegex(type(err), re.escape(str(err))): + with pytest.raises(type(err), match=re.escape(str(err))): self.jc._handle_comm_message(req_dict) self.check_error_message(req_dict, err) @@ -861,14 +815,11 @@ def test_cancel_jobs__job_id_list__failure(self): }, } - self.assertEqual(output, expected) - self.assertEqual( - self.jc._comm.last_message, - { - "msg_type": STATUS, - "content": expected, - }, - ) + assert output == expected + assert self.jc._comm.last_message == { + "msg_type": STATUS, + "content": expected, + } # ------------ # Retry list of jobs @@ -881,15 +832,12 @@ def check_retry_jobs(self, job_args, job_id_list): job_id: ALL_RESPONSE_DATA[RETRY][job_id] for job_id in job_id_list if job_id } retry_data = self.jc._handle_comm_message(req_dict) - self.assertEqual(expected, retry_data) + assert expected == retry_data retry_msg = self.jc._comm.pop_message() - self.assertEqual( - { - "msg_type": RETRY, - "content": expected, - }, - retry_msg, - ) + assert retry_msg == { + "msg_type": RETRY, + "content": expected, + } def test_retry_jobs__job_id__ok(self): job_id_list = [BATCH_TERMINATED_RETRIED] @@ -934,24 +882,21 @@ def test_retry_jobs__job_id_list__failure(self): generate_ee2_error(RETRY), "Unable to retry job(s)" ) - with self.assertRaisesRegex(type(err), re.escape(str(err))): + with pytest.raises(type(err), match=re.escape(str(err))): self.jc._handle_comm_message(req_dict) msg = self.jc._comm.last_message - self.assertEqual( - msg, - { - "msg_type": ERROR, - "content": { - "request": req_dict["content"]["data"], - "source": RETRY, - "name": "JSONRPCError", - "error": "Unable to retry job(s)", - "code": -32000, - "message": RETRY + " failed", - }, + assert msg == { + "msg_type": ERROR, + "content": { + "request": req_dict["content"]["data"], + "source": RETRY, + "name": "JSONRPCError", + "error": "Unable to retry job(s)", + "code": -32000, + "message": RETRY + " failed", }, - ) + } # ----------------- # Fetching job logs @@ -984,13 +929,13 @@ def test_get_job_logs__job_id__ok(self): req_dict = make_comm_msg(LOGS, [job_id], False, content) self.jc._handle_comm_message(req_dict) msg = self.jc._comm.last_message - self.assertEqual(LOGS, msg["msg_type"]) + assert LOGS == msg["msg_type"] msg_content = msg["content"][job_id] - self.assertEqual(job_id, msg_content["job_id"]) - self.assertEqual(None, msg_content["batch_id"]) - self.assertEqual(lines_available, msg_content["max_lines"]) - self.assertEqual(c[3], len(msg_content["lines"])) - self.assertEqual(c[2], msg_content["latest"]) + assert job_id == msg_content["job_id"] + assert None == msg_content["batch_id"] + assert lines_available == msg_content["max_lines"] + assert c[3] == len(msg_content["lines"]) + assert c[2] == msg_content["latest"] first = 0 if c[1] is None and c[2] is True else c[0] n_lines = c[1] if c[1] else lines_available if first < 0: @@ -998,10 +943,10 @@ def test_get_job_logs__job_id__ok(self): if c[2]: first = lines_available - min(n_lines, lines_available) - self.assertEqual(first, msg_content["first"]) + assert first == msg_content["first"] for idx, line in enumerate(msg_content["lines"]): - self.assertIn(str(first + idx), line["line"]) - self.assertEqual(0, line["is_error"]) + assert str(first + idx) in line["line"] + assert 0 == line["is_error"] @mock.patch(CLIENTS, get_mock_client) def test_get_job_logs__job_id__failure(self): @@ -1009,25 +954,22 @@ def test_get_job_logs__job_id__failure(self): req_dict = make_comm_msg(LOGS, job_id, False) self.jc._handle_comm_message(req_dict) msg = self.jc._comm.last_message - self.assertEqual( - msg, - { - "msg_type": LOGS, - "content": { - JOB_CREATED: { - "job_id": JOB_CREATED, - "batch_id": None, - "error": "Cannot find job log with id: " + JOB_CREATED, - } - }, + assert msg == { + "msg_type": LOGS, + "content": { + JOB_CREATED: { + "job_id": JOB_CREATED, + "batch_id": None, + "error": "Cannot find job log with id: " + JOB_CREATED, + } }, - ) + } def test_get_job_logs__job_id__no_job(self): job_id = None req_dict = make_comm_msg(LOGS, {JOB_ID: job_id}, False) err = JobRequestException(JOBS_MISSING_ERR, [job_id]) - with self.assertRaisesRegex(type(err), re.escape(str(err))): + with pytest.raises(type(err), match=re.escape(str(err))): self.jc._handle_comm_message(req_dict) self.check_error_message(req_dict, err) @@ -1036,18 +978,15 @@ def test_get_job_logs__job_id__job_dne(self): req_dict = make_comm_msg(LOGS, JOB_NOT_FOUND, False) self.jc._handle_comm_message(req_dict) msg = self.jc._comm.last_message - self.assertEqual( - msg, - { - "msg_type": LOGS, - "content": { - JOB_NOT_FOUND: { - "job_id": JOB_NOT_FOUND, - "error": generate_error(JOB_NOT_FOUND, "not_found"), - } - }, + assert msg == { + "msg_type": LOGS, + "content": { + JOB_NOT_FOUND: { + "job_id": JOB_NOT_FOUND, + "error": generate_error(JOB_NOT_FOUND, "not_found"), + } }, - ) + } @mock.patch(CLIENTS, get_mock_client) def test_get_job_logs__job_id_list__one_ok_one_bad_one_fetch_fail(self): @@ -1056,30 +995,27 @@ def test_get_job_logs__job_id_list__one_ok_one_bad_one_fetch_fail(self): ) self.jc._handle_comm_message(req_dict) msg = self.jc._comm.last_message - self.assertEqual(LOGS, msg["msg_type"]) - - self.assertEqual( - msg["content"], - { - JOB_COMPLETED: { - "job_id": JOB_COMPLETED, - "first": 0, - "max_lines": MAX_LOG_LINES, - "latest": False, - "batch_id": None, - "lines": LOG_LINES, - }, - JOB_CREATED: { - "job_id": JOB_CREATED, - "batch_id": None, - "error": generate_error(JOB_CREATED, "no_logs"), - }, - JOB_NOT_FOUND: { - "job_id": JOB_NOT_FOUND, - "error": generate_error(JOB_NOT_FOUND, "not_found"), - }, + assert LOGS == msg["msg_type"] + + assert msg["content"] == { + JOB_COMPLETED: { + "job_id": JOB_COMPLETED, + "first": 0, + "max_lines": MAX_LOG_LINES, + "latest": False, + "batch_id": None, + "lines": LOG_LINES, }, - ) + JOB_CREATED: { + "job_id": JOB_CREATED, + "batch_id": None, + "error": generate_error(JOB_CREATED, "no_logs"), + }, + JOB_NOT_FOUND: { + "job_id": JOB_NOT_FOUND, + "error": generate_error(JOB_NOT_FOUND, "not_found"), + }, + } @mock.patch(CLIENTS, get_mock_client) def test_get_job_logs__job_id_list__one_ok_one_bad_one_fetch_fail__with_params( @@ -1097,30 +1033,27 @@ def test_get_job_logs__job_id_list__one_ok_one_bad_one_fetch_fail__with_params( ) self.jc._handle_comm_message(req_dict) msg = self.jc._comm.last_message - self.assertEqual(LOGS, msg["msg_type"]) - - self.assertEqual( - msg["content"], - { - JOB_COMPLETED: { - "job_id": JOB_COMPLETED, - "first": first, - "max_lines": MAX_LOG_LINES, - "latest": True, - "batch_id": None, - "lines": lines, - }, - JOB_CREATED: { - "job_id": JOB_CREATED, - "batch_id": None, - "error": generate_error(JOB_CREATED, "no_logs"), - }, - JOB_NOT_FOUND: { - "job_id": JOB_NOT_FOUND, - "error": generate_error(JOB_NOT_FOUND, "not_found"), - }, + assert LOGS == msg["msg_type"] + + assert msg["content"] == { + JOB_COMPLETED: { + "job_id": JOB_COMPLETED, + "first": first, + "max_lines": MAX_LOG_LINES, + "latest": True, + "batch_id": None, + "lines": lines, }, - ) + JOB_CREATED: { + "job_id": JOB_CREATED, + "batch_id": None, + "error": generate_error(JOB_CREATED, "no_logs"), + }, + JOB_NOT_FOUND: { + "job_id": JOB_NOT_FOUND, + "error": generate_error(JOB_NOT_FOUND, "not_found"), + }, + } # ------------------------ # Modify job update @@ -1135,14 +1068,11 @@ def test_modify_job_update__job_id_list__start__ok(self): ) for job_id in ALL_JOBS: if job_id in job_id_list: - self.assertEqual(self.jm._running_jobs[job_id]["refresh"], True) + assert self.jm._running_jobs[job_id]["refresh"] else: - self.assertEqual( - self.jm._running_jobs[job_id]["refresh"], - REFRESH_STATE[job_id], - ) - self.assertTrue(self.jc._lookup_timer) - self.assertTrue(self.jc._running_lookup_loop) + assert self.jm._running_jobs[job_id]["refresh"] == REFRESH_STATE[job_id] + assert self.jc._lookup_timer + assert self.jc._running_lookup_loop @mock.patch(CLIENTS, get_mock_client) def test_modify_job_update__job_id_list__stop__ok(self): @@ -1154,23 +1084,17 @@ def test_modify_job_update__job_id_list__stop__ok(self): ) for job_id in ALL_JOBS: if job_id in job_id_list: - self.assertEqual( - self.jm._running_jobs[job_id]["refresh"], - False, - ) + assert self.jm._running_jobs[job_id]["refresh"] is False else: - self.assertEqual( - self.jm._running_jobs[job_id]["refresh"], - REFRESH_STATE[job_id], - ) - self.assertIsNone(self.jc._lookup_timer) - self.assertFalse(self.jc._running_lookup_loop) + assert self.jm._running_jobs[job_id]["refresh"] == REFRESH_STATE[job_id] + assert self.jc._lookup_timer is None + assert self.jc._running_lookup_loop is False def test_modify_job_update__job_id_list__no_jobs(self): job_id_list = [None] req_dict = make_comm_msg(START_UPDATE, job_id_list, False) err = JobRequestException(JOBS_MISSING_ERR, job_id_list) - with self.assertRaisesRegex(type(err), re.escape(str(err))): + with pytest.raises(type(err), match=re.escape(str(err))): self.jc._handle_comm_message(req_dict) self.check_error_message(req_dict, err) @@ -1185,14 +1109,11 @@ def test_modify_job_update__job_id_list__stop__ok_bad_job(self): for job_id in ALL_JOBS: if job_id in job_id_list: - self.assertEqual(self.jm._running_jobs[job_id]["refresh"], False) + assert self.jm._running_jobs[job_id]["refresh"] is False else: - self.assertEqual( - self.jm._running_jobs[job_id]["refresh"], - REFRESH_STATE[job_id], - ) - self.assertIsNone(self.jc._lookup_timer) - self.assertFalse(self.jc._running_lookup_loop) + assert self.jm._running_jobs[job_id]["refresh"] == REFRESH_STATE[job_id] + assert self.jc._lookup_timer is None + assert self.jc._running_lookup_loop is False @mock.patch(CLIENTS, get_mock_client) def test_modify_job_update__job_id_list__stop__loop_still_running(self): @@ -1207,14 +1128,11 @@ def test_modify_job_update__job_id_list__stop__loop_still_running(self): ) for job_id in ALL_JOBS: if job_id in job_id_list: - self.assertEqual(self.jm._running_jobs[job_id]["refresh"], False) + assert self.jm._running_jobs[job_id]["refresh"] is False else: - self.assertEqual( - self.jm._running_jobs[job_id]["refresh"], - REFRESH_STATE[job_id], - ) - self.assertTrue(self.jc._lookup_timer) - self.assertTrue(self.jc._running_lookup_loop) + assert self.jm._running_jobs[job_id]["refresh"] == REFRESH_STATE[job_id] + assert self.jc._lookup_timer + assert self.jc._running_lookup_loop # ------------------------ # Modify job update batch @@ -1230,14 +1148,11 @@ def test_modify_job_update__batch_id__start__ok(self): ) for job_id in ALL_JOBS: if job_id in job_id_list: - self.assertEqual(self.jm._running_jobs[job_id]["refresh"], True) + assert self.jm._running_jobs[job_id]["refresh"] else: - self.assertEqual( - self.jm._running_jobs[job_id]["refresh"], - REFRESH_STATE[job_id], - ) - self.assertTrue(self.jc._lookup_timer) - self.assertTrue(self.jc._running_lookup_loop) + assert self.jm._running_jobs[job_id]["refresh"] == REFRESH_STATE[job_id] + assert self.jc._lookup_timer + assert self.jc._running_lookup_loop @mock.patch(CLIENTS, get_mock_client) def test_modify_job_update__batch_id__stop__ok(self): @@ -1250,14 +1165,11 @@ def test_modify_job_update__batch_id__stop__ok(self): ) for job_id in ALL_JOBS: if job_id in job_id_list: - self.assertEqual(self.jm._running_jobs[job_id]["refresh"], False) + assert self.jm._running_jobs[job_id]["refresh"] is False else: - self.assertEqual( - self.jm._running_jobs[job_id]["refresh"], - REFRESH_STATE[job_id], - ) - self.assertIsNone(self.jc._lookup_timer) - self.assertFalse(self.jc._running_lookup_loop) + assert self.jm._running_jobs[job_id]["refresh"] == REFRESH_STATE[job_id] + assert self.jc._lookup_timer is None + assert self.jc._running_lookup_loop is False def test_modify_job_update__batch_id__no_job(self): self.check_batch_id__no_job_test(START_UPDATE) @@ -1275,16 +1187,17 @@ def test_modify_job_update__batch_id__not_batch(self): # Handle bad comm messages # ------------------------ def test_handle_comm_message_bad(self): - with self.assertRaisesRegex(JobRequestException, INVALID_REQUEST_ERR): + with pytest.raises(JobRequestException, match=INVALID_REQUEST_ERR): self.jc._handle_comm_message({"foo": "bar"}) - with self.assertRaisesRegex(JobRequestException, MISSING_REQUEST_TYPE_ERR): + with pytest.raises(JobRequestException, match=MISSING_REQUEST_TYPE_ERR): self.jc._handle_comm_message({"content": {"data": {"request_type": None}}}) def test_handle_comm_message_unknown(self): unknown = "NotAJobRequest" - with self.assertRaisesRegex( - JobRequestException, re.escape(f"Unknown KBaseJobs message '{unknown}'") + with pytest.raises( + JobRequestException, + match=re.escape(f"Unknown KBaseJobs message '{unknown}'"), ): self.jc._handle_comm_message( {"content": {"data": {"request_type": unknown}}} @@ -1304,13 +1217,13 @@ def test_request_ok(self): "content": {"data": {"request_type": "a_request"}}, } rq = JobRequest(rq_msg) - self.assertEqual(rq.msg_id, "some_id") - self.assertEqual(rq.request_type, "a_request") - self.assertEqual(rq.raw_request, rq_msg) - self.assertEqual(rq.rq_data, {"request_type": "a_request"}) - with self.assertRaisesRegex(JobRequestException, ONE_INPUT_TYPE_ONLY_ERR): + assert rq.msg_id == "some_id" + assert rq.request_type == "a_request" + assert rq.raw_request == rq_msg + assert rq.rq_data == {"request_type": "a_request"} + with pytest.raises(JobRequestException, match=ONE_INPUT_TYPE_ONLY_ERR): rq.job_id - with self.assertRaisesRegex(JobRequestException, ONE_INPUT_TYPE_ONLY_ERR): + with pytest.raises(JobRequestException, match=ONE_INPUT_TYPE_ONLY_ERR): rq.job_id_list def test_request_no_data(self): @@ -1319,7 +1232,7 @@ def test_request_no_data(self): rq_msg3 = {"msg_id": "some_id", "content": {"data": None}} rq_msg4 = {"msg_id": "some_id", "content": {"what": "?"}} for msg in [rq_msg1, rq_msg2, rq_msg3, rq_msg4]: - with self.assertRaisesRegex(JobRequestException, INVALID_REQUEST_ERR): + with pytest.raises(JobRequestException, match=INVALID_REQUEST_ERR): JobRequest(msg) def test_request_no_req(self): @@ -1327,7 +1240,7 @@ def test_request_no_req(self): rq_msg2 = {"msg_id": "some_id", "content": {"data": {"request_type": ""}}} rq_msg3 = {"msg_id": "some_id", "content": {"data": {"what": {}}}} for msg in [rq_msg1, rq_msg2, rq_msg3]: - with self.assertRaisesRegex(JobRequestException, MISSING_REQUEST_TYPE_ERR): + with pytest.raises(JobRequestException, match=MISSING_REQUEST_TYPE_ERR): JobRequest(msg) def test_request_more_than_one_input(self): @@ -1342,7 +1255,7 @@ def test_request_more_than_one_input(self): ) for co in combos: msg = make_comm_msg(STATUS, {**co[0], **co[1]}, False) - with self.assertRaisesRegex(JobRequestException, ONE_INPUT_TYPE_ONLY_ERR): + with pytest.raises(JobRequestException, match=ONE_INPUT_TYPE_ONLY_ERR): JobRequest(msg) # all three @@ -1355,20 +1268,20 @@ def test_request_more_than_one_input(self): }, False, ) - with self.assertRaisesRegex(JobRequestException, ONE_INPUT_TYPE_ONLY_ERR): + with pytest.raises(JobRequestException, match=ONE_INPUT_TYPE_ONLY_ERR): JobRequest(msg) def test_request__no_input(self): msg = make_comm_msg(STATUS, {}, False) req = JobRequest(msg) - with self.assertRaisesRegex(JobRequestException, ONE_INPUT_TYPE_ONLY_ERR): + with pytest.raises(JobRequestException, match=ONE_INPUT_TYPE_ONLY_ERR): req.job_id - with self.assertRaisesRegex(JobRequestException, ONE_INPUT_TYPE_ONLY_ERR): + with pytest.raises(JobRequestException, match=ONE_INPUT_TYPE_ONLY_ERR): req.job_id_list - with self.assertRaisesRegex(JobRequestException, ONE_INPUT_TYPE_ONLY_ERR): + with pytest.raises(JobRequestException, match=ONE_INPUT_TYPE_ONLY_ERR): req.batch_id - with self.assertRaisesRegex(JobRequestException, CELLS_NOT_PROVIDED_ERR): + with pytest.raises(JobRequestException, match=CELLS_NOT_PROVIDED_ERR): req.cell_id_list @@ -1412,23 +1325,20 @@ def test_with_nested_try__raise(self): def f(): raise RuntimeError(message) - with self.assertRaisesRegex(RuntimeError, message): + with pytest.raises(RuntimeError, match=message): f_var = [] self.bar(req, f, f_var) - self.assertEqual(["A"], f_var) + assert ["A"] == f_var msg = self.jc._comm.last_message - self.assertEqual( - { - "msg_type": ERROR, - "content": { - "source": req_type, - "name": "RuntimeError", - "message": message, - "request": req.rq_data, - }, + assert { + "msg_type": ERROR, + "content": { + "source": req_type, + "name": "RuntimeError", + "message": message, + "request": req.rq_data, }, - msg, - ) + } == msg def test_with_nested_try__succeed(self): job_id_list = [BATCH_ERROR_RETRIED, JOB_RUNNING] @@ -1445,9 +1355,9 @@ def f(): f_var = [] self.bar(req, f, f_var) - self.assertEqual(["B", "C"], f_var) + assert ["B", "C"] == f_var msg = self.jc._comm.last_message - self.assertIsNone(msg) + assert msg is None def test_NarrativeException(self): job_id_list = BATCH_CHILDREN @@ -1464,24 +1374,21 @@ def test_NarrativeException(self): def f(): raise transform_job_exception(Exception(message), error) - with self.assertRaisesRegex(NarrativeException, message): + with pytest.raises(NarrativeException, match=message): self.foo(req, f) msg = self.jc._comm.last_message - self.assertEqual( - { - "msg_type": ERROR, - "content": { - "request": req.rq_data, - "source": req_type, - # Below are from transform_job_exception - "name": "Exception", - "message": message, - "error": error, - "code": -1, - }, + assert msg == { + "msg_type": ERROR, + "content": { + "request": req.rq_data, + "source": req_type, + # Below are from transform_job_exception + "name": "Exception", + "message": message, + "error": error, + "code": -1, }, - msg, - ) + } def test_JobRequestException(self): job_id = BATCH_PARENT @@ -1497,21 +1404,18 @@ def test_JobRequestException(self): def f(): raise JobRequestException(message, "a0a0a0") - with self.assertRaisesRegex(JobRequestException, f"{message}: a0a0a0"): + with pytest.raises(JobRequestException, match=f"{message}: a0a0a0"): self.foo(req, f) msg = self.jc._comm.last_message - self.assertEqual( - { - "msg_type": ERROR, - "content": { - "request": req.rq_data, - "source": req_type, - "name": "JobRequestException", - "message": f"{message}: a0a0a0", - }, + assert msg == { + "msg_type": ERROR, + "content": { + "request": req.rq_data, + "source": req_type, + "name": "JobRequestException", + "message": f"{message}: a0a0a0", }, - msg, - ) + } def test_ValueError(self): job_id_list = [JOB_RUNNING, JOB_COMPLETED] @@ -1527,21 +1431,18 @@ def test_ValueError(self): def f(): raise ValueError(message) - with self.assertRaisesRegex(ValueError, message): + with pytest.raises(ValueError, match=message): self.foo(req, f) msg = self.jc._comm.last_message - self.assertEqual( - { - "msg_type": ERROR, - "content": { - "request": req.rq_data, - "source": req_type, - "name": "ValueError", - "message": message, - }, + assert msg == { + "msg_type": ERROR, + "content": { + "request": req.rq_data, + "source": req_type, + "name": "ValueError", + "message": message, }, - msg, - ) + } def test_dict_req__no_err(self): job_id = JOB_ERROR @@ -1559,7 +1460,7 @@ def f(): self.foo(req_dict, f) msg = self.jc._comm.last_message - self.assertIsNone(msg) + assert msg is None def test_dict_req__error_down_the_stack(self): job_id = JOB_CREATED @@ -1579,21 +1480,18 @@ def f(i=5): raise ValueError(message) f(i - 1) - with self.assertRaisesRegex(ValueError, message): + with pytest.raises(ValueError, match=message): self.foo(req_dict, f) msg = self.jc._comm.last_message - self.assertEqual( - { - "msg_type": ERROR, - "content": { - "request": req_dict["content"]["data"], - "source": req_type, - "name": "ValueError", - "message": message, - }, + assert msg == { + "msg_type": ERROR, + "content": { + "request": req_dict["content"]["data"], + "source": req_type, + "name": "ValueError", + "message": message, }, - msg, - ) + } def test_dict_req__both_inputs(self): req_type = STATUS @@ -1610,21 +1508,18 @@ def test_dict_req__both_inputs(self): def f(): raise ValueError(message) - with self.assertRaisesRegex(ValueError, message): + with pytest.raises(ValueError, match=message): self.foo(req_dict, f) msg = self.jc._comm.last_message - self.assertEqual( - { - "msg_type": ERROR, - "content": { - "request": req_dict["content"]["data"], - "source": req_type, - "name": "ValueError", - "message": message, - }, + assert msg == { + "msg_type": ERROR, + "content": { + "request": req_dict["content"]["data"], + "source": req_type, + "name": "ValueError", + "message": message, }, - msg, - ) + } def test_None_req(self): source = None @@ -1634,21 +1529,18 @@ def test_None_req(self): def f(): raise err - with self.assertRaisesRegex(type(err), str(err)): + with pytest.raises(type(err), match=str(err)): self.foo(source, f) msg = self.jc._comm.last_message - self.assertEqual( - { - "msg_type": ERROR, - "content": { - "source": source, - "name": "ValueError", - "message": message, - "request": None, - }, + assert msg == { + "msg_type": ERROR, + "content": { + "source": source, + "name": "ValueError", + "message": message, + "request": None, }, - msg, - ) + } def test_str_req(self): source = "test_jobcomm" @@ -1658,18 +1550,15 @@ def test_str_req(self): def f(): raise err - with self.assertRaisesRegex(type(err), str(err)): + with pytest.raises(type(err), match=str(err)): self.foo(source, f) msg = self.jc._comm.last_message - self.assertEqual( - { - "msg_type": ERROR, - "content": { - "source": source, - "name": "ValueError", - "message": message, - "request": source, - }, + assert msg == { + "msg_type": ERROR, + "content": { + "source": source, + "name": "ValueError", + "message": message, + "request": source, }, - msg, - ) + } diff --git a/src/biokbase/narrative/tests/test_jobmanager.py b/src/biokbase/narrative/tests/test_jobmanager.py index 5f7be32d0f..27da6d7cac 100644 --- a/src/biokbase/narrative/tests/test_jobmanager.py +++ b/src/biokbase/narrative/tests/test_jobmanager.py @@ -6,8 +6,7 @@ from datetime import datetime from unittest import mock -from IPython.display import HTML - +import pytest from biokbase.narrative.exception_util import JobRequestException, NarrativeException from biokbase.narrative.jobs.job import ( EXCLUDED_JOB_STATE_FIELDS, @@ -51,6 +50,7 @@ generate_error, get_test_job, ) +from IPython.display import HTML from .narrative_mock.mockclients import ( MockClients, @@ -87,14 +87,14 @@ def reset_job_manager(self): self.jm._jobs_by_cell_id = {} self.jm = JobManager() - self.assertEqual(self.jm._running_jobs, {}) - self.assertEqual(self.jm._jobs_by_cell_id, {}) + assert self.jm._running_jobs == {} + assert self.jm._jobs_by_cell_id == {} @mock.patch(CLIENTS, get_failing_mock_client) def test_initialize_jobs_ee2_fail(self): # init jobs should fail. specifically, ee2.check_workspace_jobs should error. - with self.assertRaisesRegex( - NarrativeException, re.escape("check_workspace_jobs failed") + with pytest.raises( + NarrativeException, match=re.escape("check_workspace_jobs failed") ): self.jm.initialize_jobs() @@ -109,18 +109,15 @@ def test_initialize_jobs(self): for job_id, d in self.jm._running_jobs.items() if d["job"].in_terminal_state() ] - self.assertEqual( - set(TERMINAL_JOBS), - set(terminal_ids), - ) - self.assertEqual(set(ALL_JOBS), set(self.jm._running_jobs.keys())) + assert set(TERMINAL_JOBS) == set(terminal_ids) + assert set(ALL_JOBS) == set(self.jm._running_jobs.keys()) for job_id in TERMINAL_IDS: - self.assertFalse(self.jm._running_jobs[job_id]["refresh"]) + assert self.jm._running_jobs[job_id]["refresh"] is False for job_id in NON_TERMINAL_IDS: - self.assertTrue(self.jm._running_jobs[job_id]["refresh"]) + assert self.jm._running_jobs[job_id]["refresh"] is True - self.assertEqual(self.jm._jobs_by_cell_id, JOBS_BY_CELL_ID) + assert self.jm._jobs_by_cell_id == JOBS_BY_CELL_ID @mock.patch(CLIENTS, get_mock_client) def test_initialize_jobs__cell_ids(self): @@ -131,48 +128,48 @@ def test_initialize_jobs__cell_ids(self): # Iterate through all combinations of cell IDs for combo_len in range(len(cell_ids) + 1): for combo in itertools.combinations(cell_ids, combo_len): - combo = list(combo) + combo_list = list(combo) # Get jobs expected to be associated with the cell IDs exp_job_ids = [ job_id for cell_id, job_ids in JOBS_BY_CELL_ID.items() for job_id in job_ids - if cell_id in combo + if cell_id in combo_list ] + self.reset_job_manager() - self.jm.initialize_jobs(cell_ids=combo) + self.jm.initialize_jobs(cell_ids=combo_list) for job_id, d in self.jm._running_jobs.items(): - refresh = d["refresh"] - - self.assertEqual( - job_id in exp_job_ids and REFRESH_STATE[job_id], - refresh, + expected_refresh_state = ( + job_id in exp_job_ids and REFRESH_STATE[job_id] ) + assert expected_refresh_state == d["refresh"] def test__check_job_list_fail__wrong_type(self): - with self.assertRaisesRegex(JobRequestException, f"{JOBS_MISSING_ERR}: {{}}"): + with pytest.raises(JobRequestException, match=f"{JOBS_MISSING_ERR}: {{}}"): self.jm._check_job_list({}) def test__check_job_list_fail__none(self): - with self.assertRaisesRegex(JobRequestException, f"{JOBS_MISSING_ERR}: {None}"): + with pytest.raises(JobRequestException, match=f"{JOBS_MISSING_ERR}: {None}"): self.jm._check_job_list(None) def test__check_job_list_fail__no_args(self): - with self.assertRaisesRegex( - JobRequestException, re.escape(f"{JOBS_MISSING_ERR}: {None}") + with pytest.raises( + JobRequestException, match=re.escape(f"{JOBS_MISSING_ERR}: {None}") ): self.jm._check_job_list() def test__check_job_list_fail__empty(self): - with self.assertRaisesRegex( - JobRequestException, re.escape(f"{JOBS_MISSING_ERR}: {[]}") + with pytest.raises( + JobRequestException, match=re.escape(f"{JOBS_MISSING_ERR}: {[]}") ): self.jm._check_job_list([]) def test__check_job_list_fail__nonsense_list_items(self): - with self.assertRaisesRegex( - JobRequestException, re.escape(f'{JOBS_MISSING_ERR}: {["", "", None]}') + with pytest.raises( + JobRequestException, + match=re.escape(f'{JOBS_MISSING_ERR}: {["", "", None]}'), ): self.jm._check_job_list(["", "", None]) @@ -183,50 +180,39 @@ def test__check_job_list(self): job_b = JOB_COMPLETED job_c = "job_c" job_d = "job_d" - self.assertEqual( - self.jm._check_job_list([job_c]), - ( - [], - [job_c], - ), + assert self.jm._check_job_list([job_c]) == ( + [], + [job_c], ) - self.assertEqual( - self.jm._check_job_list([job_c, None, "", job_c, job_c, None, job_d]), - ( - [], - [job_c, job_d], - ), + assert self.jm._check_job_list( + [job_c, None, "", job_c, job_c, None, job_d] + ) == ( + [], + [job_c, job_d], ) - self.assertEqual( - self.jm._check_job_list([job_c, None, "", None, job_a, job_a, job_a]), - ( - [job_a], - [job_c], - ), + assert self.jm._check_job_list( + [job_c, None, "", None, job_a, job_a, job_a] + ) == ( + [job_a], + [job_c], ) - self.assertEqual( - self.jm._check_job_list([None, job_a, None, "", None, job_b]), - ( - [job_a, job_b], - [], - ), + assert self.jm._check_job_list([None, job_a, None, "", None, job_b]) == ( + [job_a, job_b], + [], ) @mock.patch(CLIENTS, get_mock_client) def test__construct_job_output_state_set(self): - self.assertEqual( - self.jm._construct_job_output_state_set(ALL_JOBS), - { - job_id: ALL_RESPONSE_DATA[MESSAGE_TYPE["STATUS"]][job_id] - for job_id in ALL_JOBS - }, - ) + assert self.jm._construct_job_output_state_set(ALL_JOBS) == { + job_id: ALL_RESPONSE_DATA[MESSAGE_TYPE["STATUS"]][job_id] + for job_id in ALL_JOBS + } def test__construct_job_output_state_set__empty_list(self): - self.assertEqual(self.jm._construct_job_output_state_set([]), {}) + assert self.jm._construct_job_output_state_set([]) == {} @mock.patch(CLIENTS, get_mock_client) def test__construct_job_output_state_set__ee2_error(self): @@ -248,37 +234,34 @@ def mock_check_jobs(params): with mock.patch.object(MockClients, "check_jobs", side_effect=mock_check_jobs): job_states = self.jm._construct_job_output_state_set(ALL_JOBS) - self.assertEqual( - expected, - job_states, - ) + assert expected == job_states def test__create_jobs__empty_list(self): - self.assertEqual(self.jm._create_jobs([]), {}) + assert self.jm._create_jobs([]) == {} def test__create_jobs__jobs_already_exist(self): job_list = self.jm._running_jobs.keys() - self.assertEqual(self.jm._create_jobs(job_list), {}) + assert self.jm._create_jobs(job_list) == {} def test__get_job_good(self): job_id = ALL_JOBS[0] job = self.jm.get_job(job_id) - self.assertEqual(job_id, job.job_id) - self.assertIsInstance(job, Job) + assert job_id == job.job_id + assert isinstance(job, Job) def test__get_job_fail(self): inputs = [None, "", JOB_NOT_FOUND] for bad_input in inputs: - with self.assertRaisesRegex( - JobRequestException, f"{JOB_NOT_REG_ERR}: {bad_input}" + with pytest.raises( + JobRequestException, match=f"{JOB_NOT_REG_ERR}: {bad_input}" ): self.jm.get_job(bad_input) @mock.patch(CLIENTS, get_mock_client) def test_list_jobs_html(self): jobs_html = self.jm.list_jobs() - self.assertIsInstance(jobs_html, HTML) + assert isinstance(jobs_html, HTML) html = jobs_html.data counts = { @@ -309,28 +292,28 @@ def test_list_jobs_html(self): n_not_started += 1 for job_id in ALL_JOBS: - self.assertIn(f'