"""Teste scope cont pe GET /v1/prezentari + /{id} (US-001, PRD 3.2). Metoda TDD: testele se scriu inainte de implementare (RED) si trebuie sa ramana verzi dupa implementare (GREEN). """ from __future__ import annotations import os import tempfile import pytest from fastapi.testclient import TestClient @pytest.fixture() def env(monkeypatch): 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(**over): prez = { "vin": "WVWZZZ1KZAW000123", "nr_inmatriculare": "B999TST", "data_prestatie": "2026-06-15", "odometru_final": "123456", "prestatii": [{"cod_prestatie": "OE-1"}], } prez.update(over) return {"rar_credentials": {"email": "x@y.ro", "password": "s"}, "prezentari": [prez]} def test_lista_doar_contul_cheii(env): """Cheia A vede doar submission-urile contului A; cheia B nu vede submission-urile lui A.""" with _client() as c: from app.auth import create_api_key from app.db import get_connection conn = get_connection() try: # tier='pro' pe ambele conturi — testul verifica scoping GET, nu planuri (T4 PRD 5.17). conn.execute("UPDATE accounts SET tier='pro' WHERE id=1") conn.execute("INSERT INTO accounts (id, name, tier) VALUES (2, 'al-doilea', 'pro')") k1 = create_api_key(conn, 1) k2 = create_api_key(conn, 2) finally: conn.close() r1 = c.post("/v1/prezentari", json=_body(), headers={"X-API-Key": k1}) assert r1.status_code == 200 sid1 = r1.json()["results"][0]["submission_id"] r2 = c.post("/v1/prezentari", json=_body(vin="WVWZZZ1KZAW000456"), headers={"X-API-Key": k2}) assert r2.status_code == 200 sid2 = r2.json()["results"][0]["submission_id"] lista1 = c.get("/v1/prezentari", headers={"X-API-Key": k1}).json()["submissions"] ids1 = [s["id"] for s in lista1] assert sid1 in ids1 assert sid2 not in ids1 lista2 = c.get("/v1/prezentari", headers={"X-API-Key": k2}).json()["submissions"] ids2 = [s["id"] for s in lista2] assert sid2 in ids2 assert sid1 not in ids2 def test_detaliu_cross_account_404(env): """GET /{id} cu cheia contului B pentru submission-ul contului A -> 404. B3: detail-ul 404 cross-account trebuie byte-identic cu cel al unui id inexistent (acelasi status + acelasi mesaj) — nu dam indicii ca randul exista. """ with _client() as c: from app.auth import create_api_key from app.db import get_connection conn = get_connection() try: conn.execute("INSERT INTO accounts (id, name) VALUES (2, 'al-doilea')") k1 = create_api_key(conn, 1) k2 = create_api_key(conn, 2) finally: conn.close() r = c.post("/v1/prezentari", json=_body(), headers={"X-API-Key": k1}) sid1 = r.json()["results"][0]["submission_id"] cross = c.get(f"/v1/prezentari/{sid1}", headers={"X-API-Key": k2}) nonexist = c.get("/v1/prezentari/99999", headers={"X-API-Key": k2}) assert cross.status_code == 404 assert nonexist.status_code == 404 assert cross.json()["detail"] == nonexist.json()["detail"] == "submission inexistent" def test_legacy_null_vizibil_pentru_cont_1(env): """Randuri cu account_id=NULL apartin contului 1 (legacy OV-2); contul 2 nu le vede.""" with _client() as c: from app.auth import create_api_key from app.db import get_connection conn = get_connection() try: conn.execute("INSERT INTO accounts (id, name) VALUES (2, 'al-doilea')") k1 = create_api_key(conn, 1) k2 = create_api_key(conn, 2) conn.execute( "INSERT INTO submissions (idempotency_key, account_id, status, payload_json) " "VALUES ('legacy_null_key', NULL, 'queued', '{}')" ) finally: conn.close() lista1 = c.get("/v1/prezentari", headers={"X-API-Key": k1}).json()["submissions"] lista2 = c.get("/v1/prezentari", headers={"X-API-Key": k2}).json()["submissions"] assert len(lista1) >= 1 assert len(lista2) == 0 def test_fara_cheie_flag_off_vede_contul_1(env): """Fara cheie cu AUTOPASS_REQUIRE_API_KEY=false -> cont implicit (id=1, back-compat dev).""" with _client() as c: from app.auth import create_api_key from app.db import get_connection conn = get_connection() try: conn.execute("INSERT INTO accounts (id, name) VALUES (2, 'alt')") k2 = create_api_key(conn, 2) finally: conn.close() # Submission pentru contul 1 (fara cheie, flag off -> cont implicit) c.post("/v1/prezentari", json=_body()) # Submission pentru contul 2 c.post("/v1/prezentari", json=_body(vin="WVWZZZ1KZAW000456"), headers={"X-API-Key": k2}) # Fara cheie -> vede DOAR contul 1 (1 submission) lista = c.get("/v1/prezentari").json()["submissions"] assert len(lista) == 1 def test_detaliu_nu_expune_creds(env): """B4: GET /v1/prezentari/{id} nu expune campuri sensibile (rar_creds_enc, payload_json, idempotency_key). NOTA T9 (PRD 5.6): `rar_error` e ACUM expus intentionat (recovery API observabil) — contine doar coduri/mesaje de validare RAR, niciodata creds. """ with _client() as c: from app.auth import create_api_key from app.db import get_connection conn = get_connection() try: k1 = create_api_key(conn, 1) finally: conn.close() r = c.post("/v1/prezentari", json=_body(), headers={"X-API-Key": k1}) sid = r.json()["results"][0]["submission_id"] resp = c.get(f"/v1/prezentari/{sid}", headers={"X-API-Key": k1}) assert resp.status_code == 200 data = resp.json() for field in ("rar_creds_enc", "payload_json", "idempotency_key"): assert field not in data, f"camp sensibil expus: {field}"