mirror of
https://github.com/open-jarvis/OpenJarvis.git
synced 2026-07-28 14:07:55 +00:00
107 lines
3.8 KiB
Python
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
|