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>
579 lines
21 KiB
Python
579 lines
21 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()))
|
|
|
|
# Nomenclatorul (din fixtura conn) are OE-1..OE-4; adaug coduri cu denumiri keyword.
|
|
conn.execute(
|
|
"INSERT OR REPLACE INTO nomenclator_rar (cod_prestatie, nume_prestatie) VALUES (?, ?)",
|
|
("UL-1", "Schimb ulei"),
|
|
)
|
|
conn.execute(
|
|
"INSERT OR REPLACE INTO nomenclator_rar (cod_prestatie, nume_prestatie) VALUES (?, ?)",
|
|
("FR-1", "Placute frana"),
|
|
)
|
|
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
|