Files
FabledSteward/tests/core/test_viewer_actions.py
T
bvandeusenandClaude Opus 5.5 0c72892f58
CI / lint (push) Successful in 2s
CI / integration (push) Successful in 59s
CI / unit (push) Failing after 1m22s
CI / publish (push) Skipped
feat(ansible): schedules read as sentences, each with its own page (#5332)
- The Schedules tab: a verdict line (failing first, paused, next due),
  each schedule as one line (what runs where, how often, when next, how it
  last went); the row opens its page; Run now and Pause stay in the row.
- /ansible/schedules/new and /ansible/schedules/<id>: the form on classes,
  dry run beside the button, the playbook's variables rendered with the
  page; at the foot Delete behind the confirm dialog. ?source=&playbook=
  start a new one on a playbook (Schedule it on the playbook's page).
- A run started by a schedule links to that schedule's page.
- The run page's id guard allows the Run again link's ?from=.

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

46 lines
2.4 KiB
Python

"""A viewer is not offered what they cannot do (#5212): Schedules and
Maintenance draw no Run now / Pause / Edit / Delete / New for them, and
say intervals and scopes in words."""
from datetime import datetime, timedelta, timezone
from types import SimpleNamespace as NS
from quart import render_template, session
NOW = datetime.now(timezone.utc)
def _window(name, source, resource):
return NS(id=f"w-{name}", name=name, scope_source=source, scope_resource=resource,
start_at=NOW - timedelta(hours=1), end_at=NOW + timedelta(hours=1))
async def _maintenance(app, role):
windows = [_window("patching", "host_agent", None), _window("nas", "http", "nas web")]
async with app.test_request_context("/alerts/maintenance"):
session["user_role"] = role
return await render_template("alerts/maintenance.html", windows=windows, active=windows,
upcoming=[], expired=[])
async def test_maintenance_offers_changes_only_to_those_who_can_make_them(app):
viewer, operator = await _maintenance(app, "viewer"), await _maintenance(app, "operator")
assert "New window" not in viewer and "/delete" not in viewer
assert "New window" in operator and operator.count("/delete") == 2
assert "Every alert on Host agent" in viewer and "host_agent/*" not in viewer
assert "nas web</span>" in viewer and "HTTP monitors" in viewer
async def test_schedules_show_a_viewer_the_list_without_its_controls(app):
s = NS(id="s1", name="nightly", enabled=True, source_name="local", playbook_path="prune.yml",
inventory_scope="steward:all", interval_seconds=7200, last_error=None, last_run_at=None, params=None)
nxt = {"id": "s1", "name": "nightly", "at": NOW + timedelta(hours=2)}
rows = [{"s": s, "last_run": None, "next": nxt, "scope_label": "all inventory targets"}]
async with app.test_request_context("/ansible/schedules"):
session["user_role"] = "viewer"
page = await render_template("ansible/schedules.html", rows=rows, next=nxt, failing=0, paused=0)
assert "nightly" in page and "every 2h" in page and "7200s" not in page
assert ">in 2h</time>" in page or ">in 1h 59m</time>" in page
assert 'class="row-target" href="/ansible/schedules/s1"' in page # the row opens the schedule
for control in ("Run now", "Pause", "/delete", "New schedule"):
assert control not in page, control