Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 32 additions & 2 deletions src/murfey/server/api/bootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -1178,15 +1178,45 @@ def get_pypi_file(
"""


@plugins.get("/instruments/{instrument_name}/", response_class=HTMLResponse)
def show_plugin_wheels(instrument_name: str):
"""
Shows plugin wheels that have been configured for this instrument
"""
machine_config = get_machine_config(instrument_name=instrument_name)[
instrument_name
]
# Construct links to download the individual packages with
links = "\n".join(
f'<li><a href="{key}">{key}</a></li>'
for key in machine_config.plugin_packages.keys()
)
# Embed links in a HTML page
return f"""
<!DOCTYPE html>
<html>
<head>
<title>Packages</title>
</head>
<body>
<h1>Available packages</h1>
<ul>
{links}
</ul>
</body>
</html>
"""


@plugins.get("/instruments/{instrument_name}/{package}", response_class=FileResponse)
def get_plugin_wheel(instrument_name: str, package: str):
machine_config = get_machine_config(instrument_name=instrument_name)[
instrument_name
]
wheel_path = machine_config.plugin_packages.get(package)
if wheel_path is None:
return None
raise HTTPException(status_code=404, detail=f"Package {package} not found")
return FileResponse(
wheel_path,
headers={"Content-Disposition": "attachment; filename={wheel_path.name}"},
headers={"Content-Disposition": f"attachment; filename={wheel_path.name}"},
)
7 changes: 7 additions & 0 deletions src/murfey/util/route_manifest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,13 @@ murfey.server.api.bootstrap.msys2:
methods:
- GET
murfey.server.api.bootstrap.plugins:
- path: /plugins/instruments/{instrument_name}/
function: show_plugin_wheels
path_params:
- name: instrument_name
type: str
methods:
- GET
- path: /plugins/instruments/{instrument_name}/{package}
function: get_plugin_wheel
path_params:
Expand Down
108 changes: 108 additions & 0 deletions tests/server/api/test_bootstrap.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
from pathlib import Path

import pytest
from fastapi import FastAPI
from fastapi.testclient import TestClient
from pytest_mock import MockerFixture

from murfey.server.api.bootstrap import plugins as plugins_router
from murfey.util.api import url_path_for
from murfey.util.config import MachineConfig


def set_up_test_backend_client():
"""
Helper function to set up a test backend server whose response can be inspected
to check that the endpoint function works as expected
"""
# Set up the backend server
backend_app = FastAPI()
backend_app.include_router(plugins_router)
return TestClient(backend_app)


@pytest.mark.parametrize("packages", ([], ["package_a"], ["package_a", "package_b"]))
def test_show_plugin_wheels(
mocker: MockerFixture,
packages: list[str],
tmp_path: Path,
):
# Set up test parameters
instrument_name = "murfey-test"

# Mock the 'get_machine_config' return value
plugin_packages = {pkg: tmp_path / pkg for pkg in packages}
config = MachineConfig(plugin_packages=plugin_packages)
mock_get_machine_config = mocker.patch(
"murfey.server.api.bootstrap.get_machine_config",
return_value={instrument_name: config},
)

# Set up the test backend client and the URL to poke
backend_server = set_up_test_backend_client()
backend_url_path = url_path_for(
"api.bootstrap.plugins",
"show_plugin_wheels",
instrument_name=instrument_name,
)

# Poke it and check that the calls and response are as expected
response = backend_server.get(backend_url_path)
mock_get_machine_config.assert_called_once_with(instrument_name=instrument_name)
assert response.status_code == 200

# Manually construct the HTML page
links = "\n".join(f'<li><a href="{pkg}">{pkg}</a></li>' for pkg in packages)
html_page = f"""
<!DOCTYPE html>
<html>
<head>
<title>Packages</title>
</head>
<body>
<h1>Available packages</h1>
<ul>
{links}
</ul>
</body>
</html>
"""
# Check that it was constructed correctly
assert response.content.decode() == html_page


@pytest.mark.parametrize("package_found", (True, False))
def test_get_plugin_wheel(
mocker: MockerFixture,
package_found: bool,
tmp_path: Path,
):
# Set up test parameters
instrument_name = "murfey-test"
package_name = "package_a"

# Create a test file
test_package = tmp_path / package_name
test_package.touch(exist_ok=True)

# Mock the 'get_machine_config' return value
plugin_packages = {"package_a": test_package}
config = MachineConfig(plugin_packages=plugin_packages)
mock_get_machine_config = mocker.patch(
"murfey.server.api.bootstrap.get_machine_config",
return_value={instrument_name: config},
)

# Set up the test backend client and the URL to poke
backend_server = set_up_test_backend_client()
backend_url_path = url_path_for(
"api.bootstrap.plugins",
"get_plugin_wheel",
instrument_name=instrument_name,
package="package_a" if package_found else "package_b",
)

# Poke it and check that the calls and response are as expected
response = backend_server.get(backend_url_path)
mock_get_machine_config.assert_called_once_with(instrument_name=instrument_name)
assert response.status_code == 200 if package_found else 404
Loading