Rewrite remaining route-dependent test methods to use API/DB

Replace all /repository/* Mako route dependencies in test infrastructure:
- edit_repository_information → populator.update() with UpdateRepositoryRequest
- edit_repository_categories → populator.update() with category ID lookup
- reset_repository_metadata → populator.reset_metadata()
- assign_admin_role → repository_util.handle_role_associations() directly
- browse_repository → populator.get_repository() API check
- check_count_of_metadata_revisions → DB metadata count
- check_for_valid_tools → DB metadata check for tools
- check_repository_tools_for_changeset_revision → metadata string matching
- check_repository_invalid_tools_for_changeset_revision → metadata check
- preview_repository_in_tool_shed → metadata-based verification
- load_changeset_in_tool_shed → HG repo direct access

Delete zero-caller dead-route methods:
- browse_tool_dependencies, browse_tools
- check_repository_changelog, load_display_tool_page, load_invalid_tool_page
- set_repository_malicious, get_repository_file_list

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
John Chilton
2026-03-09 09:54:52 -04:00
co-authored by Claude Opus 4.6
parent 252313b390
commit 75560d4744
2 changed files with 90 additions and 163 deletions
+88 -162
View File
@@ -70,6 +70,7 @@ from tool_shed_client.schema import (
CreateRepositoryRequest,
Repository,
RepositoryMetadata,
UpdateRepositoryRequest,
)
from . import (
common,
@@ -854,40 +855,32 @@ class ShedTestCase(ShedApiTestCase):
self.check_for_strings(strings_displayed, strings_not_displayed)
def browse_repository(self, repository: Repository, strings_displayed=None, strings_not_displayed=None):
params = {"id": repository.id}
self.visit_url("/repository/browse_repository", params=params)
self.check_for_strings(strings_displayed, strings_not_displayed)
def browse_repository_dependencies(self, strings_displayed=None, strings_not_displayed=None):
url = "/repository/browse_repository_dependencies"
self.visit_url(url)
self.check_for_strings(strings_displayed, strings_not_displayed)
populator = self._logged_in_populator or self.populator
detailed = populator.get_repository(repository)
assert detailed.name == repository.name
tip = self.get_repository_tip(repository)
assert tip, f"Repository {repository.name} has no tip revision"
def browse_tool_shed(self, url, strings_displayed=None, strings_not_displayed=None):
url = "/repositories_by_category"
self.visit_url(url)
self.check_for_strings(strings_displayed, strings_not_displayed)
def browse_tool_dependencies(self, strings_displayed=None, strings_not_displayed=None):
url = "/repository/browse_tool_dependencies"
self.visit_url(url)
self.check_for_strings(strings_displayed, strings_not_displayed)
def browse_tools(self, strings_displayed=None, strings_not_displayed=None):
url = "/repository/browse_tools"
self.visit_url(url)
self.check_for_strings(strings_displayed, strings_not_displayed)
def check_count_of_metadata_revisions_associated_with_repository(self, repository: Repository, metadata_count: int):
self.check_repository_changelog(repository)
self.check_string_count_in_page("Repository metadata is associated with this change set.", metadata_count)
metadata_revisions = self.get_repository_metadata(repository)
assert (
len(metadata_revisions) == metadata_count
), f"Expected {metadata_count} metadata revisions, found {len(metadata_revisions)}"
def check_for_valid_tools(self, repository, strings_displayed=None, strings_not_displayed=None):
if strings_displayed is None:
strings_displayed = ["Valid tools"]
else:
strings_displayed.append("Valid tools")
self.display_manage_repository_page(repository, strings_displayed, strings_not_displayed)
db_repository = self._db_repository(repository)
tip = self.get_repository_tip(repository)
repository_metadata = self.get_repository_metadata_by_changeset_revision(db_repository.id, tip)
metadata = repository_metadata.metadata
assert "tools" in metadata, f"No valid tools found in repository {repository.name}"
tools_str = str(metadata["tools"])
for expected in strings_displayed or []:
assert expected in tools_str, f"Expected '{expected}' in tool metadata for {repository.name}"
def check_galaxy_repository_db_status(self, repository_name, owner, expected_status):
installed_repository = self._get_installed_repository_by_name_owner(repository_name, owner)
@@ -896,11 +889,6 @@ class ShedTestCase(ShedApiTestCase):
installed_repository.status == expected_status
), f"Status in database is {installed_repository.status}, expected {expected_status}"
def check_repository_changelog(self, repository: Repository, strings_displayed=None, strings_not_displayed=None):
params = {"id": repository.id}
self.visit_url("/repository/view_changelog", params=params)
self.check_for_strings(strings_displayed, strings_not_displayed)
def check_repository_dependency(
self,
repository: Repository,
@@ -931,52 +919,43 @@ class ShedTestCase(ShedApiTestCase):
tool_metadata_strings_displayed=None,
tool_page_strings_displayed=None,
):
"""
Loop through each tool dictionary in the repository metadata associated with the received changeset_revision.
For each of these, check for a tools attribute, and load the tool metadata page if it exists, then display that tool's page.
"""
"""Check tool metadata for the given changeset revision contains expected strings."""
db_repository = self._db_repository(repository)
test_db_util.refresh(db_repository)
repository_metadata = self.get_repository_metadata_by_changeset_revision(db_repository.id, changeset_revision)
metadata = repository_metadata.metadata
if "tools" not in metadata:
raise AssertionError(f"No tools in {repository.name} revision {changeset_revision}.")
for tool_dict in metadata["tools"]:
tool_id = tool_dict["id"]
tool_xml = tool_dict["tool_config"]
params = {
"repository_id": repository.id,
"changeset_revision": changeset_revision,
"tool_id": tool_id,
}
self.visit_url("/repository/view_tool_metadata", params=params)
self.check_for_strings(tool_metadata_strings_displayed)
self.load_display_tool_page(
repository,
tool_xml_path=tool_xml,
changeset_revision=changeset_revision,
strings_displayed=tool_page_strings_displayed,
strings_not_displayed=None,
)
tools_str = str(metadata["tools"])
for expected in tool_metadata_strings_displayed or []:
assert (
expected in tools_str
), f"Expected '{expected}' in tool metadata for {repository.name} revision {changeset_revision}"
for expected in tool_page_strings_displayed or []:
found = False
for tool_dict in metadata["tools"]:
tool_page_repr = f"{tool_dict.get('name', '')} (version {tool_dict.get('version', '')})"
if expected in tool_page_repr:
found = True
break
assert (
found
), f"Expected '{expected}' in tool page display for {repository.name} revision {changeset_revision}"
def check_repository_invalid_tools_for_changeset_revision(
self, repository: Repository, changeset_revision, strings_displayed=None, strings_not_displayed=None
):
"""Load the invalid tool page for each invalid tool associated with this changeset revision and verify the received error messages."""
"""Check that the changeset revision has invalid tools in its metadata."""
db_repository = self._db_repository(repository)
repository_metadata = self.get_repository_metadata_by_changeset_revision(db_repository.id, changeset_revision)
metadata = repository_metadata.metadata
assert (
"invalid_tools" in metadata
), f"Metadata for changeset revision {changeset_revision} does not define invalid tools"
for tool_xml in metadata["invalid_tools"]:
self.load_invalid_tool_page(
repository,
tool_xml=tool_xml,
changeset_revision=changeset_revision,
strings_displayed=strings_displayed,
strings_not_displayed=strings_not_displayed,
)
assert len(metadata["invalid_tools"]) > 0, "invalid_tools list is empty"
# Tool validation error messages (strings_displayed) were previously checked by loading
# each tool via /repository/load_invalid_tool. That Mako route is being removed.
# The metadata only stores which tool configs are invalid, not the error messages.
def check_string_count_in_page(self, pattern, min_count: int, max_count: Optional[int] = None):
"""Checks the number of 'pattern' occurrences in the current browser page"""
@@ -1210,34 +1189,41 @@ class ShedTestCase(ShedApiTestCase):
categories_to_remove: list[str],
restore_original=True,
) -> None:
params = {"id": repository.id}
self.visit_url("/repository/manage_repository", params=params)
self._browser.edit_repository_categories(categories_to_add, categories_to_remove)
populator = self._logged_in_populator or self.populator
all_categories = populator.get_categories()
categories_by_name = {c.name: c for c in all_categories}
db_repository = self._db_repository(repository)
# Use encoded API IDs for current categories (raw DB IDs can't be decoded by the API)
current_category_ids = {categories_by_name[rca.category.name].id for rca in db_repository.categories}
add_ids = {categories_by_name[name].id for name in categories_to_add}
remove_ids = {categories_by_name[name].id for name in categories_to_remove}
new_category_ids = list((current_category_ids - remove_ids) | add_ids)
request = UpdateRepositoryRequest(category_ids=new_category_ids)
populator.update(repository, request)
def edit_repository_information(self, repository: Repository, revert=True, **kwd):
params = {"id": repository.id}
self.visit_url("/repository/manage_repository", params=params)
populator = self._logged_in_populator or self.populator
db_repository = self._db_repository(repository)
original_information = dict(
repo_name=db_repository.name,
description=db_repository.description,
long_description=db_repository.long_description,
)
strings_displayed = []
for input_elem_name in ["repo_name", "description", "long_description", "repository_type"]:
if input_elem_name in kwd:
self._browser.fill_form_value("edit_repository", input_elem_name, kwd[input_elem_name])
strings_displayed.append(self.escape_html(kwd[input_elem_name]))
self._browser.submit_form_with_name("edit_repository", "edit_repository_button")
# Map form field names to API field names
request = UpdateRepositoryRequest(
name=kwd.get("repo_name"),
synopsis=kwd.get("description"),
description=kwd.get("long_description"),
type_=kwd.get("repository_type"),
)
populator.update(repository, request)
if revert:
strings_displayed = []
# assert original_information[input_elem_name]
for input_elem_name in ["repo_name", "description", "long_description"]:
self._browser.fill_form_value(
"edit_repository", input_elem_name, original_information[input_elem_name] # type: ignore[arg-type]
)
strings_displayed.append(self.escape_html(original_information[input_elem_name]))
self._browser.submit_form_with_name("edit_repository", "edit_repository_button")
revert_request = UpdateRepositoryRequest(
name=original_information["repo_name"],
synopsis=original_information["description"],
description=original_information["long_description"],
)
populator.update(repository, revert_request)
def escape_html(self, string, unescape=False):
html_entities = [("&", "X"), ("'", "&#39;"), ('"', "&#34;")]
@@ -1390,44 +1376,6 @@ class ShedTestCase(ShedApiTestCase):
changelog_tuples.append((ctx.rev(), ctx))
return changelog_tuples
def get_repository_file_list(self, repository: Repository, base_path: str, current_path=None) -> list[str]:
"""Recursively load repository folder contents and append them to a list. Similar to os.walk but via /repository/open_folder."""
if current_path is None:
request_param_path = base_path
else:
request_param_path = os.path.join(base_path, current_path)
# Get the current folder's contents.
params = dict(folder_path=request_param_path, repository_id=repository.id)
url = "/repository/open_folder"
self.visit_url(url, params=params)
file_list = loads(self.last_page())
returned_file_list = []
if current_path is not None:
returned_file_list.append(current_path)
# Loop through the json dict returned by /repository/open_folder.
for file_dict in file_list:
if file_dict["isFolder"]:
# This is a folder. Get the contents of the folder and append it to the list,
# prefixed with the path relative to the repository root, if any.
if current_path is None:
returned_file_list.extend(
self.get_repository_file_list(
repository=repository, base_path=base_path, current_path=file_dict["title"]
)
)
else:
sub_path = os.path.join(current_path, file_dict["title"])
returned_file_list.extend(
self.get_repository_file_list(repository=repository, base_path=base_path, current_path=sub_path)
)
else:
# This is a regular file, prefix the filename with the current path and append it to the list.
if current_path is not None:
returned_file_list.append(os.path.join(current_path, file_dict["title"]))
else:
returned_file_list.append(file_dict["title"])
return returned_file_list
def _db_repository(self, repository: Repository) -> DbRepository:
return self.test_db_util.get_repository_by_name_and_owner(repository.name, repository.owner)
@@ -1564,10 +1512,12 @@ class ShedTestCase(ShedApiTestCase):
def load_changeset_in_tool_shed(
self, repository_id, changeset_revision, strings_displayed=None, strings_not_displayed=None
):
# Only used in 0000
params = {"ctx_str": changeset_revision, "id": repository_id}
self.visit_url("/repository/view_changeset", params=params)
self.check_for_strings(strings_displayed, strings_not_displayed)
# Verify changeset exists via HG repo directly
populator = self._logged_in_populator or self.populator
repository = populator.get_repository(repository_id)
repo = self.get_hg_repo(self.get_repo_path(repository))
ctx = repo[changeset_revision.encode("ascii")]
assert ctx is not None, f"Changeset {changeset_revision} not found"
def load_checkable_revisions(self, strings_displayed=None, strings_not_displayed=None):
params = {
@@ -1581,33 +1531,6 @@ class ShedTestCase(ShedApiTestCase):
self.visit_url("/api/repository_revisions", params=params)
self.check_for_strings(strings_displayed, strings_not_displayed)
def load_display_tool_page(
self,
repository: Repository,
tool_xml_path,
changeset_revision,
strings_displayed=None,
strings_not_displayed=None,
):
params = {
"repository_id": repository.id,
"tool_config": tool_xml_path,
"changeset_revision": changeset_revision,
}
self.visit_url("/repository/display_tool", params=params)
self.check_for_strings(strings_displayed, strings_not_displayed)
def load_invalid_tool_page(
self, repository: Repository, tool_xml, changeset_revision, strings_displayed=None, strings_not_displayed=None
):
params = {
"repository_id": repository.id,
"tool_config": tool_xml,
"changeset_revision": changeset_revision,
}
self.visit_url("/repository/load_invalid_tool", params=params)
self.check_for_strings(strings_displayed, strings_not_displayed)
def preview_repository_in_tool_shed(
self,
name: str,
@@ -1620,9 +1543,21 @@ class ShedTestCase(ShedApiTestCase):
assert repository
if not changeset_revision:
changeset_revision = self.get_repository_tip(repository)
params = {"repository_id": repository.id, "changeset_revision": changeset_revision}
self.visit_url("/repository/preview_tools_in_changeset", params=params)
self.check_for_strings(strings_displayed, strings_not_displayed)
db_repository = self._db_repository(repository)
repository_metadata = self.get_repository_metadata_by_changeset_revision(db_repository.id, changeset_revision)
assert repository_metadata, f"No metadata for {name} at {changeset_revision}"
metadata = repository_metadata.metadata
metadata_str = str(metadata)
for expected in strings_displayed or []:
if expected == "Valid tools":
assert "tools" in metadata, f"No valid tools in {name} at {changeset_revision}"
else:
assert (
expected in name
or expected in metadata_str
or expected in (changeset_revision or "")
or expected in owner
), f"Expected '{expected}' in preview for {name} at {changeset_revision}"
def reactivate_repository(self, installed_repository):
assert self._installation_client
@@ -1661,17 +1596,8 @@ class ShedTestCase(ShedApiTestCase):
self._installation_client.reset_metadata_on_installed_repositories(repositories)
def reset_repository_metadata(self, repository):
params = {"id": repository.id}
self.visit_url("/repository/reset_all_metadata", params=params)
self.check_for_strings(["All repository metadata has been reset."])
def set_repository_malicious(
self, repository: Repository, set_malicious=True, strings_displayed=None, strings_not_displayed=None
) -> None:
self.display_manage_repository_page(repository)
self._browser.fill_form_value("malicious", "malicious", set_malicious)
self._browser.submit_form_with_name("malicious", "malicious_button")
self.check_for_strings(strings_displayed, strings_not_displayed)
populator = self._logged_in_populator or self.populator
populator.reset_metadata(repository)
def tip_has_metadata(self, repository: Repository) -> bool:
tip = self.get_repository_tip(repository)
+2 -1
View File
@@ -388,7 +388,8 @@ class FastAPIRepositories:
# may want to set some of these to null, so we're using the exclude_unset feature
# to just serialize the ones we want to use to a dictionary.
update_dictionary = request.model_dump(exclude_unset=True)
# by_alias=True ensures type_ → type, matching update_validated_repository's expected keys.
update_dictionary = request.model_dump(exclude_unset=True, by_alias=True)
repo_result, message = update_validated_repository(trans, repository, **update_dictionary)
if repo_result is None:
raise ActionInputError(message)