import re from datetime import datetime, timedelta, timezone from fastapi.testclient import TestClient from sqlalchemy import select from central.app.blocked_products import upsert_blocker from central.app.events import record_event from central.app.main import create_app from central.app.models import Agent, AgentSnapshot, Installation, Job from central.tests.test_control_plane import seed, setup_db def _panel_client(): session_factory = setup_db() agent_id, _ = seed(session_factory) now = datetime(2026, 9, 22, 19, 11, tzinfo=timezone.utc) with session_factory() as session: installation = session.scalar(select(Installation).where(Installation.agent_id == agent_id)) agent = session.get(Agent, agent_id) agent.agent_version = "0.9.0" snapshot = session.scalar(select(AgentSnapshot).where(AgentSnapshot.installation_id == installation.id)) snapshot.total_products = 50 snapshot.fiscally_complete = 49 snapshot.coverage_percent = 98 job = Job( agent_id=agent_id, kind="REPROCESS_PRODUCTS", status="RUNNING", created_at=now - timedelta(minutes=3), started_at=now - timedelta(minutes=2), payload={"codigos": ["P-001"]}, ) session.add(job) session.flush() blocker = upsert_blocker( session, installation.tenant_id, { "company": "Acme", "product_code": "P-001", "description": "Produto de teste", "blocker_type": "BLOCKED_MISSING_EVIDENCE", "field": "CFOP", "reason_code": "MISSING_CONTEXT", "input_fingerprint": "page-test", "evidence_summary": {"source": "snapshot"}, }, installation_id=installation.id, ) record_event( session, tenant_id=installation.tenant_id, installation_id=installation.id, agent_id=agent_id, severity="WARNING", event_type="PRODUCT_BLOCKED", message="Contexto fiscal ausente", job_id=job.id, product_code="P-001", blocker_id=blocker.id, occurred_at=now, ) session.commit() client = TestClient(create_app(database_probe=lambda: True, session_factory=session_factory, bootstrap_token=None)) login = client.post("/login", data={"username": "operator", "password": "a-secure-password"}, follow_redirects=False) assert login.status_code == 303 return client, agent_id, blocker.id def test_dashboard_renders_operational_queue_labels_and_human_dates(): client, _, _ = _panel_client() response = client.get("/") assert response.status_code == 200 for label in ("Agents total", "Online", "Degraded", "Offline", "Failed", "Unknown"): assert label in response.text assert "Produtos bloqueados" in response.text assert "Blockers abertos" in response.text assert "ATENÇÃO NECESSÁRIA" in response.text assert "Ver todos" in response.text assert "Jobs com falha" in response.text assert "Agents desatualizados" in response.text assert not re.search(r"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}", response.text) def test_agents_render_separate_health_and_fiscal_status_with_server_sort(): client, _, _ = _panel_client() response = client.get("/agents?sort=worst_coverage&health=ONLINE&search=Acme") assert response.status_code == 200 for label in ("Cliente", "Nome de exibição", "Hostname", "Saúde técnica", "Status fiscal", "Cobertura fiscal", "Versão"): assert label in response.text assert "Acme ERP" in response.text assert "srv-01" in response.text assert "98,0%" in response.text assert 'name="health"' in response.text assert 'value="Acme"' in response.text assert 'value="worst_coverage"' in response.text def test_jobs_render_client_identity_duration_and_preserve_filters(): client, _, _ = _panel_client() response = client.get("/jobs?status=RUNNING&search=Acme") assert response.status_code == 200 for label in ("Cliente", "Nome de exibição", "Hostname", "Job ID", "Tipo", "Status", "Criado", "Finalizado", "Duração", "Erro"): assert label in response.text assert "Acme" in response.text assert "srv-01" in response.text assert "em andamento" in response.text assert 'value="Acme"' in response.text assert '