Files
GTI_GERENCIADOR_FISCAL/fiscal/central/tests/test_field_contract.py
T
gti d21a9d1140 Add 'fiscal/' from commit '102ba84500dbd9655a0471288c3e278652593ca9'
git-subtree-dir: fiscal
git-subtree-mainline: d95165fc8f
git-subtree-split: 102ba84500
2026-09-29 14:43:23 -03:00

350 lines
22 KiB
Python

import importlib.util
import sys
from pathlib import Path
import pytest
from central.app.field_contract import (ALREADY, CHANGE, CONTRACT, MANUAL, NA, WRITE_FIELDS, Context, infer_role, resolve_product)
ROOT = Path(__file__).parents[2]
REFS = {"cfop_valid": {"5101", "5102", "5401", "5405", "1102", "1101", "1403", "2102"}, "csosn": {"101", "102", "103", "300", "400", "500"}, "cst_icms": {"00", "20", "40", "41", "60"},
"cst_pis": {"01", "49", "98"}, "cst_cofins": {"01", "49", "98"}, "origem": set("012345678"), "fcp_uf": {}, "cbs_rate_2026": None, "cclasstrib_count": 0}
CO1 = {"uf": "BA", "crt": "1", "contribuinte_ipi": "N", "company_code": "000001"}
CO3 = {"uf": "BA", "crt": "3", "contribuinte_ipi": "S", "company_code": "000001"}
def product(**kw):
p = {f: None for f in WRITE_FIELDS}
p.update({"CODIGO": "000001", "PRODUTO": "CERVEJA LATA", "CODGRUPO": "000019", "TIPO": "VENDA", "PROD_COZINHA": None, "PRODUTO_SERVICO": None, "CLASSIFICACAO_FISCAL": "22030000",
"CFOP": "5102", "CSOSN": "102", "CST": "000", "SITUACAO_TRIBUTARIA": "T", "IAT": "A", "IPPT": "T", "INCIDENCIA_PISCOFINS": "NENHUMA", "PISCOFINS": "N"})
p.update(kw)
return p
def decision(**kw):
d = {"status": "EXACT_NCM", "cest_status": "NO_CEST_MAPPING_FOUND", "ipi_status": "IPI_RESOLVED", "ba_st_mva_status": "NO_MVA_MAPPING_FOUND", "dataset_versions": {},
"classification": {"ncm": "22030000", "ipi": {"rate_kind": "NUMERIC", "rate_numeric": 3.9, "rate": "3.9"}, "ipi_candidates": [], "cest": None, "cest_candidates": [], "ba_st_mva": None, "ba_st_mva_candidates": []}}
cls = kw.pop("classification", {})
d.update(kw)
d["classification"] = {**d["classification"], **cls}
return d
EV = {"recipes": set(), "composed": set(), "purchases": {}, "stock_entry": {}, "stock_in": {"000001": True}}
IPI_POP = {"scope": "COMPANY", "scope_key": "", "key": "store_ipi_profile", "value": "POPULATE_REFERENCE_RATE"}
def run(p=None, d=None, co=CO1, ctx=None, ev=EV, manual=None, ipi_profile=IPI_POP):
return resolve_product(p or product(), d or decision(), co, REFS, ev, Context((ctx or []) + ([ipi_profile] if ipi_profile else [])), manual_ncm=manual)
def st(res, f):
return res[f]["state"], res[f]["proposed"]
def test_every_write_field_has_a_complete_contract():
keys = {"ERP_FIELD", "UI_LABEL", "SEMANTICS", "DATA_TYPE", "SOURCE_OF_TRUTH", "REQUIRED_INPUTS", "RESOLUTION_RULE", "VALID_VALUES", "NOT_APPLICABLE_RULE", "MANUAL_INPUT_RULE", "WRITE_ALLOWED", "VALIDATION_RULE", "PROVENANCE"}
assert set(CONTRACT) == set(WRITE_FIELDS) and len(WRITE_FIELDS) == 23
for f, c in CONTRACT.items():
assert keys <= set(c) and all(c[k] not in (None, "", []) for k in keys - {"WRITE_ALLOWED"}), f
def test_only_five_final_states_and_manual_always_names_its_input():
res = run()
for f in WRITE_FIELDS:
assert res[f]["state"] in (ALREADY, CHANGE, NA, MANUAL)
if res[f]["state"] == MANUAL:
assert res[f]["input_key"]
assert res[f]["write_eligible"] == (res[f]["state"] == CHANGE)
def test_ncm_states_and_operator_ncm_reprocessing():
bad = run(p=product(CLASSIFICACAO_FISCAL="12345678"), d=decision(status="INVALID_NCM"))
assert st(bad, "CLASSIFICACAO_FISCAL")[0] == MANUAL and bad["CLASSIFICACAO_FISCAL"]["input_key"] == "ncm"
for f in ("IPI", "EX_TIPI", "CEST", "CFOP", "CSOSN"):
assert bad[f]["state"] == MANUAL and bad[f]["reason"] == "BLOCKED_BY_NCM"
assert st(run(manual=("22030000", False), p=product(CLASSIFICACAO_FISCAL="12345678"), d=decision(status="INVALID_NCM")), "CLASSIFICACAO_FISCAL")[0] == MANUAL
fixed = run(manual=("22030000", True), p=product(CLASSIFICACAO_FISCAL="12345678"), d=decision())
assert st(fixed, "CLASSIFICACAO_FISCAL") == (CHANGE, "22030000") and fixed["IPI"]["state"] == CHANGE and fixed["IPI"]["proposed"] == 3.9 # dependents reprocessed with the manual NCM
def test_ipi_rules_never_write_nt_or_blank_and_ask_when_ambiguous():
assert st(run(), "IPI") == (CHANGE, 3.9)
assert st(run(p=product(IPI=3.9)), "IPI")[0] == ALREADY
nt = run(d=decision(classification={"ipi": {"rate_kind": "NON_NUMERIC", "rate_classification": "NT"}}))
assert nt["IPI"]["state"] == NA and not nt["IPI"]["write_eligible"]
blank = run(d=decision(classification={"ipi": {"rate_kind": "BLANK"}}))
assert blank["IPI"]["state"] == NA and "NEVER_ZERO" in blank["IPI"]["reason"]
amb = decision(ipi_status="IPI_CANDIDATES", classification={"ipi": None, "ipi_candidates": [{"ex": None, "rate": "5", "description": "base"}, {"ex": "1", "rate": "0", "description": "Chope"}]})
r = run(d=amb)
assert r["IPI"]["state"] == MANUAL and r["IPI"]["input_key"] == "ex_tipi" and len(r["IPI"]["choices"]) == 2
solved = run(d=amb, ctx=[{"scope": "PRODUCT", "scope_key": "000001", "key": "ex_tipi", "value": "NONE"}])
assert st(solved, "IPI") == (CHANGE, 5.0) and solved["EX_TIPI"]["state"] == NA
def test_cest_contract():
off = [{"code": "03.021.03", "official_description": "Cerveja em lata", "description_score": 1.0}, {"code": "03.021.05", "official_description": "PET", "description_score": 0.5}]
conf = run(d=decision(cest_status="CEST_CONFIRMED", classification={"cest": ["03.021.03"], "cest_candidates": off}))
assert st(conf, "CEST") == (CHANGE, "0302103")
assert run(p=product(CEST="0302103"), d=decision(cest_status="CEST_CANDIDATES", classification={"cest_candidates": off}))["CEST"]["state"] == ALREADY
many = run(d=decision(cest_status="CEST_CANDIDATES", classification={"cest_candidates": off}))
assert many["CEST"]["state"] == MANUAL and many["CEST"]["input_key"] == "cest" and len(many["CEST"]["choices"]) == 2
assert run(d=decision(cest_status="CEST_DESCRIPTION_MISMATCH", classification={"cest_candidates": off}))["CEST"]["state"] == NA
assert run()["CEST"]["state"] == NA
assert run(p=product(CEST="9999999"))["CEST"]["state"] == MANUAL
picked = run(d=decision(cest_status="CEST_CANDIDATES", classification={"cest_candidates": off}), ctx=[{"scope": "NCM", "scope_key": "22030000", "key": "cest", "value": "03.021.05"}])
assert st(picked, "CEST") == (CHANGE, "0302105")
def test_cfop_role_and_st_rules():
ok = run() # RESALE (stock entries), not subject to ST, declared 5102 == derived
assert ok["CFOP"]["state"] == ALREADY and ok["_facts"]["role"] == "RESALE"
assert st(run(p=product(CFOP="5405")), "CFOP") == (CHANGE, "5102") # 5405 but proven NOT_SUBJECT
assert st(run(p=product(CFOP=None)), "CFOP") == (CHANGE, "5102") # blank filled
unk = run(d=decision(ba_st_mva_status="MVA_CANDIDATES"), p=product(CFOP=None)) # ST unknown, blank -> asks
assert unk["CFOP"]["state"] == MANUAL and unk["CFOP"]["input_key"] == "st_status"
assert run(d=decision(ba_st_mva_status="MVA_CANDIDATES"))["CFOP"]["state"] == ALREADY # declared valid, not contradicted
role_unknown = run(ev={**EV, "stock_in": {}}, p=product(CFOP=None))
assert role_unknown["CFOP"]["state"] == MANUAL and role_unknown["CFOP"]["input_key"] == "product_role"
own = run(ev={**EV, "recipes": {"000001"}}, p=product(CFOP="5102"))
assert st(own, "CFOP") == (CHANGE, "5101")
assert run(p=product(TIPO="MATERIA-PRIMA"))["CFOP"]["state"] == NA
def test_cfop_entrada_needs_company_entry_scope_then_derives():
r = run(p=product(CFOP_ENTRADA=None))
assert r["CFOP_ENTRADA"]["state"] == MANUAL and r["CFOP_ENTRADA"]["input_key"] == "entry_scope"
r = run(p=product(CFOP_ENTRADA=None), ctx=[{"scope": "COMPANY", "scope_key": "", "key": "entry_scope", "value": "INTRA"}])
assert st(r, "CFOP_ENTRADA") == (CHANGE, "1102")
assert run(p=product(CFOP_ENTRADA="1102"))["CFOP_ENTRADA"]["state"] == ALREADY
def test_cfop_entrada_mixed_scope_is_operation_dependent_and_never_invents_a_cfop():
mixed = [{"scope": "COMPANY", "scope_key": "", "key": "entry_scope", "value": "MIXED_OR_DYNAMIC"}]
r = run(p=product(CFOP_ENTRADA=None), ctx=mixed)
e = r["CFOP_ENTRADA"]
# NA (not MANUAL): the audited entry_scope=MIXED_OR_DYNAMIC decision already answered "why no value" -
# this must never block product completeness nor re-ask a question the admin already answered (Missão 2 / Bloco 3.4).
assert e["state"] == NA and e["proposed"] is None and e.get("input_key") is None
assert e["reason"] == "CFOP_ENTRADA_OPERATION_DEPENDENT_MIXED_SCOPE"
r2 = run(p=product(CFOP_ENTRADA="2102"), ctx=mixed)
assert r2["CFOP_ENTRADA"]["state"] == ALREADY and r2["CFOP_ENTRADA"]["reason"] == "DECLARED_VALID_OPERATION_DEPENDENT_MIXED_SCOPE"
from central.app.context_keys import validate_entry
assert validate_entry({"scope": "COMPANY", "key": "entry_scope", "value": "MIXED_OR_DYNAMIC"})[0]["value"] == "MIXED_OR_DYNAMIC"
def test_icms_code_fields_follow_the_regime():
r1 = run()
assert r1["CST"]["state"] == NA and r1["CST_ENTRADA"]["state"] == NA and r1["CSOSN"]["state"] == ALREADY
assert run(p=product(CSOSN=None))["CSOSN"]["state"] == MANUAL
assert st(run(p=product(CSOSN=None), ctx=[{"scope": "GROUP", "scope_key": "000019", "key": "icms_treatment", "value": "EXEMPT"}]), "CSOSN") == (CHANGE, "400")
assert st(run(p=product(CSOSN="102"), ctx=[{"scope": "GROUP", "scope_key": "000019", "key": "icms_treatment", "value": "ST_WITHHELD"}]), "CSOSN") == (CHANGE, "500")
r3 = run(co=CO3, p=product(CSOSN=None))
assert r3["CSOSN"]["state"] == NA and r3["CST"]["state"] == ALREADY and r3["ALIQUOTA"]["state"] in (ALREADY, MANUAL)
blank = run(co=CO3, p=product(CST=None, COD_ORIGEM_MERCADORIA="0"))
assert blank["CST"]["state"] == MANUAL and blank["CST"]["input_key"] == "icms_treatment"
filled = run(co=CO3, p=product(CST=None, COD_ORIGEM_MERCADORIA="0"), ctx=[{"scope": "COMPANY", "scope_key": "", "key": "icms_treatment", "value": "TAXED"}])
assert st(filled, "CST") == (CHANGE, "000")
def test_aliquota_not_copied_blindly():
assert run()["ALIQUOTA"]["state"] == NA # Simples CSOSN 102 highlights no ICMS
assert run(p=product(CSOSN="101", ALIQUOTA=None))["ALIQUOTA"]["input_key"] == "credit_rate"
assert st(run(co=CO3, p=product(ALIQUOTA="17"), ctx=[{"scope": "COMPANY", "scope_key": "", "key": "icms_rate", "value": "20,5"}]), "ALIQUOTA") == (CHANGE, 20.5)
def test_origin_binding_rules():
r0 = run(p=product(COD_ORIGEM_MERCADORIA=None, CST="040"))["COD_ORIGEM_MERCADORIA"] # audit V2: the CST first digit is padding, never origin evidence
assert r0["state"] == MANUAL and r0["reason"] == "ORIGIN_NO_EVIDENCE" and r0["proposed"] is None
assert run(p=product(COD_ORIGEM_MERCADORIA="0", CST="040"))["COD_ORIGEM_MERCADORIA"]["state"] == ALREADY
assert run(p=product(COD_ORIGEM_MERCADORIA="2", CST="040"))["COD_ORIGEM_MERCADORIA"]["state"] == ALREADY # declared origin stays; CST digit is padding
assert run(p=product(COD_ORIGEM_MERCADORIA=None, CST="540"))["COD_ORIGEM_MERCADORIA"]["state"] == MANUAL # 5 is not on the SGV screen
assert st(run(p=product(COD_ORIGEM_MERCADORIA=None, CST="540"), ctx=[{"scope": "GROUP", "scope_key": "000019", "key": "origin", "value": "1"}]), "COD_ORIGEM_MERCADORIA") == (CHANGE, 1)
def test_cst_ipi_pis_cofins_incidence_iat_ippt():
assert run()["CST_IPI"]["state"] == NA
assert run(co=CO3)["CST_IPI"]["state"] == MANUAL
r = run()
assert r["COD_SAIDA_PIS"]["state"] == MANUAL and r["COD_SAIDA_PIS"]["input_key"] == "pis_cofins_profile" and r["ALIQ_COFINS"]["state"] == MANUAL
prof = '{"pis_out_cst": "49", "pis_in_cst": "98", "pis_rate": "0", "cofins_out_cst": "49", "cofins_in_cst": "98", "cofins_rate": "0"}'
r = run(ctx=[{"scope": "COMPANY", "scope_key": "", "key": "pis_cofins_profile", "value": prof}])
assert st(r, "COD_SAIDA_PIS") == (CHANGE, "49") and st(r, "COD_ENTRADA_COFINS") == (CHANGE, "98") and st(r, "ALIQ_PIS") == (CHANGE, "0")
bad = run(ctx=[{"scope": "COMPANY", "scope_key": "", "key": "pis_cofins_profile", "value": '{"pis_out_cst": "77"}'}])
assert bad["COD_SAIDA_PIS"]["state"] == MANUAL # not in the official CST table
assert run(p=product(IAT=None))["IAT"]["input_key"] == "rounding"
assert run()["INCIDENCIA_PISCOFINS"]["state"] == ALREADY
assert st(run(ev={**EV, "recipes": {"000001"}}), "IPPT") == (CHANGE, "P") and run()["IPPT"]["state"] == ALREADY
def test_pis_cofins_not_applicable_and_operation_time_require_an_audited_admin_decision():
"""Missão 2 / Bloco 2.4: NOT_APPLICABLE/OPERATION_TIME for PIS/COFINS must never be inferred (e.g. from emission
statistics) - only ever accepted from an explicit, audited FiscalContextEntry(key="pis_cofins_profile")."""
pis_cofins_fields = ("COD_ENTRADA_PIS", "COD_SAIDA_PIS", "ALIQ_PIS", "COD_ENTRADA_COFINS", "COD_SAIDA_COFINS", "ALIQ_COFINS")
# Sem decisão ADMIN => comportamento antigo (continua bloqueando, pedindo pis_cofins_profile).
r = run()
for f in pis_cofins_fields:
assert r[f]["state"] == MANUAL and r[f]["input_key"] == "pis_cofins_profile"
# Com decisão ADMIN NOT_APPLICABLE => NA em todos, nunca escreve, nunca bloqueia completude.
na_ctx = [{"scope": "COMPANY", "scope_key": "", "key": "pis_cofins_profile", "value": "NOT_APPLICABLE"}]
r_na = run(ctx=na_ctx)
for f in pis_cofins_fields:
assert r_na[f]["state"] == NA and r_na[f]["proposed"] is None and r_na[f]["reason"] == "ADMIN_DECISION_PIS_COFINS_NOT_APPLICABLE"
# Com decisão ADMIN OPERATION_TIME => NA em todos (não bloqueia), motivo distinto, nunca escreve.
ot_ctx = [{"scope": "COMPANY", "scope_key": "", "key": "pis_cofins_profile", "value": "OPERATION_TIME"}]
r_ot = run(ctx=ot_ctx)
for f in pis_cofins_fields:
assert r_ot[f]["state"] == NA and r_ot[f]["proposed"] is None and r_ot[f]["reason"] == "ADMIN_DECISION_PIS_COFINS_OPERATION_TIME"
# Uma decisão de PROFILE (JSON) continua funcionando exatamente como antes (não regressivo).
prof = '{"pis_out_cst": "49", "pis_in_cst": "98", "pis_rate": "0", "cofins_out_cst": "49", "cofins_in_cst": "98", "cofins_rate": "0"}'
r_prof = run(ctx=[{"scope": "COMPANY", "scope_key": "", "key": "pis_cofins_profile", "value": prof}])
assert st(r_prof, "COD_SAIDA_PIS") == (CHANGE, "49")
assert run(p=product(TIPO="MATERIA-PRIMA"))["IPPT"]["state"] == NA
def test_context_hierarchy_product_beats_group_beats_company():
ctx = Context([{"scope": "COMPANY", "scope_key": "", "key": "product_role", "value": "RESALE"}, {"scope": "GROUP", "scope_key": "G1", "key": "product_role", "value": "OWN_PRODUCTION"},
{"scope": "PRODUCT", "scope_key": "P1", "key": "product_role", "value": "INPUT"}])
assert ctx.get("product_role", "P1", "G1") == ("INPUT", "PRODUCT")
assert ctx.get("product_role", "P2", "G1") == ("OWN_PRODUCTION", "GROUP")
assert ctx.get("product_role", "P2", "G9") == ("RESALE", "COMPANY")
assert Context([]).get("product_role", "P2", "G9") == (None, None)
def test_role_inference_is_evidence_based_and_conflicts_stay_unknown():
assert infer_role(product(TIPO="MATERIA-PRIMA"), EV, Context([]), "000001", "1")[0] == "INPUT"
assert infer_role(product(), {**EV, "stock_in": {}}, Context([]), "000001", "1")[0] == "UNKNOWN"
assert infer_role(product(PROD_COZINHA=1), {**EV, "purchases": {"000001": [{}]}}, Context([]), "000001", "1")[0] == "UNKNOWN"
assert infer_role(product(PROD_COZINHA=1), EV, Context([]), "000001", "1")[0] == "UNKNOWN" # kitchen flag alone is not proof
assert infer_role(product(PROD_COZINHA=1), {**EV, "recipes": {"000001"}}, Context([]), "000001", "1")[0] == "OWN_PRODUCTION"
def test_composed_evidence_needs_a_dominant_majority_never_a_single_occurrence():
"""Missão 4 / Bloco A.1: C000032.TIPO=COMPOSICAO must be a deterministic MAJORITY of that
product's own movement history to prove OWN_PRODUCTION - real cases audited from Agent 2 (2026-09-28)."""
def ev_with(composed, other):
return {**EV, "composed_ratio": {"000001": {"composed": composed, "other": other}}}
# A cerveja: 1 composição vs 19 vendas (5%) - minoria clara, evidência descartada -> cai para o
# papel padrão (aqui sem TIPO=VENDA/docs no fixture, então UNKNOWN por falta de outra evidência;
# o ponto do teste é que NUNCA vira OWN_PRODUCTION).
assert infer_role(product(), ev_with(1, 19), Context([]), "000001", "1")[0] != "OWN_PRODUCTION"
r = infer_role(product(TIPO="VENDA"), {**ev_with(1, 19), "stock_in": {"000001": True}}, Context([]), "000001", "1")
assert r[0] == "RESALE" # a cerveja, com evidência de venda, é revenda - nunca produção própria
# O refrigerante/2ª cerveja: 3 composições vs 4 vendas (42,9%) - amostra pequena mas na zona
# ambígua -> evidência CONFLITANTE, nunca adivinhada para nenhum dos dois lados.
r = infer_role(product(TIPO="VENDA"), {**ev_with(3, 4), "stock_in": {"000001": True}}, Context([]), "000001", "1")
assert r[0] == "UNKNOWN" and "MIXED" in r[1][0]
# O licor: 34 composições vs 22 vendas (60,7%) - maioria real, mas abaixo do limiar dominante
# (70%) -> também CONFLITANTE, nunca OWN_PRODUCTION por uma maioria fraca.
r = infer_role(product(TIPO="VENDA"), {**ev_with(34, 22), "stock_in": {"000001": True}}, Context([]), "000001", "1")
assert r[0] == "UNKNOWN" and "MIXED" in r[1][0]
# Suco com PROD_COZINHA=1 + RECEITA_INGREDIENTES: evidência forte e DIRETA (não é composição
# estatística) - continua OWN_PRODUCTION, o limiar de composição não se aplica a ela.
assert infer_role(product(PROD_COZINHA=1), {**EV, "recipes": {"000001"}}, Context([]), "000001", "1")[0] == "OWN_PRODUCTION"
# Uma maioria REAL e forte (90%, amostra suficiente) ainda prova produção própria.
r = infer_role(product(), ev_with(90, 10), Context([]), "000001", "1")
assert r[0] == "OWN_PRODUCTION" and r[1] == ["C000032.TIPO=COMPOSICAO"]
# Amostra insuficiente (< 5 eventos), mesmo 100% composição: não confia num histórico tão raso.
r = infer_role(product(TIPO="VENDA"), {**ev_with(2, 0), "stock_in": {"000001": True}}, Context([]), "000001", "1")
assert r[0] == "RESALE" # a evidência de composição é ignorada, cai para a evidência de venda real
# Tilápia/corvina: sem NENHUMA evidência de composição - continua revenda normalmente (o fix
# não pode quebrar o caso que já funcionava).
r = infer_role(product(TIPO="VENDA", CFOP=None), {**EV, "purchases": {"000001": [{}]}}, Context([]), "000001", "1")
assert r[0] == "RESALE"
# ---------------- executor guards ----------------
def load_executor():
spec = importlib.util.spec_from_file_location("fwe_local", ROOT / "scripts" / "firebird_write_executor.py")
m = importlib.util.module_from_spec(spec); sys.modules[spec.name] = m; spec.loader.exec_module(m)
return m
class FakeCursor:
def __init__(self, db):
self.db, self.rowcount, self._row = db, 0, None
def execute(self, sql, params=()):
s = " ".join(sql.split())
if "RDB$FIELD_TYPE" in s:
self._row = (37, 20)
elif s.startswith("SELECT") and "FROM C000025" in s:
fields = [x.strip() for x in s.split("SELECT")[1].split("FROM")[0].split(",")]
row = self.db["rows"].get(params[0])
self._row = tuple(row[f] for f in fields) if row else None
elif s.startswith("UPDATE"):
row = self.db["rows"].get(params[-1])
self.rowcount = 0 if row is None else self.db["update_rowcount"]
if row is not None and self.rowcount == 1:
for f, v in zip([x.split("=")[0].strip() for x in s.split("SET")[1].split("WHERE")[0].split(",")], params[:-1]):
self.db["pending"][f] = v
def fetchone(self):
return self._row
def close(self):
pass
class FakeConn:
def __init__(self, rows, update_rowcount=1):
self.db = {"rows": rows, "update_rowcount": update_rowcount, "pending": {}}
self.rolled_back = self.committed = False
def cursor(self):
return FakeCursor(self.db)
def commit(self):
for row in self.db["rows"].values():
row.update(self.db["pending"])
self.committed = True
def rollback(self):
self.db["pending"].clear(); self.rolled_back = True
ALLOWED = r"D:\PROJETOS\GTISISTEMAS\Projeto\work_copies\CONSTRUCAO.FDB"
def test_executor_allowlist_is_exact_and_field_set_is_the_contract():
ex = load_executor()
assert ex._LOCAL_COMPLETION_FIELDS == set(WRITE_FIELDS)
allow = ex.load_local_allowlist()
assert len(allow) == 5
for path in (ALLOWED, r"D:\PROJETOS\GTISISTEMAS\Projeto\TESTESOFICIAIS.FDB"):
ex.assert_local_completion_target(path, allow)
for bad in (r"D:\PROJETOS\GTISISTEMAS\Banco\GTI.FDB", r"D:\PROJETOS\GTISISTEMAS\Projeto\Banco\OUTRA.FDB", r"D:\PROJETOS\GTISISTEMAS\Projeto\work_copies\CONSTRUCAO.FDB.bak", r"D:\PROJETOS\GTISISTEMAS\Projeto\Banco", r"D:\PROJETOS\GTISISTEMAS\Projeto\Banco\CONSTRUCAO.FDB"):
with pytest.raises(ex.WriteRefused):
ex.assert_local_completion_target(bad, allow)
def test_guarded_product_update_success_drift_rowcount_and_refusals():
ex = load_executor()
allow = ex.load_local_allowlist()
conn = FakeConn({"000001": {"IPI": None, "CEST": "1234567"}})
rec = ex.execute_guarded_product_update(conn, database_path=ALLOWED, product_code="000001", changes={"IPI": (None, 3.9), "CEST": ("1234567", "7654321")}, now="t", allowlist=allow)
assert rec["rowcount"] == 1 and rec["fields"]["IPI"]["after"] == 3.9 and conn.committed
with pytest.raises(ex.WriteRefused, match="drift"):
ex.execute_guarded_product_update(FakeConn({"000001": {"IPI": 1.0}}), database_path=ALLOWED, product_code="000001", changes={"IPI": (None, 3.9)}, now="t", allowlist=allow)
c2 = FakeConn({"000001": {"IPI": None}}, update_rowcount=2)
with pytest.raises(ex.WriteRefused, match="rowcount"):
ex.execute_guarded_product_update(c2, database_path=ALLOWED, product_code="000001", changes={"IPI": (None, 3.9)}, now="t", allowlist=allow)
assert c2.rolled_back
with pytest.raises(ex.WriteRefused, match="allowlist"):
ex.execute_guarded_product_update(conn, database_path=r"D:\PROJETOS\GTISISTEMAS\Banco\GTI.FDB", product_code="000001", changes={"IPI": (None, 1)}, now="t", allowlist=allow)
with pytest.raises(ex.WriteRefused, match="contract write target"):
ex.execute_guarded_product_update(conn, database_path=ALLOWED, product_code="000001", changes={"CODIGO": ("000001", "9")}, now="t", allowlist=allow)
with pytest.raises(ex.WriteRefused, match="null"):
ex.execute_guarded_product_update(conn, database_path=ALLOWED, product_code="000001", changes={"IPI": (None, None)}, now="t", allowlist=allow)
with pytest.raises(ex.WriteRefused, match="only C000025"):
ex.execute_guarded_product_update(conn, database_path=ALLOWED, product_code="000001", changes={"IPI": (None, 1)}, now="t", allowlist=allow, table="NFCE_ITENS")