Files
rar-autopass/tests/test_worker_rar_env.py
Claude Agent 19d8aaa7aa 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>
2026-06-29 20:30:11 +00:00

327 lines
12 KiB
Python

"""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