feat: central RTC write modes (DISABLED/DRY_RUN/APPROVAL/ENABLED), R4 suggestion-only, preview/approval, per-record rollback, bulk blocker upsert; 1.2.0
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -44,7 +44,7 @@ def test_rtc_off_is_not_applicable_and_never_a_candidate():
|
||||
|
||||
def test_simples_is_insufficient_until_the_official_orientation_exists():
|
||||
r = resolve("10063021", company=SIMPLES)
|
||||
assert r.status == "INSUFFICIENT_EVIDENCE" and r.rule_id == "RTC-R1" and not r.auto_apply_candidate and r.source_references[-1]["document"].startswith("NT 2025.002")
|
||||
assert r.status == "INSUFFICIENT_EVIDENCE" and r.rule_id == "RTC-R1" and not r.auto_apply_candidate and r.source_references[-1]["document"].startswith("Ato Conjunto RFB/CGIBS") and r.rule_class == "BLOCKED_BY_EVIDENCE"
|
||||
assert resolve("10063021", company={"crt": None}).reason == "COMPANY_REGIME_UNKNOWN"
|
||||
|
||||
|
||||
@@ -88,35 +88,59 @@ def test_nature_based_chapters_are_ambiguous():
|
||||
assert r.status == "AMBIGUOUS" and r.rule_id in ("RTC-A3", "RTC-A1", "RTC-A2"), ncm
|
||||
|
||||
|
||||
def test_residual_ncm_outside_every_annex_and_sensitive_chapter_is_integral_taxation():
|
||||
def test_residual_ncm_outside_every_annex_is_only_a_suggestion_and_never_a_write_candidate():
|
||||
r = resolve("39235000") # plastic closures: no annex, no nature-based regime
|
||||
assert r.status == "RESOLVED_WITH_EXPLICIT_RULE" and r.resolved_cclasstrib == "000001" and r.resolved_cst_ibscbs == "000" and r.rule_id == "RTC-R4" and r.confidence_type == "EXCLUSION_OF_ALL_SPECIAL_REGIMES"
|
||||
assert r.auto_apply_candidate and annex_matches("39235000") == []
|
||||
assert r.status == "SUGGESTION_ONLY" and r.resolved_cclasstrib == "000001" and r.suggested_cclasstrib == "000001" and r.rule_id == "RTC-R4" and r.rule_class == "SUGGESTION_ONLY"
|
||||
assert r.auto_apply_candidate is False and annex_matches("39235000") == [] and r.confidence_type == "EXCLUSION_OF_ALL_SPECIAL_REGIMES"
|
||||
assert "not positive evidence" in r.evidence[0]["note"] and r.as_dict()["auto_apply_candidate"] is False
|
||||
|
||||
|
||||
def test_only_auto_apply_safe_rules_can_authorize_a_write():
|
||||
from central.app.cclasstrib_resolver import RULE_CLASSES
|
||||
assert [rid for rid, cls in RULE_CLASSES.items() if cls == "AUTO_APPLY_SAFE"] == ["RTC-R3"]
|
||||
assert RULE_CLASSES["RTC-R4"] == "SUGGESTION_ONLY" and RULE_CLASSES["RTC-R0"] == "VALIDATION_ONLY"
|
||||
assert {c for c in RULE_CLASSES.values()} == {"AUTO_APPLY_SAFE", "VALIDATION_ONLY", "SUGGESTION_ONLY", "BLOCKED_BY_EVIDENCE"}
|
||||
|
||||
|
||||
def test_regime_other_than_normal_never_reaches_a_positive_rule():
|
||||
for company in ({"crt": "2"}, {"crt": "4"}, {"crt": "9"}, {"crt": ""}):
|
||||
assert not resolve("10063021", company=company).auto_apply_candidate
|
||||
|
||||
|
||||
def test_a_class_missing_or_different_in_the_local_catalog_is_never_proposed():
|
||||
r = resolve("39235000", local=local_from_official(skip=("000001",)))
|
||||
r = resolve("10063021", local=local_from_official(skip=("200003",)))
|
||||
assert r.status == "INSUFFICIENT_EVIDENCE" and r.reason == "CLASS_NOT_IN_LOCAL_CATALOG" and not r.auto_apply_candidate
|
||||
r2 = resolve("39235000", local=local_from_official(tweak={"000001": {"cst": "010"}}))
|
||||
r2 = resolve("10063021", local=local_from_official(tweak={"200003": {"cst": "010"}}))
|
||||
assert r2.reason == "LOCAL_OFFICIAL_MISMATCH" and not r2.auto_apply_candidate
|
||||
r3 = resolve("10063021", local=local_from_official(tweak={"200003": {"ind_nfe": False, "ind_nfce": False}}))
|
||||
assert r3.reason == "LOCAL_OFFICIAL_MISMATCH"
|
||||
|
||||
|
||||
def test_invalid_local_catalog_blocks_every_proposal():
|
||||
assert resolve("39235000", ctx={"rtc_enabled": True, "local_catalog_valid": False}).reason == "LOCAL_RTC_CATALOG_INVALID"
|
||||
assert resolve("10063021", ctx={"rtc_enabled": True, "local_catalog_valid": False}).reason == "LOCAL_RTC_CATALOG_INVALID"
|
||||
|
||||
|
||||
def test_catalog_comparison_classifies_every_code_and_flags_material_problems():
|
||||
full = compare_catalogs(local_from_official(), TODAY)
|
||||
assert full["counts"]["MATCH"] == 161 and full["counts"]["EXPIRED"] == 3 and full["compatibility"] == "PARTIAL_LOCAL_OUTDATED" # 220001-3 ended on 2026-01-01
|
||||
assert full["counts"]["MATCH_SAFE"] == 161 and full["counts"]["EXPIRED"] == 3 and full["compatibility"] == "PARTIAL_LOCAL_OUTDATED" # 220001-3 ended on 2026-01-01
|
||||
assert compare_catalogs(local_from_official(skip=("220001", "220002", "220003")), TODAY)["compatibility"] == "FULL"
|
||||
outdated = compare_catalogs(local_from_official(skip=("000005", "200053")), TODAY)
|
||||
assert outdated["counts"]["OFFICIAL_ONLY"] == 2 and outdated["compatibility"] == "PARTIAL_LOCAL_OUTDATED" and outdated["counts"]["MISMATCH"] == 0
|
||||
assert outdated["counts"]["MISSING_LOCAL"] == 2 and outdated["compatibility"] == "PARTIAL_LOCAL_OUTDATED" and outdated["counts"]["MISMATCH"] == 0
|
||||
local = local_from_official(tweak={"200003": {"p_red_ibs": "60"}})
|
||||
local["999999"] = {"cst": "000", "p_red_ibs": "0", "p_red_cbs": "0", "ind_nfe": True, "ind_nfce": True}
|
||||
bad = compare_catalogs(local, TODAY)
|
||||
assert bad["per_code"]["200003"] == "MISMATCH" and bad["per_code"]["999999"] == "LOCAL_ONLY" and bad["compatibility"] == "MATERIAL_MISMATCH"
|
||||
assert bad["per_code"]["200003"] == "MISMATCH" and bad["per_code"]["999999"] == "MISSING_OFFICIAL" and bad["compatibility"] == "MATERIAL_MISMATCH"
|
||||
|
||||
|
||||
def test_non_material_differences_are_classified_but_not_treated_as_a_mismatch():
|
||||
earlier = compare_catalogs(local_from_official(tweak={"200003": {"start": "2025-05-05"}}), TODAY) # SGV imports an earlier start: both in force today => same emission effect
|
||||
assert earlier["per_code"]["200003"] == "MATCH_SAFE" and earlier["details"]["200003"] == ["VIGENCY_START_DIFFERS_BOTH_IN_FORCE"]
|
||||
assert compare_catalogs(local_from_official(tweak={"200003": {"start": "2027-01-01"}}), TODAY)["per_code"]["200003"] == "MISMATCH"
|
||||
assert compare_catalogs(local_from_official(tweak={"200003": {"end": "2026-01-01"}}), TODAY)["per_code"]["200003"] == "MISMATCH"
|
||||
local = local_from_official(tweak={"200003": {"end": "2027-12-31"}}) # a scheduled local end the official table does not have
|
||||
c = compare_catalogs(local, TODAY)
|
||||
assert c["per_code"]["200003"] == "MATCH_WITH_NON_MATERIAL_DIFFERENCE" and c["counts"]["MISMATCH"] == 0
|
||||
assert resolve("10063021", local=local).status == "INSUFFICIENT_EVIDENCE" # auto-apply requires MATCH_SAFE: a non-material difference still blocks the WRITE
|
||||
|
||||
|
||||
def test_local_catalog_hash_is_stable_and_sensitive():
|
||||
|
||||
Reference in New Issue
Block a user