feat: operational panel UI - active-scope dashboard, drill-down cards, scoped Agents/Blockers/Jobs, PT-BR labels, responsive CSS
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -5,15 +5,27 @@ from sqlalchemy.dialects import postgresql
|
||||
|
||||
from central.app.audit import record_audit
|
||||
from central.app.blocked_products import upsert_blocker
|
||||
from central.app.control_plane import _blocker_breakdown_query, dashboard_summary, list_blockers, list_installations, list_jobs
|
||||
from central.app.control_plane import _blocker_breakdown_query, blocker_filter_summary, dashboard_summary, jobs_status_summary, list_blockers, list_installations, list_jobs
|
||||
from central.app.config import settings
|
||||
from central.app.events import record_event
|
||||
from central.app.models import Agent, AgentSnapshot, AuditEvent, BlockedProduct, Installation, Job, Tenant
|
||||
from central.app.models import Agent, AgentEnrollment, AgentRelease, AgentSnapshot, AuditEvent, BlockedProduct, Installation, Job, Tenant
|
||||
from central.app.panel_presentation import blocker_reason_label, blocker_type_label, fiscal_status_label, job_kind_label, job_status_label, priority_label
|
||||
from central.tests.test_control_plane import setup_db
|
||||
|
||||
|
||||
NOW = datetime(2026, 9, 22, 19, 0, tzinfo=timezone.utc)
|
||||
|
||||
|
||||
def test_operator_labels_are_ptbr_without_changing_internal_codes():
|
||||
assert job_status_label("FAILED") == "Falhou"
|
||||
assert job_status_label("RUNNING") == "Executando"
|
||||
assert job_kind_label("SYNC_INCREMENTAL") == "Sincronização incremental"
|
||||
assert blocker_type_label("BLOCKED_MISSING_EVIDENCE") == "Evidência fiscal ausente"
|
||||
assert blocker_reason_label("INVALID_NCM") == "NCM inválido"
|
||||
assert priority_label("HIGH") == "Alta"
|
||||
assert fiscal_status_label("BLOCKED") == "Bloqueado"
|
||||
|
||||
|
||||
def _installation(session, index, *, health="ONLINE", coverage=80, blocked=0, version="1.0.0rc1"):
|
||||
tenant = Tenant(name=f"Client {index}")
|
||||
session.add(tenant)
|
||||
@@ -80,14 +92,14 @@ def _blocker(session, tenant, *, product, field="CFOP", blocker_type="BLOCKED_MI
|
||||
def test_dashboard_reconciles_five_health_states_and_counts_distinct_safe_products():
|
||||
session_factory = setup_db()
|
||||
with session_factory() as session:
|
||||
first = _installation(session, 1, health="ONLINE", blocked=2)[0]
|
||||
second = _installation(session, 2, health="DEGRADED")[0]
|
||||
first, _, first_installation, _ = _installation(session, 1, health="ONLINE", blocked=2)
|
||||
second, _, second_installation, _ = _installation(session, 2, health="DEGRADED")
|
||||
_installation(session, 3, health="OFFLINE")
|
||||
_installation(session, 4, health="FAILED")
|
||||
_installation(session, 5, health="UNKNOWN")
|
||||
_blocker(session, first, product="P1", field="CFOP", index=1)
|
||||
_blocker(session, first, product="P1", field="NCM", index=2)
|
||||
_blocker(session, second, product="P2", field="ICMS", index=3)
|
||||
_blocker(session, first, product="P1", field="CFOP", index=1, installation_id=first_installation.id)
|
||||
_blocker(session, first, product="P1", field="NCM", index=2, installation_id=first_installation.id)
|
||||
_blocker(session, second, product="P2", field="ICMS", index=3, installation_id=second_installation.id)
|
||||
session.commit()
|
||||
|
||||
summary = dashboard_summary(session, now=NOW)
|
||||
@@ -100,6 +112,173 @@ def test_dashboard_reconciles_five_health_states_and_counts_distinct_safe_produc
|
||||
assert summary["online"] == 1 and summary["safe_blocked"] == 2
|
||||
|
||||
|
||||
def test_installation_inventory_active_scope_excludes_inactive_and_revoked_but_keeps_history():
|
||||
factory = setup_db()
|
||||
with factory() as session:
|
||||
active = _installation(session, 1)[2]
|
||||
inactive_tenant, _, _, _ = _installation(session, 2)
|
||||
inactive_tenant.active = False
|
||||
_, disabled_agent, _, _ = _installation(session, 3)
|
||||
disabled_agent.enabled = False
|
||||
tenant, revoked_agent, revoked, _ = _installation(session, 4, health="UNKNOWN")
|
||||
_, _, paused, _ = _installation(session, 5)
|
||||
paused.processing_enabled = False
|
||||
release = AgentRelease(version="test-inventory", platform="windows", architecture="x64", filename="test.exe", storage_path="test.exe", size_bytes=1, sha256="x" * 64)
|
||||
session.add(release)
|
||||
session.flush()
|
||||
session.add(AgentEnrollment(enrollment_uuid=revoked.installation_id, tenant_id=tenant.id, agent_id=revoked_agent.id, display_name="Histórico", release_id=release.id, status="REVOKED"))
|
||||
session.commit()
|
||||
|
||||
current = list_installations(session, page_size=10, now=NOW)
|
||||
historical = list_installations(session, page_size=10, scope="all", now=NOW)
|
||||
unknown = list_installations(session, health="UNKNOWN", now=NOW)
|
||||
|
||||
assert {row["installation_id"] for row in current["items"]} == {active.installation_id, paused.installation_id}
|
||||
assert current["total"] == 2
|
||||
assert historical["total"] == 5
|
||||
assert unknown["total"] == 0
|
||||
assert {row["installation_id"]: row["operational_active"] for row in historical["items"]}[revoked.installation_id] is False
|
||||
|
||||
|
||||
def test_outdated_agent_filter_returns_only_outdated_active_installations():
|
||||
factory = setup_db()
|
||||
with factory() as session:
|
||||
_, _, outdated, _ = _installation(session, 1, version="0.9.0")
|
||||
_, _, _, current_snapshot = _installation(session, 2, version=settings.agent_server_version)
|
||||
current_snapshot.version_drift = "CURRENT"
|
||||
_, disabled, _, _ = _installation(session, 3, version="0.9.0")
|
||||
disabled.enabled = False
|
||||
session.commit()
|
||||
|
||||
result = list_installations(session, version="outdated", now=NOW)
|
||||
|
||||
assert result["total"] == 1
|
||||
assert result["items"][0]["installation_id"] == outdated.installation_id
|
||||
|
||||
|
||||
def test_dashboard_excludes_inactive_history_and_groups_repeated_blockers():
|
||||
factory = setup_db()
|
||||
with factory() as session:
|
||||
tenant, agent, installation, snapshot = _installation(session, 1, health="ONLINE", coverage=74, blocked=26, version=settings.agent_server_version)
|
||||
snapshot.version_drift = "CURRENT"
|
||||
for index in range(26):
|
||||
_blocker(session, tenant, product=f"P{index:03}", field="NCM", index=index, installation_id=installation.id)
|
||||
session.add(Job(agent_id=agent.id, kind="REPROCESS_PRODUCTS", status="FAILED", error="erro real", created_at=NOW))
|
||||
|
||||
old_tenant, old_agent, old_installation, _ = _installation(session, 2, health="UNKNOWN", version="0.9.0")
|
||||
_blocker(session, old_tenant, product="HISTORICAL", field="CFOP", index=80, installation_id=old_installation.id)
|
||||
session.add(Job(agent_id=old_agent.id, kind="REPROCESS_ALL", status="FAILED", error="erro histórico", created_at=NOW))
|
||||
release = AgentRelease(version="test-dashboard", platform="windows", architecture="x64", filename="test.exe", storage_path="test.exe", size_bytes=1, sha256="y" * 64)
|
||||
session.add(release)
|
||||
session.flush()
|
||||
session.add(AgentEnrollment(enrollment_uuid=old_installation.installation_id, tenant_id=old_tenant.id, agent_id=old_agent.id, display_name="Encerrada", release_id=release.id, status="REVOKED"))
|
||||
inactive_tenant, _, _, _ = _installation(session, 3, health="OFFLINE")
|
||||
inactive_tenant.active = False
|
||||
_, disabled_agent, _, _ = _installation(session, 4, health="FAILED")
|
||||
disabled_agent.enabled = False
|
||||
session.commit()
|
||||
|
||||
summary = dashboard_summary(session, now=NOW)
|
||||
|
||||
assert summary["agents_total"] == 1
|
||||
assert summary["health_counts"] == {"ONLINE": 1, "DEGRADED": 0, "OFFLINE": 0, "FAILED": 0, "UNKNOWN": 0}
|
||||
assert summary["total_products"] == 100
|
||||
assert summary["safe_blocked"] == 26
|
||||
assert summary["open_blockers"] == 26
|
||||
assert summary["safe_blocked_products"] == 26
|
||||
assert summary["open_blocker_breakdown"]["NCM"] == 26
|
||||
assert summary["open_blocker_breakdown"]["CFOP"] == 0
|
||||
assert summary["failed_jobs"] == 1
|
||||
fiscal_groups = [item for item in summary["attention_items"] if item.get("field") == "NCM"]
|
||||
assert len(fiscal_groups) == 1
|
||||
assert fiscal_groups[0]["group_count"] == 26
|
||||
assert fiscal_groups[0]["products_affected"] == 26
|
||||
assert fiscal_groups[0]["link"].startswith("/blockers?")
|
||||
assert len(summary["attention_items"]) <= 10
|
||||
assert all(item["client"] != "Client 2" for item in summary["attention_items"])
|
||||
|
||||
|
||||
def test_blockers_and_jobs_keep_revoked_history_out_of_active_scope():
|
||||
factory = setup_db()
|
||||
with factory() as session:
|
||||
tenant, agent, installation, _ = _installation(session, 1)
|
||||
current = _blocker(session, tenant, product="CURRENT", field="NCM", index=1, installation_id=installation.id)
|
||||
current.reason_code = "INVALID_NCM"
|
||||
resolved = _blocker(session, tenant, product="RESOLVED", field="NCM", index=2, installation_id=installation.id)
|
||||
resolved.resolved_at = NOW
|
||||
session.add(Job(agent_id=agent.id, kind="REPROCESS_PRODUCTS", status="FAILED", created_at=NOW, error="current"))
|
||||
|
||||
old_tenant, old_agent, old_installation, _ = _installation(session, 2)
|
||||
_blocker(session, old_tenant, product="OLD", field="NCM", index=3, installation_id=old_installation.id)
|
||||
session.add(Job(agent_id=old_agent.id, kind="REPROCESS_ALL", status="FAILED", created_at=NOW, error="historical"))
|
||||
old_agent.enabled = False
|
||||
session.commit()
|
||||
|
||||
assert list_blockers(session, field="NCM")["total"] == 1
|
||||
assert list_blockers(session, field="NCM", reason_code="INVALID_NCM")["items"][0]["id"] == current.id
|
||||
assert list_blockers(session, field="NCM", scope="all")["total"] == 2
|
||||
assert list_blockers(session, status="RESOLVED", scope="all")["items"][0]["id"] == resolved.id
|
||||
assert blocker_filter_summary(session)["open_blockers"] == 1
|
||||
assert list_jobs(session, status="FAILED")["total"] == 1
|
||||
assert list_jobs(session, status="FAILED", scope="all")["total"] == 2
|
||||
|
||||
|
||||
def test_jobs_status_kpis_count_active_jobs_by_status_in_one_group():
|
||||
factory = setup_db()
|
||||
with factory() as session:
|
||||
_, agent, _, _ = _installation(session, 1)
|
||||
_, disabled, _, _ = _installation(session, 2)
|
||||
disabled.enabled = False
|
||||
for status in ("PENDING", "RUNNING", "COMPLETED", "FAILED"):
|
||||
session.add(Job(agent_id=agent.id, kind="SYNC_INCREMENTAL", status=status, created_at=NOW))
|
||||
session.add(Job(agent_id=disabled.id, kind="REPROCESS_ALL", status="FAILED", created_at=NOW))
|
||||
session.commit()
|
||||
|
||||
assert jobs_status_summary(session) == {"PENDING": 1, "RUNNING": 1, "COMPLETED": 1, "FAILED": 1}
|
||||
assert jobs_status_summary(session, scope="all")["FAILED"] == 2
|
||||
|
||||
|
||||
def test_attention_groups_rank_high_priority_before_limiting_recent_low_priority():
|
||||
factory = setup_db()
|
||||
with factory() as session:
|
||||
tenant, _, installation, _ = _installation(session, 1, version=settings.agent_server_version)
|
||||
high = _blocker(session, tenant, product="IMPORTANT", field="NCM", index=90, installation_id=installation.id)
|
||||
high.last_seen_at = NOW - timedelta(days=3)
|
||||
for index in range(12):
|
||||
low = _blocker(session, tenant, product=f"LOW-{index}", field="OTHER", blocker_type="UNMAPPED", index=index, installation_id=installation.id)
|
||||
low.reason_code = f"OTHER_{index}"
|
||||
low.last_seen_at = NOW - timedelta(minutes=index)
|
||||
session.commit()
|
||||
|
||||
attention = dashboard_summary(session, now=NOW)["attention_items"]
|
||||
|
||||
assert len(attention) <= 10
|
||||
assert any(item.get("field") == "NCM" for item in attention)
|
||||
|
||||
|
||||
def test_dashboard_query_count_stays_bounded_with_many_blockers():
|
||||
factory = setup_db()
|
||||
with factory() as session:
|
||||
tenant, _, installation, _ = _installation(session, 1, version=settings.agent_server_version)
|
||||
for index in range(130):
|
||||
_blocker(session, tenant, product=f"P-{index:03}", field="NCM", index=index, installation_id=installation.id)
|
||||
session.commit()
|
||||
statements = []
|
||||
|
||||
def count_statement(conn, cursor, statement, parameters, context, executemany):
|
||||
statements.append(statement)
|
||||
|
||||
event.listen(session.bind, "before_cursor_execute", count_statement)
|
||||
try:
|
||||
summary = dashboard_summary(session, now=NOW)
|
||||
finally:
|
||||
event.remove(session.bind, "before_cursor_execute", count_statement)
|
||||
|
||||
assert summary["open_blockers"] == 130
|
||||
assert len(statements) <= 18
|
||||
assert not any("SELECT" in sql.upper() and "LIMIT 130" in sql.upper() for sql in statements)
|
||||
|
||||
|
||||
def test_snapshot_coverage_exposes_unclassified_residual_and_overcount():
|
||||
session_factory = setup_db()
|
||||
with session_factory() as session:
|
||||
@@ -290,7 +469,7 @@ def test_legacy_tenant_blocker_has_no_fabricated_installation_context():
|
||||
with session_factory() as session:
|
||||
tenant, _, installation, _ = _installation(session, 1)
|
||||
row = _blocker(session, tenant, product="LEGACY", index=99)
|
||||
result = list_blockers(session, tenant_id=tenant.id)
|
||||
result = list_blockers(session, tenant_id=tenant.id, scope="all")
|
||||
|
||||
assert result["items"][0]["id"] == row.id
|
||||
assert result["items"][0]["installation_id"] is None
|
||||
@@ -370,11 +549,11 @@ def test_lowercase_required_blocker_field_reaches_attention_candidates():
|
||||
session_factory = setup_db()
|
||||
with session_factory() as session:
|
||||
tenant, _, installation, _ = _installation(session, 1)
|
||||
blocker = _blocker(session, tenant, product="LOWER", field="cfop", index=120, installation_id=installation.id)
|
||||
_blocker(session, tenant, product="LOWER", field="cfop", index=120, installation_id=installation.id)
|
||||
|
||||
attention = dashboard_summary(session, now=NOW)["attention_items"]
|
||||
|
||||
assert any(item["link"] == f"/blockers/{blocker.id}" for item in attention)
|
||||
assert any(item.get("field") == "CFOP" and item["link"].startswith("/blockers?scope=active") for item in attention)
|
||||
|
||||
|
||||
def test_blocker_breakdown_query_groups_on_same_postgres_expression():
|
||||
|
||||
Reference in New Issue
Block a user