feat(ansible): one strip of tabs, a run list that opens on its verdict, a run page in one line (#5328, #5329, #5330)
- section_tabs macro; Runs · Playbooks · Schedules · Inventory on every Ansible page, replacing each page's own set of header buttons. - Runs: a verdict line (last 30 days, failures first with the latest named, running now, the schedule due next); under each status what the run did across its hosts (ansible/recap.py); a filter bar (playbook, status, who started it) that swaps the table and carries into the scroll paging. - Run page: status, duration, start and who started it in one line; each host's result in words, worst first; the playbook it ran links to it; the log and failures on classes rather than inline colours. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
"""Integration: the Ansible Runs tab (#5329) against real Postgres. Its
|
||||
verdict counts the last 30 days; the filter bar narrows by playbook, status
|
||||
and origin; an HTMX request from the bar gets the table alone, and the next
|
||||
page as the reader scrolls keeps the filters. Requires STEWARD_DATABASE_URL."""
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import os
|
||||
import time
|
||||
import uuid
|
||||
from datetime import datetime, timedelta, timezone
|
||||
|
||||
import pytest
|
||||
|
||||
pytestmark = pytest.mark.integration
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def app():
|
||||
if not os.environ.get("STEWARD_DATABASE_URL"):
|
||||
pytest.skip("needs Postgres")
|
||||
from steward.app import create_app
|
||||
return create_app(testing=False)
|
||||
|
||||
|
||||
def test_the_runs_tab_counts_and_filters(app):
|
||||
from steward.models.ansible import AnsibleRun, AnsibleRunStatus
|
||||
from steward.models.users import User, UserRole
|
||||
|
||||
async def _go():
|
||||
tag = uuid.uuid4().hex[:8]
|
||||
uid = str(uuid.uuid4())
|
||||
now = datetime.now(timezone.utc)
|
||||
|
||||
def run(name, status, mins, origin=None, by=None, results=None):
|
||||
return AnsibleRun(playbook_path=f"it{tag}/{name}.yml", source_name="steward-builtin",
|
||||
inventory_scope="steward:all", status=status, origin=origin,
|
||||
triggered_by=by, started_at=now - timedelta(minutes=mins),
|
||||
finished_at=now - timedelta(minutes=mins - 1), results=results)
|
||||
async with app.db_sessionmaker() as s:
|
||||
async with s.begin():
|
||||
s.add(User(id=uid, username=f"runs-{tag}", email=f"runs-{tag}@test",
|
||||
password_hash="x", role=UserRole.viewer, is_active=True))
|
||||
async with s.begin():
|
||||
s.add_all([
|
||||
run("prune", AnsibleRunStatus.success, 10, origin="schedule",
|
||||
results={"hosts": {"a": {"ok": 2, "changed": 1}, "b": {"ok": 2, "changed": 1}}}),
|
||||
run("update", AnsibleRunStatus.failed, 20, by=uid,
|
||||
results={"hosts": {f"db-{tag}": {"ok": 1, "failed": 1}}}),
|
||||
])
|
||||
|
||||
client = app.test_client()
|
||||
async with client.session_transaction() as sess:
|
||||
sess.update(user_id=uid, user_role="viewer", username=f"runs-{tag}", login_at=time.time())
|
||||
|
||||
async def get(url, **kw):
|
||||
resp = await client.get(url, **kw)
|
||||
return resp.status_code, await resp.get_data(as_text=True)
|
||||
|
||||
out = {"page": await get(f"/ansible/?q=it{tag}"),
|
||||
"failed": await get(f"/ansible/?q=it{tag}&status=failed", headers={"HX-Request": "true"}),
|
||||
"sched": await get(f"/ansible/?q=it{tag}&origin=schedule", headers={"HX-Request": "true"}),
|
||||
"person": await get(f"/ansible/?q=it{tag}&origin=manual", headers={"HX-Request": "true"})}
|
||||
async with app.db_sessionmaker() as s:
|
||||
async with s.begin():
|
||||
from sqlalchemy import delete
|
||||
await s.execute(delete(AnsibleRun).where(AnsibleRun.playbook_path.like(f"it{tag}/%")))
|
||||
await s.delete(await s.get(User, uid))
|
||||
return tag, out
|
||||
|
||||
tag, out = asyncio.run(_go())
|
||||
status, page = out["page"]
|
||||
assert status == 200
|
||||
assert '<p class="page-verdict">' in page and "in the last 30 days" in page # the verdict queries ran
|
||||
assert f"it{tag}/prune.yml" in page and f"it{tag}/update.yml" in page
|
||||
assert "2 hosts · 2 changed" in page and f'<span class="tone-crit">failed on db-{tag}</span>' in page
|
||||
assert '<nav class="section-tabs" aria-label="Ansible">' in page
|
||||
|
||||
failed = out["failed"][1]
|
||||
assert "<html" not in failed and '<p class="page-verdict">' not in failed # the table alone
|
||||
assert f"it{tag}/update.yml" in failed and f"it{tag}/prune.yml" not in failed
|
||||
sched = out["sched"][1]
|
||||
assert f"it{tag}/prune.yml" in sched and f"it{tag}/update.yml" not in sched
|
||||
person = out["person"][1]
|
||||
assert f"it{tag}/update.yml" in person and f"it{tag}/prune.yml" not in person
|
||||
Reference in New Issue
Block a user