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