fix: clean up pre-existing lint in test_api.py and document per-request key read
This commit is contained in:
+22
-3
@@ -558,6 +558,7 @@ async def instance_info() -> dict:
|
|||||||
discover peer names and verify reachability.
|
discover peer names and verify reachability.
|
||||||
"""
|
"""
|
||||||
settings = load_settings()
|
settings = load_settings()
|
||||||
|
# Read fresh so the UI reflects key-file changes without requiring a restart.
|
||||||
fed_key = load_federation_key()
|
fed_key = load_federation_key()
|
||||||
return {
|
return {
|
||||||
"name": settings["device_name"],
|
"name": settings["device_name"],
|
||||||
@@ -819,7 +820,13 @@ async def federation_sessions(request: Request) -> list[dict]:
|
|||||||
headers={"Authorization": f"Bearer {key}"},
|
headers={"Authorization": f"Bearer {key}"},
|
||||||
)
|
)
|
||||||
if resp.status_code in (401, 403):
|
if resp.status_code in (401, 403):
|
||||||
return [{"status": "auth_failed", "remoteId": remote_id, "deviceName": remote_name}]
|
return [
|
||||||
|
{
|
||||||
|
"status": "auth_failed",
|
||||||
|
"remoteId": remote_id,
|
||||||
|
"deviceName": remote_name,
|
||||||
|
}
|
||||||
|
]
|
||||||
resp.raise_for_status()
|
resp.raise_for_status()
|
||||||
sessions = resp.json()
|
sessions = resp.json()
|
||||||
# Tag each session with deviceName and remoteId
|
# Tag each session with deviceName and remoteId
|
||||||
@@ -828,9 +835,21 @@ async def federation_sessions(request: Request) -> list[dict]:
|
|||||||
for s in sessions
|
for s in sessions
|
||||||
]
|
]
|
||||||
except httpx.HTTPStatusError:
|
except httpx.HTTPStatusError:
|
||||||
return [{"status": "auth_failed", "remoteId": remote_id, "deviceName": remote_name}]
|
return [
|
||||||
|
{
|
||||||
|
"status": "auth_failed",
|
||||||
|
"remoteId": remote_id,
|
||||||
|
"deviceName": remote_name,
|
||||||
|
}
|
||||||
|
]
|
||||||
except Exception:
|
except Exception:
|
||||||
return [{"status": "unreachable", "remoteId": remote_id, "deviceName": remote_name}]
|
return [
|
||||||
|
{
|
||||||
|
"status": "unreachable",
|
||||||
|
"remoteId": remote_id,
|
||||||
|
"deviceName": remote_name,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
remote_results: list[list[dict]] = await asyncio.gather(
|
remote_results: list[list[dict]] = await asyncio.gather(
|
||||||
*(fetch_remote(remote) for remote in remote_instances)
|
*(fetch_remote(remote) for remote in remote_instances)
|
||||||
|
|||||||
+30
-13
@@ -1427,7 +1427,6 @@ def test_federation_bearer_auth_accepted(monkeypatch):
|
|||||||
After implementation: _federation_key exists, middleware is found and patched,
|
After implementation: _federation_key exists, middleware is found and patched,
|
||||||
Bearer request is accepted.
|
Bearer request is accepted.
|
||||||
"""
|
"""
|
||||||
from muxplex.main import _federation_key # ImportError before implementation
|
|
||||||
from muxplex.auth import AuthMiddleware
|
from muxplex.auth import AuthMiddleware
|
||||||
import muxplex.main as main_module
|
import muxplex.main as main_module
|
||||||
|
|
||||||
@@ -1474,7 +1473,7 @@ def test_federation_client_exists_on_app_state(monkeypatch):
|
|||||||
|
|
||||||
monkeypatch.setenv("MUXPLEX_PASSWORD", "test-password")
|
monkeypatch.setenv("MUXPLEX_PASSWORD", "test-password")
|
||||||
|
|
||||||
with TestClient(app) as c:
|
with TestClient(app):
|
||||||
# Inside the context manager the lifespan has completed startup,
|
# Inside the context manager the lifespan has completed startup,
|
||||||
# so app.state.federation_client must be set.
|
# so app.state.federation_client must be set.
|
||||||
assert hasattr(app.state, "federation_client"), (
|
assert hasattr(app.state, "federation_client"), (
|
||||||
@@ -1515,11 +1514,16 @@ def test_federation_sessions_returns_local_sessions(client, monkeypatch, tmp_pat
|
|||||||
|
|
||||||
# Write settings with a known device_name and no remote instances
|
# Write settings with a known device_name and no remote instances
|
||||||
import json
|
import json
|
||||||
settings_path.write_text(json.dumps({"device_name": "my-workstation", "remote_instances": []}))
|
|
||||||
|
settings_path.write_text(
|
||||||
|
json.dumps({"device_name": "my-workstation", "remote_instances": []})
|
||||||
|
)
|
||||||
|
|
||||||
# Mock local session data
|
# Mock local session data
|
||||||
monkeypatch.setattr("muxplex.main.get_session_list", lambda: ["session-one"])
|
monkeypatch.setattr("muxplex.main.get_session_list", lambda: ["session-one"])
|
||||||
monkeypatch.setattr("muxplex.main.get_snapshots", lambda: {"session-one": "pane text"})
|
monkeypatch.setattr(
|
||||||
|
"muxplex.main.get_snapshots", lambda: {"session-one": "pane text"}
|
||||||
|
)
|
||||||
|
|
||||||
response = client.get("/api/federation/sessions")
|
response = client.get("/api/federation/sessions")
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
@@ -1538,7 +1542,9 @@ def test_federation_sessions_returns_local_sessions(client, monkeypatch, tmp_pat
|
|||||||
assert local["remoteId"] is None
|
assert local["remoteId"] is None
|
||||||
|
|
||||||
|
|
||||||
def test_federation_sessions_includes_remote_failure_status(client, monkeypatch, tmp_path):
|
def test_federation_sessions_includes_remote_failure_status(
|
||||||
|
client, monkeypatch, tmp_path
|
||||||
|
):
|
||||||
"""GET /api/federation/sessions includes a status entry for unreachable remotes.
|
"""GET /api/federation/sessions includes a status entry for unreachable remotes.
|
||||||
|
|
||||||
When a remote instance cannot be reached (connection error), the result must
|
When a remote instance cannot be reached (connection error), the result must
|
||||||
@@ -1554,19 +1560,28 @@ def test_federation_sessions_includes_remote_failure_status(client, monkeypatch,
|
|||||||
monkeypatch.setattr(settings_mod, "SETTINGS_PATH", settings_path)
|
monkeypatch.setattr(settings_mod, "SETTINGS_PATH", settings_path)
|
||||||
|
|
||||||
# Configure one remote instance that will fail
|
# Configure one remote instance that will fail
|
||||||
settings_path.write_text(json.dumps({
|
settings_path.write_text(
|
||||||
"device_name": "local-host",
|
json.dumps(
|
||||||
"remote_instances": [
|
{
|
||||||
{"url": "http://remote-host:8088", "key": "abc123", "name": "remote-host", "id": "remote-1"}
|
"device_name": "local-host",
|
||||||
],
|
"remote_instances": [
|
||||||
}))
|
{
|
||||||
|
"url": "http://remote-host:8088",
|
||||||
|
"key": "abc123",
|
||||||
|
"name": "remote-host",
|
||||||
|
"id": "remote-1",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
# Mock local sessions (empty for simplicity)
|
# Mock local sessions (empty for simplicity)
|
||||||
monkeypatch.setattr("muxplex.main.get_session_list", lambda: [])
|
monkeypatch.setattr("muxplex.main.get_session_list", lambda: [])
|
||||||
monkeypatch.setattr("muxplex.main.get_snapshots", lambda: {})
|
monkeypatch.setattr("muxplex.main.get_snapshots", lambda: {})
|
||||||
|
|
||||||
# Patch the federation_client to raise a ConnectError (unreachable)
|
# Patch the federation_client to raise a ConnectError (unreachable)
|
||||||
from unittest.mock import AsyncMock, MagicMock
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
async def mock_get(url, **kwargs):
|
async def mock_get(url, **kwargs):
|
||||||
raise httpx.ConnectError("Connection refused")
|
raise httpx.ConnectError("Connection refused")
|
||||||
@@ -1583,7 +1598,9 @@ def test_federation_sessions_includes_remote_failure_status(client, monkeypatch,
|
|||||||
assert isinstance(data, list)
|
assert isinstance(data, list)
|
||||||
|
|
||||||
# Find the failure status entry for the remote
|
# Find the failure status entry for the remote
|
||||||
failure_entries = [s for s in data if s.get("status") in ("unreachable", "auth_failed")]
|
failure_entries = [
|
||||||
|
s for s in data if s.get("status") in ("unreachable", "auth_failed")
|
||||||
|
]
|
||||||
assert len(failure_entries) == 1, (
|
assert len(failure_entries) == 1, (
|
||||||
f"Expected 1 failure status entry, got: {failure_entries}. Full data: {data}"
|
f"Expected 1 failure status entry, got: {failure_entries}. Full data: {data}"
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user