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"
|
||||
Reference in New Issue
Block a user