"""monitor tests — page renders, no sensitive data, escapes input.""" from __future__ import annotations from cayceops.components.monitor import monitor from cayceops.models import ContentItem, DaemonRun, MailMessage, Proposal def _seed(sess): sess.add(DaemonRun(daemon="scanner", status="ok", detail={"proposed": 3})) sess.add(Proposal(kind="affiliate", title="Title with ")) sess.add( MailMessage( direction="in", from_addr="secret-person@private.example.org", subject="private subject", classification="approval", draft_status="drafted", ) ) sess.add(ContentItem(theme="atm-fees", channel="nostr", caption="caption", status="draft")) sess.flush() def test_build_renders_all_sections(cfg, sess, tmp_path): _seed(sess) out = tmp_path / "status.html" monitor._build(cfg(CAYCE_OPS_MONITOR_OUTPUT=str(out)), sess) page = out.read_text() assert "daemon health" in page assert "proposal queue" in page assert "recent audit activity" in page assert "scanner" in page assert "proposed: 1" in page assert "mail drafts awaiting approval: 1" in page def test_build_leaks_no_sensitive_data(cfg, sess, tmp_path): _seed(sess) out = tmp_path / "status.html" monitor._build(cfg(CAYCE_OPS_MONITOR_OUTPUT=str(out)), sess) page = out.read_text() assert "secret-person@private.example.org" not in page # no addresses assert "private subject" not in page # no subjects assert "caption" not in page # no content bodies assert "Title with" not in page # proposal titles not rendered def test_build_escapes_injected_content(cfg, sess, tmp_path): from datetime import UTC, datetime from cayceops.models import AuditEntry sess.add( AuditEntry( ts=datetime.now(UTC), actor="", action="probe", detail={}, ) ) sess.flush() out = tmp_path / "status.html" monitor._build(cfg(CAYCE_OPS_MONITOR_OUTPUT=str(out)), sess) page = out.read_text() assert "" not in page assert "<script>x</script>" in page def test_anchor_signs_and_logs_once_per_day(cfg, sess, monkeypatch): from cayceops.components.monitor import monitor from cayceops.models import AuditEntry # seed the chain from cayceops.models import audit as audit_fn audit_fn(sess, actor="daemon:executor", action="execute_done", detail={}) sess.flush() published = [] def fake_publish(relays, event, timeout=10.0): published.append(event) return [{"relay": r, "ok": "true"} for r in relays] import cayceops.components.publisher.nostr as nostr_mod monkeypatch.setattr(nostr_mod, "publish", fake_publish) config = cfg( CAYCE_OPS_NOSTR_PRIVATE_KEY="11" * 32, CAYCE_OPS_NOSTR_RELAYS="wss://r.example.org", CAYCE_OPS_DEPLOYMENT_ID="itest", ) code = monitor._anchor(config, sess) assert code == 0 assert len(published) == 1 content = published[0]["content"] assert "audit anchor" in content and "itest" in content entry = sess.query(AuditEntry).filter_by(action="anchor").one() assert entry.detail["last_hash"] # second call same day -> skipped, no new nostr event code2 = monitor._anchor(config, sess) assert code2 == 0 assert len(published) == 1