refactor: comentarii strict functionale, fara referinte PRD/stories
Curatare globala a comentariilor si docstring-urilor (app, tools, teste, scripturi): eliminate referintele la PRD-uri, US-xxx, task-uri istorice si review-uri; pastrata doar informatia functionala, formulata scurt. Regula adaugata in CLAUDE.md (sectiunea Stil). Fara modificari de cod sau comportament. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
"""Teste API import Treapta 2 — POST /v1/import, preview, commit, export-failed.
|
||||
"""Teste API import — POST /v1/import, preview, commit, export-failed.
|
||||
|
||||
Acopera:
|
||||
- #11 U1+T4: upload + staging + mapare coloane semnatura/drift/fuzzy
|
||||
- #12 T2+T11: preview 6 stari + already_sent batch lookup + intra-batch collision
|
||||
- #13 T5+T12: gate HARD confirmare + atestare valori + commit ON CONFLICT (TOCTOU)
|
||||
- #14 T8: export randuri esuate CSV
|
||||
- upload + staging + mapare coloane semnatura/drift/fuzzy
|
||||
- preview 6 stari + already_sent batch lookup + intra-batch collision
|
||||
- gate HARD confirmare + atestare valori + commit ON CONFLICT (TOCTOU)
|
||||
- export randuri esuate CSV
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -91,15 +91,11 @@ def _default_column_mapping() -> dict:
|
||||
|
||||
def _setup_nomenclator(client: TestClient) -> None:
|
||||
"""Seed nomenclator cu un cod de prestatie pentru teste."""
|
||||
# Folosim POST /v1/prezentari pentru a forta seed-ul nomenclatorului
|
||||
# care are loc in init_db -> seed_nomenclator_if_empty
|
||||
pass # seed-ul se face automat in init_db
|
||||
|
||||
|
||||
def _seed_operation_mapping(client: TestClient, cod_op: str = "Revizie", cod_prest: str = "OE-1") -> None:
|
||||
"""Salveaza o mapare de operatii pentru teste."""
|
||||
# Adauga mai intai in nomenclator daca nu exista (prin POST prezentare care creeaza cod)
|
||||
# De fapt, cod OE-1 e in nomenclatorul seed
|
||||
client.post("/v1/mapari", json={
|
||||
"cod_op_service": cod_op,
|
||||
"cod_prestatie": cod_prest,
|
||||
@@ -108,7 +104,7 @@ def _seed_operation_mapping(client: TestClient, cod_op: str = "Revizie", cod_pre
|
||||
|
||||
|
||||
# =========================================================================== #
|
||||
# #11 — Upload + staging (U1+T4) #
|
||||
# Upload + staging #
|
||||
# =========================================================================== #
|
||||
|
||||
class TestUploadStaging:
|
||||
@@ -144,7 +140,7 @@ class TestUploadStaging:
|
||||
assert r.status_code == 422
|
||||
|
||||
def test_issue5a_raw_json_criptat(self, client):
|
||||
"""Issue 5a: raw_json din import_rows trebuie sa fie criptat (ciphertext la rest)."""
|
||||
"""raw_json din import_rows trebuie sa fie criptat (ciphertext la rest)."""
|
||||
data = _make_xlsx([_HEADER, _ROW_OK])
|
||||
r = _upload_file(client, data, "test.xlsx")
|
||||
assert r.status_code == 200
|
||||
@@ -173,7 +169,7 @@ class TestUploadStaging:
|
||||
conn.close()
|
||||
|
||||
def test_issue5b_fuzzy_coloane_refoloseste_normalize_for_match(self, client):
|
||||
"""Issue 5b: fuzzy_suggestions din raspuns foloseste normalize_for_match (fara duplicat)."""
|
||||
"""fuzzy_suggestions din raspuns foloseste normalize_for_match (fara duplicat)."""
|
||||
data = _make_xlsx([_HEADER, _ROW_OK])
|
||||
r = _upload_file(client, data, "test.xlsx")
|
||||
assert r.status_code == 200
|
||||
@@ -190,7 +186,7 @@ class TestUploadStaging:
|
||||
assert "odometru_final" in camps, f"'odometru_final' trebuie in sugestii"
|
||||
|
||||
def test_drift_semnatura_coloane(self, client):
|
||||
"""T4/D3: upload 2 cu coloane mutate -> mapping_status='new' (nu aplica orb)."""
|
||||
"""Upload 2 cu coloane mutate -> mapping_status='new' (nu aplica orb)."""
|
||||
# Upload 1 cu header standard
|
||||
data1 = _make_xlsx([_HEADER, _ROW_OK])
|
||||
r1 = _upload_file(client, data1, "test.xlsx")
|
||||
@@ -275,7 +271,7 @@ class TestUploadStaging:
|
||||
assert body["sample_rows"][0]["VIN"] == _ROW_OK2[0]
|
||||
|
||||
def test_purge_after_setat_la_insert(self, client):
|
||||
"""T16: purge_after trebuie setat la insert import_batches."""
|
||||
"""purge_after trebuie setat la insert import_batches."""
|
||||
data = _make_xlsx([_HEADER, _ROW_OK])
|
||||
r = _upload_file(client, data, "test.xlsx")
|
||||
import_id = r.json()["import_id"]
|
||||
@@ -294,7 +290,7 @@ class TestUploadStaging:
|
||||
|
||||
|
||||
# =========================================================================== #
|
||||
# #11 — Mapare coloane (T4) #
|
||||
# Mapare coloane #
|
||||
# =========================================================================== #
|
||||
|
||||
class TestColumnMapping:
|
||||
@@ -349,7 +345,7 @@ class TestColumnMapping:
|
||||
|
||||
|
||||
# =========================================================================== #
|
||||
# #12 — Preview 6 stari (T2 + T11) #
|
||||
# Preview 6 stari #
|
||||
# =========================================================================== #
|
||||
|
||||
class TestPreview:
|
||||
@@ -405,7 +401,7 @@ class TestPreview:
|
||||
assert any(r["resolved_status"] in ("needs_data",) for r in body["rows"])
|
||||
|
||||
def test_preview_already_sent_dupa_submit(self, client):
|
||||
"""Rand deja trimis prin API -> stare already_sent la preview (T2/D5)."""
|
||||
"""Rand deja trimis prin API -> stare already_sent la preview."""
|
||||
# Trimite prin API canalul standard
|
||||
client.post("/v1/prezentari", json={
|
||||
"rar_credentials": {"email": "x@y.ro", "password": "s"},
|
||||
@@ -430,7 +426,7 @@ class TestPreview:
|
||||
assert "already_sent" in statuses, f"Asteptat 'already_sent', primit: {statuses}"
|
||||
|
||||
def test_preview_duplicate_in_file(self, client):
|
||||
"""T11/OV-3: 2 randuri identice in ACELASI fisier -> duplicate_in_file."""
|
||||
"""2 randuri identice in ACELASI fisier -> duplicate_in_file."""
|
||||
# Acelasi rand de doua ori
|
||||
rows = [_HEADER, _ROW_OK, _ROW_OK] # duplicat exact
|
||||
import_id = self._upload_and_map(client, rows=rows)
|
||||
@@ -444,7 +440,7 @@ class TestPreview:
|
||||
f"Asteptat 'duplicate_in_file', primit: {statuses}"
|
||||
|
||||
def test_preview_already_sent_batch_lookup_nu_n_plus_1(self, client):
|
||||
"""Eng#5: already_sent lookup BATCH (nu N+1) — ≤7 interogari pentru 5 randuri."""
|
||||
"""already_sent lookup BATCH (nu N+1) — ≤7 interogari pentru 5 randuri."""
|
||||
# Cream 5 randuri distincte
|
||||
rows_data = [_HEADER]
|
||||
for i in range(5):
|
||||
@@ -492,7 +488,7 @@ class TestPreview:
|
||||
|
||||
|
||||
# =========================================================================== #
|
||||
# #13 — Commit gate HARD + atestare + TOCTOU (T5 + T12) #
|
||||
# Commit gate HARD + atestare + TOCTOU #
|
||||
# =========================================================================== #
|
||||
|
||||
class TestCommit:
|
||||
@@ -528,7 +524,7 @@ class TestCommit:
|
||||
assert body["enqueued"] == n_ok
|
||||
|
||||
def test_commit_cu_n_gresit_reject(self, client):
|
||||
"""T5/D3: commit cu N gresit -> 422, nu enqueue."""
|
||||
"""Commit cu N gresit -> 422, nu enqueue."""
|
||||
import_id, preview = self._upload_preview_ok(client)
|
||||
n_ok = preview["summary"].get("ok", 0)
|
||||
|
||||
@@ -543,7 +539,7 @@ class TestCommit:
|
||||
f"Eroare neasteptata: {detail}"
|
||||
|
||||
def test_commit_log_atestare(self, client):
|
||||
"""T12/Voce#9: commit scrie import_attestations cu rows_hash + n_confirmed."""
|
||||
"""Commit scrie import_attestations cu rows_hash + n_confirmed."""
|
||||
import_id, preview = self._upload_preview_ok(client)
|
||||
n_ok = preview["summary"].get("ok", 0)
|
||||
|
||||
@@ -574,7 +570,7 @@ class TestCommit:
|
||||
conn.close()
|
||||
|
||||
def test_commit_batch_id_setat_pe_submission(self, client):
|
||||
"""T7: submission creata la commit trebuie sa aiba batch_id + row_index setate."""
|
||||
"""Submission creata la commit trebuie sa aiba batch_id + row_index setate."""
|
||||
import_id, preview = self._upload_preview_ok(client)
|
||||
n_ok = preview["summary"].get("ok", 0)
|
||||
|
||||
@@ -602,7 +598,7 @@ class TestCommit:
|
||||
conn.close()
|
||||
|
||||
def test_commit_toctou_cheie_inserata_concurent(self, client):
|
||||
"""Issue 1 (TOCTOU): cheie inserata de canal concurent -> reclasificata already_sent.
|
||||
"""Cheie inserata de canal concurent (TOCTOU) -> reclasificata already_sent.
|
||||
|
||||
Simulam TOCTOU inserand cheia direct in submissions inainte de commit.
|
||||
"""
|
||||
@@ -635,7 +631,6 @@ class TestCommit:
|
||||
"reviewed_rows": [],
|
||||
})
|
||||
# Poate fi 200 cu toctou_collisions sau 422 cu informatii clare
|
||||
# Conform planului Issue 1: reclasificat already_sent, nu rollback
|
||||
# Dar n_enqueued va fi 0 daca toate colideaza
|
||||
if rc.status_code == 200:
|
||||
body = rc.json()
|
||||
@@ -644,7 +639,7 @@ class TestCommit:
|
||||
# (dupa reclasificare, n_total_ok scade)
|
||||
|
||||
def test_commit_needs_review_nebifat_exclus_din_n(self, client):
|
||||
"""Voce#1: rand needs_review nebifat explicit -> NU intra in N, NU se enqueued."""
|
||||
"""Rand needs_review nebifat explicit -> NU intra in N, NU se enqueued."""
|
||||
# Rand cu VIN numeric (coercion -> needs_review)
|
||||
import datetime as dt
|
||||
wb = openpyxl.Workbook()
|
||||
@@ -704,7 +699,7 @@ class TestCommit:
|
||||
assert r2.status_code == 409
|
||||
|
||||
def test_atestare_purge_after_setat_pe_submission(self, client):
|
||||
"""T16: submissions create la commit trebuie sa aiba purge_after setat."""
|
||||
"""Submissions create la commit trebuie sa aiba purge_after setat."""
|
||||
import_id, preview = self._upload_preview_ok(client)
|
||||
n_ok = preview["summary"].get("ok", 0)
|
||||
|
||||
@@ -732,7 +727,7 @@ class TestCommit:
|
||||
|
||||
|
||||
# =========================================================================== #
|
||||
# #14 — Export randuri esuate CSV (T8) #
|
||||
# Export randuri esuate CSV #
|
||||
# =========================================================================== #
|
||||
|
||||
class TestExportFailed:
|
||||
@@ -806,12 +801,12 @@ class TestExportFailed:
|
||||
|
||||
|
||||
# =========================================================================== #
|
||||
# Regresie: reconcile.py ramane op-blind (OV-3) #
|
||||
# Regresie: reconcile.py ramane op-blind #
|
||||
# =========================================================================== #
|
||||
|
||||
class TestReconcileRegresie:
|
||||
def test_match_finalizata_ramane_op_blind(self):
|
||||
"""OV-3: reconcile.py trebuie sa ramana op-blind (nu editat de import).
|
||||
"""reconcile.py trebuie sa ramana op-blind (nu editat de import).
|
||||
|
||||
Importam reconcile si verificam ca nu s-au adaugat parametri de operatie.
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user