Files
rar-autopass/tests/test_shared_store.py
Claude Agent 3fc53534e2 feat(5.15+5.14): CLOSE — fix-uri code-review + embeddings functional
5.15 (propagare design + dashboard editare) si 5.14 (mapare LLM distilata)
inchise dupa /code-review high. 8 buguri reparate TDD:

- HIGH modal nu se deschidea pe randul slim (base.html: trimitere-slim)
- HIGH /repune trunchia prestatii (declaratie incompleta la RAR) -> iterare
  peste existing, codes pozitional
- HIGH embeddings incarca model ~230MB degeaba pe corpus gol -> poarta has_corpus()
- HIGH picker chips gol pe re-render eroare -> conn/account_id pe toate ramurile
- MED obs re-derivat dupa stergere explicita -> _merge_override pastreaza obs=''
- MED mapare salvata fara denumire poluă GOLD -> _record_gold_validation guard
- MED typo nome_prestatie -> nume_prestatie in select /repune
- MED bucketare timp +3h gresita iarna -> SQLite localtime + TZ=Europe/Bucharest

Embeddings WIRE-uit functional (PRD #15, decizie user): ensure_embeddings_corpus
construieste corpus din nomenclator, gated pe AUTOPASS_EMBEDDINGS_ENABLED (default
off). Marime model corectata ~50MB->~230MB (estimare PRD gresita).

Cleanup: hoist load_* din bucla bulk-fix; import re la top.
Regresie: 1256 passed, 1 deselected (live), 0 failed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 20:48:34 +00:00

291 lines
10 KiB
Python

"""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_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