"""TDD pentru L14-S3 — shared_store: SILVER (mapping_suggestions) + GOLD partajat (shared_mappings). Scenarii acoperite: - seed_suggestions: idempotent (INSERT OR IGNORE, nu clobberuie randuri existente) - seed_suggestions: NUL marcat -> cod_prestatie NULL, is_nul=1 (supresie, #4) - lookup_suggestion: cauta pe denumire_normalizata - lookup_shared_gold: cauta pe denumire_normalizata - record_human_validation: insert nou + increment confirmations la al doilea apel - provenance/source/confidence pastrate (#5) """ from __future__ import annotations import os import tempfile import pytest # --------------------------------------------------------------------------- # # Fixtures # # --------------------------------------------------------------------------- # @pytest.fixture() def env(monkeypatch): """DB temporara cu schema initiata.""" tmp = tempfile.mkdtemp() monkeypatch.setenv("AUTOPASS_DB_PATH", os.path.join(tmp, "shared_store_test.db")) from app.config import get_settings get_settings.cache_clear() from app.db import init_db init_db() yield monkeypatch get_settings.cache_clear() @pytest.fixture() def conn(env): from app.db import get_connection c = get_connection() yield c c.close() # --------------------------------------------------------------------------- # # seed_suggestions — strat SILVER # # --------------------------------------------------------------------------- # def test_seed_suggestions_inserteaza(conn): """seed_suggestions insereaza un rand si returneaza 1 (numarul de randuri inserate).""" from app.shared_store import seed_suggestions, lookup_suggestion n = seed_suggestions(conn, [ {"denumire": "Schimb ulei motor", "cod_prestatie": "OE-3", "source": "llm", "confidence": 0.9}, ]) conn.commit() assert n == 1 row = lookup_suggestion(conn, "Schimb ulei motor") assert row is not None assert row["cod_prestatie"] == "OE-3" assert row["source"] == "llm" assert abs(row["confidence"] - 0.9) < 0.001 assert row["is_nul"] == 0 def test_seed_suggestions_idempotent(conn): """seed_suggestions de doua ori cu acelasi item -> al doilea INSERT OR IGNORE, n=0 la re-seed.""" from app.shared_store import seed_suggestions, lookup_suggestion n1 = seed_suggestions(conn, [ {"denumire": "Verificare faruri", "cod_prestatie": "OE-2", "source": "llm", "confidence": 0.8}, ]) conn.commit() n2 = seed_suggestions(conn, [ {"denumire": "Verificare faruri", "cod_prestatie": "OE-2", "source": "llm", "confidence": 0.8}, ]) conn.commit() assert n1 == 1 assert n2 == 0 # INSERT OR IGNORE: randul deja exista row = lookup_suggestion(conn, "Verificare faruri") assert row is not None assert row["cod_prestatie"] == "OE-2" def test_seed_suggestions_nu_clobberuie_randul_existent(conn): """Re-seed cu cod diferit -> INSERT OR IGNORE pastreaza valoarea veche (#2).""" from app.shared_store import seed_suggestions, lookup_suggestion seed_suggestions(conn, [ {"denumire": "Reparatie motor", "cod_prestatie": "OE-1", "source": "llm", "confidence": 0.85}, ]) conn.commit() # Al doilea seed cu alt cod: trebuie ignorat (nu suprascrie) seed_suggestions(conn, [ {"denumire": "Reparatie motor", "cod_prestatie": "OE-2", "source": "llm", "confidence": 0.5}, ]) conn.commit() row = lookup_suggestion(conn, "Reparatie motor") assert row is not None assert row["cod_prestatie"] == "OE-1" # valoarea veche pastrata def test_seed_suggestions_nul_marcat_fara_cod(conn): """is_nul=True -> cod_prestatie NULL, is_nul=1 in DB (supresie, #4).""" from app.shared_store import seed_suggestions, lookup_suggestion seed_suggestions(conn, [ {"denumire": "ITP CT 12 ABC", "is_nul": True, "source": "llm", "confidence": 0.95}, ]) conn.commit() row = lookup_suggestion(conn, "ITP CT 12 ABC") assert row is not None assert row["is_nul"] == 1 assert row["cod_prestatie"] is None # NUL nu se promoveaza la cod (#4) def test_seed_suggestions_nul_cu_cod_explicit_tot_nul(conn): """Daca is_nul=True, cod_prestatie e ignorat si stocat NULL (supresie stricta, #4).""" from app.shared_store import seed_suggestions, lookup_suggestion seed_suggestions(conn, [ { "denumire": "DISCOUNT MATERIALE 5%", "cod_prestatie": "OE-1", # ignorat cand is_nul=True "is_nul": True, "source": "llm", "confidence": 0.99, }, ]) conn.commit() row = lookup_suggestion(conn, "DISCOUNT MATERIALE 5%") assert row is not None assert row["is_nul"] == 1 assert row["cod_prestatie"] is None # cod explicit ignorat cand is_nul def test_seed_suggestions_cod_whitespace_devine_null(conn): """Rand non-NUL cu cod whitespace-only (' ') -> cod_prestatie NULL, NU '' (corectitudine).""" from app.shared_store import seed_suggestions, lookup_suggestion seed_suggestions(conn, [ {"denumire": "OPERATIE CU COD GOL", "cod_prestatie": " ", "source": "llm", "confidence": 0.5}, ]) conn.commit() row = lookup_suggestion(conn, "OPERATIE CU COD GOL") assert row is not None assert row["is_nul"] == 0 # nu e marcat NUL assert row["cod_prestatie"] is None # whitespace -> NULL, nu '' (rand non-NUL fara cod gol) def test_seed_suggestions_normalizare_diacritice(conn): """Lookup pe forma cu diacritice gaseste randul seedat fara diacritice (normalize_for_match).""" from app.shared_store import seed_suggestions, lookup_suggestion seed_suggestions(conn, [ {"denumire": "Înlocuit filtru aer", "cod_prestatie": "OE-3", "source": "llm", "confidence": 0.7}, ]) conn.commit() # Lookup cu accentele, fara accente, uppercase — trebuie sa gaseasca acelasi rand row1 = lookup_suggestion(conn, "Înlocuit filtru aer") row2 = lookup_suggestion(conn, "Inlocuit filtru aer") row3 = lookup_suggestion(conn, "INLOCUIT FILTRU AER") assert row1 is not None and row1["cod_prestatie"] == "OE-3" assert row2 is not None and row2["cod_prestatie"] == "OE-3" assert row3 is not None and row3["cod_prestatie"] == "OE-3" def test_lookup_suggestion_lipseste_returneaza_none(conn): """lookup_suggestion pe o denumire care nu exista -> None.""" from app.shared_store import lookup_suggestion row = lookup_suggestion(conn, "Denumire inexistenta") assert row is None # --------------------------------------------------------------------------- # # record_human_validation + lookup_shared_gold — strat GOLD partajat # # --------------------------------------------------------------------------- # def test_record_human_validation_insert(conn): """Prima confirmare umana creeaza un rand nou in shared_mappings.""" from app.shared_store import record_human_validation, lookup_shared_gold record_human_validation( conn, denumire="Schimb ulei", cod_prestatie="oe-3", # se normalizeaza la OE-3 source="human", provenance="cont_2/user@test.com", confidence=1.0, ) conn.commit() row = lookup_shared_gold(conn, "Schimb ulei") assert row is not None assert row["cod_prestatie"] == "OE-3" # normalizat uppercase assert row["source"] == "human" assert row["provenance"] == "cont_2/user@test.com" assert abs(row["confidence"] - 1.0) < 0.001 assert row["confirmations"] == 1 def test_record_human_validation_increment_confirmations(conn): """A doua confirmare umana pe aceeasi denumire -> confirmations += 1.""" from app.shared_store import record_human_validation, lookup_shared_gold record_human_validation(conn, "Revizie anuala", "OE-3") conn.commit() record_human_validation(conn, "Revizie anuala", "OE-3") conn.commit() row = lookup_shared_gold(conn, "Revizie anuala") assert row is not None assert row["confirmations"] == 2 def test_record_human_validation_normalizare(conn): """Lookup pe diacritice sau uppercase gaseste acelasi rand GOLD.""" from app.shared_store import record_human_validation, lookup_shared_gold record_human_validation(conn, "Înlocuit garnitura chiulasa", "OE-1") conn.commit() row1 = lookup_shared_gold(conn, "Înlocuit garnitura chiulasa") row2 = lookup_shared_gold(conn, "Inlocuit garnitura chiulasa") row3 = lookup_shared_gold(conn, "INLOCUIT GARNITURA CHIULASA") assert row1 is not None and row1["cod_prestatie"] == "OE-1" assert row2 is not None assert row3 is not None def test_lookup_shared_gold_lipseste_returneaza_none(conn): """lookup_shared_gold pe denumire inexistenta -> None.""" from app.shared_store import lookup_shared_gold row = lookup_shared_gold(conn, "Operatie fara GOLD") assert row is None def test_provenance_source_confidence_pastrate(conn): """source, provenance, confidence sunt stocate si returnate corect (#5).""" from app.shared_store import seed_suggestions, lookup_suggestion seed_suggestions(conn, [ { "denumire": "Reglat directie", "cod_prestatie": "OE-2", "source": "embedding", "confidence": 0.73, }, ]) conn.commit() row = lookup_suggestion(conn, "Reglat directie") assert row is not None assert row["source"] == "embedding" assert abs(row["confidence"] - 0.73) < 0.001 # --------------------------------------------------------------------------- # # Separare structurala (#13): tabelele noi NU sunt citite de resolve_prestatii# # --------------------------------------------------------------------------- # def test_mapping_suggestions_nu_e_folosita_de_resolve_prestatii(): """resolve_prestatii NU citeste din mapping_suggestions — separare structurala (#13). Daca un cod e in SILVER dar nu in operations_mapping, resolve_prestatii NU il gaseste -> submission ramane needs_mapping (om in bucla). """ from app.mapping import resolve_prestatii # Apelam resolve_prestatii fara niciun mapping -> operatia e nemapata resolved, unmapped = resolve_prestatii( [{"cod_op_service": "OP_SILVER", "denumire": "Reglat faruri"}], {}, # operations_mapping gol ) assert resolved[0]["cod_prestatie"] is None assert len(unmapped) == 1 # Nu exista cale prin care SILVER sa ajunga in resolve fara wiring explicit (L14-S6) def test_shared_mappings_nu_e_folosita_de_resolve_prestatii(): """resolve_prestatii NU citeste din shared_mappings — separare structurala (#13). Chiar daca GOLD partajat exista, resolve_prestatii nu il vede fara wiring explicit. """ from app.mapping import resolve_prestatii resolved, unmapped = resolve_prestatii( [{"cod_op_service": "OP_GOLD", "denumire": "Revizie periodica"}], {}, # operations_mapping gol ) assert resolved[0]["cod_prestatie"] is None assert len(unmapped) == 1