#5093 "Shared format filters and status_class driven by Settings → Thresholds". - steward/core/formatting.py: human_bytes (binary units), human_rate, human_duration (two largest units), ago (a <time> titled with the absolute UTC time) and status_class(value, kind, thresholds) -> ok/warn/crit/unknown, which raises on a kind the thresholds don't name. - app.py registers them as the bytes, rate, duration and ago filters and the status_class global; threshold_style is now graded by status_class (and caps its weight at 500). - Replaces the copies: docker's _human_bytes/_human_uptime, core alerts' _duration, the host-agent fmt_bps/fmt_bytes macros, the Traefik and UniFi byte-rate arithmetic, the swarm-failures ago macro and two uptime spans. - Tests: a table per helper, and every template compiled against the app's filters, so an unknown filter fails CI rather than a page. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
50 lines
1.7 KiB
Python
50 lines
1.7 KiB
Python
"""Unit tests for the docker plugin's presentation layer (no DB).
|
|
|
|
CI never renders these templates through the running app (the unit-lane app is
|
|
created with testing=True, which skips plugin loading), so a Jinja syntax error
|
|
would otherwise ship green. These tests parse every docker template and smoke
|
|
the routes module so a broken tag or import is caught in the unit lane.
|
|
"""
|
|
import pathlib
|
|
|
|
import jinja2
|
|
|
|
from plugins.docker import routes as r
|
|
|
|
_TEMPLATES = pathlib.Path(r.__file__).parent / "templates" / "docker"
|
|
|
|
|
|
def test_all_docker_templates_parse():
|
|
env = jinja2.Environment()
|
|
files = sorted(_TEMPLATES.glob("*.html"))
|
|
assert files, "no docker templates found"
|
|
for f in files:
|
|
# Raises TemplateSyntaxError on an unbalanced/typo'd tag.
|
|
env.parse(f.read_text())
|
|
|
|
|
|
def test_routes_module_exposes_new_views():
|
|
# Import-smoke + confirms the #942 view functions are defined.
|
|
for name in ("container_detail", "container_history", "swarm", "disk", "index", "rows"):
|
|
assert callable(getattr(r, name)), name
|
|
assert r.docker_bp.name == "docker"
|
|
|
|
|
|
def test_disk_prune_view_defined():
|
|
# M78 admin-gated prune action.
|
|
assert callable(r.disk_prune)
|
|
|
|
|
|
def test_container_log_views_defined():
|
|
# M79 per-container log viewer + its HTMX-polled line fragment.
|
|
assert callable(r.container_logs)
|
|
assert callable(r.container_logs_lines)
|
|
|
|
|
|
def test_uptime_is_the_shared_duration_or_none():
|
|
from datetime import datetime, timedelta, timezone
|
|
now = datetime.now(timezone.utc)
|
|
assert r._uptime(None) is None
|
|
assert r._uptime(now + timedelta(minutes=5)) is None # clock skew: not "-5m"
|
|
assert r._uptime(now - timedelta(days=3, hours=4, seconds=30)) == "3d 4h"
|