feat(5.20): US-004/005/006/009 ingestie+API+worker+import pe mediu RAR
US-004: rezolva_rar_env (cerere>default cont>ancora globala) + MediuIndisponibil + cod RAR_MEDIU_INDISPONIBIL. US-005: camp rar_env pe POST /v1/prezentari + /valideaza (Literal), echo in SubmissionResult/ValidareResult/GET, build_key + INSERT env-aware. US-006: AccountSessions re-cheiat (account_id, rar_env); RarClient base_url per env; creds din slotul env; purge + recover_orphans scoped pe env (E1/1a, 1b/E6); claim_one propaga rar_env (1c/E8); keepalive pe ancora globala (M2). US-009: selector mediu la import (>=2 medii), eticheta la 1, banner la 0; commit seteaza rar_env pe submissions. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
226
tests/test_api_rar_target.py
Normal file
226
tests/test_api_rar_target.py
Normal file
@@ -0,0 +1,226 @@
|
||||
"""Teste US-005 — camp rar_env pe POST /v1/prezentari si /valideaza.
|
||||
|
||||
Acopera: default din cont, tinta explicita, respingere tinta indisponibila,
|
||||
echo GET, valoare invalida (422 Pydantic), echo dry-run valideaza.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def env(monkeypatch):
|
||||
"""DB temporara izolata per test + settings reincarcate."""
|
||||
tmp = tempfile.mkdtemp()
|
||||
monkeypatch.setenv("AUTOPASS_DB_PATH", os.path.join(tmp, "t.db"))
|
||||
from app.config import get_settings
|
||||
get_settings.cache_clear()
|
||||
yield monkeypatch
|
||||
get_settings.cache_clear()
|
||||
|
||||
|
||||
def _client():
|
||||
from app.main import app
|
||||
return TestClient(app)
|
||||
|
||||
|
||||
def _body(rar_env=None, **over):
|
||||
prez = {
|
||||
"vin": "WVWZZZ1KZAW000123",
|
||||
"nr_inmatriculare": "B999TST",
|
||||
"data_prestatie": "2026-06-15",
|
||||
"odometru_final": "123456",
|
||||
"prestatii": [{"cod_prestatie": "OE-1"}],
|
||||
}
|
||||
prez.update(over)
|
||||
body = {"rar_credentials": {"email": "x@y.ro", "password": "s"}, "prezentari": [prez]}
|
||||
if rar_env is not None:
|
||||
body["rar_env"] = rar_env
|
||||
return body
|
||||
|
||||
|
||||
def _setup_prod_only(conn):
|
||||
"""Configureaza contul 1 ca prod-only (rar_prod_enabled=1, creds prod, default prod)."""
|
||||
from app.crypto import encrypt_creds
|
||||
enc = encrypt_creds({"email": "prod@rar.ro", "password": "paraprod"})
|
||||
conn.execute(
|
||||
"UPDATE accounts SET rar_prod_enabled=1, rar_creds_prod_enc=?, "
|
||||
"rar_test_enabled=0, rar_creds_test_enc=NULL, rar_env_default='prod' WHERE id=1",
|
||||
(enc,),
|
||||
)
|
||||
conn.commit()
|
||||
|
||||
|
||||
def _setup_dual_env(conn):
|
||||
"""Configureaza contul 1 cu ambele medii disponibile, default test."""
|
||||
from app.crypto import encrypt_creds
|
||||
enc_test = encrypt_creds({"email": "test@rar.ro", "password": "paratest"})
|
||||
enc_prod = encrypt_creds({"email": "prod@rar.ro", "password": "paraprod"})
|
||||
conn.execute(
|
||||
"UPDATE accounts SET rar_prod_enabled=1, rar_creds_prod_enc=?, "
|
||||
"rar_test_enabled=1, rar_creds_test_enc=?, rar_env_default='test' WHERE id=1",
|
||||
(enc_prod, enc_test),
|
||||
)
|
||||
conn.commit()
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# test_default_din_cont_cand_lipseste #
|
||||
# --------------------------------------------------------------------------- #
|
||||
|
||||
def test_default_din_cont_cand_lipseste(env):
|
||||
"""Cont prod-only, POST fara rar_env -> submission rar_env='prod'."""
|
||||
with _client() as c:
|
||||
from app.db import get_connection
|
||||
conn = get_connection()
|
||||
try:
|
||||
_setup_prod_only(conn)
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
r = c.post("/v1/prezentari", json=_body())
|
||||
assert r.status_code == 200, r.text
|
||||
res = r.json()["results"][0]
|
||||
assert res["status"] == "queued"
|
||||
assert res["rar_env"] == "prod"
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# test_target_explicit #
|
||||
# --------------------------------------------------------------------------- #
|
||||
|
||||
def test_target_explicit(env):
|
||||
"""Cont cu ambele medii, POST cu rar_env='test' -> submission rar_env='test'."""
|
||||
with _client() as c:
|
||||
from app.db import get_connection
|
||||
conn = get_connection()
|
||||
try:
|
||||
_setup_dual_env(conn)
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
r = c.post("/v1/prezentari", json=_body(rar_env="test"))
|
||||
assert r.status_code == 200, r.text
|
||||
res = r.json()["results"][0]
|
||||
assert res["status"] == "queued"
|
||||
assert res["rar_env"] == "test"
|
||||
|
||||
# Aceeasi prezentare cu rar_env='prod' -> cheie diferita (env-aware) -> alt submission
|
||||
r2 = c.post("/v1/prezentari", json=_body(rar_env="prod"))
|
||||
assert r2.status_code == 200, r2.text
|
||||
res2 = r2.json()["results"][0]
|
||||
assert res2["rar_env"] == "prod"
|
||||
# Nu e dedup (env diferit -> cheie diferita)
|
||||
assert res2["submission_id"] != res["submission_id"]
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# test_target_indisponibil_respins #
|
||||
# --------------------------------------------------------------------------- #
|
||||
|
||||
def test_target_indisponibil_respins(env):
|
||||
"""Cont prod-only, POST cu rar_env='test' -> 422 RAR_MEDIU_INDISPONIBIL, fara enqueue."""
|
||||
with _client() as c:
|
||||
from app.db import get_connection
|
||||
conn = get_connection()
|
||||
try:
|
||||
_setup_prod_only(conn)
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
r = c.post("/v1/prezentari", json=_body(rar_env="test"))
|
||||
assert r.status_code == 422, r.text
|
||||
detail = r.json()["detail"]
|
||||
assert detail["cod"] == "RAR_MEDIU_INDISPONIBIL"
|
||||
# Cauza contine mediul cerut si lista disponibilelor
|
||||
assert "test" in detail["cauza"]
|
||||
assert "prod" in detail["cauza"]
|
||||
|
||||
# Verifica ca nu s-a facut enqueue
|
||||
lista = c.get("/v1/prezentari").json()["submissions"]
|
||||
assert lista == []
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# test_get_ecou_rar_env #
|
||||
# --------------------------------------------------------------------------- #
|
||||
|
||||
def test_get_ecou_rar_env(env):
|
||||
"""Dupa enqueue, GET /v1/prezentari/{id} si lista contin rar_env."""
|
||||
with _client() as c:
|
||||
from app.db import get_connection
|
||||
conn = get_connection()
|
||||
try:
|
||||
_setup_prod_only(conn)
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
# Enqueue pe prod (default contului prod-only)
|
||||
r = c.post("/v1/prezentari", json=_body())
|
||||
assert r.status_code == 200, r.text
|
||||
sub_id = r.json()["results"][0]["submission_id"]
|
||||
assert sub_id is not None
|
||||
|
||||
# GET detaliu
|
||||
r_det = c.get(f"/v1/prezentari/{sub_id}")
|
||||
assert r_det.status_code == 200, r_det.text
|
||||
assert r_det.json()["rar_env"] == "prod"
|
||||
|
||||
# GET lista
|
||||
r_lst = c.get("/v1/prezentari")
|
||||
assert r_lst.status_code == 200, r_lst.text
|
||||
sub_in_lista = next(s for s in r_lst.json()["submissions"] if s["id"] == sub_id)
|
||||
assert sub_in_lista["rar_env"] == "prod"
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# test_valoare_invalida_422 #
|
||||
# --------------------------------------------------------------------------- #
|
||||
|
||||
def test_valoare_invalida_422(env):
|
||||
"""POST cu rar_env='staging' -> 422 din Pydantic Literal, fara echo de input."""
|
||||
with _client() as c:
|
||||
body = _body(rar_env="staging")
|
||||
r = c.post("/v1/prezentari", json=body)
|
||||
assert r.status_code == 422, r.text
|
||||
# Handler-ul global sterge 'input'/'ctx' — valoarea invalida nu se ecou-ieste.
|
||||
assert "staging" not in r.text
|
||||
# Fara enqueue
|
||||
lista = c.get("/v1/prezentari").json()["submissions"]
|
||||
assert lista == []
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# test_valideaza_ecou_rar_env #
|
||||
# --------------------------------------------------------------------------- #
|
||||
|
||||
def test_valideaza_ecou_rar_env(env):
|
||||
"""POST /valideaza (dry-run) ecou-ieste rar_env rezolvat in ValidareResult."""
|
||||
with _client() as c:
|
||||
from app.db import get_connection
|
||||
conn = get_connection()
|
||||
try:
|
||||
_setup_prod_only(conn)
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
# Dry-run fara rar_env -> default cont = prod
|
||||
r = c.post("/v1/prezentari/valideaza", json=_body())
|
||||
assert r.status_code == 200, r.text
|
||||
res = r.json()["results"][0]
|
||||
assert res["rar_env"] == "prod"
|
||||
|
||||
# Dry-run cu rar_env='prod' explicit
|
||||
r2 = c.post("/v1/prezentari/valideaza", json=_body(rar_env="prod"))
|
||||
assert r2.status_code == 200, r2.text
|
||||
assert r2.json()["results"][0]["rar_env"] == "prod"
|
||||
|
||||
# Dry-run tinta indisponibila -> 422, fara echo sensibil
|
||||
r3 = c.post("/v1/prezentari/valideaza", json=_body(rar_env="test"))
|
||||
assert r3.status_code == 422, r3.text
|
||||
assert r3.json()["detail"]["cod"] == "RAR_MEDIU_INDISPONIBIL"
|
||||
@@ -157,7 +157,7 @@ class FakeRarClient:
|
||||
|
||||
made: list = []
|
||||
|
||||
def __init__(self, settings=None, login_exc=None):
|
||||
def __init__(self, settings=None, *, base_url=None, login_exc=None):
|
||||
self.closed = False
|
||||
self.login_calls = 0
|
||||
self._login_exc = login_exc
|
||||
@@ -234,7 +234,7 @@ def test_get_token_bad_creds_raises(env, monkeypatch):
|
||||
import app.worker.__main__ as w
|
||||
from app.db import get_connection
|
||||
|
||||
def _factory(settings=None):
|
||||
def _factory(settings=None, **kwargs):
|
||||
return FakeRarClient(settings, login_exc=RarAuthError("Credentiale RAR invalide", status_code=401))
|
||||
|
||||
monkeypatch.setattr(w, "RarClient", _factory)
|
||||
|
||||
@@ -511,7 +511,7 @@ class TestE2EMixedQueue:
|
||||
|
||||
# 4. Worker cu MockRar injectat prin AccountSessions (simulam bucla worker)
|
||||
mock_rar = MockRar(id_prezentare=66001, login_token="tok-mock")
|
||||
monkeypatch.setattr(w, "RarClient", lambda settings=None: mock_rar)
|
||||
monkeypatch.setattr(w, "RarClient", lambda settings=None, **kw: mock_rar)
|
||||
|
||||
sessions = w.AccountSessions(settings)
|
||||
conn = get_connection()
|
||||
@@ -595,7 +595,7 @@ class TestE2EMixedQueue:
|
||||
conn.close()
|
||||
|
||||
mock_rar = MockRar(id_prezentare=77777)
|
||||
monkeypatch.setattr(w, "RarClient", lambda settings=None: mock_rar)
|
||||
monkeypatch.setattr(w, "RarClient", lambda settings=None, **kw: mock_rar)
|
||||
|
||||
sessions = w.AccountSessions(settings)
|
||||
conn = get_connection()
|
||||
|
||||
359
tests/test_import_rar_env.py
Normal file
359
tests/test_import_rar_env.py
Normal file
@@ -0,0 +1,359 @@
|
||||
"""Teste US-009 (PRD 5.20) — Import web: selector mediu RAR conditionat de disponibilitate.
|
||||
|
||||
Verifica:
|
||||
- La 0 medii: banner avertisment non-blocant (upload functioneaza, commit foloseste ancora globala).
|
||||
- La 1 mediu: eticheta statica, fara selector; submissions primesc acel mediu.
|
||||
- La 2 medii: selector vizibil pre-bifat pe default-ul contului.
|
||||
- La commit: toate submission-urile lotului primesc rar_env ales (sau fallback ancora globala).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import csv
|
||||
import io
|
||||
import os
|
||||
import re
|
||||
import tempfile
|
||||
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Fixture client cu DB izolat #
|
||||
# --------------------------------------------------------------------------- #
|
||||
|
||||
@pytest.fixture()
|
||||
def client(monkeypatch):
|
||||
tmp = tempfile.mkdtemp()
|
||||
monkeypatch.setenv("AUTOPASS_DB_PATH", os.path.join(tmp, "rar_env_test.db"))
|
||||
monkeypatch.setenv("AUTOPASS_WEB_AUTH_REQUIRED", "false")
|
||||
monkeypatch.setenv("AUTOPASS_RAR_ENV", "test") # ancora globala = test
|
||||
from app.config import get_settings
|
||||
get_settings.cache_clear()
|
||||
from app.crypto import reset_cache
|
||||
reset_cache()
|
||||
from app.main import app
|
||||
with TestClient(app) as c:
|
||||
yield c
|
||||
get_settings.cache_clear()
|
||||
reset_cache()
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Utilitare #
|
||||
# --------------------------------------------------------------------------- #
|
||||
|
||||
def _csv_bytes(rows: list[dict], sep: str = ";") -> bytes:
|
||||
buf = io.StringIO()
|
||||
writer = csv.DictWriter(buf, fieldnames=list(rows[0].keys()), delimiter=sep)
|
||||
writer.writeheader()
|
||||
writer.writerows(rows)
|
||||
return buf.getvalue().encode("utf-8")
|
||||
|
||||
|
||||
def _seed_nomenclator_si_mapare(client: TestClient) -> None:
|
||||
"""Semeaza nomenclatorul si o mapare pentru randuri ok."""
|
||||
from app.db import get_connection
|
||||
conn = get_connection()
|
||||
try:
|
||||
conn.execute(
|
||||
"INSERT OR REPLACE INTO nomenclator_rar (cod_prestatie, nume_prestatie) VALUES (?,?)",
|
||||
("R-FRANE", "Reparatie frane"),
|
||||
)
|
||||
conn.execute(
|
||||
"INSERT OR IGNORE INTO operations_mapping "
|
||||
"(account_id, cod_op_service, cod_prestatie, auto_send) VALUES (1,?,?,1)",
|
||||
("OP-FRANE", "R-FRANE"),
|
||||
)
|
||||
conn.commit()
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def _configureaza_un_mediu(client: TestClient, env: str = "test") -> None:
|
||||
"""Activeaza un singur mediu RAR pe contul 1 (simulate creds disponibile)."""
|
||||
from app.db import get_connection
|
||||
from app.crypto import encrypt_creds
|
||||
conn = get_connection()
|
||||
try:
|
||||
fake_creds = encrypt_creds({"email": "test@rar.ro", "password": "pass"})
|
||||
if env == "test":
|
||||
conn.execute(
|
||||
"UPDATE accounts SET rar_test_enabled=1, rar_creds_test_enc=?, "
|
||||
"rar_prod_enabled=0, rar_creds_prod_enc=NULL, rar_env_default='test' WHERE id=1",
|
||||
(fake_creds,),
|
||||
)
|
||||
else:
|
||||
conn.execute(
|
||||
"UPDATE accounts SET rar_prod_enabled=1, rar_creds_prod_enc=?, "
|
||||
"rar_test_enabled=0, rar_creds_test_enc=NULL, rar_env_default='prod' WHERE id=1",
|
||||
(fake_creds,),
|
||||
)
|
||||
conn.commit()
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def _configureaza_doua_medii(client: TestClient, default_env: str = "test") -> None:
|
||||
"""Activeaza ambele medii RAR pe contul 1."""
|
||||
from app.db import get_connection
|
||||
from app.crypto import encrypt_creds
|
||||
conn = get_connection()
|
||||
try:
|
||||
fake_test = encrypt_creds({"email": "test@rar.ro", "password": "pass_test"})
|
||||
fake_prod = encrypt_creds({"email": "prod@rar.ro", "password": "pass_prod"})
|
||||
conn.execute(
|
||||
"UPDATE accounts SET "
|
||||
"rar_test_enabled=1, rar_creds_test_enc=?, "
|
||||
"rar_prod_enabled=1, rar_creds_prod_enc=?, "
|
||||
"rar_env_default=? WHERE id=1",
|
||||
(fake_test, fake_prod, default_env),
|
||||
)
|
||||
conn.commit()
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
_ROWS_OK = [
|
||||
{
|
||||
"VIN": "WVWZZZ1KZAW009001",
|
||||
"Nr": "B009TST",
|
||||
"Data": "2026-06-15",
|
||||
"KM": "77000",
|
||||
"Operatie": "OP-FRANE",
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
def _upload_si_mapare(client: TestClient, rows: list[dict]) -> int:
|
||||
"""Upload CSV si seteaza mapare coloane. Intoarce import_id."""
|
||||
data = _csv_bytes(rows)
|
||||
r = client.post(
|
||||
"/_import/upload",
|
||||
files={"file": ("test.csv", io.BytesIO(data), "text/csv")},
|
||||
)
|
||||
assert r.status_code == 200, r.text
|
||||
m = re.search(r"/_import/(\d+)/", r.text)
|
||||
assert m, f"import_id negasit in raspunsul de upload: {r.text[:400]}"
|
||||
iid = int(m.group(1))
|
||||
|
||||
# Seteaza maparea daca nu e deja
|
||||
if f"/_import/{iid}/mapare-coloane" in r.text or "mapare-coloane" in r.text.lower():
|
||||
r2 = client.post(
|
||||
f"/_import/{iid}/mapare-coloane",
|
||||
data={
|
||||
"colname": ["VIN", "Nr", "Data", "KM", "Operatie"],
|
||||
"canon": ["vin", "nr_inmatriculare", "data_prestatie", "odometru_final", "operatie"],
|
||||
"format_data": "YYYY-MM-DD",
|
||||
},
|
||||
)
|
||||
assert r2.status_code == 200, r2.text
|
||||
|
||||
return iid
|
||||
|
||||
|
||||
def _get_preview(client: TestClient, iid: int) -> str:
|
||||
rp = client.get(f"/_import/{iid}/preview")
|
||||
assert rp.status_code == 200, rp.text
|
||||
return rp.text
|
||||
|
||||
|
||||
def _commit(client: TestClient, iid: int, n_ok: int, rar_env: str | None = None) -> object:
|
||||
data = {
|
||||
"csrf_token": "",
|
||||
"n_confirmat": str(n_ok),
|
||||
"confirmed_by": "test@us009.ro",
|
||||
}
|
||||
if rar_env:
|
||||
data["rar_env"] = rar_env
|
||||
return client.post(f"/_import/{iid}/confirma", data=data)
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Tests #
|
||||
# --------------------------------------------------------------------------- #
|
||||
|
||||
def test_selector_ascuns_la_un_mediu(client):
|
||||
"""La 1 mediu disponibil: nu apare selector; apare eticheta statica cu mediul."""
|
||||
_seed_nomenclator_si_mapare(client)
|
||||
_configureaza_un_mediu(client, env="test")
|
||||
|
||||
# GET fragment/import: verifica ca nu exista selector si apare eticheta
|
||||
r = client.get("/_fragments/import")
|
||||
assert r.status_code == 200, r.text
|
||||
html = r.text
|
||||
|
||||
# Eticheta statica "Testare" trebuie sa fie prezenta
|
||||
assert "Testare" in html, "Eticheta mediu 'Testare' lipseste la 1 mediu disponibil"
|
||||
|
||||
# Selectorul nu trebuie sa apara (input cu name=rar_env hidden, dar fara <select>)
|
||||
assert "<select" not in html or 'name="rar_env"' not in html or "rar-env-select" not in html, (
|
||||
"Selector mediu RAR nu trebuie sa apara la 1 mediu disponibil"
|
||||
)
|
||||
|
||||
|
||||
def test_selector_prezent_si_prebifat_la_doua(client):
|
||||
"""La 2 medii disponibile: selectorul apare si e pre-bifat pe default-ul contului."""
|
||||
_seed_nomenclator_si_mapare(client)
|
||||
_configureaza_doua_medii(client, default_env="test")
|
||||
|
||||
r = client.get("/_fragments/import")
|
||||
assert r.status_code == 200, r.text
|
||||
html = r.text
|
||||
|
||||
# Selectorul trebuie sa apara
|
||||
assert "rar-env-select" in html, "Selectorul mediu RAR lipseste la 2 medii disponibile"
|
||||
assert 'name="rar_env"' in html, 'Atribut name="rar_env" lipsa din selector'
|
||||
|
||||
# Default pre-selectat = "test" (default contului)
|
||||
# Optiunea Testare trebuie sa fie selectata
|
||||
assert 'value="test"' in html and "selected" in html, (
|
||||
"Optiunea Testare nu e pre-selectata (default cont = test)"
|
||||
)
|
||||
|
||||
|
||||
def test_banner_avertisment_la_zero_medii(client):
|
||||
"""La 0 medii configurate: apare un banner de avertisment (non-blocant)."""
|
||||
# Contul 1 implicit nu are medii configurate
|
||||
r = client.get("/_fragments/import")
|
||||
assert r.status_code == 200, r.text
|
||||
html = r.text
|
||||
|
||||
# Banner avertisment sau link catre configurare credentiale
|
||||
assert "mediu" in html.lower() or "configureaza" in html.lower() or "credentiale" in html.lower(), (
|
||||
"Bannerul de avertisment pentru 0 medii lipseste din pagina de upload"
|
||||
)
|
||||
|
||||
# Upload-ul NU e blocat: formularul de upload trebuie sa fie prezent
|
||||
assert "upload-form" in html, (
|
||||
"Formularul de upload lipseste — la 0 medii upload-ul nu trebuie blocat"
|
||||
)
|
||||
|
||||
|
||||
def test_commit_seteaza_env_pe_submissions(client):
|
||||
"""La commit: submissions primesc rar_env ales (fallback la ancora globala pt 0 medii)."""
|
||||
_seed_nomenclator_si_mapare(client)
|
||||
# Contul 1 fara medii configurate -> ancora globala = "test"
|
||||
|
||||
iid = _upload_si_mapare(client, _ROWS_OK)
|
||||
preview_html = _get_preview(client, iid)
|
||||
|
||||
m_ok = re.search(r'id="n-confirmat"[^>]*?value="(\d+)"', preview_html)
|
||||
n_ok = int(m_ok.group(1)) if m_ok else 1
|
||||
|
||||
r = _commit(client, iid, n_ok)
|
||||
assert r.status_code == 200, r.text
|
||||
assert any(kw in r.text.lower() for kw in ("coada", "prezenta", "trimiter")), (
|
||||
"Mesajul de succes lipseste din raspunsul de commit"
|
||||
)
|
||||
|
||||
# Verifica ca submission-ul are rar_env setat (fallback "test" via ancora globala)
|
||||
from app.db import get_connection
|
||||
conn = get_connection()
|
||||
try:
|
||||
sub = conn.execute(
|
||||
"SELECT rar_env FROM submissions WHERE account_id=1 ORDER BY id DESC LIMIT 1"
|
||||
).fetchone()
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
assert sub is not None, "Niciun submission gasit dupa commit"
|
||||
assert sub["rar_env"] in ("test", "prod"), f"rar_env invalid: {sub['rar_env']!r}"
|
||||
# Cu AUTOPASS_RAR_ENV=test si 0 medii configurate, expect "test"
|
||||
assert sub["rar_env"] == "test", (
|
||||
f"Expected rar_env='test' (ancora globala) dar primit {sub['rar_env']!r}"
|
||||
)
|
||||
|
||||
|
||||
def test_commit_cu_un_mediu_seteaza_acel_mediu(client):
|
||||
"""La commit cu 1 mediu configurat: submission primeste mediul respectiv."""
|
||||
_seed_nomenclator_si_mapare(client)
|
||||
_configureaza_un_mediu(client, env="test")
|
||||
|
||||
iid = _upload_si_mapare(client, _ROWS_OK)
|
||||
preview_html = _get_preview(client, iid)
|
||||
|
||||
m_ok = re.search(r'id="n-confirmat"[^>]*?value="(\d+)"', preview_html)
|
||||
n_ok = int(m_ok.group(1)) if m_ok else 1
|
||||
|
||||
r = _commit(client, iid, n_ok)
|
||||
assert r.status_code == 200, r.text
|
||||
|
||||
from app.db import get_connection
|
||||
conn = get_connection()
|
||||
try:
|
||||
sub = conn.execute(
|
||||
"SELECT rar_env FROM submissions WHERE account_id=1 ORDER BY id DESC LIMIT 1"
|
||||
).fetchone()
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
assert sub is not None
|
||||
assert sub["rar_env"] == "test", (
|
||||
f"Expected rar_env='test' (singurul mediu disponibil) dar primit {sub['rar_env']!r}"
|
||||
)
|
||||
|
||||
|
||||
def test_commit_cu_doua_medii_respecta_alegerea(client):
|
||||
"""La 2 medii: commit cu rar_env explicit seteaza mediul ales pe submissions."""
|
||||
_seed_nomenclator_si_mapare(client)
|
||||
_configureaza_doua_medii(client, default_env="test")
|
||||
|
||||
iid = _upload_si_mapare(client, _ROWS_OK)
|
||||
preview_html = _get_preview(client, iid)
|
||||
|
||||
m_ok = re.search(r'id="n-confirmat"[^>]*?value="(\d+)"', preview_html)
|
||||
n_ok = int(m_ok.group(1)) if m_ok else 1
|
||||
|
||||
# Commit explicit pe "prod"
|
||||
r = _commit(client, iid, n_ok, rar_env="prod")
|
||||
assert r.status_code == 200, r.text
|
||||
|
||||
from app.db import get_connection
|
||||
conn = get_connection()
|
||||
try:
|
||||
sub = conn.execute(
|
||||
"SELECT rar_env FROM submissions WHERE account_id=1 ORDER BY id DESC LIMIT 1"
|
||||
).fetchone()
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
assert sub is not None
|
||||
assert sub["rar_env"] == "prod", (
|
||||
f"Expected rar_env='prod' (ales explicit) dar primit {sub['rar_env']!r}"
|
||||
)
|
||||
|
||||
|
||||
def test_badge_mediu_in_preview(client):
|
||||
"""Preview-ul afiseaza badge-ul cu mediul tinta (US-009, F9/F10)."""
|
||||
_seed_nomenclator_si_mapare(client)
|
||||
_configureaza_un_mediu(client, env="test")
|
||||
|
||||
iid = _upload_si_mapare(client, _ROWS_OK)
|
||||
|
||||
r = client.get(f"/_import/{iid}/preview")
|
||||
assert r.status_code == 200, r.text
|
||||
html = r.text
|
||||
|
||||
# Badge cu mediul trebuie sa fie prezent in HTML
|
||||
assert "Testare" in html or "PRODUCTIE" in html or "rar_env" in html, (
|
||||
"Badge-ul de mediu RAR lipseste din preview"
|
||||
)
|
||||
|
||||
|
||||
def test_rar_env_in_confirm_form(client):
|
||||
"""Preview-ul contine un field hidden rar_env in formularul de confirmare."""
|
||||
_seed_nomenclator_si_mapare(client)
|
||||
_configureaza_un_mediu(client, env="test")
|
||||
|
||||
iid = _upload_si_mapare(client, _ROWS_OK)
|
||||
|
||||
r = client.get(f"/_import/{iid}/preview")
|
||||
assert r.status_code == 200, r.text
|
||||
html = r.text
|
||||
|
||||
# Formularul de confirmare trebuie sa contina rar_env ca hidden field
|
||||
assert 'name="rar_env"' in html, (
|
||||
"Campul hidden 'rar_env' lipseste din formularul de confirmare preview"
|
||||
)
|
||||
96
tests/test_rar_env_resolve.py
Normal file
96
tests/test_rar_env_resolve.py
Normal file
@@ -0,0 +1,96 @@
|
||||
"""Teste US-004 (PRD 5.20): rezolvare mediu tinta la ingestie + respingere tinte indisponibile.
|
||||
|
||||
Fixtura `conn` urmareste acelasi pattern ca tests/test_accounts.py:
|
||||
monkeypatch AUTOPASS_DB_PATH pe tempdir, cache_clear, init_db, get_connection.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def conn(monkeypatch):
|
||||
tmp = tempfile.mkdtemp()
|
||||
monkeypatch.setenv("AUTOPASS_DB_PATH", os.path.join(tmp, "test_rar_env_resolve.db"))
|
||||
from app.config import get_settings
|
||||
get_settings.cache_clear()
|
||||
from app.db import get_connection, init_db
|
||||
init_db()
|
||||
c = get_connection()
|
||||
yield c
|
||||
c.close()
|
||||
get_settings.cache_clear()
|
||||
|
||||
|
||||
def _seteaza_cont_ambele(conn) -> None:
|
||||
"""Configureaza contul id=1 cu ambele medii disponibile, default = prod."""
|
||||
conn.execute(
|
||||
"""UPDATE accounts
|
||||
SET rar_test_enabled=1, rar_creds_test_enc='T',
|
||||
rar_prod_enabled=1, rar_creds_prod_enc='P',
|
||||
rar_env_default='prod'
|
||||
WHERE id=1"""
|
||||
)
|
||||
conn.commit()
|
||||
|
||||
|
||||
def _seteaza_cont_doar_prod(conn) -> None:
|
||||
"""Configureaza contul id=1 cu doar prod disponibil, test off."""
|
||||
conn.execute(
|
||||
"""UPDATE accounts
|
||||
SET rar_test_enabled=0, rar_creds_test_enc=NULL,
|
||||
rar_prod_enabled=1, rar_creds_prod_enc='P',
|
||||
rar_env_default='prod'
|
||||
WHERE id=1"""
|
||||
)
|
||||
conn.commit()
|
||||
|
||||
|
||||
def test_cerere_castiga(conn):
|
||||
"""Cererea explicita bate default-ul contului (prod)."""
|
||||
_seteaza_cont_ambele(conn)
|
||||
from app.rar_env import rezolva_rar_env
|
||||
rezultat = rezolva_rar_env(conn, 1, "test")
|
||||
assert rezultat == "test"
|
||||
|
||||
|
||||
def test_fallback_default_cont(conn):
|
||||
"""Fara cerere explicita -> default-ul contului (prod)."""
|
||||
_seteaza_cont_ambele(conn)
|
||||
from app.rar_env import rezolva_rar_env
|
||||
rezultat = rezolva_rar_env(conn, 1, None)
|
||||
assert rezultat == "prod"
|
||||
|
||||
|
||||
def test_tinta_indisponibila_respinsa(conn):
|
||||
"""Cerere pentru 'test' pe un cont doar-prod -> MediuIndisponibil cu .disponibile=['prod']."""
|
||||
_seteaza_cont_doar_prod(conn)
|
||||
from app.rar_env import MediuIndisponibil, rezolva_rar_env
|
||||
with pytest.raises(MediuIndisponibil) as exc_info:
|
||||
rezolva_rar_env(conn, 1, "test")
|
||||
err = exc_info.value
|
||||
assert err.env == "test"
|
||||
assert err.disponibile == ["prod"]
|
||||
|
||||
|
||||
def test_valoare_invalida(conn):
|
||||
"""Cerere cu valoare in afara VALID_ENVS -> ValueError, fara fallback silentios."""
|
||||
from app.rar_env import rezolva_rar_env
|
||||
with pytest.raises(ValueError, match="mediu invalid"):
|
||||
rezolva_rar_env(conn, 1, "staging")
|
||||
|
||||
|
||||
def test_zero_medii_cade_pe_ancora(conn, monkeypatch):
|
||||
"""Cont fara niciun mediu disponibil -> ancora globala AUTOPASS_RAR_ENV."""
|
||||
# id=1 din fresh DB: rar_prod_enabled=1 dar rar_creds_prod_enc=NULL -> 0 disponibile
|
||||
# (valoarea implicita a schemei: prod enabled fara creds -> nedisponibil)
|
||||
monkeypatch.setenv("AUTOPASS_RAR_ENV", "test")
|
||||
from app.config import get_settings
|
||||
get_settings.cache_clear()
|
||||
from app.rar_env import rezolva_rar_env
|
||||
rezultat = rezolva_rar_env(conn, 1, None)
|
||||
assert rezultat == "test"
|
||||
@@ -39,7 +39,7 @@ def env(monkeypatch):
|
||||
class FakeRar:
|
||||
"""Stub RarClient pentru teste."""
|
||||
|
||||
def __init__(self, settings=None):
|
||||
def __init__(self, settings=None, *, base_url=None):
|
||||
self.login_calls = 0
|
||||
self.closed = False
|
||||
|
||||
|
||||
@@ -42,10 +42,10 @@ class _FakeSessions:
|
||||
self.invalidated: list[int] = []
|
||||
self.tokens: list[int] = []
|
||||
|
||||
def invalidate(self, account_id: int) -> None:
|
||||
def invalidate(self, account_id: int, rar_env=None) -> None:
|
||||
self.invalidated.append(account_id)
|
||||
|
||||
def get_token(self, conn, account_id: int, creds) -> str | None:
|
||||
def get_token(self, conn, account_id: int, creds, rar_env="test") -> str | None:
|
||||
self.tokens.append(account_id)
|
||||
if self._fail:
|
||||
raise RuntimeError("RAR jos")
|
||||
|
||||
@@ -63,7 +63,7 @@ class FakeRar:
|
||||
def test_login_reusit_logat(env, monkeypatch):
|
||||
conn, settings = env
|
||||
import app.worker.__main__ as w
|
||||
monkeypatch.setattr(w, "RarClient", lambda s: FakeRar())
|
||||
monkeypatch.setattr(w, "RarClient", lambda s, **kw: FakeRar())
|
||||
sessions = w.AccountSessions(settings)
|
||||
tok = sessions.get_token(conn, 2, {"email": "a@b.ro", "password": "secretaXY"})
|
||||
assert tok == "JWT-TEST"
|
||||
@@ -80,7 +80,7 @@ def test_login_reusit_logat(env, monkeypatch):
|
||||
def test_login_401_logat_fara_parola(env, monkeypatch):
|
||||
conn, settings = env
|
||||
import app.worker.__main__ as w
|
||||
monkeypatch.setattr(w, "RarClient", lambda s: FakeRar(login_exc=RarAuthError("401", status_code=401)))
|
||||
monkeypatch.setattr(w, "RarClient", lambda s, **kw: FakeRar(login_exc=RarAuthError("401", status_code=401)))
|
||||
sessions = w.AccountSessions(settings)
|
||||
with pytest.raises(RarAuthError):
|
||||
sessions.get_token(conn, 3, {"email": "a@b.ro", "password": "parolaGRESITA"})
|
||||
|
||||
326
tests/test_worker_rar_env.py
Normal file
326
tests/test_worker_rar_env.py
Normal file
@@ -0,0 +1,326 @@
|
||||
"""Teste US-006 (PRD 5.20) — sesiuni si trimitere worker per (cont, env).
|
||||
|
||||
Verifica:
|
||||
- AccountSessions re-cheiat pe (account_id, rar_env): doua env ale aceluiasi cont
|
||||
au sesiuni distincte.
|
||||
- RarClient creat cu base_url-ul mediului (test -> rar_base_url_test,
|
||||
prod -> rar_base_url_prod), nu ancora globala.
|
||||
- Creds extrase din slotul accounts.rar_creds_{env}_enc corect per env.
|
||||
- Purjarea creds efemere scoped pe (account_id, rar_env): login pe test NU sterge
|
||||
creds efemere ale submission-urilor PROD ale aceluiasi cont (auto-fix E1/1a).
|
||||
- recover_orphans per (cont, env): orfanii prod reconciliati contra endpoint prod,
|
||||
nu contra test (auto-fix 1b/E6).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
import pytest
|
||||
from cryptography.fernet import Fernet
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Fixture DB
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@pytest.fixture()
|
||||
def env(monkeypatch):
|
||||
tmp = tempfile.mkdtemp()
|
||||
monkeypatch.setenv("AUTOPASS_DB_PATH", os.path.join(tmp, "t.db"))
|
||||
monkeypatch.setenv("AUTOPASS_CREDS_KEY", Fernet.generate_key().decode())
|
||||
monkeypatch.setenv("AUTOPASS_WORKER_USE_TEST_CREDS", "false")
|
||||
monkeypatch.setenv("AUTOPASS_REQUIRE_API_KEY", "false")
|
||||
from app.config import get_settings
|
||||
from app import crypto
|
||||
get_settings.cache_clear()
|
||||
crypto.reset_cache()
|
||||
from app.db import get_connection, init_db
|
||||
init_db()
|
||||
conn = get_connection()
|
||||
settings = get_settings()
|
||||
yield conn, settings
|
||||
conn.close()
|
||||
get_settings.cache_clear()
|
||||
crypto.reset_cache()
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Helpers
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
_CONTENT = {
|
||||
"vin": "WVWZZZ1KZAW000123", "nr_inmatriculare": "B999TST",
|
||||
"data_prestatie": "2026-06-15", "odometru_final": "123456",
|
||||
"prestatii": [{"cod_prestatie": "OE-1"}], "sistem_reparat": "null",
|
||||
}
|
||||
|
||||
|
||||
def _insert_sub(conn, account_id=1, rar_env="test", creds_enc=None, status="queued"):
|
||||
"""Insereaza un submission cu env si creds explicite."""
|
||||
content = _CONTENT.copy()
|
||||
cur = conn.execute(
|
||||
"INSERT INTO submissions "
|
||||
"(idempotency_key, account_id, status, payload_json, rar_env, rar_creds_enc) "
|
||||
"VALUES (?, ?, ?, ?, ?, ?)",
|
||||
(f"k-{os.urandom(4).hex()}", account_id, status, json.dumps(content), rar_env, creds_enc),
|
||||
)
|
||||
return int(cur.lastrowid)
|
||||
|
||||
|
||||
def _row(conn, sid):
|
||||
return conn.execute("SELECT * FROM submissions WHERE id=?", (sid,)).fetchone()
|
||||
|
||||
|
||||
# Captura base_url-urilor clientilor creati de AccountSessions
|
||||
_created_clients: list = []
|
||||
|
||||
|
||||
class FakeRarClient:
|
||||
"""RarClient stub care captura base_url-ul pentru assertii."""
|
||||
|
||||
def __init__(self, settings=None, *, base_url=None, login_exc=None):
|
||||
self.base_url = base_url
|
||||
self._login_exc = login_exc
|
||||
self.login_calls = 0
|
||||
self.closed = False
|
||||
_created_clients.append(self)
|
||||
|
||||
def login(self, email, password):
|
||||
self.login_calls += 1
|
||||
if self._login_exc:
|
||||
raise self._login_exc
|
||||
return f"TOK-{email}-{self.base_url}"
|
||||
|
||||
def get_nomenclator(self, token):
|
||||
return []
|
||||
|
||||
def close(self):
|
||||
self.closed = True
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# test_sesiune_separata_per_env
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def test_sesiune_separata_per_env(env, monkeypatch):
|
||||
"""Doua submission-uri ale aceluiasi cont, env test + prod -> doua login-uri distincte.
|
||||
|
||||
Cheia sesiunii e (account_id, rar_env): sesiunile test si prod sunt independente.
|
||||
"""
|
||||
import app.worker.__main__ as w
|
||||
|
||||
_created_clients.clear()
|
||||
monkeypatch.setattr(w, "RarClient", FakeRarClient)
|
||||
|
||||
conn, settings = env
|
||||
# Cont secundar (contul 1 e default din schema)
|
||||
conn.execute("INSERT INTO accounts (id, name) VALUES (2, 'Cont2')")
|
||||
conn.commit()
|
||||
|
||||
sessions = w.AccountSessions(settings)
|
||||
|
||||
creds_test = {"email": "test@example.ro", "password": "ptest"}
|
||||
creds_prod = {"email": "prod@example.ro", "password": "pprod"}
|
||||
|
||||
tok_test = sessions.get_token(conn, 2, creds_test, "test")
|
||||
tok_prod = sessions.get_token(conn, 2, creds_prod, "prod")
|
||||
|
||||
# Doua login-uri distincte
|
||||
assert len(_created_clients) == 2
|
||||
assert _created_clients[0].login_calls == 1
|
||||
assert _created_clients[1].login_calls == 1
|
||||
|
||||
# Tokenuri distincte (de la email-uri diferite)
|
||||
assert tok_test != tok_prod
|
||||
|
||||
# Sesiunile active: doua intrari, ambele pt cont 2, env diferite
|
||||
active = sessions.active()
|
||||
assert len(active) == 2
|
||||
envs_active = {env for _, env, _, _ in active}
|
||||
assert envs_active == {"test", "prod"}
|
||||
|
||||
# Al doilea apel cu acelasi (cont, env) -> cache, NU re-login
|
||||
tok_test2 = sessions.get_token(conn, 2, creds_test, "test")
|
||||
assert tok_test2 == tok_test
|
||||
assert len(_created_clients) == 2 # niciun client nou creat
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# test_base_url_dupa_submission
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def test_base_url_dupa_submission(env, monkeypatch):
|
||||
"""Un submission prod foloseste rar_base_url_prod; un submission test foloseste rar_base_url_test."""
|
||||
import app.worker.__main__ as w
|
||||
|
||||
_created_clients.clear()
|
||||
monkeypatch.setattr(w, "RarClient", FakeRarClient)
|
||||
|
||||
conn, settings = env
|
||||
sessions = w.AccountSessions(settings)
|
||||
|
||||
creds = {"email": "x@example.ro", "password": "pw"}
|
||||
sessions.get_token(conn, 1, creds, "test")
|
||||
sessions.get_token(conn, 1, creds, "prod")
|
||||
|
||||
urls = {c.base_url for c in _created_clients}
|
||||
assert settings.rar_base_url_test in urls, f"URL test asteptat in {urls}"
|
||||
assert settings.rar_base_url_prod in urls, f"URL prod asteptat in {urls}"
|
||||
# Cele doua URL-uri trebuie sa fie diferite (sisteme RAR separate)
|
||||
assert settings.rar_base_url_test != settings.rar_base_url_prod
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# test_creds_din_slotul_env
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def test_creds_din_slotul_env(env, monkeypatch):
|
||||
"""Cand submissions.rar_creds_enc lipseste, worker ia din accounts.rar_creds_{env}_enc.
|
||||
|
||||
Prod ia din rar_creds_prod_enc, nu din slotul test (auto-fix 1c/E8 + fallback per-env).
|
||||
"""
|
||||
import app.worker.__main__ as w
|
||||
from app.crypto import encrypt_creds
|
||||
|
||||
conn, settings = env
|
||||
|
||||
enc_test = encrypt_creds({"email": "test@rar.ro", "password": "ptest"})
|
||||
enc_prod = encrypt_creds({"email": "prod@rar.ro", "password": "pprod"})
|
||||
|
||||
# Salveaza creds in ambele sloturi per-env
|
||||
conn.execute(
|
||||
"UPDATE accounts SET rar_creds_test_enc=?, rar_creds_prod_enc=? WHERE id=1",
|
||||
(enc_test, enc_prod),
|
||||
)
|
||||
conn.commit()
|
||||
|
||||
# Fara creds efemere pe submission -> fallback la slotul per-env
|
||||
creds_test = w._creds_from_account(conn, 1, "test")
|
||||
creds_prod = w._creds_from_account(conn, 1, "prod")
|
||||
|
||||
assert creds_test is not None, "slotul test trebuia sa aiba creds"
|
||||
assert creds_test["email"] == "test@rar.ro"
|
||||
|
||||
assert creds_prod is not None, "slotul prod trebuia sa aiba creds"
|
||||
assert creds_prod["email"] == "prod@rar.ro"
|
||||
|
||||
# Crucialmente: prod NU ia creds din slotul test
|
||||
assert creds_prod["email"] != creds_test["email"]
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# test_purge_creds_doar_pe_env (auto-fix E1/1a)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def test_purge_creds_doar_pe_env(env, monkeypatch):
|
||||
"""Dupa login pe env=test, creds efemere ale submission-urilor PROD raman neatinse.
|
||||
|
||||
Scopul purjarii: WHERE account_id=? AND rar_env=?. Altfel un login TEST sterge
|
||||
creds ale submission-urilor PROD -> prod blocat (auto-fix E1/1a).
|
||||
"""
|
||||
import app.worker.__main__ as w
|
||||
from app.crypto import encrypt_creds
|
||||
|
||||
_created_clients.clear()
|
||||
monkeypatch.setattr(w, "RarClient", FakeRarClient)
|
||||
|
||||
conn, settings = env
|
||||
|
||||
enc = encrypt_creds({"email": "u@rar.ro", "password": "pw"})
|
||||
|
||||
# Doua submission-uri ale aceluiasi cont: unul test, unul prod (ambele cu creds efemere)
|
||||
sid_test = _insert_sub(conn, account_id=1, rar_env="test", creds_enc=enc)
|
||||
sid_prod = _insert_sub(conn, account_id=1, rar_env="prod", creds_enc=enc)
|
||||
|
||||
sessions = w.AccountSessions(settings)
|
||||
|
||||
# Login pe env=test
|
||||
sessions.get_token(conn, 1, {"email": "u@rar.ro", "password": "pw"}, "test")
|
||||
|
||||
# Creds efemere ale submission-ului TEST trebuie sterse (purjare normala)
|
||||
row_test = _row(conn, sid_test)
|
||||
assert row_test["rar_creds_enc"] is None, "creds test trebuiau sterse dupa login test"
|
||||
|
||||
# Creds efemere ale submission-ului PROD trebuie PASTRATE (nu sunt pentru env=test)
|
||||
row_prod = _row(conn, sid_prod)
|
||||
assert row_prod["rar_creds_enc"] is not None, \
|
||||
"creds prod NU trebuiau sterse la login test (auto-fix E1/1a)"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# test_reconcile_pe_env_corect (auto-fix 1b/E6)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def test_reconcile_pe_env_corect(env, monkeypatch):
|
||||
"""Un orfan env=prod e reconciliat contra endpoint PROD, nu contra test.
|
||||
|
||||
auto-fix 1b/E6: recover_orphans filtreaza pe rar_env si foloseste clientul/token-ul
|
||||
env-ului corect. Orfanii prod contra endpoint test -> no-match -> re-POST prod =
|
||||
DUPLICAT real ireversibil.
|
||||
"""
|
||||
import app.worker.__main__ as w
|
||||
|
||||
conn, settings = env
|
||||
|
||||
# Submission prod orfan (sending de mult timp)
|
||||
sid_prod = _insert_sub(conn, account_id=1, rar_env="prod", status="sending")
|
||||
conn.execute(
|
||||
"UPDATE submissions SET sending_since=datetime('now', '-1 hour') WHERE id=?", (sid_prod,)
|
||||
)
|
||||
conn.commit()
|
||||
|
||||
# Submission test orfan (de verificat ca NU e atins de recover_orphans(rar_env='prod'))
|
||||
sid_test = _insert_sub(conn, account_id=1, rar_env="test", status="sending")
|
||||
conn.execute(
|
||||
"UPDATE submissions SET sending_since=datetime('now', '-1 hour') WHERE id=?", (sid_test,)
|
||||
)
|
||||
conn.commit()
|
||||
|
||||
# Clientul prod fake — "gaseste" prezentarea prod la RAR
|
||||
class FakeProdRar:
|
||||
def __init__(self):
|
||||
self.get_finalizate_calls = 0
|
||||
|
||||
def get_finalizate(self, token):
|
||||
self.get_finalizate_calls += 1
|
||||
return [{"id": 9999, "vin": "WVWZZZ1KZAW000123",
|
||||
"dataPrestatie": "2026-06-15", "odometruFinal": 123456}]
|
||||
|
||||
def post_prezentare(self, token, payload):
|
||||
return {"id": 9999}
|
||||
|
||||
# Clientul test fake — nu gaseste nimic (sistemul test nu are prezentarea)
|
||||
class FakeTestRar:
|
||||
def __init__(self):
|
||||
self.get_finalizate_calls = 0
|
||||
|
||||
def get_finalizate(self, token):
|
||||
self.get_finalizate_calls += 1
|
||||
return [] # nu e la RAR test
|
||||
|
||||
def post_prezentare(self, token, payload):
|
||||
return {"id": 1111}
|
||||
|
||||
rar_prod = FakeProdRar()
|
||||
rar_test = FakeTestRar()
|
||||
|
||||
# Apelam recover_orphans cu clientul PROD si env='prod' -> trebuie sa gaseasca orfanul prod
|
||||
n = w.recover_orphans(conn, settings, rar_prod, "tok-prod", account_id=1, rar_env="prod")
|
||||
assert n == 1, f"trebuia sa reconcilieze 1 orfan prod, a gasit {n}"
|
||||
|
||||
row_prod = _row(conn, sid_prod)
|
||||
assert row_prod["status"] == "sent", f"orfanul prod trebuia marcat sent, e {row_prod['status']}"
|
||||
assert row_prod["id_prezentare"] == 9999
|
||||
|
||||
# Submission-ul TEST nu trebuia atins de recover_orphans cu rar_env='prod'
|
||||
row_test = _row(conn, sid_test)
|
||||
assert row_test["status"] == "sending", \
|
||||
f"orfanul test NU trebuia atins de recover cu env=prod, e {row_test['status']}"
|
||||
|
||||
# Confirmare ca clientul prod a interogat finalizate (reconciliere pe endpoint corect)
|
||||
assert rar_prod.get_finalizate_calls == 1
|
||||
# Clientul test NU trebuia folosit (recover_orphans cu env=prod NU atinge endpoint test)
|
||||
assert rar_test.get_finalizate_calls == 0
|
||||
Reference in New Issue
Block a user