Files
OpenJarvis/tests/server/test_pwa_serving.py
T
Jon Saad-FalconandClaude Opus 4.6 24972e3e52 Add Phase 12+13: energy measurement, install polish, PWA, cross-hardware
Phase 12 — Energy Measurement Upgrade:
- EnergyMonitor ABC with multi-vendor support (NVIDIA hw counters,
  AMD amdsmi, Apple zeus-ml, CPU RAPL sysfs)
- EnergyBatch batch-level energy-per-token accounting
- SteadyStateDetector CV-based thermal equilibrium detection
- EnergyBenchmark with warmup phase
- InstrumentedEngine prefers EnergyMonitor over legacy GpuMonitor
- Telemetry store/aggregator extended with energy fields

Phase 13 — Install, Hosting, Cross-Hardware:
- jarvis doctor diagnostic command (8 checks, --json output)
- jarvis init post-setup guidance with engine-specific next steps
- README Quick Start section
- MLX engine backend (Apple Silicon → mlx recommendation)
- AMD VRAM/multi-GPU detection via rocm-smi
- PyTorch MPS device selection in orchestrator trainers
- PWA support (vite-plugin-pwa, service worker, manifest, icons)
- Server static file serving fix for PWA files
- Dockerfile.gpu.rocm + docker-compose.gpu.rocm.yml for ROCm
- Eval framework display module and efficiency metrics

2244 tests pass, 37 skipped.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 20:09:07 +00:00

107 lines
3.8 KiB
Python

"""Tests for PWA static file serving in the SPA catch-all endpoint."""
from __future__ import annotations
import pathlib
from unittest.mock import MagicMock
import pytest
fastapi = pytest.importorskip("fastapi")
from fastapi.testclient import TestClient # noqa: E402
from openjarvis.server.app import create_app # noqa: E402
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
def _make_engine():
engine = MagicMock()
engine.engine_id = "mock"
engine.health.return_value = True
engine.list_models.return_value = ["test-model"]
engine.generate.return_value = {
"content": "hello",
"usage": {"prompt_tokens": 1, "completion_tokens": 1, "total_tokens": 2},
"model": "test-model",
"finish_reason": "stop",
}
return engine
def _create_static_dir(tmp_path: pathlib.Path) -> pathlib.Path:
"""Create a temporary static directory with index.html and PWA files."""
static = tmp_path / "static"
static.mkdir()
(static / "index.html").write_text("<html><body>SPA</body></html>")
(static / "sw.js").write_text("// service worker")
(static / "manifest.webmanifest").write_text('{"name":"OpenJarvis"}')
(static / "pwa-192x192.png").write_bytes(b"\x89PNG placeholder")
assets = static / "assets"
assets.mkdir()
(assets / "app.js").write_text("console.log('app')")
return static
@pytest.fixture()
def client_with_static(tmp_path, monkeypatch):
"""Create a test client with a real temporary static directory."""
static_dir = _create_static_dir(tmp_path)
engine = _make_engine()
# Patch Path(__file__).parent to make static_dir resolve to our tmp dir
original_truediv = pathlib.Path.__truediv__
def patched_truediv(self, key):
result = original_truediv(self, key)
# Intercept the "static" lookup in app.py
if key == "static" and str(self).endswith("server"):
return static_dir
return result
monkeypatch.setattr(pathlib.Path, "__truediv__", patched_truediv)
app = create_app(engine, "test-model")
monkeypatch.undo() # Restore immediately after app creation
return TestClient(app)
# ---------------------------------------------------------------------------
# Tests
# ---------------------------------------------------------------------------
class TestPWAServing:
def test_sw_js_served_as_file(self, client_with_static):
"""Service worker file should be served directly, not as index.html."""
resp = client_with_static.get("/sw.js")
assert resp.status_code == 200
assert "// service worker" in resp.text
def test_manifest_served_as_file(self, client_with_static):
"""Web manifest should be served directly."""
resp = client_with_static.get("/manifest.webmanifest")
assert resp.status_code == 200
assert "OpenJarvis" in resp.text
def test_icon_served_as_file(self, client_with_static):
"""PWA icon should be served directly."""
resp = client_with_static.get("/pwa-192x192.png")
assert resp.status_code == 200
assert b"PNG" in resp.content
def test_api_routes_bypass_spa(self, client_with_static):
"""API routes should still work regardless of SPA catch-all."""
resp = client_with_static.get("/v1/models")
assert resp.status_code == 200
data = resp.json()
assert data["object"] == "list"
def test_path_traversal_blocked(self, client_with_static):
"""Path traversal attempts should fall back to index.html."""
resp = client_with_static.get("/../../etc/passwd")
assert resp.status_code == 200
# Should get index.html, not the passwd file
assert "SPA" in resp.text