Files
GTI_GERENCIADOR_FISCAL/central/tests/test_blocker_resolution_panel.py
T

156 lines
7.0 KiB
Python

from fastapi.testclient import TestClient
from sqlalchemy import select
from central.app.blocked_products import upsert_blocker
from central.app.blocker_resolution import resolve_blocker_with_context, validate_resolution_input
from central.app.main import create_app
from central.app.models import AdminUser, AuditEvent, FiscalContextEntry, Job, Installation
from central.tests.test_control_plane import seed, setup_db
def _seed_blocker(session_factory):
agent_id, admin_id = seed(session_factory)
with session_factory() as session:
installation = session.scalar(select(Installation).where(Installation.agent_id == agent_id))
installation.company_code = "ACME-COMPANY"
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": "input-1",
"evidence_summary": {"source": "test"},
},
installation_id=installation.id,
)
return blocker.id, admin_id, installation.id
def test_validate_resolution_input_accepts_known_enum_values():
for key, value in (("entry_scope", "INTER"), ("product_role", "RESALE")):
normalized, error = validate_resolution_input(
{"scope": "PRODUCT", "scope_key": "P-001", "key": key, "value": value, "reason": "operator note"}
)
assert error is None
assert normalized == {
"scope": "PRODUCT",
"scope_key": "P-001",
"key": key,
"value": value,
"reason": "operator note",
}
def test_validate_resolution_input_rejects_unsafe_or_incomplete_values():
invalid_payloads = (
{"scope": "PRODUCT", "scope_key": "P-001", "key": "entry_scope", "value": "ARBITRARY", "reason": "note"},
{"scope": "PRODUCT", "scope_key": "P-001", "key": "not-a-key", "value": "x", "reason": "note"},
{"scope": "NCM", "scope_key": "22030000", "key": "entry_scope", "value": "INTER", "reason": "note"},
{"scope": "PRODUCT", "scope_key": "P-001", "key": "product_role", "value": "RESALE", "reason": ""},
{"scope": "PRODUCT", "key": "product_role", "value": "RESALE", "reason": "note"},
{"scope": "PRODUCT", "scope_key": "P-001", "key": "product_role", "value": "RESALE", "reason": "note", "context": "free text"},
)
for payload in invalid_payloads:
normalized, error = validate_resolution_input(payload)
assert normalized is None
assert error
def test_resolve_blocker_with_context_persists_one_context_row_job_and_sanitized_audit():
session_factory = setup_db()
blocker_id, admin_id, installation_id = _seed_blocker(session_factory)
with session_factory() as session:
admin = session.get(AdminUser, admin_id)
result = resolve_blocker_with_context(
session,
blocker_id,
admin,
{
"scope": "PRODUCT",
"scope_key": "P-001",
"key": "product_role",
"value": "RESALE",
"reason": "reviewed token=secret-value",
},
request_ip="192.0.2.10",
)
entry = session.scalar(select(FiscalContextEntry).where(FiscalContextEntry.key == "product_role"))
job = session.get(Job, result["job_id"])
audit = session.get(AuditEvent, result["audit_id"])
assert result["blocker_id"] == blocker_id
assert entry.company_key == "ACME-COMPANY"
assert entry.scope == "PRODUCT"
assert entry.scope_key == "P-001"
assert entry.value == "RESALE"
assert entry.source == "OPERATOR"
assert job.agent_id == session.get(Installation, installation_id).agent_id
assert job.kind == "REPROCESS_PRODUCTS"
assert job.payload == {"codigos": ["P-001"]}
assert audit.action == "RESOLVE_BLOCKER_WITH_CONTEXT"
assert audit.target_type == "blocker"
assert audit.target_id == str(blocker_id)
assert audit.context["reprocess_job_id"] == job.id
assert audit.context["before"] is None
assert audit.context["after"]["value"] == "RESALE"
assert audit.context["request_ip"] == "192.0.2.10"
assert "secret-value" not in str(audit.context)
assert session.query(FiscalContextEntry).count() == 1
def test_human_resolution_detail_requires_auth_csrf_and_renders_safe_form():
session_factory = setup_db()
blocker_id, _, _ = _seed_blocker(session_factory)
client = TestClient(create_app(database_probe=lambda: True, session_factory=session_factory, bootstrap_token=None))
assert client.get(f"/blockers/{blocker_id}", follow_redirects=False).status_code == 303
client.post("/login", data={"username": "operator", "password": "a-secure-password"}, follow_redirects=False)
detail = client.get(f"/blockers/{blocker_id}")
assert detail.status_code == 200
assert "Salvar contexto e reprocessar" in detail.text
assert "Cliente" in detail.text and "Servidor" in detail.text and "Produto" in detail.text
assert "Evidência" in detail.text and "Valores aceitos" in detail.text
assert "data-confirm" not in detail.text
assert 'name="context"' not in detail.text
missing_csrf = client.post(
f"/blockers/{blocker_id}/resolve-context",
data={"scope": "PRODUCT", "scope_key": "P-001", "key": "product_role", "value": "RESALE", "reason": "reviewed"},
)
assert missing_csrf.status_code == 403
def test_human_and_json_resolution_routes_use_the_same_validated_workflow():
session_factory = setup_db()
blocker_id, _, _ = _seed_blocker(session_factory)
client = TestClient(create_app(database_probe=lambda: True, session_factory=session_factory, bootstrap_token=None))
client.post("/login", data={"username": "operator", "password": "a-secure-password"}, follow_redirects=False)
csrf = client.cookies.get("gti_csrf")
invalid = client.post(
f"/v1/blockers/{blocker_id}/resolve-human",
json={"csrf_token": csrf, "scope": "PRODUCT", "scope_key": "P-001", "key": "product_role", "value": "free text", "reason": "reviewed"},
)
assert invalid.status_code == 422
api_result = client.post(
f"/v1/blockers/{blocker_id}/resolve-human",
json={"csrf_token": csrf, "scope": "PRODUCT", "scope_key": "P-001", "key": "entry_scope", "value": "INTER", "reason": "reviewed"},
)
assert api_result.status_code == 200
assert set(api_result.json()) == {"blocker_id", "job_id", "audit_id"}
unauthenticated = TestClient(create_app(database_probe=lambda: True, session_factory=session_factory, bootstrap_token=None)).post(
f"/v1/blockers/{blocker_id}/resolve-human",
json={"scope": "PRODUCT", "scope_key": "P-001", "key": "entry_scope", "value": "INTER", "reason": "reviewed"},
)
assert unauthenticated.status_code == 401