Files
FabledSteward/tests/integration/test_ansible_runs_list.py
T
bvandeusenandClaude Opus 5.5 72a3261582
CI / lint (push) Successful in 2s
CI / integration (push) Successful in 55s
CI / unit (push) Failing after 1m20s
CI / publish (push) Skipped
feat(ansible): Playbooks as one list, and a page per playbook that runs it (#5331, #5330)
- /ansible/playbooks lists every playbook across sources: what it says it
  does, its category and destructive flag, how it last ran; the whole row
  opens the playbook. A source with none is one quiet line.
- /ansible/playbooks/<source>/<path>: the verdict (what it does, flagged
  destructive, last ran), the run form in place (dry run beside the button,
  options folded), the schedules that use it, its recent runs, the playbook
  itself folded. ?from=<run> fills the form from that run: Run again on a
  run's page.
- /ansible/browse, its view page and the /run-form fragment are gone, not
  redirected (rule 122); every link points at the new pages.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 23:49:05 -04:00

97 lines
4.6 KiB
Python

"""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"}),
# The Playbooks tab and a built-in playbook's page (#5331): their
# newest-run and schedule queries against Postgres.
"playbooks": await get("/ansible/playbooks"),
"playbook": await get("/ansible/playbooks/steward-builtin/maintenance/docker_prune.yml"),
"missing": await get("/ansible/playbooks/steward-builtin/nope.yml")}
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
status, playbooks = out["playbooks"]
assert status == 200 and 'href="/ansible/playbooks/steward-builtin/maintenance/docker_prune.yml"' in playbooks
status, playbook = out["playbook"]
assert status == 200 and "The playbook" in playbook and 'id="run-form"' not in playbook # a viewer
assert out["missing"][0] == 404