feat: add health endpoints, static serving, and base template (Step 7.2)
- Add /healthz endpoint with DB writable check - Add /metrics endpoint with Prometheus-compatible format (enabled by default) - Configure static file serving at /static/v1/... with immutable cache headers - Create base page template with MonsterUI slate theme - Create industrial farm aesthetic bottom navigation with custom SVG icons - Add StaticCacheMiddleware for adding cache-control headers Changes: - src/animaltrack/web/routes/health.py: Health and metrics endpoints - src/animaltrack/web/templates/: Base template, nav, and icons - src/animaltrack/web/app.py: Integrate theme, routes, static serving - src/animaltrack/config.py: metrics_enabled defaults to True 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
85
tests/test_web_static.py
Normal file
85
tests/test_web_static.py
Normal file
@@ -0,0 +1,85 @@
|
||||
# ABOUTME: Tests for static file serving with cache headers.
|
||||
# ABOUTME: Verifies /static/v1/ path and immutable cache-control.
|
||||
|
||||
import os
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
def make_test_settings(
|
||||
csrf_secret: str = "test-secret",
|
||||
trusted_proxy_ips: str = "127.0.0.1",
|
||||
):
|
||||
"""Create Settings for testing by setting env vars temporarily."""
|
||||
from animaltrack.config import Settings
|
||||
|
||||
old_env = os.environ.copy()
|
||||
try:
|
||||
os.environ["CSRF_SECRET"] = csrf_secret
|
||||
os.environ["TRUSTED_PROXY_IPS"] = trusted_proxy_ips
|
||||
return Settings()
|
||||
finally:
|
||||
os.environ.clear()
|
||||
os.environ.update(old_env)
|
||||
|
||||
|
||||
class TestStaticFileServing:
|
||||
"""Tests for static file serving."""
|
||||
|
||||
@pytest.fixture
|
||||
def static_dir(self, tmp_path):
|
||||
"""Create a temporary static directory with test files."""
|
||||
static_v1 = tmp_path / "static" / "v1"
|
||||
static_v1.mkdir(parents=True)
|
||||
|
||||
# Create a test CSS file
|
||||
test_css = static_v1 / "test.css"
|
||||
test_css.write_text("body { color: red; }")
|
||||
|
||||
# Create a test JS file
|
||||
test_js = static_v1 / "test.js"
|
||||
test_js.write_text("console.log('test');")
|
||||
|
||||
return tmp_path / "static"
|
||||
|
||||
@pytest.fixture
|
||||
def client(self, seeded_db, static_dir):
|
||||
"""Create a test client with static file serving configured."""
|
||||
from starlette.testclient import TestClient
|
||||
|
||||
from animaltrack.web.app import create_app
|
||||
|
||||
settings = make_test_settings(trusted_proxy_ips="testclient")
|
||||
app, rt = create_app(
|
||||
settings=settings,
|
||||
db=seeded_db,
|
||||
static_dir=static_dir,
|
||||
)
|
||||
return TestClient(app, raise_server_exceptions=False)
|
||||
|
||||
def test_static_files_are_served(self, client):
|
||||
"""Static files under /static/v1/ are accessible."""
|
||||
resp = client.get("/static/v1/test.css")
|
||||
assert resp.status_code == 200
|
||||
assert "body { color: red; }" in resp.text
|
||||
|
||||
def test_static_files_have_immutable_cache_headers(self, client):
|
||||
"""Static files have Cache-Control: immutable header."""
|
||||
resp = client.get("/static/v1/test.css")
|
||||
assert resp.status_code == 200
|
||||
|
||||
cache_control = resp.headers.get("cache-control", "")
|
||||
assert "immutable" in cache_control
|
||||
assert "max-age=31536000" in cache_control
|
||||
assert "public" in cache_control
|
||||
|
||||
def test_static_js_files_served(self, client):
|
||||
"""JavaScript files are served correctly."""
|
||||
resp = client.get("/static/v1/test.js")
|
||||
assert resp.status_code == 200
|
||||
assert "console.log" in resp.text
|
||||
|
||||
def test_static_404_for_missing_files(self, client):
|
||||
"""Missing static files return 404."""
|
||||
resp = client.get("/static/v1/nonexistent.css")
|
||||
assert resp.status_code == 404
|
||||
Reference in New Issue
Block a user