Seed app/data/operatii-etichetate.json regenerat cu subagenti Haiku pe TOATE cele 17181 operatii distincte (ordine frecventa, 100%), inlocuind seed-ul Groq (3758). Validare Haiku vs Groq pe 157 op etichetate: la dezacorduri Haiku corect ~22/30, Groq ~0. Haiku prinde gunoiul ratat de Groq (ITP, chirie anvelope, nume piese fara actiune): NUL 2200 (12.8%) vs ~7.6% Groq; adaptare electronica OE-7 (nu OE-5), placute frana uzura OE-1 (nu OE-F avarie). US-001..006: prefiltru NUL determinist, etichetator offline, generator seed, seeder mapping_suggestions (in init_db, gated seed_operatii_enabled), embeddings indexeaza corpus etichetat, enrich NUL+kNN. Distributie seed: OE-1 80.1%, NUL 12.8%, OE-2 3.5%, restul rar (OE-4/3/7/8/R/I/5, AITLV, R-ODO). config: seed_operatii_enabled=True + embeddings_enabled=True implicit (SILVER populat + sugestii semantice; ambele suggestion-only, dezactivabile prin env). Suita: 1387 passed, 1 deselected (live). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
583 lines
22 KiB
Python
583 lines
22 KiB
Python
"""TDD L14-S6 — Integrare Layer 2/3 in editor (suggestion-only, DUPA 5.15).
|
|
|
|
Scenarii acoperite:
|
|
- F1-regression CRITIC: SILVER/shared GOLD NU auto-trimit (resolve_prestatii neschimbat)
|
|
- pending_unmapped include sugestie GOLD partajat > SILVER > embeddings (precedenta Eng-F2)
|
|
- record_human_validation apelat la confirmare umana (POST /mapari -> shared_mappings)
|
|
- Degradare gratioasa cand embeddings indisponibil (mock is_available=False)
|
|
- Separare structurala #13: resolve_prestatii/load_mapping NU citesc tabelele de sugestii
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import tempfile
|
|
|
|
import pytest
|
|
|
|
|
|
# --------------------------------------------------------------------------- #
|
|
# Fixtures #
|
|
# --------------------------------------------------------------------------- #
|
|
|
|
@pytest.fixture()
|
|
def env(monkeypatch):
|
|
"""DB temporara cu schema initiata, auth dezactivata (mod dev)."""
|
|
tmp = tempfile.mkdtemp()
|
|
monkeypatch.setenv("AUTOPASS_DB_PATH", os.path.join(tmp, "l14_s6_test.db"))
|
|
monkeypatch.setenv("AUTOPASS_WEB_AUTH_REQUIRED", "false")
|
|
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()
|
|
# Seed nomenclator (OE-1, OE-2, OE-3, OE-4 suficient pentru teste)
|
|
c.executemany(
|
|
"INSERT OR IGNORE INTO nomenclator_rar (cod_prestatie, nume_prestatie) VALUES (?, ?)",
|
|
[
|
|
("OE-1", "REPARATIE MOTOR"),
|
|
("OE-2", "INTRETINERE"),
|
|
("OE-3", "REVIZIE PERIODICA"),
|
|
("OE-4", "REGLARE"),
|
|
],
|
|
)
|
|
c.commit()
|
|
yield c
|
|
c.close()
|
|
|
|
|
|
@pytest.fixture()
|
|
def client(env):
|
|
from app.main import app
|
|
from fastapi.testclient import TestClient
|
|
with TestClient(app) as c:
|
|
yield c
|
|
|
|
|
|
# --------------------------------------------------------------------------- #
|
|
# F1-regression CRITIC: SILVER/shared GOLD NU auto-trimit #
|
|
# --------------------------------------------------------------------------- #
|
|
|
|
def test_f1_silver_nu_auto_trimite(conn):
|
|
"""CRITICAL F1: un cod in SILVER (mapping_suggestions) NU produce auto-trimitere.
|
|
|
|
resolve_prestatii cu mapping gol + SILVER existent -> operatie ramane nemapata.
|
|
Submissionul ar ramane needs_mapping, NU queued.
|
|
"""
|
|
from app.shared_store import seed_suggestions
|
|
from app.mapping import resolve_prestatii
|
|
|
|
seed_suggestions(conn, [
|
|
{"denumire": "Revizie periodica", "cod_prestatie": "OE-3", "source": "llm", "confidence": 0.95},
|
|
])
|
|
conn.commit()
|
|
|
|
# resolve_prestatii cu mapping gol -> SILVER nu se vede
|
|
resolved, unmapped = resolve_prestatii(
|
|
[{"cod_op_service": "OP-REV", "denumire": "Revizie periodica"}],
|
|
{}, # operations_mapping gol
|
|
)
|
|
# Operatia ramane nemapata (SILVER nu e in resolve, #13)
|
|
assert resolved[0]["cod_prestatie"] is None
|
|
assert len(unmapped) == 1
|
|
|
|
|
|
def test_f1_shared_gold_nu_auto_trimite(conn):
|
|
"""CRITICAL F1: un cod in shared_mappings (GOLD partajat) NU produce auto-trimitere.
|
|
|
|
resolve_prestatii cu mapping gol + shared GOLD existent -> operatie ramane nemapata.
|
|
"""
|
|
from app.shared_store import record_human_validation
|
|
from app.mapping import resolve_prestatii
|
|
|
|
record_human_validation(conn, "Schimb ulei motor", "OE-3")
|
|
conn.commit()
|
|
|
|
# resolve_prestatii cu mapping gol -> GOLD partajat nu se vede
|
|
resolved, unmapped = resolve_prestatii(
|
|
[{"cod_op_service": "OP-ULEI", "denumire": "Schimb ulei motor"}],
|
|
{}, # operations_mapping gol
|
|
)
|
|
# Operatia ramane nemapata (GOLD partajat nu e in resolve, #13)
|
|
assert resolved[0]["cod_prestatie"] is None
|
|
assert len(unmapped) == 1
|
|
|
|
|
|
def test_f1_load_mapping_nu_citeste_shared_gold(conn):
|
|
"""Separare #13: load_mapping NU returneaza coduri din shared_mappings."""
|
|
from app.shared_store import record_human_validation
|
|
from app.mapping import load_mapping
|
|
|
|
record_human_validation(conn, "Revizie anuala", "OE-3")
|
|
conn.commit()
|
|
|
|
mapping = load_mapping(conn, account_id=1)
|
|
# GOLD partajat nu trebuie sa apara in load_mapping (citit de resolve_prestatii)
|
|
assert "Revizie anuala" not in mapping
|
|
# Maparea propriu-zisa (operations_mapping) ramane goala
|
|
assert len(mapping) == 0
|
|
|
|
|
|
# --------------------------------------------------------------------------- #
|
|
# enrich_suggestions: GOLD partajat > SILVER > embeddings #
|
|
# --------------------------------------------------------------------------- #
|
|
|
|
def test_enrich_fara_surse_returneaza_none(conn):
|
|
"""Fara GOLD/SILVER/embedding -> sugestie_principala = None."""
|
|
from app.mapping import enrich_suggestions
|
|
|
|
result = enrich_suggestions(conn, "Operatie inexistenta")
|
|
assert result["sugestie_principala"] is None
|
|
assert result["surse"]["gold_partajat"] is None
|
|
assert result["surse"]["silver"] is None
|
|
assert result["surse"]["embedding"] is None
|
|
|
|
|
|
def test_enrich_include_gold_partajat(conn):
|
|
"""enrich_suggestions returneaza sugestie GOLD partajat cand shared_mappings are match."""
|
|
from app.shared_store import record_human_validation
|
|
from app.mapping import enrich_suggestions
|
|
|
|
record_human_validation(conn, "Schimb ulei", "OE-3")
|
|
conn.commit()
|
|
|
|
result = enrich_suggestions(conn, "Schimb ulei")
|
|
|
|
assert result["sugestie_principala"] is not None
|
|
assert result["sugestie_principala"]["cod_prestatie"] == "OE-3"
|
|
assert result["sugestie_principala"]["sursa"] == "gold_partajat"
|
|
assert result["surse"]["gold_partajat"] == "OE-3"
|
|
|
|
|
|
def test_enrich_include_silver(conn):
|
|
"""enrich_suggestions returneaza sugestie SILVER cand mapping_suggestions are match."""
|
|
from app.shared_store import seed_suggestions
|
|
from app.mapping import enrich_suggestions
|
|
|
|
seed_suggestions(conn, [
|
|
{"denumire": "Reparatie motor", "cod_prestatie": "OE-1", "source": "llm", "confidence": 0.9},
|
|
])
|
|
conn.commit()
|
|
|
|
result = enrich_suggestions(conn, "Reparatie motor")
|
|
|
|
assert result["sugestie_principala"] is not None
|
|
assert result["sugestie_principala"]["cod_prestatie"] == "OE-1"
|
|
assert result["sugestie_principala"]["sursa"] == "silver"
|
|
assert result["surse"]["silver"] == "OE-1"
|
|
|
|
|
|
def test_enrich_precedenta_gold_peste_silver(conn):
|
|
"""Precedenta Eng-F2: GOLD partajat castiga fata de SILVER cand ambele exista."""
|
|
from app.shared_store import seed_suggestions, record_human_validation
|
|
from app.mapping import enrich_suggestions
|
|
|
|
# SILVER spune OE-1, GOLD spune OE-3
|
|
seed_suggestions(conn, [
|
|
{"denumire": "Verificare tehnica", "cod_prestatie": "OE-1", "source": "llm", "confidence": 0.8},
|
|
])
|
|
record_human_validation(conn, "Verificare tehnica", "OE-3")
|
|
conn.commit()
|
|
|
|
result = enrich_suggestions(conn, "Verificare tehnica")
|
|
|
|
assert result["sugestie_principala"] is not None
|
|
assert result["sugestie_principala"]["cod_prestatie"] == "OE-3"
|
|
assert result["sugestie_principala"]["sursa"] == "gold_partajat"
|
|
# SILVER prezent dar nu castiga
|
|
assert result["surse"]["silver"] == "OE-1"
|
|
assert result["surse"]["gold_partajat"] == "OE-3"
|
|
|
|
|
|
def test_enrich_degradare_embeddings_indisponibil(conn, monkeypatch):
|
|
"""Degradare gratioasa (#16b): cand embeddings nu e disponibil, nu eroare."""
|
|
import app.embeddings as emb_mod
|
|
monkeypatch.setattr(emb_mod, "is_available", lambda: False)
|
|
|
|
from app.mapping import enrich_suggestions
|
|
|
|
# Fara surse -> sugestie_principala = None, fara exceptie
|
|
result = enrich_suggestions(conn, "Operatie demo", include_embeddings=True)
|
|
assert result["sugestie_principala"] is None
|
|
assert result["surse"]["embedding"] is None
|
|
|
|
|
|
def test_enrich_corpus_gol_nu_incarca_modelul(conn, monkeypatch):
|
|
"""Bug fix (code-review): enrich_suggestions NU lazy-load-eaza modelul de 220MB
|
|
cand corpus-ul embeddings e gol.
|
|
|
|
Implementarea veche apela `is_available()` neconditionat -> `_get_engine()` ->
|
|
`_load_engine()` -> `FastEmbedBackend()` (incarcare sincrona 30-120s) chiar daca
|
|
`index_corpus` nu a fost apelat niciodata in productie -> corpus gol ->
|
|
`suggest_nearest` ar fi returnat [] oricum (zero beneficiu, cost mare).
|
|
Fix: poarta `has_corpus()` (ieftina, nu construieste engine-ul cand `_engine is None`).
|
|
"""
|
|
import app.embeddings as emb_mod
|
|
|
|
# Engine ne-initializat -> corpus gol prin definitie.
|
|
monkeypatch.setattr(emb_mod, "_engine", None, raising=False)
|
|
incarcari = {"n": 0}
|
|
orig_load = emb_mod._load_engine
|
|
|
|
def _spy_load():
|
|
incarcari["n"] += 1
|
|
return orig_load()
|
|
|
|
monkeypatch.setattr(emb_mod, "_load_engine", _spy_load)
|
|
|
|
from app.mapping import enrich_suggestions
|
|
result = enrich_suggestions(conn, "Operatie oarecare", include_embeddings=True)
|
|
|
|
assert result["surse"]["embedding"] is None
|
|
assert incarcari["n"] == 0, (
|
|
"Modelul de embeddings NU trebuie incarcat cand corpus-ul e gol "
|
|
f"(index_corpus nu e wired). _load_engine apelat de {incarcari['n']} ori."
|
|
)
|
|
|
|
|
|
class _FakeEmbedBackend:
|
|
"""Backend embedding determinist (3 dimensiuni keyword) — fara model real 230MB."""
|
|
|
|
def embed(self, texts):
|
|
out = []
|
|
for t in texts:
|
|
tl = str(t).lower()
|
|
out.append([
|
|
1.0 if "ulei" in tl else 0.0,
|
|
1.0 if "motor" in tl else 0.0,
|
|
1.0 if "frana" in tl else 0.0,
|
|
])
|
|
return out
|
|
|
|
|
|
def test_embeddings_functional_cand_flag_activ(conn, monkeypatch):
|
|
"""PRD #15: cu AUTOPASS_EMBEDDINGS_ENABLED=true, embeddings produce efectiv o sugestie.
|
|
|
|
Wire-uieste ensure_embeddings_corpus (corpus din nomenclator) + enrich_suggestions.
|
|
Backend injectat (determinist) -> nu incarca modelul real de 230MB.
|
|
"""
|
|
import app.embeddings as emb_mod
|
|
from app.embeddings import EmbeddingEngine
|
|
from app.config import get_settings
|
|
|
|
# Activeaza flagul + injecteaza backend fals in singleton-ul global.
|
|
monkeypatch.setenv("AUTOPASS_EMBEDDINGS_ENABLED", "true")
|
|
get_settings.cache_clear()
|
|
monkeypatch.setattr(emb_mod, "_engine", EmbeddingEngine(backend=_FakeEmbedBackend()))
|
|
|
|
# Corpusul sursa = mapping_suggestions (SILVER) -- PRD 5.18 US-005.
|
|
# (Inainte era nomenclator_rar; migrat la mapping_suggestions ca k-NN sa
|
|
# opereze pe exemple reale etichetate, nu pe categorii generice RAR.)
|
|
conn.execute(
|
|
"INSERT OR REPLACE INTO mapping_suggestions "
|
|
"(denumire_normalizata, cod_prestatie, is_nul, source, confidence) VALUES (?, ?, ?, ?, ?)",
|
|
("Schimb ulei", "UL-1", 0, "llm", 0.95),
|
|
)
|
|
conn.execute(
|
|
"INSERT OR REPLACE INTO mapping_suggestions "
|
|
"(denumire_normalizata, cod_prestatie, is_nul, source, confidence) VALUES (?, ?, ?, ?, ?)",
|
|
("Placute frana", "FR-1", 0, "llm", 0.95),
|
|
)
|
|
conn.commit()
|
|
|
|
from app.mapping import ensure_embeddings_corpus, enrich_suggestions
|
|
|
|
ensure_embeddings_corpus(conn)
|
|
assert emb_mod.has_corpus(), "corpusul trebuie indexat cand flagul e activ"
|
|
|
|
# "schimbat uleiul motor" -> vector [1,1,0] -> cel mai apropiat = UL-1 (Schimb ulei).
|
|
result = enrich_suggestions(conn, "schimbat uleiul motor", include_embeddings=True)
|
|
assert result["surse"]["embedding"] == "UL-1", (
|
|
f"embeddings trebuie sa sugereze UL-1, got {result['surse']}"
|
|
)
|
|
|
|
get_settings.cache_clear()
|
|
|
|
|
|
def test_embeddings_flag_off_ramane_noop(conn, monkeypatch):
|
|
"""Cu flagul off (default), ensure_embeddings_corpus e no-op total (nu indexeaza)."""
|
|
import app.embeddings as emb_mod
|
|
from app.embeddings import EmbeddingEngine
|
|
from app.config import get_settings
|
|
|
|
monkeypatch.setenv("AUTOPASS_EMBEDDINGS_ENABLED", "false")
|
|
get_settings.cache_clear()
|
|
# Engine cu backend disponibil, dar flagul off -> NU se indexeaza nimic.
|
|
monkeypatch.setattr(emb_mod, "_engine", EmbeddingEngine(backend=_FakeEmbedBackend()))
|
|
|
|
from app.mapping import ensure_embeddings_corpus
|
|
ensure_embeddings_corpus(conn)
|
|
assert not emb_mod.has_corpus(), "flag off -> corpusul NU trebuie indexat"
|
|
|
|
get_settings.cache_clear()
|
|
|
|
|
|
def test_enrich_silver_nul_ignorat(conn):
|
|
"""SILVER cu is_nul=1 (non-operatie) NU apare ca sugestie."""
|
|
from app.shared_store import seed_suggestions
|
|
from app.mapping import enrich_suggestions
|
|
|
|
seed_suggestions(conn, [
|
|
{"denumire": "ITP CT 12 ABC", "is_nul": True, "source": "llm", "confidence": 0.99},
|
|
])
|
|
conn.commit()
|
|
|
|
result = enrich_suggestions(conn, "ITP CT 12 ABC")
|
|
assert result["sugestie_principala"] is None
|
|
assert result["surse"]["silver"] is None
|
|
|
|
|
|
# --------------------------------------------------------------------------- #
|
|
# pending_unmapped: include sugestie_principala #
|
|
# --------------------------------------------------------------------------- #
|
|
|
|
def test_pending_unmapped_include_sugestie_principala(conn):
|
|
"""pending_unmapped returneaza entries cu sugestie_principala din GOLD/SILVER."""
|
|
from app.shared_store import record_human_validation
|
|
from app.mapping import pending_unmapped
|
|
import json
|
|
|
|
record_human_validation(conn, "Schimb ulei motor", "OE-3")
|
|
conn.commit()
|
|
|
|
# Creeaza un submission needs_mapping cu "Schimb ulei motor"
|
|
conn.execute(
|
|
"INSERT INTO submissions (account_id, status, payload_json, idempotency_key) "
|
|
"VALUES (1, 'needs_mapping', ?, 'key-test-001')",
|
|
(json.dumps({
|
|
"vin": "WVWZZZ1KZAW001111",
|
|
"prestatii": [{"cod_op_service": "OP-ULEI", "denumire": "Schimb ulei motor"}],
|
|
}),),
|
|
)
|
|
conn.commit()
|
|
|
|
pending = pending_unmapped(conn, account_id=1)
|
|
assert len(pending) == 1
|
|
entry = pending[0]
|
|
|
|
# sugestie_principala adaugat de enrich_suggestions (L14-S6)
|
|
assert "sugestie_principala" in entry
|
|
sp = entry["sugestie_principala"]
|
|
assert sp is not None
|
|
assert sp["cod_prestatie"] == "OE-3"
|
|
assert sp["sursa"] == "gold_partajat"
|
|
|
|
|
|
def test_pending_unmapped_fara_surse_sugestie_principala_none(conn, monkeypatch):
|
|
"""pending_unmapped -> sugestie_principala = None cand nu exista nicio sursa.
|
|
|
|
Dezactiveaza embeddings prin poarta reala `has_corpus`=False (gate-ul folosit de
|
|
enrich_suggestions dupa wiring), independent de starea singleton-ului global lasata
|
|
de alte teste (izolare de ordine).
|
|
"""
|
|
import app.embeddings as emb_mod
|
|
monkeypatch.setattr(emb_mod, "has_corpus", lambda: False)
|
|
monkeypatch.setattr(emb_mod, "is_available", lambda: False)
|
|
|
|
from app.mapping import pending_unmapped
|
|
import json
|
|
|
|
conn.execute(
|
|
"INSERT INTO submissions (account_id, status, payload_json, idempotency_key) "
|
|
"VALUES (1, 'needs_mapping', ?, 'key-test-002')",
|
|
(json.dumps({
|
|
"vin": "WVWZZZ1KZAW002222",
|
|
"prestatii": [{"cod_op_service": "OP-FARA-SURSA", "denumire": "Operatie de nisa"}],
|
|
}),),
|
|
)
|
|
conn.commit()
|
|
|
|
pending = pending_unmapped(conn, account_id=1)
|
|
assert len(pending) == 1
|
|
entry = pending[0]
|
|
|
|
assert "sugestie_principala" in entry
|
|
assert entry["sugestie_principala"] is None
|
|
|
|
|
|
# --------------------------------------------------------------------------- #
|
|
# record_human_validation apelat la confirmare umana #
|
|
# --------------------------------------------------------------------------- #
|
|
|
|
def test_record_human_validation_la_post_mapari(env, client):
|
|
"""POST /mapari (tab Mapari) -> record_human_validation scrie in shared_mappings.
|
|
|
|
Testul verifica ca GOLD partajat se populeaza automat la confirmarea umana
|
|
din interfata de mapari.
|
|
"""
|
|
from app.db import get_connection
|
|
import json
|
|
|
|
# Creeaza un submission needs_mapping
|
|
conn_setup = get_connection()
|
|
try:
|
|
conn_setup.executemany(
|
|
"INSERT OR IGNORE INTO nomenclator_rar (cod_prestatie, nume_prestatie) VALUES (?, ?)",
|
|
[("OE-3", "REVIZIE PERIODICA"), ("OE-1", "REPARATIE")],
|
|
)
|
|
conn_setup.execute(
|
|
"INSERT INTO submissions (account_id, status, payload_json, idempotency_key) "
|
|
"VALUES (1, 'needs_mapping', ?, 'key-hv-001')",
|
|
(json.dumps({
|
|
"vin": "WVWZZZ1KZAW003333",
|
|
"prestatii": [{"cod_op_service": "OP-REV", "denumire": "Revizie anuala"}],
|
|
}),),
|
|
)
|
|
conn_setup.commit()
|
|
finally:
|
|
conn_setup.close()
|
|
|
|
# POST /mapari cu denumire (L14-S6: form include denumire hidden)
|
|
resp = client.post(
|
|
"/mapari",
|
|
data={
|
|
"cod_op_service": "OP-REV",
|
|
"cod_prestatie": "OE-3",
|
|
"denumire": "Revizie anuala",
|
|
"csrf_token": "",
|
|
},
|
|
)
|
|
assert resp.status_code == 200, resp.text
|
|
|
|
# Verifica ca shared_mappings contine intrarea
|
|
conn_check = get_connection()
|
|
try:
|
|
from app.shared_store import lookup_shared_gold
|
|
row = lookup_shared_gold(conn_check, "Revizie anuala")
|
|
assert row is not None, "record_human_validation nu a scris in shared_mappings"
|
|
assert row["cod_prestatie"] == "OE-3"
|
|
finally:
|
|
conn_check.close()
|
|
|
|
|
|
def test_record_human_validation_la_mapeaza_inline(env, client):
|
|
"""POST /trimitere/{id}/mapeaza -> record_human_validation scrie in shared_mappings.
|
|
|
|
Testul verifica ca GOLD partajat se populeaza la maparea inline din panoul de detaliu.
|
|
"""
|
|
from app.db import get_connection
|
|
import json
|
|
|
|
# Setup submission needs_mapping
|
|
conn_setup = get_connection()
|
|
try:
|
|
conn_setup.executemany(
|
|
"INSERT OR IGNORE INTO nomenclator_rar (cod_prestatie, nume_prestatie) VALUES (?, ?)",
|
|
[("OE-1", "REPARATIE"), ("OE-3", "REVIZIE")],
|
|
)
|
|
conn_setup.execute(
|
|
"INSERT INTO submissions (account_id, status, payload_json, idempotency_key) "
|
|
"VALUES (1, 'needs_mapping', ?, 'key-inline-001')",
|
|
(json.dumps({
|
|
"vin": "WVWZZZ1KZAW004444",
|
|
"data_prestatie": "2026-06-15",
|
|
"odometru_final": 100000,
|
|
"prestatii": [{"cod_op_service": "OP-REP", "denumire": "Reparatie chiulasa"}],
|
|
}),),
|
|
)
|
|
conn_setup.commit()
|
|
|
|
# Preia ID-ul submission-ului
|
|
sid = conn_setup.execute("SELECT id FROM submissions WHERE idempotency_key='key-inline-001'").fetchone()["id"]
|
|
finally:
|
|
conn_setup.close()
|
|
|
|
resp = client.post(
|
|
f"/trimitere/{sid}/mapeaza",
|
|
data={
|
|
"cod_op_service": "OP-REP",
|
|
"cod_prestatie": "OE-1",
|
|
"csrf_token": "",
|
|
},
|
|
)
|
|
assert resp.status_code == 200, resp.text
|
|
|
|
# Verifica shared_mappings
|
|
conn_check = get_connection()
|
|
try:
|
|
from app.shared_store import lookup_shared_gold
|
|
row = lookup_shared_gold(conn_check, "Reparatie chiulasa")
|
|
assert row is not None, "record_human_validation nu a scris in shared_mappings pentru mapeaza inline"
|
|
assert row["cod_prestatie"] == "OE-1"
|
|
finally:
|
|
conn_check.close()
|
|
|
|
|
|
def test_mapare_salvata_fara_denumire_nu_polueaza_gold(env, client):
|
|
"""Bug fix (code-review 5.15): editarea unei mapari salvate FARA denumire NU scrie
|
|
o intrare bogus in GOLD partajat (cheiata pe cod_op_service in loc de denumire umana).
|
|
|
|
Formularul din _mapari.html nu trimite denumire; vechiul fallback `denumire or
|
|
cod_op_service` scria shared_mappings cheiat pe cod_op_service -> lookup_shared_gold
|
|
(pe denumirea umana) nu il potrivea niciodata -> poluare. Fix: _record_gold_validation
|
|
sare scrierea cand denumire lipseste sau == cod_op_service.
|
|
"""
|
|
from app.db import get_connection
|
|
|
|
conn_setup = get_connection()
|
|
try:
|
|
conn_setup.execute(
|
|
"INSERT OR IGNORE INTO nomenclator_rar (cod_prestatie, nume_prestatie) VALUES (?, ?)",
|
|
("OE-1", "REPARATIE"),
|
|
)
|
|
conn_setup.commit()
|
|
finally:
|
|
conn_setup.close()
|
|
|
|
# Editare mapare salvata FARA denumire (ca formularul real din _mapari.html).
|
|
resp = client.post(
|
|
"/mapari/salvate",
|
|
data={
|
|
"cod_op_service": "OP-SALV",
|
|
"cod_prestatie": "OE-1",
|
|
"csrf_token": "",
|
|
},
|
|
)
|
|
assert resp.status_code == 200, resp.text
|
|
|
|
conn_check = get_connection()
|
|
try:
|
|
from app.shared_store import lookup_shared_gold
|
|
# NICIO intrare bogus cheiata pe cod_op_service.
|
|
assert lookup_shared_gold(conn_check, "OP-SALV") is None, (
|
|
"GOLD partajat poluat cu cod_op_service ca si cheie (denumire lipsa)"
|
|
)
|
|
finally:
|
|
conn_check.close()
|
|
|
|
|
|
# --------------------------------------------------------------------------- #
|
|
# Separare structurala #13 (redundant cu test_shared_store dar explicit L14) #
|
|
# --------------------------------------------------------------------------- #
|
|
|
|
def test_separare_silver_din_resolve_prestatii():
|
|
"""#13: resolve_prestatii nu citeste mapping_suggestions (SILVER)."""
|
|
from app.mapping import resolve_prestatii
|
|
|
|
# Apelam fara conn (pur) — SILVER nu e parametru si nu e accesat
|
|
resolved, unmapped = resolve_prestatii(
|
|
[{"cod_op_service": "OP-TEST", "denumire": "Test silver"}],
|
|
{}, # mapping gol
|
|
)
|
|
assert resolved[0]["cod_prestatie"] is None
|
|
assert len(unmapped) == 1
|
|
|
|
|
|
def test_separare_shared_gold_din_resolve_prestatii():
|
|
"""#13: resolve_prestatii nu citeste shared_mappings (GOLD partajat)."""
|
|
from app.mapping import resolve_prestatii
|
|
|
|
resolved, unmapped = resolve_prestatii(
|
|
[{"cod_op_service": "OP-TEST2", "denumire": "Test gold partajat"}],
|
|
{}, # mapping gol
|
|
)
|
|
assert resolved[0]["cod_prestatie"] is None
|
|
assert len(unmapped) == 1
|