"""Missão 7 — Endurecimento e testes de cobertura dos módulos fiscais.""" from datetime import datetime, timezone import pytest from sqlalchemy import create_engine from sqlalchemy.orm import Session from fastapi.testclient import TestClient from central.app.models import Base, Dataset from central.app.reference_registry import ( register_draft, approve_draft, reject_draft, list_registry, diff_against_vigente, technical_checklist, RegistryError ) from central.app.classification import ( Classifier, classify_tipi_rate, description_tokens, normalize_description ) from central.app.main import create_app @pytest.fixture def memory_db(): engine = create_engine("sqlite:///:memory:") Base.metadata.create_all(engine) with Session(engine) as session: yield session def test_reference_registry_lifecycle_and_approval_workflow(memory_db): """Testa o ciclo de vida completo do Reference Registry: registro, diff, checklist, aprovação e rejeição.""" # 1. Registrar versão 1 d1 = register_draft( memory_db, kind="NCM", provider="Siscomex", source_url="https://siscomex.gov.br", sha256="a" * 64, version_label="v1", record_count=15100, act="Resolução Gecex 2026", ) assert d1.status == "DRAFT" # Idempotência de registro d1_again = register_draft( memory_db, kind="NCM", provider="Siscomex", source_url="https://siscomex.gov.br", sha256="a" * 64, version_label="v1", ) assert d1_again.id == d1.id # Checklist técnico chk = technical_checklist(memory_db, d1) assert chk["ready_to_approve"] is True assert chk["volume"]["passed"] is True # Aprovar versão 1 -> VIGENTE approved = approve_draft(memory_db, d1.id, admin_name="Operador Teste") assert approved.status == "VIGENTE" assert approved.approved_by == "Operador Teste" # 2. Registrar versão 2 d2 = register_draft( memory_db, kind="NCM", provider="Siscomex", source_url="https://siscomex.gov.br", sha256="b" * 64, version_label="v2", record_count=15200, ) diff = diff_against_vigente(memory_db, d2) assert diff["is_first_version"] is False assert diff["record_count_delta"] == 100 # Aprovar versão 2 -> Torna d1 OBSOLETO approve_draft(memory_db, d2.id, admin_name="Admin 2") memory_db.refresh(d1) assert d1.status == "OBSOLETO" assert d2.status == "VIGENTE" # Rejeitar draft com erro d3 = register_draft( memory_db, kind="NCM", provider="Siscomex", source_url="https://siscomex.gov.br", sha256="c" * 64, version_label="v3", parse_errors={"line_1": "invalid format"}, ) # Não pode aprovar draft com erros de parse with pytest.raises(RegistryError, match="recorded parse errors"): approve_draft(memory_db, d3.id, admin_name="Admin") # Rejeitar draft rejected = reject_draft(memory_db, d3.id, admin_name="Admin") assert rejected.status == "REJECTED" # Listar registry items = list_registry(memory_db) assert len(items) == 3 def test_classification_tipi_rate_classification_and_tokens(): """Testa classificação determinística de taxas TIPI e tratamento de stopwords.""" class MockEntry: def __init__(self, ex=None, text=None, rate=None): self.ex = ex self.ipi_rate_text = text self.ipi_rate = rate assert classify_tipi_rate(MockEntry(ex="01")) == "EX_SPECIFIC" assert classify_tipi_rate(MockEntry(text=None)) == "SOURCE_BLANK" assert classify_tipi_rate(MockEntry(text="NT")) == "NT_OR_NON_TAXED" assert classify_tipi_rate(MockEntry(text="NÃO TRIBUTADO")) == "NT_OR_NON_TAXED" assert classify_tipi_rate(MockEntry(rate=0, text="0")) == "EXPLICIT_ZERO" assert classify_tipi_rate(MockEntry(rate=15.0, text="15%")) == "OTHER" # Stopwords tokens = description_tokens("CERVEJA DE MALTE COM LUPULO E CEVADA") assert "DE" not in tokens assert "COM" not in tokens assert "E" not in tokens assert "CERVEJA" in tokens assert "MALTE" in tokens def test_http_security_headers_middleware(): """Testa se o middleware de cabeçalhos de segurança HTTP adiciona os headers corretos.""" app = create_app(database_probe=lambda: True, bootstrap_token="test-token") client = TestClient(app) response = client.get("/health") assert response.status_code == 200 assert response.headers.get("X-Content-Type-Options") == "nosniff" assert response.headers.get("X-Frame-Options") == "SAMEORIGIN" assert response.headers.get("X-XSS-Protection") == "1; mode=block" assert response.headers.get("Referrer-Policy") == "strict-origin-when-cross-origin"