Files
muxplex/muxplex/tests/test_main.py
T
Ken 46c6199087
CI / test (3.13) (push) Failing after 12m18s
CI / test (3.12) (push) Failing after 12m20s
CI / test (3.11) (push) Failing after 12m22s
perf: optimize muxplex performance across frontend and backend
- Optimize app.js rendering and event handling for faster UI responsiveness
- Optimize style.css with improved selectors and reduced re-layouts
- Optimize terminal.js WebSocket handling and message processing
- Optimize main.py with faster request handling and caching
- Optimize muxterm.py with improved state management and pooling
- Optimize pool.go with better goroutine and connection pooling
- Add performance benchmarks for frontend CSS selectors
- Add performance tests for main module routines

Generated with Amplifier

Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
2026-05-29 04:04:24 +00:00

171 lines
6.1 KiB
Python

"""
Tests for the cache-busting version suffix on static asset URLs served by index_page().
Verifies that GET / (the main dashboard) injects ?v=<version> on every
<script src=""> and <link href=""> URL so browsers pick up new code
immediately after each release, rather than serving stale JS/CSS from
the HTTP cache.
"""
import pytest
from bs4 import BeautifulSoup
from fastapi.testclient import TestClient
from muxplex.main import _UI_VERSION, app
# ---------------------------------------------------------------------------
# Shared fixtures (mirror test_api.py setup so tests run cleanly in isolation)
# ---------------------------------------------------------------------------
@pytest.fixture(autouse=True)
def patch_startup_and_state(tmp_path, monkeypatch):
"""Redirect state files to tmp_path and stub out long-running startup tasks."""
tmp_state_dir = tmp_path / "state"
tmp_state_path = tmp_state_dir / "state.json"
monkeypatch.setattr("muxplex.state.STATE_DIR", tmp_state_dir)
monkeypatch.setattr("muxplex.state.STATE_PATH", tmp_state_path)
# Mock start_muxterm/stop_muxterm so startup doesn't touch real processes
from unittest.mock import AsyncMock
monkeypatch.setattr("muxplex.main.start_muxterm", AsyncMock())
monkeypatch.setattr("muxplex.main.stop_muxterm", AsyncMock())
async def noop_poll_loop() -> None:
pass
monkeypatch.setattr("muxplex.main._poll_loop", noop_poll_loop)
@pytest.fixture(autouse=True)
def reset_federation_cache():
"""Clear _federation_cache before and after each test."""
import muxplex.main as main_mod
main_mod._federation_cache.clear()
yield
main_mod._federation_cache.clear()
@pytest.fixture
def client(monkeypatch):
"""Authenticated TestClient with the app lifespan active."""
monkeypatch.setenv("MUXPLEX_PASSWORD", "test-password")
with TestClient(app) as c:
from muxplex.auth import create_session_cookie
from muxplex.main import _auth_secret, _auth_ttl
cookie = create_session_cookie(_auth_secret, _auth_ttl)
c.cookies.set("muxplex_session", cookie)
yield c
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
def _get_index_soup(client) -> BeautifulSoup:
response = client.get("/")
assert response.status_code == 200, f"GET / returned {response.status_code}"
return BeautifulSoup(response.text, "html.parser")
# ---------------------------------------------------------------------------
# Test 1 — every script src and link href carries the version suffix
# ---------------------------------------------------------------------------
def test_index_all_asset_urls_have_version_suffix(client):
"""GET / must inject ?v=<version> on every <script src> and <link href> asset URL.
Regression guard for the "is the user seeing stale JS?" investigation:
verifies that the standard HTTP cache is busted on every release by
appending a version query parameter to each static asset reference.
"""
version = _UI_VERSION
soup = _get_index_soup(client)
# All <script src="…"> tags
script_tags = soup.find_all("script", src=True)
assert len(script_tags) >= 6, (
f"Expected at least 6 <script src> tags, found {len(script_tags)}"
)
for tag in script_tags:
src = tag["src"]
assert f"?v={version}" in src, (
f"<script src> missing ?v={version} suffix: {src!r}"
)
# All <link href="…"> tags
link_tags = soup.find_all("link", href=True)
assert len(link_tags) >= 1, "Expected at least one <link href> tag"
for tag in link_tags:
href = tag["href"]
assert f"?v={version}" in href, (
f"<link href> missing ?v={version} suffix: {href!r}"
)
# ---------------------------------------------------------------------------
# Test 2 — vendor scripts are individually versioned (not just app.js)
# ---------------------------------------------------------------------------
def test_index_vendor_scripts_each_versioned(client):
"""All five vendor JS bundles must carry the version suffix, not just app.js.
The browser-tester on spark-1 observed bare vendor URLs. This test
ensures that ghostty-web.js and its addons are cache-busted alongside the
first-party scripts.
"""
version = _UI_VERSION
soup = _get_index_soup(client)
script_srcs = [tag["src"] for tag in soup.find_all("script", src=True)]
expected_versioned = [
f"/vendor/ghostty-web.js?v={version}",
f"/vendor/xterm-addon-web-links.js?v={version}",
f"/vendor/xterm-addon-search.js?v={version}",
f"/vendor/addon-image.js?v={version}",
f"/app.js?v={version}",
f"/terminal.js?v={version}",
]
for expected in expected_versioned:
assert expected in script_srcs, (
f"Expected versioned script {expected!r}; found srcs: {script_srcs}"
)
# ---------------------------------------------------------------------------
# Test 3 — versioned asset URLs still resolve to the actual static files
# ---------------------------------------------------------------------------
def test_versioned_asset_url_resolves_to_static_file(client):
"""GET /app.js?v=<version> must return HTTP 200 (static handler ignores query string).
Sanity check: adding the version suffix must not break asset loading.
Starlette's StaticFiles handler ignores query parameters when looking up
files on disk, so the versioned URL must serve identically to the bare URL.
"""
version = _UI_VERSION
# First-party assets
for path in ("/app.js", "/terminal.js", "/style.css"):
url = f"{path}?v={version}"
resp = client.get(url)
assert resp.status_code == 200, (
f"Versioned URL {url!r} returned {resp.status_code}, expected 200"
)
# Vendor asset
vendor_url = f"/vendor/ghostty-web.js?v={version}"
resp = client.get(vendor_url)
assert resp.status_code == 200, (
f"Versioned vendor URL {vendor_url!r} returned {resp.status_code}, expected 200"
)